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"
13 # define getpid _getpid
15 # include <unistd.h> // for getpid()
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/AutoRestore.h"
21 #include "mozilla/BasePrincipal.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/CheckedInt.h"
24 #include "mozilla/Components.h"
25 #include "mozilla/DebugOnly.h"
26 #include "mozilla/Encoding.h"
27 #include "mozilla/EventStateManager.h"
28 #include "mozilla/HTMLEditor.h"
29 #include "mozilla/InputTaskManager.h"
30 #include "mozilla/LoadInfo.h"
31 #include "mozilla/Logging.h"
32 #include "mozilla/MediaFeatureChange.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/PresShell.h"
35 #include "mozilla/ResultExtensions.h"
36 #include "mozilla/SchedulerGroup.h"
37 #include "mozilla/ScopeExit.h"
38 #include "mozilla/ScrollTypes.h"
39 #include "mozilla/SimpleEnumerator.h"
40 #include "mozilla/StaticPrefs_browser.h"
41 #include "mozilla/StaticPrefs_docshell.h"
42 #include "mozilla/StaticPrefs_dom.h"
43 #include "mozilla/StaticPrefs_extensions.h"
44 #include "mozilla/StaticPrefs_privacy.h"
45 #include "mozilla/StaticPrefs_security.h"
46 #include "mozilla/StaticPrefs_ui.h"
47 #include "mozilla/StaticPrefs_fission.h"
48 #include "mozilla/StartupTimeline.h"
49 #include "mozilla/StorageAccess.h"
50 #include "mozilla/StoragePrincipalHelper.h"
51 #include "mozilla/Telemetry.h"
53 #include "mozilla/Unused.h"
54 #include "mozilla/WidgetUtils.h"
56 #include "mozilla/dom/AutoEntryScript.h"
57 #include "mozilla/dom/ChildProcessChannelListener.h"
58 #include "mozilla/dom/ClientChannelHelper.h"
59 #include "mozilla/dom/ClientHandle.h"
60 #include "mozilla/dom/ClientInfo.h"
61 #include "mozilla/dom/ClientManager.h"
62 #include "mozilla/dom/ClientSource.h"
63 #include "mozilla/dom/ContentChild.h"
64 #include "mozilla/dom/ContentFrameMessageManager.h"
65 #include "mozilla/dom/DocGroup.h"
66 #include "mozilla/dom/Element.h"
67 #include "mozilla/dom/HTMLAnchorElement.h"
68 #include "mozilla/dom/HTMLIFrameElement.h"
69 #include "mozilla/dom/PerformanceNavigation.h"
70 #include "mozilla/dom/PermissionMessageUtils.h"
71 #include "mozilla/dom/PopupBlocker.h"
72 #include "mozilla/dom/ScreenOrientation.h"
73 #include "mozilla/dom/ScriptSettings.h"
74 #include "mozilla/dom/ServiceWorkerInterceptController.h"
75 #include "mozilla/dom/ServiceWorkerUtils.h"
76 #include "mozilla/dom/SessionHistoryEntry.h"
77 #include "mozilla/dom/SessionStorageManager.h"
78 #include "mozilla/dom/SessionStoreChangeListener.h"
79 #include "mozilla/dom/SessionStoreChild.h"
80 #include "mozilla/dom/SessionStoreUtils.h"
81 #include "mozilla/dom/BrowserChild.h"
82 #include "mozilla/dom/ToJSValue.h"
83 #include "mozilla/dom/UserActivation.h"
84 #include "mozilla/dom/ChildSHistory.h"
85 #include "mozilla/dom/nsCSPContext.h"
86 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
87 #include "mozilla/dom/LoadURIOptionsBinding.h"
88 #include "mozilla/dom/JSWindowActorChild.h"
89 #include "mozilla/dom/DocumentBinding.h"
90 #include "mozilla/ipc/ProtocolUtils.h"
91 #include "mozilla/net/DocumentChannel.h"
92 #include "mozilla/net/DocumentChannelChild.h"
93 #include "mozilla/net/ParentChannelWrapper.h"
94 #include "mozilla/net/UrlClassifierFeatureFactory.h"
95 #include "ReferrerInfo.h"
97 #include "nsIAuthPrompt.h"
98 #include "nsIAuthPrompt2.h"
99 #include "nsICachingChannel.h"
100 #include "nsICaptivePortalService.h"
101 #include "nsIChannel.h"
102 #include "nsIChannelEventSink.h"
103 #include "nsIClassOfService.h"
104 #include "nsIConsoleReportCollector.h"
105 #include "nsIContent.h"
106 #include "nsIContentInlines.h"
107 #include "nsIContentSecurityPolicy.h"
108 #include "nsIController.h"
109 #include "nsIDocShellTreeItem.h"
110 #include "nsIDocShellTreeOwner.h"
111 #include "nsIDocumentViewer.h"
112 #include "mozilla/dom/Document.h"
113 #include "nsHTMLDocument.h"
114 #include "nsIDocumentLoaderFactory.h"
115 #include "nsIDOMWindow.h"
116 #include "nsIEditingSession.h"
117 #include "nsIEffectiveTLDService.h"
118 #include "nsIExternalProtocolService.h"
119 #include "nsIFormPOSTActionChannel.h"
120 #include "nsIFrame.h"
121 #include "nsIGlobalObject.h"
122 #include "nsIHttpChannel.h"
123 #include "nsIHttpChannelInternal.h"
124 #include "nsIIDNService.h"
125 #include "nsIInputStreamChannel.h"
126 #include "nsIInterfaceRequestorUtils.h"
127 #include "nsILayoutHistoryState.h"
128 #include "nsILoadInfo.h"
129 #include "nsILoadURIDelegate.h"
130 #include "nsIMultiPartChannel.h"
131 #include "nsINestedURI.h"
132 #include "nsINetworkPredictor.h"
134 #include "nsINSSErrorsService.h"
135 #include "nsIObserverService.h"
136 #include "nsIOService.h"
137 #include "nsIPrincipal.h"
138 #include "nsIPrivacyTransitionObserver.h"
139 #include "nsIPrompt.h"
140 #include "nsIPromptCollection.h"
141 #include "nsIPromptFactory.h"
142 #include "nsIPublicKeyPinningService.h"
143 #include "nsIReflowObserver.h"
144 #include "nsIScriptChannel.h"
145 #include "nsIScriptObjectPrincipal.h"
146 #include "nsIScriptSecurityManager.h"
147 #include "nsIScrollableFrame.h"
148 #include "nsIScrollObserver.h"
149 #include "nsISupportsPrimitives.h"
150 #include "nsISecureBrowserUI.h"
151 #include "nsISeekableStream.h"
152 #include "nsISelectionDisplay.h"
153 #include "nsISHEntry.h"
154 #include "nsISiteSecurityService.h"
155 #include "nsISocketProvider.h"
156 #include "nsIStringBundle.h"
157 #include "nsIStructuredCloneContainer.h"
158 #include "nsIBrowserChild.h"
159 #include "nsITextToSubURI.h"
160 #include "nsITimedChannel.h"
161 #include "nsITimer.h"
162 #include "nsITransportSecurityInfo.h"
163 #include "nsIUploadChannel.h"
164 #include "nsIURIFixup.h"
165 #include "nsIURIMutator.h"
166 #include "nsIURILoader.h"
167 #include "nsIViewSourceChannel.h"
168 #include "nsIWebBrowserChrome.h"
169 #include "nsIWebBrowserChromeFocus.h"
170 #include "nsIWebBrowserFind.h"
171 #include "nsIWebProgress.h"
172 #include "nsIWidget.h"
173 #include "nsIWindowWatcher.h"
174 #include "nsIWritablePropertyBag2.h"
175 #include "nsIX509Cert.h"
176 #include "nsIXULRuntime.h"
178 #include "nsCommandManager.h"
179 #include "nsPIDOMWindow.h"
180 #include "nsPIWindowRoot.h"
182 #include "IHistory.h"
183 #include "IUrlClassifierUITelemetry.h"
186 #include "nsArrayUtils.h"
187 #include "nsCExternalHandlerService.h"
188 #include "nsContentDLF.h"
189 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
190 #include "nsContentSecurityManager.h"
191 #include "nsContentSecurityUtils.h"
192 #include "nsContentUtils.h"
193 #include "nsCURILoader.h"
194 #include "nsDocShellCID.h"
195 #include "nsDocShellEditorData.h"
196 #include "nsDocShellEnumerator.h"
197 #include "nsDocShellLoadState.h"
198 #include "nsDocShellLoadTypes.h"
199 #include "nsDOMCID.h"
200 #include "nsDOMNavigationTiming.h"
201 #include "nsDSURIContentListener.h"
202 #include "nsEditingSession.h"
204 #include "nsEscape.h"
205 #include "nsFocusManager.h"
206 #include "nsGlobalWindowInner.h"
207 #include "nsGlobalWindowOuter.h"
208 #include "nsJSEnvironment.h"
209 #include "nsNetCID.h"
210 #include "nsNetUtil.h"
211 #include "nsObjectLoadingContent.h"
212 #include "nsPingListener.h"
214 #include "nsQueryObject.h"
215 #include "nsQueryActor.h"
217 #include "nsRefreshTimer.h"
218 #include "nsSandboxFlags.h"
219 #include "nsSHEntry.h"
220 #include "nsSHistory.h"
221 #include "nsSHEntry.h"
222 #include "nsStructuredCloneContainer.h"
223 #include "nsSubDocumentFrame.h"
224 #include "nsURILoader.h"
225 #include "nsURLHelper.h"
227 #include "nsViewManager.h"
228 #include "nsViewSourceHandler.h"
229 #include "nsWebBrowserFind.h"
230 #include "nsWhitespaceTokenizer.h"
231 #include "nsWidgetsCID.h"
232 #include "nsXULAppAPI.h"
234 #include "ThirdPartyUtil.h"
235 #include "GeckoProfiler.h"
236 #include "mozilla/NullPrincipal.h"
237 #include "Navigator.h"
239 #include "mozilla/ipc/URIUtils.h"
241 #include "mozpkix/pkix.h"
242 #include "NSSErrorsService.h"
244 #include "nsDocShellTelemetryUtils.h"
247 # include "nsIFaviconService.h"
248 # include "mozIPlacesPendingOperation.h"
252 # include "nsIDocumentViewerPrint.h"
253 # include "nsIWebBrowserPrint.h"
256 using namespace mozilla
;
257 using namespace mozilla::dom
;
258 using namespace mozilla::net
;
260 using mozilla::ipc::Endpoint
;
262 // Threshold value in ms for META refresh based redirects
263 #define REFRESH_REDIRECT_TIMER 15000
265 static mozilla::LazyLogModule
gCharsetMenuLog("CharsetMenu");
267 #define LOGCHARSETMENU(args) \
268 MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args)
271 unsigned long nsDocShell::gNumberOfDocShells
= 0;
272 static uint64_t gDocshellIDCounter
= 0;
274 static mozilla::LazyLogModule
gDocShellLog("nsDocShell");
275 static mozilla::LazyLogModule
gDocShellAndDOMWindowLeakLogging(
276 "DocShellAndDOMWindowLeak");
278 static mozilla::LazyLogModule
gDocShellLeakLog("nsDocShellLeak");
279 extern mozilla::LazyLogModule gPageCacheLog
;
280 mozilla::LazyLogModule
gSHLog("SessionHistory");
281 extern mozilla::LazyLogModule gSHIPBFCacheLog
;
283 const char kAppstringsBundleURL
[] =
284 "chrome://global/locale/appstrings.properties";
286 static bool IsTopLevelDoc(BrowsingContext
* aBrowsingContext
,
287 nsILoadInfo
* aLoadInfo
) {
288 MOZ_ASSERT(aBrowsingContext
);
289 MOZ_ASSERT(aLoadInfo
);
291 if (aLoadInfo
->GetExternalContentPolicyType() !=
292 ExtContentPolicy::TYPE_DOCUMENT
) {
296 return aBrowsingContext
->IsTopContent();
299 // True if loading for top level document loading in active tab.
300 static bool IsUrgentStart(BrowsingContext
* aBrowsingContext
,
301 nsILoadInfo
* aLoadInfo
, uint32_t aLoadType
) {
302 MOZ_ASSERT(aBrowsingContext
);
303 MOZ_ASSERT(aLoadInfo
);
305 if (!IsTopLevelDoc(aBrowsingContext
, aLoadInfo
)) {
310 (nsIDocShell::LOAD_CMD_NORMAL
| nsIDocShell::LOAD_CMD_HISTORY
)) {
314 return aBrowsingContext
->IsActive();
317 nsDocShell::nsDocShell(BrowsingContext
* aBrowsingContext
,
318 uint64_t aContentWindowID
)
320 mContentWindowID(aContentWindowID
),
321 mBrowsingContext(aBrowsingContext
),
322 mParentCharset(nullptr),
324 mScrollbarPref(ScrollbarPreference::Auto
),
325 mCharsetReloadState(eCharsetReloadInit
),
326 mParentCharsetSource(0),
327 mFrameMargins(-1, -1),
328 mItemType(aBrowsingContext
->IsContent() ? typeContent
: typeChrome
),
329 mPreviousEntryIndex(-1),
330 mLoadedEntryIndex(-1),
331 mBusyFlags(BUSY_FLAGS_NONE
),
332 mAppType(nsIDocShell::APP_TYPE_UNKNOWN
),
335 mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE
),
336 mChannelToDisconnectOnPageHide(0),
337 mCreatingDocument(false),
339 mInEnsureScriptEnv(false),
342 mAllowSubframes(true),
343 mAllowMetaRedirects(true),
346 mAllowDNSPrefetch(true),
347 mAllowWindowControl(true),
348 mCSSErrorReportingEnabled(false),
349 mAllowAuth(mItemType
== typeContent
),
350 mAllowKeywordFixup(false),
351 mDisableMetaRefreshWhenInactive(false),
352 mWindowDraggingAllowed(false),
354 mFiredUnloadEvent(false),
355 mEODForCurrentDocument(false),
356 mURIResultedInDocument(false),
357 mIsBeingDestroyed(false),
358 mIsExecutingOnLoadHandler(false),
359 mSavingOldViewer(false),
361 mHasLoadedNonBlankURI(false),
363 mTitleValidForCurrentURI(false),
364 mWillChangeProcess(false),
365 mIsNavigating(false),
366 mForcedAutodetection(false),
367 mCheckingSessionHistory(false),
368 mNeedToReportActiveAfterLoadingBecomesActive(false) {
369 // If no outer window ID was provided, generate a new one.
370 if (aContentWindowID
== 0) {
371 mContentWindowID
= nsContentUtils::GenerateWindowId();
374 MOZ_LOG(gDocShellLeakLog
, LogLevel::Debug
, ("DOCSHELL %p created\n", this));
377 mDocShellID
= gDocshellIDCounter
++;
378 // We're counting the number of |nsDocShells| to help find leaks
379 ++gNumberOfDocShells
;
380 MOZ_LOG(gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
,
381 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64
"]\n", (void*)this,
382 gNumberOfDocShells
, getpid(), mDocShellID
));
386 nsDocShell::~nsDocShell() {
387 // Avoid notifying observers while we're in the dtor.
388 mIsBeingDestroyed
= true;
392 if (mDocumentViewer
) {
393 mDocumentViewer
->Close(nullptr);
394 mDocumentViewer
->Destroy();
395 mDocumentViewer
= nullptr;
398 MOZ_LOG(gDocShellLeakLog
, LogLevel::Debug
, ("DOCSHELL %p destroyed\n", this));
401 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
)) {
403 if (mLastOpenedURI
) {
404 url
= mLastOpenedURI
->GetSpecOrDefault();
406 // Data URLs can be very long, so truncate to avoid flooding the log.
407 const uint32_t maxURLLength
= 1000;
408 if (url
.Length() > maxURLLength
) {
409 url
.Truncate(maxURLLength
);
413 // We're counting the number of |nsDocShells| to help find leaks
414 --gNumberOfDocShells
;
416 gDocShellAndDOMWindowLeakLogging
, LogLevel::Info
,
417 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64
"] [url = %s]\n",
418 (void*)this, gNumberOfDocShells
, getpid(), mDocShellID
, url
.get()));
423 bool nsDocShell::Initialize() {
425 // We've already been initialized.
429 NS_ASSERTION(mItemType
== typeContent
|| mItemType
== typeChrome
,
430 "Unexpected item type in docshell");
432 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
435 mDisableMetaRefreshWhenInactive
=
436 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
437 mDisableMetaRefreshWhenInactive
);
439 if (nsCOMPtr
<nsIObserverService
> serv
= services::GetObserverService()) {
440 const char* msg
= mItemType
== typeContent
? NS_WEBNAVIGATION_CREATE
441 : NS_CHROME_WEBNAVIGATION_CREATE
;
442 serv
->NotifyWhenScriptSafe(GetAsSupports(this), msg
, nullptr);
449 already_AddRefed
<nsDocShell
> nsDocShell::Create(
450 BrowsingContext
* aBrowsingContext
, uint64_t aContentWindowID
) {
451 MOZ_ASSERT(aBrowsingContext
, "DocShell without a BrowsingContext!");
454 RefPtr
<nsDocShell
> ds
= new nsDocShell(aBrowsingContext
, aContentWindowID
);
456 // Initialize the underlying nsDocLoader.
457 rv
= ds
->nsDocLoader::InitWithBrowsingContext(aBrowsingContext
);
458 if (NS_WARN_IF(NS_FAILED(rv
))) {
462 // Create our ContentListener
463 ds
->mContentListener
= new nsDSURIContentListener(ds
);
465 // We enable if we're in the parent process in order to support non-e10s
467 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
469 if (XRE_IsParentProcess()) {
470 ds
->mInterceptController
= new ServiceWorkerInterceptController();
473 // We want to hold a strong ref to the loadgroup, so it better hold a weak
474 // ref to us... use an InterfaceRequestorProxy to do this.
475 nsCOMPtr
<nsIInterfaceRequestor
> proxy
= new InterfaceRequestorProxy(ds
);
476 ds
->mLoadGroup
->SetNotificationCallbacks(proxy
);
478 // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
479 // It could be nice to directly set up our DocLoader tree?
480 rv
= nsDocLoader::AddDocLoaderAsChildOfRoot(ds
);
481 if (NS_WARN_IF(NS_FAILED(rv
))) {
485 // Add |ds| as a progress listener to itself. A little weird, but simpler
486 // than reproducing all the listener-notification logic in overrides of the
487 // various methods via which nsDocLoader can be notified. Note that this
488 // holds an nsWeakPtr to |ds|, so it's ok.
489 rv
= ds
->AddProgressListener(ds
, nsIWebProgress::NOTIFY_STATE_DOCUMENT
|
490 nsIWebProgress::NOTIFY_STATE_NETWORK
|
491 nsIWebProgress::NOTIFY_LOCATION
);
492 if (NS_WARN_IF(NS_FAILED(rv
))) {
496 // If our BrowsingContext has private browsing enabled, update the number of
497 // private browsing docshells.
498 if (aBrowsingContext
->UsePrivateBrowsing()) {
499 ds
->NotifyPrivateBrowsingChanged();
502 // If our parent window is present in this process, set up our parent now.
503 RefPtr
<WindowContext
> parentWC
= aBrowsingContext
->GetParentWindowContext();
504 if (parentWC
&& parentWC
->IsInProcess()) {
505 // If we don't have a parent element anymore, we can't finish this load!
506 // How'd we get here?
507 RefPtr
<Element
> parentElement
= aBrowsingContext
->GetEmbedderElement();
508 if (!parentElement
) {
509 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
513 // We have an in-process parent window, but don't have a parent nsDocShell?
514 // How'd we get here!
515 nsCOMPtr
<nsIDocShell
> parentShell
=
516 parentElement
->OwnerDoc()->GetDocShell();
518 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
521 parentShell
->AddChild(ds
);
524 // Make |ds| the primary DocShell for the given context.
525 aBrowsingContext
->SetDocShell(ds
);
527 // Set |ds| default load flags on load group.
528 ds
->SetLoadGroupDefaultLoadFlags(aBrowsingContext
->GetDefaultLoadFlags());
530 if (XRE_IsParentProcess()) {
531 aBrowsingContext
->Canonical()->MaybeAddAsProgressListener(ds
);
537 void nsDocShell::DestroyChildren() {
538 for (auto* child
: mChildList
.ForwardRange()) {
539 nsCOMPtr
<nsIDocShellTreeItem
> shell
= do_QueryObject(child
);
540 NS_ASSERTION(shell
, "docshell has null child");
543 shell
->SetTreeOwner(nullptr);
547 nsDocLoader::DestroyChildren();
550 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell
, nsDocLoader
,
551 mScriptGlobal
, mInitialClientSource
,
555 NS_IMPL_ADDREF_INHERITED(nsDocShell
, nsDocLoader
)
556 NS_IMPL_RELEASE_INHERITED(nsDocShell
, nsDocLoader
)
558 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell
)
559 NS_INTERFACE_MAP_ENTRY(nsIDocShell
)
560 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem
)
561 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation
)
562 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow
)
563 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI
)
564 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener
)
565 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
566 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor
)
567 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider
)
568 NS_INTERFACE_MAP_ENTRY(nsILoadContext
)
569 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController
,
570 mInterceptController
)
571 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader
)
574 nsDocShell::GetInterface(const nsIID
& aIID
, void** aSink
) {
575 MOZ_ASSERT(aSink
, "null out param");
579 if (aIID
.Equals(NS_GET_IID(nsICommandManager
))) {
580 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE
);
581 *aSink
= static_cast<nsICommandManager
*>(mCommandManager
.get());
582 } else if (aIID
.Equals(NS_GET_IID(nsIURIContentListener
))) {
583 *aSink
= mContentListener
;
584 } else if ((aIID
.Equals(NS_GET_IID(nsIScriptGlobalObject
)) ||
585 aIID
.Equals(NS_GET_IID(nsIGlobalObject
)) ||
586 aIID
.Equals(NS_GET_IID(nsPIDOMWindowOuter
)) ||
587 aIID
.Equals(NS_GET_IID(mozIDOMWindowProxy
)) ||
588 aIID
.Equals(NS_GET_IID(nsIDOMWindow
))) &&
589 NS_SUCCEEDED(EnsureScriptEnvironment())) {
590 return mScriptGlobal
->QueryInterface(aIID
, aSink
);
591 } else if (aIID
.Equals(NS_GET_IID(Document
)) &&
592 NS_SUCCEEDED(EnsureDocumentViewer())) {
593 RefPtr
<Document
> doc
= mDocumentViewer
->GetDocument();
595 return *aSink
? NS_OK
: NS_NOINTERFACE
;
596 } else if (aIID
.Equals(NS_GET_IID(nsIPrompt
)) &&
597 NS_SUCCEEDED(EnsureScriptEnvironment())) {
599 nsCOMPtr
<nsIWindowWatcher
> wwatch
=
600 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
601 NS_ENSURE_SUCCESS(rv
, rv
);
603 // Get the an auth prompter for our window so that the parenting
604 // of the dialogs works as it should when using tabs.
606 rv
= wwatch
->GetNewPrompter(mScriptGlobal
, &prompt
);
607 NS_ENSURE_SUCCESS(rv
, rv
);
611 } else if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
612 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
613 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL
, aIID
, aSink
))
616 } else if (aIID
.Equals(NS_GET_IID(nsISHistory
))) {
617 // This is deprecated, you should instead directly get
618 // ChildSHistory from the browsing context.
619 MOZ_DIAGNOSTIC_ASSERT(
620 false, "Do not try to get a nsISHistory interface from nsIDocShell");
621 return NS_NOINTERFACE
;
622 } else if (aIID
.Equals(NS_GET_IID(nsIWebBrowserFind
))) {
623 nsresult rv
= EnsureFind();
629 NS_ADDREF((nsISupports
*)*aSink
);
631 } else if (aIID
.Equals(NS_GET_IID(nsISelectionDisplay
))) {
632 if (PresShell
* presShell
= GetPresShell()) {
633 return presShell
->QueryInterface(aIID
, aSink
);
635 } else if (aIID
.Equals(NS_GET_IID(nsIDocShellTreeOwner
))) {
636 nsCOMPtr
<nsIDocShellTreeOwner
> treeOwner
;
637 nsresult rv
= GetTreeOwner(getter_AddRefs(treeOwner
));
638 if (NS_SUCCEEDED(rv
) && treeOwner
) {
639 return treeOwner
->QueryInterface(aIID
, aSink
);
641 } else if (aIID
.Equals(NS_GET_IID(nsIBrowserChild
))) {
642 *aSink
= GetBrowserChild().take();
643 return *aSink
? NS_OK
: NS_ERROR_FAILURE
;
645 return nsDocLoader::GetInterface(aIID
, aSink
);
648 NS_IF_ADDREF(((nsISupports
*)*aSink
));
649 return *aSink
? NS_OK
: NS_NOINTERFACE
;
653 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch
) {
654 // Note: this gets called fairly early (before a pageload actually starts).
655 // We could probably defer this even longer.
656 nsCOMPtr
<nsIBrowserChild
> browserChild
= GetBrowserChild();
657 static_cast<BrowserChild
*>(browserChild
.get())
658 ->SetCancelContentJSEpoch(aEpoch
);
662 nsresult
nsDocShell::CheckDisallowedJavascriptLoad(
663 nsDocShellLoadState
* aLoadState
) {
664 if (!net::SchemeIsJavascript(aLoadState
->URI())) {
668 if (nsCOMPtr
<nsIPrincipal
> targetPrincipal
=
669 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
670 if (!aLoadState
->TriggeringPrincipal()->Subsumes(targetPrincipal
)) {
671 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI
;
675 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI
;
679 nsDocShell::LoadURI(nsDocShellLoadState
* aLoadState
, bool aSetNavigating
) {
680 return LoadURI(aLoadState
, aSetNavigating
, false);
683 nsresult
nsDocShell::LoadURI(nsDocShellLoadState
* aLoadState
,
685 bool aContinueHandlingSubframeHistory
) {
686 MOZ_ASSERT(aLoadState
, "Must have a valid load state!");
687 // NOTE: This comparison between what appears to be internal/external load
688 // flags is intentional, as it's ensuring that the caller isn't using any of
689 // the flags reserved for implementations by the `nsIWebNavigation` interface.
690 // In the future, this check may be dropped.
692 (aLoadState
->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS
) == 0,
693 "Should not have these flags set");
694 MOZ_ASSERT(aLoadState
->TargetBrowsingContext().IsNull(),
695 "Targeting doesn't occur until InternalLoad");
697 if (!aLoadState
->TriggeringPrincipal()) {
698 MOZ_ASSERT(false, "LoadURI must have a triggering principal");
699 return NS_ERROR_FAILURE
;
702 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState
));
704 bool oldIsNavigating
= mIsNavigating
;
705 auto cleanupIsNavigating
=
706 MakeScopeExit([&]() { mIsNavigating
= oldIsNavigating
; });
707 if (aSetNavigating
) {
708 mIsNavigating
= true;
711 PopupBlocker::PopupControlState popupState
= PopupBlocker::openOverridden
;
712 if (aLoadState
->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS
)) {
713 popupState
= PopupBlocker::openAllowed
;
714 // If we allow popups as part of the navigation, ensure we fake a user
715 // interaction, so that popups can, in fact, be allowed to open.
716 if (WindowContext
* wc
= mBrowsingContext
->GetCurrentWindowContext()) {
717 wc
->NotifyUserGestureActivation();
721 AutoPopupStatePusher
statePusher(popupState
);
723 if (aLoadState
->GetCancelContentJSEpoch().isSome()) {
724 SetCancelContentJSEpoch(*aLoadState
->GetCancelContentJSEpoch());
727 // Note: we allow loads to get through here even if mFiredUnloadEvent is
728 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
729 // so we pass false as the second parameter to IsNavigationAllowed.
730 // However, we don't allow the page to change location *in the middle of*
731 // firing beforeunload, so we do need to check if *beforeunload* is currently
732 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
733 if (!IsNavigationAllowed(true, false)) {
734 return NS_OK
; // JS may not handle returning of an error code
737 nsLoadFlags defaultLoadFlags
= mBrowsingContext
->GetDefaultLoadFlags();
738 if (aLoadState
->HasLoadFlags(LOAD_FLAGS_FORCE_TRR
)) {
739 defaultLoadFlags
|= nsIRequest::LOAD_TRR_ONLY_MODE
;
740 } else if (aLoadState
->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR
)) {
741 defaultLoadFlags
|= nsIRequest::LOAD_TRR_DISABLED_MODE
;
744 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext
->SetDefaultLoadFlags(defaultLoadFlags
));
746 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI
) &&
747 mItemType
== typeContent
&& !NS_IsAboutBlank(aLoadState
->URI())) {
748 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI
);
751 // LoadType used to be set to a default value here, if no LoadInfo/LoadState
752 // object was passed in. That functionality has been removed as of bug
753 // 1492648. LoadType should now be set up by the caller at the time they
754 // create their nsDocShellLoadState object to pass into LoadURI.
757 gDocShellLeakLog
, LogLevel::Debug
,
758 ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
759 aLoadState
->URI()->GetSpecOrDefault().get(), aLoadState
->LoadFlags()));
761 if ((!aLoadState
->LoadIsFromSessionHistory() &&
762 !LOAD_TYPE_HAS_FLAGS(aLoadState
->LoadType(),
763 LOAD_FLAGS_REPLACE_HISTORY
)) ||
764 aContinueHandlingSubframeHistory
) {
765 // This is possibly a subframe, so handle it accordingly.
767 // If history exists, it will be loaded into the aLoadState object, and the
768 // LoadType will be changed.
769 if (MaybeHandleSubframeHistory(aLoadState
,
770 aContinueHandlingSubframeHistory
)) {
771 // MaybeHandleSubframeHistory returns true if we need to continue loading
777 if (aLoadState
->LoadIsFromSessionHistory()) {
778 MOZ_LOG(gSHLog
, LogLevel::Debug
,
779 ("nsDocShell[%p]: loading from session history", this));
781 if (!mozilla::SessionHistoryInParent()) {
782 nsCOMPtr
<nsISHEntry
> entry
= aLoadState
->SHEntry();
783 return LoadHistoryEntry(entry
, aLoadState
->LoadType(),
784 aLoadState
->HasValidUserGestureActivation());
787 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
788 return LoadHistoryEntry(*aLoadState
->GetLoadingSessionHistoryInfo(),
789 aLoadState
->LoadType(),
790 aLoadState
->HasValidUserGestureActivation());
793 // On history navigation via Back/Forward buttons, don't execute
794 // automatic JavaScript redirection such as |location.href = ...| or
797 // LOAD_NORMAL: window.open(...) etc.
798 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
799 if ((aLoadState
->LoadType() == LOAD_NORMAL
||
800 aLoadState
->LoadType() == LOAD_STOP_CONTENT
) &&
801 ShouldBlockLoadingForBackButton()) {
805 BrowsingContext::Type bcType
= mBrowsingContext
->GetType();
807 // Set up the inheriting principal in LoadState.
808 nsresult rv
= aLoadState
->SetupInheritingPrincipal(
809 bcType
, mBrowsingContext
->OriginAttributesRef());
810 NS_ENSURE_SUCCESS(rv
, rv
);
812 rv
= aLoadState
->SetupTriggeringPrincipal(
813 mBrowsingContext
->OriginAttributesRef());
814 NS_ENSURE_SUCCESS(rv
, rv
);
816 aLoadState
->CalculateLoadURIFlags();
818 MOZ_ASSERT(aLoadState
->TypeHint().IsVoid(),
819 "Typehint should be null when calling InternalLoad from LoadURI");
820 MOZ_ASSERT(aLoadState
->FileName().IsVoid(),
821 "FileName should be null when calling InternalLoad from LoadURI");
822 MOZ_ASSERT(!aLoadState
->LoadIsFromSessionHistory(),
823 "Shouldn't be loading from an entry when calling InternalLoad "
826 // If we have a system triggering principal, we can assume that this load was
827 // triggered by some UI in the browser chrome, such as the URL bar or
828 // bookmark bar. This should count as a user interaction for the current sh
829 // entry, so that the user may navigate back to the current entry, from the
830 // entry that is going to be added as part of this load.
831 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
=
832 aLoadState
->TriggeringPrincipal();
833 if (triggeringPrincipal
&& triggeringPrincipal
->IsSystemPrincipal()) {
834 if (mozilla::SessionHistoryInParent()) {
835 WindowContext
* topWc
= mBrowsingContext
->GetTopWindowContext();
836 if (topWc
&& !topWc
->IsDiscarded()) {
837 MOZ_ALWAYS_SUCCEEDS(topWc
->SetSHEntryHasUserInteraction(true));
841 nsCOMPtr
<nsISHEntry
> currentSHEntry
;
842 GetCurrentSHEntry(getter_AddRefs(currentSHEntry
), &oshe
);
843 if (currentSHEntry
) {
844 currentSHEntry
->SetHasUserInteraction(true);
849 rv
= InternalLoad(aLoadState
);
850 NS_ENSURE_SUCCESS(rv
, rv
);
852 if (aLoadState
->GetOriginalURIString().isSome()) {
853 // Save URI string in case it's needed later when
854 // sending to search engine service in EndPageLoad()
855 mOriginalUriString
= *aLoadState
->GetOriginalURIString();
861 bool nsDocShell::IsLoadingFromSessionHistory() {
862 return mActiveEntryIsLoadingFromSessionHistory
;
865 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
866 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
867 // detect Cancel calls.
868 class StopDetector final
: public nsIRequest
{
870 StopDetector() = default;
875 bool Canceled() { return mCanceled
; }
878 ~StopDetector() = default;
880 bool mCanceled
= false;
883 NS_IMPL_ISUPPORTS(StopDetector
, nsIRequest
)
886 StopDetector::GetName(nsACString
& aResult
) {
887 aResult
.AssignLiteral("about:stop-detector");
892 StopDetector::IsPending(bool* aRetVal
) {
898 StopDetector::GetStatus(nsresult
* aStatus
) {
903 NS_IMETHODIMP
StopDetector::SetCanceledReason(const nsACString
& aReason
) {
904 return SetCanceledReasonImpl(aReason
);
907 NS_IMETHODIMP
StopDetector::GetCanceledReason(nsACString
& aReason
) {
908 return GetCanceledReasonImpl(aReason
);
911 NS_IMETHODIMP
StopDetector::CancelWithReason(nsresult aStatus
,
912 const nsACString
& aReason
) {
913 return CancelWithReasonImpl(aStatus
, aReason
);
917 StopDetector::Cancel(nsresult aStatus
) {
923 StopDetector::Suspend(void) { return NS_OK
; }
925 StopDetector::Resume(void) { return NS_OK
; }
928 StopDetector::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
929 *aLoadGroup
= nullptr;
934 StopDetector::SetLoadGroup(nsILoadGroup
* aLoadGroup
) { return NS_OK
; }
937 StopDetector::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
938 *aLoadFlags
= nsIRequest::LOAD_NORMAL
;
943 StopDetector::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
944 return GetTRRModeImpl(aTRRMode
);
948 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
949 return SetTRRModeImpl(aTRRMode
);
953 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags
) { return NS_OK
; }
955 bool nsDocShell::MaybeHandleSubframeHistory(
956 nsDocShellLoadState
* aLoadState
, bool aContinueHandlingSubframeHistory
) {
957 // First, verify if this is a subframe.
958 // Note, it is ok to rely on docshell here and not browsing context since when
959 // an iframe is created, it has first in-process docshell.
960 nsCOMPtr
<nsIDocShellTreeItem
> parentAsItem
;
961 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem
));
962 nsCOMPtr
<nsIDocShell
> parentDS(do_QueryInterface(parentAsItem
));
964 if (!parentDS
|| parentDS
== static_cast<nsIDocShell
*>(this)) {
965 if (mBrowsingContext
&& mBrowsingContext
->IsTop()) {
966 // This is the root docshell. If we got here while
967 // executing an onLoad Handler,this load will not go
968 // into session history.
969 // XXX Why is this code in a method which deals with iframes!
970 if (aLoadState
->IsFormSubmission()) {
972 if (!mEODForCurrentDocument
) {
973 const MaybeDiscarded
<BrowsingContext
>& targetBC
=
974 aLoadState
->TargetBrowsingContext();
975 MOZ_ASSERT_IF(GetBrowsingContext() == targetBC
.get(),
976 aLoadState
->LoadType() == LOAD_NORMAL_REPLACE
);
980 bool inOnLoadHandler
= false;
981 GetIsExecutingOnLoadHandler(&inOnLoadHandler
);
982 if (inOnLoadHandler
) {
983 aLoadState
->SetLoadType(LOAD_NORMAL_REPLACE
);
990 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
991 * loaded through a history mechanism, then get the SH entry for the child
992 * from the parent. This is done to restore frameset navigation while going
993 * back/forward. If the parent was loaded through any other loadType, set the
994 * child's loadType too accordingly, so that session history does not get
998 // Get the parent's load type
999 uint32_t parentLoadType
;
1000 parentDS
->GetLoadType(&parentLoadType
);
1002 if (!aContinueHandlingSubframeHistory
) {
1003 if (mozilla::SessionHistoryInParent()) {
1004 if (nsDocShell::Cast(parentDS
.get())->IsLoadingFromSessionHistory() &&
1005 !GetCreatedDynamically()) {
1006 if (XRE_IsContentProcess()) {
1007 dom::ContentChild
* contentChild
= dom::ContentChild::GetSingleton();
1008 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1009 GetLoadGroup(getter_AddRefs(loadGroup
));
1010 if (contentChild
&& loadGroup
&& !mCheckingSessionHistory
) {
1011 RefPtr
<Document
> parentDoc
= parentDS
->GetDocument();
1012 parentDoc
->BlockOnload();
1013 RefPtr
<BrowsingContext
> browsingContext
= mBrowsingContext
;
1014 Maybe
<uint64_t> currentLoadIdentifier
=
1015 mBrowsingContext
->GetCurrentLoadIdentifier();
1016 RefPtr
<nsDocShellLoadState
> loadState
= aLoadState
;
1017 bool isNavigating
= mIsNavigating
;
1018 RefPtr
<StopDetector
> stopDetector
= new StopDetector();
1019 loadGroup
->AddRequest(stopDetector
, nullptr);
1020 // Need to set mCheckingSessionHistory so that
1021 // GetIsAttemptingToNavigate() returns true.
1022 mCheckingSessionHistory
= true;
1025 [currentLoadIdentifier
, browsingContext
, parentDoc
, loadState
,
1026 isNavigating
, loadGroup
, stopDetector
](
1027 mozilla::Maybe
<LoadingSessionHistoryInfo
>&& aResult
) {
1028 RefPtr
<nsDocShell
> docShell
=
1029 static_cast<nsDocShell
*>(browsingContext
->GetDocShell());
1030 auto unblockParent
= MakeScopeExit(
1031 [loadGroup
, stopDetector
, parentDoc
, docShell
]() {
1033 docShell
->mCheckingSessionHistory
= false;
1035 loadGroup
->RemoveRequest(stopDetector
, nullptr, NS_OK
);
1036 parentDoc
->UnblockOnload(false);
1039 if (!docShell
|| !docShell
->mCheckingSessionHistory
) {
1043 if (stopDetector
->Canceled()) {
1046 if (currentLoadIdentifier
==
1047 browsingContext
->GetCurrentLoadIdentifier() &&
1049 loadState
->SetLoadingSessionHistoryInfo(aResult
.value());
1050 // This is an initial subframe load from the session
1051 // history, index doesn't need to be updated.
1052 loadState
->SetLoadIsFromSessionHistory(0, false);
1055 // We got the results back from the parent process, call
1056 // LoadURI again with the possibly updated data.
1057 docShell
->LoadURI(loadState
, isNavigating
, true);
1059 auto reject
= [loadGroup
, stopDetector
, browsingContext
,
1060 parentDoc
](mozilla::ipc::ResponseRejectReason
) {
1061 RefPtr
<nsDocShell
> docShell
=
1062 static_cast<nsDocShell
*>(browsingContext
->GetDocShell());
1064 docShell
->mCheckingSessionHistory
= false;
1066 // In practise reject shouldn't be called ever.
1067 loadGroup
->RemoveRequest(stopDetector
, nullptr, NS_OK
);
1068 parentDoc
->UnblockOnload(false);
1070 contentChild
->SendGetLoadingSessionHistoryInfoFromParent(
1071 mBrowsingContext
, std::move(resolve
), std::move(reject
));
1075 Maybe
<LoadingSessionHistoryInfo
> info
;
1076 mBrowsingContext
->Canonical()->GetLoadingSessionHistoryInfoFromParent(
1078 if (info
.isSome()) {
1079 aLoadState
->SetLoadingSessionHistoryInfo(info
.value());
1080 // This is an initial subframe load from the session
1081 // history, index doesn't need to be updated.
1082 aLoadState
->SetLoadIsFromSessionHistory(0, false);
1087 // Get the ShEntry for the child from the parent
1088 nsCOMPtr
<nsISHEntry
> currentSH
;
1090 parentDS
->GetCurrentSHEntry(getter_AddRefs(currentSH
), &oshe
);
1091 bool dynamicallyAddedChild
= GetCreatedDynamically();
1093 if (!dynamicallyAddedChild
&& !oshe
&& currentSH
) {
1094 // Only use the old SHEntry, if we're sure enough that
1095 // it wasn't originally for some other frame.
1096 nsCOMPtr
<nsISHEntry
> shEntry
;
1097 currentSH
->GetChildSHEntryIfHasNoDynamicallyAddedChild(
1098 mBrowsingContext
->ChildOffset(), getter_AddRefs(shEntry
));
1100 aLoadState
->SetSHEntry(shEntry
);
1106 // Make some decisions on the child frame's loadType based on the
1107 // parent's loadType, if the subframe hasn't loaded anything into it.
1109 // In some cases privileged scripts may try to get the DOMWindow
1110 // reference of this docshell before the loading starts, causing the
1111 // initial about:blank content viewer being created and mCurrentURI being
1112 // set. To handle this case we check if mCurrentURI is about:blank and
1113 // currentSHEntry is null.
1115 nsCOMPtr
<nsISHEntry
> currentChildEntry
;
1116 GetCurrentSHEntry(getter_AddRefs(currentChildEntry
), &oshe
);
1118 if (mCurrentURI
&& (!NS_IsAboutBlank(mCurrentURI
) || currentChildEntry
||
1119 mLoadingEntry
|| mActiveEntry
)) {
1120 // This is a pre-existing subframe. If
1121 // 1. The load of this frame was not originally initiated by session
1122 // history directly (i.e. (!shEntry) condition succeeded, but it can
1123 // still be a history load on parent which causes this frame being
1124 // loaded), which we checked with the above assert, and
1125 // 2. mCurrentURI is not null, nor the initial about:blank,
1126 // it is possible that a parent's onLoadHandler or even self's
1127 // onLoadHandler is loading a new page in this child. Check parent's and
1128 // self's busy flag and if it is set, we don't want this onLoadHandler
1129 // load to get in to session history.
1130 BusyFlags parentBusy
= parentDS
->GetBusyFlags();
1131 BusyFlags selfBusy
= GetBusyFlags();
1133 if (parentBusy
& BUSY_FLAGS_BUSY
|| selfBusy
& BUSY_FLAGS_BUSY
) {
1134 aLoadState
->SetLoadType(LOAD_NORMAL_REPLACE
);
1135 aLoadState
->ClearLoadIsFromSessionHistory();
1140 // This is a newly created frame. Check for exception cases first.
1141 // By default the subframe will inherit the parent's loadType.
1142 if (aLoadState
->LoadIsFromSessionHistory() &&
1143 (parentLoadType
== LOAD_NORMAL
|| parentLoadType
== LOAD_LINK
)) {
1144 // The parent was loaded normally. In this case, this *brand new*
1145 // child really shouldn't have a SHEntry. If it does, it could be
1146 // because the parent is replacing an existing frame with a new frame,
1147 // in the onLoadHandler. We don't want this url to get into session
1148 // history. Clear off shEntry, and set load type to
1149 // LOAD_BYPASS_HISTORY.
1150 bool inOnLoadHandler
= false;
1151 parentDS
->GetIsExecutingOnLoadHandler(&inOnLoadHandler
);
1152 if (inOnLoadHandler
) {
1153 aLoadState
->SetLoadType(LOAD_NORMAL_REPLACE
);
1154 aLoadState
->ClearLoadIsFromSessionHistory();
1156 } else if (parentLoadType
== LOAD_REFRESH
) {
1157 // Clear shEntry. For refresh loads, we have to load
1158 // what comes through the pipe, not what's in history.
1159 aLoadState
->ClearLoadIsFromSessionHistory();
1160 } else if ((parentLoadType
== LOAD_BYPASS_HISTORY
) ||
1161 (aLoadState
->LoadIsFromSessionHistory() &&
1162 ((parentLoadType
& LOAD_CMD_HISTORY
) ||
1163 (parentLoadType
== LOAD_RELOAD_NORMAL
) ||
1164 (parentLoadType
== LOAD_RELOAD_CHARSET_CHANGE
) ||
1165 (parentLoadType
== LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE
) ||
1167 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE
)))) {
1168 // If the parent url, bypassed history or was loaded from
1169 // history, pass on the parent's loadType to the new child
1170 // frame too, so that the child frame will also
1171 // avoid getting into history.
1172 aLoadState
->SetLoadType(parentLoadType
);
1173 } else if (parentLoadType
== LOAD_ERROR_PAGE
) {
1174 // If the parent document is an error page, we don't
1175 // want to update global/session history. However,
1176 // this child frame is not an error page.
1177 aLoadState
->SetLoadType(LOAD_BYPASS_HISTORY
);
1178 } else if ((parentLoadType
== LOAD_RELOAD_BYPASS_CACHE
) ||
1179 (parentLoadType
== LOAD_RELOAD_BYPASS_PROXY
) ||
1180 (parentLoadType
== LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
)) {
1181 // the new frame should inherit the parent's load type so that it also
1182 // bypasses the cache and/or proxy
1183 aLoadState
->SetLoadType(parentLoadType
);
1190 * Reset state to a new content model within the current document and the
1191 * document viewer. Called by the document before initiating an out of band
1195 nsDocShell::PrepareForNewContentModel() {
1196 // Clear out our form control state, because the state of controls
1197 // in the pre-open() document should not affect the state of
1198 // controls that are now going to be written.
1199 SetLayoutHistoryState(nullptr);
1200 mEODForCurrentDocument
= false;
1205 nsDocShell::FirePageHideNotification(bool aIsUnload
) {
1206 FirePageHideNotificationInternal(aIsUnload
, false);
1210 void nsDocShell::FirePageHideNotificationInternal(
1211 bool aIsUnload
, bool aSkipCheckingDynEntries
) {
1212 if (mDocumentViewer
&& !mFiredUnloadEvent
) {
1213 // Keep an explicit reference since calling PageHide could release
1215 nsCOMPtr
<nsIDocumentViewer
> viewer(mDocumentViewer
);
1216 mFiredUnloadEvent
= true;
1219 mTiming
->NotifyUnloadEventStart();
1222 viewer
->PageHide(aIsUnload
);
1225 mTiming
->NotifyUnloadEventEnd();
1228 AutoTArray
<nsCOMPtr
<nsIDocShell
>, 8> kids
;
1229 uint32_t n
= mChildList
.Length();
1230 kids
.SetCapacity(n
);
1231 for (uint32_t i
= 0; i
< n
; i
++) {
1232 kids
.AppendElement(do_QueryInterface(ChildAt(i
)));
1236 for (uint32_t i
= 0; i
< n
; ++i
) {
1237 RefPtr
<nsDocShell
> child
= static_cast<nsDocShell
*>(kids
[i
].get());
1239 // Skip checking dynamic subframe entries in our children.
1240 child
->FirePageHideNotificationInternal(aIsUnload
, true);
1244 // If the document is unloading, remove all dynamic subframe entries.
1245 if (aIsUnload
&& !aSkipCheckingDynEntries
) {
1246 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
1249 gSHLog
, LogLevel::Debug
,
1250 ("nsDocShell %p unloading, remove dynamic subframe entries", this));
1251 if (mozilla::SessionHistoryInParent()) {
1253 mBrowsingContext
->RemoveDynEntriesFromActiveSessionHistoryEntry();
1255 MOZ_LOG(gSHLog
, LogLevel::Debug
,
1256 ("nsDocShell %p unloading, no active entries", this));
1258 int32_t index
= rootSH
->Index();
1259 rootSH
->LegacySHistory()->RemoveDynEntries(index
, mOSHE
);
1264 // Now make sure our editor, if any, is detached before we go
1266 DetachEditorFromWindow();
1270 void nsDocShell::ThawFreezeNonRecursive(bool aThaw
) {
1271 MOZ_ASSERT(mozilla::BFCacheInParent());
1273 if (!mScriptGlobal
) {
1277 if (RefPtr
<nsGlobalWindowInner
> inner
=
1278 nsGlobalWindowInner::Cast(mScriptGlobal
->GetCurrentInnerWindow())) {
1282 inner
->Freeze(false);
1287 void nsDocShell::FirePageHideShowNonRecursive(bool aShow
) {
1288 MOZ_ASSERT(mozilla::BFCacheInParent());
1290 if (!mDocumentViewer
) {
1294 // Emulate what non-SHIP BFCache does too. In pageshow case
1295 // add and remove a request and before that call SetCurrentURI to get
1296 // the location change notification.
1297 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
1298 nsCOMPtr
<nsIDocumentViewer
> viewer(mDocumentViewer
);
1300 viewer
->SetIsHidden(false);
1301 mRefreshURIList
= std::move(mBFCachedRefreshURIList
);
1302 RefreshURIFromQueue();
1303 mFiredUnloadEvent
= false;
1304 RefPtr
<Document
> doc
= viewer
->GetDocument();
1306 doc
->NotifyActivityChanged();
1307 nsCOMPtr
<nsPIDOMWindowInner
> inner
=
1308 mScriptGlobal
? mScriptGlobal
->GetCurrentInnerWindow() : nullptr;
1309 if (mBrowsingContext
->IsTop()) {
1310 doc
->NotifyPossibleTitleChange(false);
1311 doc
->SetLoadingOrRestoredFromBFCacheTimeStampToNow();
1313 // Now that we have found the inner window of the page restored
1314 // from the history, we have to make sure that
1315 // performance.navigation.type is 2.
1316 // Traditionally this type change has been done to the top level page
1318 Performance
* performance
= inner
->GetPerformance();
1320 performance
->GetDOMTiming()->NotifyRestoreStart();
1325 nsCOMPtr
<nsIChannel
> channel
= doc
->GetChannel();
1327 SetLoadType(LOAD_HISTORY
);
1328 mEODForCurrentDocument
= false;
1329 mIsRestoringDocument
= true;
1330 mLoadGroup
->AddRequest(channel
, nullptr);
1331 SetCurrentURI(doc
->GetDocumentURI(), channel
,
1332 /* aFireOnLocationChange */ true,
1333 /* aIsInitialAboutBlank */ false,
1334 /* aLocationFlags */ 0);
1335 mLoadGroup
->RemoveRequest(channel
, nullptr, NS_OK
);
1336 mIsRestoringDocument
= false;
1338 RefPtr
<PresShell
> presShell
= GetPresShell();
1340 presShell
->Thaw(false);
1344 inner
->FireDelayedDOMEvents(false);
1347 } else if (!mFiredUnloadEvent
) {
1348 // XXXBFCache check again that the page can enter bfcache.
1349 // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
1351 if (mRefreshURIList
) {
1352 RefreshURIToQueue();
1353 mBFCachedRefreshURIList
= std::move(mRefreshURIList
);
1355 // If Stop was called, the list was moved to mSavedRefreshURIList after
1356 // calling SuspendRefreshURIs, which calls RefreshURIToQueue.
1357 mBFCachedRefreshURIList
= std::move(mSavedRefreshURIList
);
1360 mFiredUnloadEvent
= true;
1361 viewer
->PageHide(false);
1363 RefPtr
<PresShell
> presShell
= GetPresShell();
1365 presShell
->Freeze(false);
1370 nsresult
nsDocShell::Dispatch(already_AddRefed
<nsIRunnable
>&& aRunnable
) {
1371 nsCOMPtr
<nsIRunnable
> runnable(aRunnable
);
1372 if (NS_WARN_IF(!GetWindow())) {
1373 // Window should only be unavailable after destroyed.
1374 MOZ_ASSERT(mIsBeingDestroyed
);
1375 return NS_ERROR_FAILURE
;
1377 return SchedulerGroup::Dispatch(runnable
.forget());
1381 nsDocShell::DispatchLocationChangeEvent() {
1382 return Dispatch(NewRunnableMethod("nsDocShell::FireDummyOnLocationChange",
1384 &nsDocShell::FireDummyOnLocationChange
));
1388 nsDocShell::StartDelayedAutoplayMediaComponents() {
1389 RefPtr
<nsPIDOMWindowOuter
> outerWindow
= GetWindow();
1391 outerWindow
->ActivateMediaComponents();
1396 bool nsDocShell::MaybeInitTiming() {
1397 if (mTiming
&& !mBlankTiming
) {
1401 bool canBeReset
= false;
1403 if (mScriptGlobal
&& mBlankTiming
) {
1404 nsPIDOMWindowInner
* innerWin
= mScriptGlobal
->GetCurrentInnerWindow();
1405 if (innerWin
&& innerWin
->GetPerformance()) {
1406 mTiming
= innerWin
->GetPerformance()->GetDOMTiming();
1407 mBlankTiming
= false;
1412 mTiming
= new nsDOMNavigationTiming(this);
1416 mTiming
->NotifyNavigationStart(
1417 mBrowsingContext
->IsActive()
1418 ? nsDOMNavigationTiming::DocShellState::eActive
1419 : nsDOMNavigationTiming::DocShellState::eInactive
);
1424 void nsDocShell::MaybeResetInitTiming(bool aReset
) {
1430 nsDOMNavigationTiming
* nsDocShell::GetNavigationTiming() const {
1434 nsPresContext
* nsDocShell::GetEldestPresContext() {
1435 nsIDocumentViewer
* viewer
= mDocumentViewer
;
1437 nsIDocumentViewer
* prevViewer
= viewer
->GetPreviousViewer();
1439 return viewer
->GetPresContext();
1441 viewer
= prevViewer
;
1447 nsPresContext
* nsDocShell::GetPresContext() {
1448 if (!mDocumentViewer
) {
1452 return mDocumentViewer
->GetPresContext();
1455 PresShell
* nsDocShell::GetPresShell() {
1456 nsPresContext
* presContext
= GetPresContext();
1457 return presContext
? presContext
->GetPresShell() : nullptr;
1460 PresShell
* nsDocShell::GetEldestPresShell() {
1461 nsPresContext
* presContext
= GetEldestPresContext();
1464 return presContext
->GetPresShell();
1471 nsDocShell::GetDocViewer(nsIDocumentViewer
** aDocumentViewer
) {
1472 NS_ENSURE_ARG_POINTER(aDocumentViewer
);
1474 *aDocumentViewer
= mDocumentViewer
;
1475 NS_IF_ADDREF(*aDocumentViewer
);
1480 nsDocShell::GetOuterWindowID(uint64_t* aWindowID
) {
1481 *aWindowID
= mContentWindowID
;
1486 nsDocShell::SetChromeEventHandler(EventTarget
* aChromeEventHandler
) {
1487 mChromeEventHandler
= aChromeEventHandler
;
1489 if (mScriptGlobal
) {
1490 mScriptGlobal
->SetChromeEventHandler(mChromeEventHandler
);
1497 nsDocShell::GetChromeEventHandler(EventTarget
** aChromeEventHandler
) {
1498 NS_ENSURE_ARG_POINTER(aChromeEventHandler
);
1499 RefPtr
<EventTarget
> handler
= mChromeEventHandler
;
1500 handler
.forget(aChromeEventHandler
);
1505 nsDocShell::SetCurrentURIForSessionStore(nsIURI
* aURI
) {
1506 // Note that securityUI will set STATE_IS_INSECURE, even if
1507 // the scheme of |aURI| is "https".
1508 SetCurrentURI(aURI
, nullptr,
1509 /* aFireOnLocationChange */
1511 /* aIsInitialAboutBlank */
1513 /* aLocationFlags */
1514 nsIWebProgressListener::LOCATION_CHANGE_SESSION_STORE
);
1518 bool nsDocShell::SetCurrentURI(nsIURI
* aURI
, nsIRequest
* aRequest
,
1519 bool aFireOnLocationChange
,
1520 bool aIsInitialAboutBlank
,
1521 uint32_t aLocationFlags
) {
1522 MOZ_ASSERT(!mIsBeingDestroyed
);
1524 MOZ_LOG(gDocShellLeakLog
, LogLevel::Debug
,
1525 ("DOCSHELL %p SetCurrentURI %s\n", this,
1526 aURI
? aURI
->GetSpecOrDefault().get() : ""));
1528 // We don't want to send a location change when we're displaying an error
1529 // page, and we don't want to change our idea of "current URI" either
1530 if (mLoadType
== LOAD_ERROR_PAGE
) {
1534 bool uriIsEqual
= false;
1535 if (!mCurrentURI
|| !aURI
||
1536 NS_FAILED(mCurrentURI
->Equals(aURI
, &uriIsEqual
)) || !uriIsEqual
) {
1537 mTitleValidForCurrentURI
= false;
1540 SetCurrentURIInternal(aURI
);
1543 mLastOpenedURI
= aURI
;
1546 if (!NS_IsAboutBlank(mCurrentURI
)) {
1547 mHasLoadedNonBlankURI
= true;
1550 // Don't fire onLocationChange when creating a subframe's initial about:blank
1551 // document, as this can happen when it's not safe for us to run script.
1552 if (aIsInitialAboutBlank
&& !mHasLoadedNonBlankURI
&&
1553 !mBrowsingContext
->IsTop()) {
1554 MOZ_ASSERT(!aRequest
&& aLocationFlags
== 0);
1558 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1560 if (aFireOnLocationChange
) {
1561 FireOnLocationChange(this, aRequest
, aURI
, aLocationFlags
);
1563 return !aFireOnLocationChange
;
1566 void nsDocShell::SetCurrentURIInternal(nsIURI
* aURI
) {
1568 if (mBrowsingContext
) {
1569 mBrowsingContext
->ClearCachedValuesOfLocations();
1574 nsDocShell::GetCharset(nsACString
& aCharset
) {
1575 aCharset
.Truncate();
1577 PresShell
* presShell
= GetPresShell();
1578 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
1579 Document
* doc
= presShell
->GetDocument();
1580 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
1581 doc
->GetDocumentCharacterSet()->Name(aCharset
);
1586 nsDocShell::ForceEncodingDetection() {
1587 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1588 GetDocViewer(getter_AddRefs(viewer
));
1593 Document
* doc
= viewer
->GetDocument();
1594 if (!doc
|| doc
->WillIgnoreCharsetOverride()) {
1598 mForcedAutodetection
= true;
1600 nsIURI
* url
= doc
->GetOriginalURI();
1601 bool isFileURL
= url
&& SchemeIsFile(url
);
1603 int32_t charsetSource
= doc
->GetDocumentCharacterSetSource();
1604 auto encoding
= doc
->GetDocumentCharacterSet();
1605 // AsHTMLDocument is valid, because we called
1606 // WillIgnoreCharsetOverride() above.
1607 if (doc
->AsHTMLDocument()->IsPlainText()) {
1608 switch (charsetSource
) {
1609 case kCharsetFromInitialAutoDetectionASCII
:
1610 // Deliberately no final version
1611 LOGCHARSETMENU(("TEXT:UnlabeledAscii"));
1612 Telemetry::AccumulateCategorical(
1613 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledAscii
);
1615 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic
:
1616 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic
:
1617 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII
:
1618 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content
:
1619 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content
:
1620 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII
:
1621 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8"));
1622 Telemetry::AccumulateCategorical(
1623 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1626 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD
:
1627 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD
:
1628 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII
:
1629 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD"));
1630 Telemetry::AccumulateCategorical(
1631 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1632 UnlabeledNonUtf8TLD
);
1634 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8
:
1635 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII
:
1636 LOGCHARSETMENU(("TEXT:UnlabeledUtf8"));
1637 Telemetry::AccumulateCategorical(
1638 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledUtf8
);
1640 case kCharsetFromChannel
:
1641 if (encoding
== UTF_8_ENCODING
) {
1642 LOGCHARSETMENU(("TEXT:ChannelUtf8"));
1643 Telemetry::AccumulateCategorical(
1644 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::ChannelUtf8
);
1646 LOGCHARSETMENU(("TEXT:ChannelNonUtf8"));
1647 Telemetry::AccumulateCategorical(
1648 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1653 LOGCHARSETMENU(("TEXT:Bug"));
1654 Telemetry::AccumulateCategorical(
1655 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::Bug
);
1659 switch (charsetSource
) {
1660 case kCharsetFromInitialAutoDetectionASCII
:
1661 // Deliberately no final version
1662 LOGCHARSETMENU(("HTML:UnlabeledAscii"));
1663 Telemetry::AccumulateCategorical(
1664 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledAscii
);
1666 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic
:
1667 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic
:
1668 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII
:
1669 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content
:
1670 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content
:
1671 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII
:
1672 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8"));
1673 Telemetry::AccumulateCategorical(
1674 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1677 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD
:
1678 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD
:
1679 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII
:
1680 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD"));
1681 Telemetry::AccumulateCategorical(
1682 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1683 UnlabeledNonUtf8TLD
);
1685 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8
:
1686 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII
:
1687 LOGCHARSETMENU(("HTML:UnlabeledUtf8"));
1688 Telemetry::AccumulateCategorical(
1689 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledUtf8
);
1691 case kCharsetFromChannel
:
1692 if (encoding
== UTF_8_ENCODING
) {
1693 LOGCHARSETMENU(("HTML:ChannelUtf8"));
1694 Telemetry::AccumulateCategorical(
1695 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::ChannelUtf8
);
1697 LOGCHARSETMENU(("HTML:ChannelNonUtf8"));
1698 Telemetry::AccumulateCategorical(
1699 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1703 case kCharsetFromXmlDeclaration
:
1704 case kCharsetFromMetaTag
:
1706 LOGCHARSETMENU(("HTML:LocalLabeled"));
1707 Telemetry::AccumulateCategorical(
1708 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::LocalLabeled
);
1709 } else if (encoding
== UTF_8_ENCODING
) {
1710 LOGCHARSETMENU(("HTML:MetaUtf8"));
1711 Telemetry::AccumulateCategorical(
1712 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::InternalUtf8
);
1714 LOGCHARSETMENU(("HTML:MetaNonUtf8"));
1715 Telemetry::AccumulateCategorical(
1716 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1721 LOGCHARSETMENU(("HTML:Bug"));
1722 Telemetry::AccumulateCategorical(
1723 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::Bug
);
1730 void nsDocShell::SetParentCharset(const Encoding
*& aCharset
,
1731 int32_t aCharsetSource
,
1732 nsIPrincipal
* aPrincipal
) {
1733 mParentCharset
= aCharset
;
1734 mParentCharsetSource
= aCharsetSource
;
1735 mParentCharsetPrincipal
= aPrincipal
;
1738 void nsDocShell::GetParentCharset(const Encoding
*& aCharset
,
1739 int32_t* aCharsetSource
,
1740 nsIPrincipal
** aPrincipal
) {
1741 aCharset
= mParentCharset
;
1742 *aCharsetSource
= mParentCharsetSource
;
1743 NS_IF_ADDREF(*aPrincipal
= mParentCharsetPrincipal
);
1747 nsDocShell::GetHasTrackingContentBlocked(Promise
** aPromise
) {
1748 MOZ_ASSERT(aPromise
);
1751 RefPtr
<Document
> doc(GetDocument());
1752 RefPtr
<Promise
> retPromise
= Promise::Create(doc
->GetOwnerGlobal(), rv
);
1753 if (NS_WARN_IF(rv
.Failed())) {
1754 return rv
.StealNSResult();
1757 // Retrieve the document's content blocking events from the parent process.
1758 RefPtr
<Document::GetContentBlockingEventsPromise
> promise
=
1759 doc
->GetContentBlockingEvents();
1762 GetCurrentSerialEventTarget(), __func__
,
1763 [retPromise
](const Document::GetContentBlockingEventsPromise::
1764 ResolveOrRejectValue
& aValue
) {
1765 if (aValue
.IsResolve()) {
1766 bool has
= aValue
.ResolveValue() &
1767 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT
;
1768 retPromise
->MaybeResolve(has
);
1770 retPromise
->MaybeResolve(false);
1774 retPromise
->MaybeResolve(false);
1777 retPromise
.forget(aPromise
);
1782 nsDocShell::GetAllowPlugins(bool* aAllowPlugins
) {
1783 NS_ENSURE_ARG_POINTER(aAllowPlugins
);
1785 *aAllowPlugins
= mBrowsingContext
->GetAllowPlugins();
1790 nsDocShell::SetAllowPlugins(bool aAllowPlugins
) {
1791 // XXX should enable or disable a plugin host
1792 return mBrowsingContext
->SetAllowPlugins(aAllowPlugins
);
1796 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled
) {
1797 MOZ_ASSERT(aEnabled
);
1798 *aEnabled
= mCSSErrorReportingEnabled
;
1803 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled
) {
1804 mCSSErrorReportingEnabled
= aEnabled
;
1809 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing
) {
1810 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing
);
1811 return mBrowsingContext
->GetUsePrivateBrowsing(aUsePrivateBrowsing
);
1814 void nsDocShell::NotifyPrivateBrowsingChanged() {
1815 MOZ_ASSERT(!mIsBeingDestroyed
);
1817 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mPrivacyObservers
);
1818 while (iter
.HasMore()) {
1819 nsWeakPtr ref
= iter
.GetNext();
1820 nsCOMPtr
<nsIPrivacyTransitionObserver
> obs
= do_QueryReferent(ref
);
1824 obs
->PrivateModeChanged(UsePrivateBrowsing());
1830 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing
) {
1831 return mBrowsingContext
->SetUsePrivateBrowsing(aUsePrivateBrowsing
);
1835 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing
) {
1836 return mBrowsingContext
->SetPrivateBrowsing(aUsePrivateBrowsing
);
1840 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult
) {
1841 NS_ENSURE_ARG_POINTER(aResult
);
1843 *aResult
= mHasLoadedNonBlankURI
;
1848 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs
) {
1849 NS_ENSURE_ARG_POINTER(aUseRemoteTabs
);
1850 return mBrowsingContext
->GetUseRemoteTabs(aUseRemoteTabs
);
1854 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs
) {
1855 return mBrowsingContext
->SetRemoteTabs(aUseRemoteTabs
);
1859 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes
) {
1860 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes
);
1861 return mBrowsingContext
->GetUseRemoteSubframes(aUseRemoteSubframes
);
1865 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes
) {
1866 return mBrowsingContext
->SetRemoteSubframes(aUseRemoteSubframes
);
1870 nsDocShell::AddWeakPrivacyTransitionObserver(
1871 nsIPrivacyTransitionObserver
* aObserver
) {
1872 nsWeakPtr weakObs
= do_GetWeakReference(aObserver
);
1874 return NS_ERROR_NOT_AVAILABLE
;
1876 mPrivacyObservers
.AppendElement(weakObs
);
1881 nsDocShell::AddWeakReflowObserver(nsIReflowObserver
* aObserver
) {
1882 nsWeakPtr weakObs
= do_GetWeakReference(aObserver
);
1884 return NS_ERROR_FAILURE
;
1886 mReflowObservers
.AppendElement(weakObs
);
1891 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver
* aObserver
) {
1892 nsWeakPtr obs
= do_GetWeakReference(aObserver
);
1893 return mReflowObservers
.RemoveElement(obs
) ? NS_OK
: NS_ERROR_FAILURE
;
1897 nsDocShell::NotifyReflowObservers(bool aInterruptible
,
1898 DOMHighResTimeStamp aStart
,
1899 DOMHighResTimeStamp aEnd
) {
1900 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mReflowObservers
);
1901 while (iter
.HasMore()) {
1902 nsWeakPtr ref
= iter
.GetNext();
1903 nsCOMPtr
<nsIReflowObserver
> obs
= do_QueryReferent(ref
);
1906 } else if (aInterruptible
) {
1907 obs
->ReflowInterruptible(aStart
, aEnd
);
1909 obs
->Reflow(aStart
, aEnd
);
1916 nsDocShell::GetAllowMetaRedirects(bool* aReturn
) {
1917 NS_ENSURE_ARG_POINTER(aReturn
);
1919 *aReturn
= mAllowMetaRedirects
;
1924 nsDocShell::SetAllowMetaRedirects(bool aValue
) {
1925 mAllowMetaRedirects
= aValue
;
1930 nsDocShell::GetAllowSubframes(bool* aAllowSubframes
) {
1931 NS_ENSURE_ARG_POINTER(aAllowSubframes
);
1933 *aAllowSubframes
= mAllowSubframes
;
1938 nsDocShell::SetAllowSubframes(bool aAllowSubframes
) {
1939 mAllowSubframes
= aAllowSubframes
;
1944 nsDocShell::GetAllowImages(bool* aAllowImages
) {
1945 NS_ENSURE_ARG_POINTER(aAllowImages
);
1947 *aAllowImages
= mAllowImages
;
1952 nsDocShell::SetAllowImages(bool aAllowImages
) {
1953 mAllowImages
= aAllowImages
;
1958 nsDocShell::GetAllowMedia(bool* aAllowMedia
) {
1959 *aAllowMedia
= mAllowMedia
;
1964 nsDocShell::SetAllowMedia(bool aAllowMedia
) {
1965 mAllowMedia
= aAllowMedia
;
1967 // Mute or unmute audio contexts attached to the inner window.
1968 if (mScriptGlobal
) {
1969 if (nsPIDOMWindowInner
* innerWin
= mScriptGlobal
->GetCurrentInnerWindow()) {
1971 innerWin
->UnmuteAudioContexts();
1973 innerWin
->MuteAudioContexts();
1982 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch
) {
1983 *aAllowDNSPrefetch
= mAllowDNSPrefetch
;
1988 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch
) {
1989 mAllowDNSPrefetch
= aAllowDNSPrefetch
;
1994 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl
) {
1995 *aAllowWindowControl
= mAllowWindowControl
;
2000 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl
) {
2001 mAllowWindowControl
= aAllowWindowControl
;
2006 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting
) {
2007 *aAllowContentRetargeting
= mBrowsingContext
->GetAllowContentRetargeting();
2012 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting
) {
2013 BrowsingContext::Transaction txn
;
2014 txn
.SetAllowContentRetargeting(aAllowContentRetargeting
);
2015 txn
.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting
);
2016 return txn
.Commit(mBrowsingContext
);
2020 nsDocShell::GetAllowContentRetargetingOnChildren(
2021 bool* aAllowContentRetargetingOnChildren
) {
2022 *aAllowContentRetargetingOnChildren
=
2023 mBrowsingContext
->GetAllowContentRetargetingOnChildren();
2028 nsDocShell::SetAllowContentRetargetingOnChildren(
2029 bool aAllowContentRetargetingOnChildren
) {
2030 return mBrowsingContext
->SetAllowContentRetargetingOnChildren(
2031 aAllowContentRetargetingOnChildren
);
2035 nsDocShell::GetMayEnableCharacterEncodingMenu(
2036 bool* aMayEnableCharacterEncodingMenu
) {
2037 *aMayEnableCharacterEncodingMenu
= false;
2038 if (!mDocumentViewer
) {
2041 Document
* doc
= mDocumentViewer
->GetDocument();
2045 if (doc
->WillIgnoreCharsetOverride()) {
2049 *aMayEnableCharacterEncodingMenu
= true;
2054 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType
,
2055 DocShellEnumeratorDirection aDirection
,
2056 nsTArray
<RefPtr
<nsIDocShell
>>& aResult
) {
2059 nsDocShellEnumerator
docShellEnum(
2060 (aDirection
== ENUMERATE_FORWARDS
)
2061 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2062 : nsDocShellEnumerator::EnumerationDirection::Backwards
,
2065 nsresult rv
= docShellEnum
.BuildDocShellArray(aResult
);
2066 if (NS_FAILED(rv
)) {
2074 nsDocShell::GetAppType(AppType
* aAppType
) {
2075 *aAppType
= mAppType
;
2080 nsDocShell::SetAppType(AppType aAppType
) {
2081 mAppType
= aAppType
;
2086 nsDocShell::GetAllowAuth(bool* aAllowAuth
) {
2087 *aAllowAuth
= mAllowAuth
;
2092 nsDocShell::SetAllowAuth(bool aAllowAuth
) {
2093 mAllowAuth
= aAllowAuth
;
2098 nsDocShell::GetZoom(float* aZoom
) {
2099 NS_ENSURE_ARG_POINTER(aZoom
);
2105 nsDocShell::SetZoom(float aZoom
) { return NS_ERROR_NOT_IMPLEMENTED
; }
2108 nsDocShell::GetBusyFlags(BusyFlags
* aBusyFlags
) {
2109 NS_ENSURE_ARG_POINTER(aBusyFlags
);
2111 *aBusyFlags
= mBusyFlags
;
2116 nsDocShell::TabToTreeOwner(bool aForward
, bool aForDocumentNavigation
,
2118 NS_ENSURE_ARG_POINTER(aTookFocus
);
2120 nsCOMPtr
<nsIWebBrowserChromeFocus
> chromeFocus
= do_GetInterface(mTreeOwner
);
2124 NS_SUCCEEDED(chromeFocus
->FocusNextElement(aForDocumentNavigation
));
2127 NS_SUCCEEDED(chromeFocus
->FocusPrevElement(aForDocumentNavigation
));
2130 *aTookFocus
= false;
2137 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate
** aLoadURIDelegate
) {
2138 nsCOMPtr
<nsILoadURIDelegate
> delegate
= GetLoadURIDelegate();
2139 delegate
.forget(aLoadURIDelegate
);
2143 already_AddRefed
<nsILoadURIDelegate
> nsDocShell::GetLoadURIDelegate() {
2144 if (nsCOMPtr
<nsILoadURIDelegate
> result
=
2145 do_QueryActor("LoadURIDelegate", GetDocument())) {
2146 return result
.forget();
2153 nsDocShell::GetUseErrorPages(bool* aUseErrorPages
) {
2154 *aUseErrorPages
= mBrowsingContext
->GetUseErrorPages();
2159 nsDocShell::SetUseErrorPages(bool aUseErrorPages
) {
2160 return mBrowsingContext
->SetUseErrorPages(aUseErrorPages
);
2164 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex
) {
2165 *aPreviousEntryIndex
= mPreviousEntryIndex
;
2170 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex
) {
2171 *aLoadedEntryIndex
= mLoadedEntryIndex
;
2176 nsDocShell::HistoryPurged(int32_t aNumEntries
) {
2177 // These indices are used for fastback cache eviction, to determine
2178 // which session history entries are candidates for content viewer
2179 // eviction. We need to adjust by the number of entries that we
2180 // just purged from history, so that we look at the right session history
2181 // entries during eviction.
2182 mPreviousEntryIndex
= std::max(-1, mPreviousEntryIndex
- aNumEntries
);
2183 mLoadedEntryIndex
= std::max(0, mLoadedEntryIndex
- aNumEntries
);
2185 for (auto* child
: mChildList
.ForwardRange()) {
2186 nsCOMPtr
<nsIDocShell
> shell
= do_QueryObject(child
);
2188 shell
->HistoryPurged(aNumEntries
);
2195 nsresult
nsDocShell::HistoryEntryRemoved(int32_t aIndex
) {
2196 // These indices are used for fastback cache eviction, to determine
2197 // which session history entries are candidates for content viewer
2198 // eviction. We need to adjust by the number of entries that we
2199 // just purged from history, so that we look at the right session history
2200 // entries during eviction.
2201 if (aIndex
== mPreviousEntryIndex
) {
2202 mPreviousEntryIndex
= -1;
2203 } else if (aIndex
< mPreviousEntryIndex
) {
2204 --mPreviousEntryIndex
;
2206 if (mLoadedEntryIndex
== aIndex
) {
2207 mLoadedEntryIndex
= 0;
2208 } else if (aIndex
< mLoadedEntryIndex
) {
2209 --mLoadedEntryIndex
;
2212 for (auto* child
: mChildList
.ForwardRange()) {
2213 nsCOMPtr
<nsIDocShell
> shell
= do_QueryObject(child
);
2215 static_cast<nsDocShell
*>(shell
.get())->HistoryEntryRemoved(aIndex
);
2222 nsresult
nsDocShell::Now(DOMHighResTimeStamp
* aWhen
) {
2223 *aWhen
= (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2228 nsDocShell::SetWindowDraggingAllowed(bool aValue
) {
2229 RefPtr
<nsDocShell
> parent
= GetInProcessParentDocshell();
2230 if (!aValue
&& mItemType
== typeChrome
&& !parent
) {
2231 // Window dragging is always allowed for top level
2232 // chrome docshells.
2233 return NS_ERROR_FAILURE
;
2235 mWindowDraggingAllowed
= aValue
;
2240 nsDocShell::GetWindowDraggingAllowed(bool* aValue
) {
2241 // window dragging regions in CSS (-moz-window-drag:drag)
2242 // can be slow. Default behavior is to only allow it for
2243 // chrome top level windows.
2244 RefPtr
<nsDocShell
> parent
= GetInProcessParentDocshell();
2245 if (mItemType
== typeChrome
&& !parent
) {
2246 // Top level chrome window
2249 *aValue
= mWindowDraggingAllowed
;
2255 nsDocShell::GetCurrentDocumentChannel(nsIChannel
** aResult
) {
2256 NS_IF_ADDREF(*aResult
= GetCurrentDocChannel());
2260 nsIChannel
* nsDocShell::GetCurrentDocChannel() {
2261 if (mDocumentViewer
) {
2262 Document
* doc
= mDocumentViewer
->GetDocument();
2264 return doc
->GetChannel();
2271 nsDocShell::AddWeakScrollObserver(nsIScrollObserver
* aObserver
) {
2272 nsWeakPtr weakObs
= do_GetWeakReference(aObserver
);
2274 return NS_ERROR_FAILURE
;
2276 mScrollObservers
.AppendElement(weakObs
);
2281 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver
* aObserver
) {
2282 nsWeakPtr obs
= do_GetWeakReference(aObserver
);
2283 return mScrollObservers
.RemoveElement(obs
) ? NS_OK
: NS_ERROR_FAILURE
;
2286 void nsDocShell::NotifyAsyncPanZoomStarted() {
2287 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mScrollObservers
);
2288 while (iter
.HasMore()) {
2289 nsWeakPtr ref
= iter
.GetNext();
2290 nsCOMPtr
<nsIScrollObserver
> obs
= do_QueryReferent(ref
);
2292 obs
->AsyncPanZoomStarted();
2299 void nsDocShell::NotifyAsyncPanZoomStopped() {
2300 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mScrollObservers
);
2301 while (iter
.HasMore()) {
2302 nsWeakPtr ref
= iter
.GetNext();
2303 nsCOMPtr
<nsIScrollObserver
> obs
= do_QueryReferent(ref
);
2305 obs
->AsyncPanZoomStopped();
2313 nsDocShell::NotifyScrollObservers() {
2314 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mScrollObservers
);
2315 while (iter
.HasMore()) {
2316 nsWeakPtr ref
= iter
.GetNext();
2317 nsCOMPtr
<nsIScrollObserver
> obs
= do_QueryReferent(ref
);
2319 obs
->ScrollPositionChanged();
2327 //*****************************************************************************
2328 // nsDocShell::nsIDocShellTreeItem
2329 //*****************************************************************************
2332 nsDocShell::GetName(nsAString
& aName
) {
2333 aName
= mBrowsingContext
->Name();
2338 nsDocShell::SetName(const nsAString
& aName
) {
2339 return mBrowsingContext
->SetName(aName
);
2343 nsDocShell::NameEquals(const nsAString
& aName
, bool* aResult
) {
2344 NS_ENSURE_ARG_POINTER(aResult
);
2345 *aResult
= mBrowsingContext
->NameEquals(aName
);
2350 nsDocShell::GetCustomUserAgent(nsAString
& aCustomUserAgent
) {
2351 mBrowsingContext
->GetCustomUserAgent(aCustomUserAgent
);
2356 nsDocShell::SetCustomUserAgent(const nsAString
& aCustomUserAgent
) {
2357 if (mWillChangeProcess
) {
2358 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2359 return NS_ERROR_FAILURE
;
2362 return mBrowsingContext
->SetCustomUserAgent(aCustomUserAgent
);
2366 nsDocShell::ClearCachedPlatform() {
2367 nsCOMPtr
<nsPIDOMWindowInner
> win
=
2368 mScriptGlobal
? mScriptGlobal
->GetCurrentInnerWindow() : nullptr;
2370 Navigator
* navigator
= win
->Navigator();
2372 navigator
->ClearPlatformCache();
2380 nsDocShell::ClearCachedUserAgent() {
2381 nsCOMPtr
<nsPIDOMWindowInner
> win
=
2382 mScriptGlobal
? mScriptGlobal
->GetCurrentInnerWindow() : nullptr;
2384 Navigator
* navigator
= win
->Navigator();
2386 navigator
->ClearUserAgentCache();
2394 nsDocShell::GetMetaViewportOverride(
2395 MetaViewportOverride
* aMetaViewportOverride
) {
2396 NS_ENSURE_ARG_POINTER(aMetaViewportOverride
);
2398 *aMetaViewportOverride
= mMetaViewportOverride
;
2403 nsDocShell::SetMetaViewportOverride(
2404 MetaViewportOverride aMetaViewportOverride
) {
2405 // We don't have a way to verify this coming from Javascript, so this check is
2407 if (!(aMetaViewportOverride
== META_VIEWPORT_OVERRIDE_NONE
||
2408 aMetaViewportOverride
== META_VIEWPORT_OVERRIDE_ENABLED
||
2409 aMetaViewportOverride
== META_VIEWPORT_OVERRIDE_DISABLED
)) {
2410 return NS_ERROR_INVALID_ARG
;
2413 mMetaViewportOverride
= aMetaViewportOverride
;
2415 // Inform our presShell that it needs to re-check its need for a viewport
2417 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
2418 presShell
->MaybeRecreateMobileViewportManager(true);
2425 int32_t nsDocShell::ItemType() { return mItemType
; }
2428 nsDocShell::GetItemType(int32_t* aItemType
) {
2429 NS_ENSURE_ARG_POINTER(aItemType
);
2431 MOZ_DIAGNOSTIC_ASSERT(
2432 (mBrowsingContext
->IsContent() ? typeContent
: typeChrome
) == mItemType
);
2433 *aItemType
= mItemType
;
2438 nsDocShell::GetInProcessParent(nsIDocShellTreeItem
** aParent
) {
2442 CallQueryInterface(mParent
, aParent
);
2444 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2445 // don't want to throw; we just want to return null.
2449 // With Fission, related nsDocShell objects may exist in a different process. In
2450 // that case, this method will return `nullptr`, despite a parent nsDocShell
2453 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2454 // parent entry is not in the current process, and handle the case where the
2455 // parent nsDocShell is inaccessible.
2456 already_AddRefed
<nsDocShell
> nsDocShell::GetInProcessParentDocshell() {
2457 nsCOMPtr
<nsIDocShell
> docshell
= do_QueryInterface(GetAsSupports(mParent
));
2458 return docshell
.forget().downcast
<nsDocShell
>();
2461 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal
* aPrincipal
) {
2462 MOZ_ASSERT(!mIsBeingDestroyed
);
2464 // If there is an existing document then there is no need to create
2465 // a client for a future initial about:blank document.
2466 if (mScriptGlobal
&& mScriptGlobal
->GetCurrentInnerWindow() &&
2467 mScriptGlobal
->GetCurrentInnerWindow()->GetExtantDoc()) {
2468 MOZ_DIAGNOSTIC_ASSERT(
2469 mScriptGlobal
->GetCurrentInnerWindow()->GetClientInfo().isSome());
2470 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource
);
2474 // Don't recreate the initial client source. We call this multiple times
2475 // when DoChannelLoad() is called before CreateAboutBlankDocumentViewer.
2476 if (mInitialClientSource
) {
2480 // Don't pre-allocate the client when we are sandboxed. The inherited
2481 // principal does not take sandboxing into account.
2482 // TODO: Refactor sandboxing principal code out so we can use it here.
2483 if (!aPrincipal
&& mBrowsingContext
->GetSandboxFlags()) {
2487 // We cannot get inherited foreign partitioned principal here. Instead, we
2488 // directly check which principal we want to inherit for the service worker.
2489 nsIPrincipal
* principal
=
2492 : GetInheritedPrincipal(
2493 false, StoragePrincipalHelper::
2494 ShouldUsePartitionPrincipalForServiceWorker(this));
2496 // Sometimes there is no principal available when we are called from
2497 // CreateAboutBlankDocumentViewer. For example, sometimes the principal
2498 // is only extracted from the load context after the document is created
2499 // in Document::ResetToURI(). Ideally we would do something similar
2500 // here, but for now lets just avoid the issue by not preallocating the
2506 nsCOMPtr
<nsPIDOMWindowOuter
> win
= GetWindow();
2511 mInitialClientSource
= ClientManager::CreateSource(
2512 ClientType::Window
, GetMainThreadSerialEventTarget(), principal
);
2513 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource
);
2515 // Mark the initial client as execution ready, but owned by the docshell.
2516 // If the client is actually used this will cause ClientSource to force
2517 // the creation of the initial about:blank by calling
2518 // nsDocShell::GetDocument().
2519 mInitialClientSource
->DocShellExecutionReady(this);
2521 // Next, check to see if the parent is controlled.
2522 nsCOMPtr
<nsIDocShell
> parent
= GetInProcessParentDocshell();
2523 nsPIDOMWindowOuter
* parentOuter
= parent
? parent
->GetWindow() : nullptr;
2524 nsPIDOMWindowInner
* parentInner
=
2525 parentOuter
? parentOuter
->GetCurrentInnerWindow() : nullptr;
2530 nsCOMPtr
<nsIURI
> uri
;
2531 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri
), "about:blank"_ns
));
2533 // We're done if there is no parent controller or if this docshell
2534 // is not permitted to control for some reason.
2535 Maybe
<ServiceWorkerDescriptor
> controller(parentInner
->GetController());
2536 if (controller
.isNothing() ||
2537 !ServiceWorkerAllowedToControlWindow(principal
, uri
)) {
2541 mInitialClientSource
->InheritController(controller
.ref());
2544 Maybe
<ClientInfo
> nsDocShell::GetInitialClientInfo() const {
2545 if (mInitialClientSource
) {
2546 Maybe
<ClientInfo
> result
;
2547 result
.emplace(mInitialClientSource
->Info());
2551 nsPIDOMWindowInner
* innerWindow
=
2552 mScriptGlobal
? mScriptGlobal
->GetCurrentInnerWindow() : nullptr;
2553 Document
* doc
= innerWindow
? innerWindow
->GetExtantDoc() : nullptr;
2555 if (!doc
|| !doc
->IsInitialDocument()) {
2556 return Maybe
<ClientInfo
>();
2559 return innerWindow
->GetClientInfo();
2562 nsresult
nsDocShell::SetDocLoaderParent(nsDocLoader
* aParent
) {
2563 bool wasFrame
= IsSubframe();
2565 nsresult rv
= nsDocLoader::SetDocLoaderParent(aParent
);
2566 NS_ENSURE_SUCCESS(rv
, rv
);
2568 nsCOMPtr
<nsISupportsPriority
> priorityGroup
= do_QueryInterface(mLoadGroup
);
2569 if (wasFrame
!= IsSubframe() && priorityGroup
) {
2570 priorityGroup
->AdjustPriority(wasFrame
? -1 : 1);
2573 // Curse ambiguous nsISupports inheritance!
2574 nsISupports
* parent
= GetAsSupports(aParent
);
2576 // If parent is another docshell, we inherit all their flags for
2577 // allowing plugins, scripting etc.
2579 nsCOMPtr
<nsIDocShell
> parentAsDocShell(do_QueryInterface(parent
));
2581 if (parentAsDocShell
) {
2582 if (mAllowMetaRedirects
&&
2583 NS_SUCCEEDED(parentAsDocShell
->GetAllowMetaRedirects(&value
))) {
2584 SetAllowMetaRedirects(value
);
2586 if (mAllowSubframes
&&
2587 NS_SUCCEEDED(parentAsDocShell
->GetAllowSubframes(&value
))) {
2588 SetAllowSubframes(value
);
2591 NS_SUCCEEDED(parentAsDocShell
->GetAllowImages(&value
))) {
2592 SetAllowImages(value
);
2594 SetAllowMedia(parentAsDocShell
->GetAllowMedia() && mAllowMedia
);
2595 if (mAllowWindowControl
&&
2596 NS_SUCCEEDED(parentAsDocShell
->GetAllowWindowControl(&value
))) {
2597 SetAllowWindowControl(value
);
2599 if (NS_FAILED(parentAsDocShell
->GetAllowDNSPrefetch(&value
))) {
2602 SetAllowDNSPrefetch(mAllowDNSPrefetch
&& value
);
2604 // We don't need to inherit metaViewportOverride, because the viewport
2605 // is only relevant for the outermost nsDocShell, not for any iframes
2606 // like this that might be embedded within it.
2609 nsCOMPtr
<nsIURIContentListener
> parentURIListener(do_GetInterface(parent
));
2610 if (parentURIListener
) {
2611 mContentListener
->SetParentContentListener(parentURIListener
);
2617 void nsDocShell::MaybeRestoreWindowName() {
2618 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2622 // We only restore window.name for the top-level content.
2623 if (!mBrowsingContext
->IsTopContent()) {
2629 // Following implements https://html.spec.whatwg.org/#history-traversal:
2630 // Step 4.4. Check if the loading entry has a name.
2633 mLSHE
->GetName(name
);
2636 if (mLoadingEntry
) {
2637 name
= mLoadingEntry
->mInfo
.GetName();
2640 if (name
.IsEmpty()) {
2644 // Step 4.4.1. Set the name to the browsing context.
2645 Unused
<< mBrowsingContext
->SetName(name
);
2647 // Step 4.4.2. Clear the name of all entries that are contiguous and
2648 // same-origin with the loading entry.
2650 nsSHistory::WalkContiguousEntries(
2651 mLSHE
, [](nsISHEntry
* aEntry
) { aEntry
->SetName(EmptyString()); });
2654 if (mLoadingEntry
) {
2655 // Clear the name of the session entry in the child side. For parent side,
2656 // the clearing will be done when we commit the history to the parent.
2657 mLoadingEntry
->mInfo
.SetName(EmptyString());
2661 void nsDocShell::StoreWindowNameToSHEntries() {
2662 MOZ_ASSERT(mBrowsingContext
->IsTopContent());
2665 mBrowsingContext
->GetName(name
);
2668 nsSHistory::WalkContiguousEntries(
2669 mOSHE
, [&](nsISHEntry
* aEntry
) { aEntry
->SetName(name
); });
2672 if (mozilla::SessionHistoryInParent()) {
2673 if (XRE_IsParentProcess()) {
2674 SessionHistoryEntry
* entry
=
2675 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
2677 nsSHistory::WalkContiguousEntries(
2678 entry
, [&](nsISHEntry
* aEntry
) { aEntry
->SetName(name
); });
2681 // Ask parent process to store the name in entries.
2683 << ContentChild::GetSingleton()
2684 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2685 mBrowsingContext
, name
);
2691 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem
** aParent
) {
2692 if (BrowsingContext
* parentBC
= mBrowsingContext
->GetParent()) {
2693 *aParent
= do_AddRef(parentBC
->GetDocShell()).take();
2699 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem
** aRootTreeItem
) {
2700 NS_ENSURE_ARG_POINTER(aRootTreeItem
);
2702 RefPtr
<nsDocShell
> root
= this;
2703 RefPtr
<nsDocShell
> parent
= root
->GetInProcessParentDocshell();
2706 parent
= root
->GetInProcessParentDocshell();
2709 root
.forget(aRootTreeItem
);
2714 nsDocShell::GetInProcessSameTypeRootTreeItem(
2715 nsIDocShellTreeItem
** aRootTreeItem
) {
2716 NS_ENSURE_ARG_POINTER(aRootTreeItem
);
2717 *aRootTreeItem
= static_cast<nsIDocShellTreeItem
*>(this);
2719 nsCOMPtr
<nsIDocShellTreeItem
> parent
;
2720 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent
)),
2723 *aRootTreeItem
= parent
;
2725 (*aRootTreeItem
)->GetInProcessSameTypeParent(getter_AddRefs(parent
)),
2728 NS_ADDREF(*aRootTreeItem
);
2733 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner
** aTreeOwner
) {
2734 NS_ENSURE_ARG_POINTER(aTreeOwner
);
2736 *aTreeOwner
= mTreeOwner
;
2737 NS_IF_ADDREF(*aTreeOwner
);
2742 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner
* aTreeOwner
) {
2743 if (mIsBeingDestroyed
&& aTreeOwner
) {
2744 return NS_ERROR_FAILURE
;
2747 // Don't automatically set the progress based on the tree owner for frames
2748 if (!IsSubframe()) {
2749 nsCOMPtr
<nsIWebProgress
> webProgress
=
2750 do_QueryInterface(GetAsSupports(this));
2753 nsCOMPtr
<nsIWebProgressListener
> oldListener
=
2754 do_QueryInterface(mTreeOwner
);
2755 nsCOMPtr
<nsIWebProgressListener
> newListener
=
2756 do_QueryInterface(aTreeOwner
);
2759 webProgress
->RemoveProgressListener(oldListener
);
2763 webProgress
->AddProgressListener(newListener
,
2764 nsIWebProgress::NOTIFY_ALL
);
2769 mTreeOwner
= aTreeOwner
; // Weak reference per API
2771 for (auto* childDocLoader
: mChildList
.ForwardRange()) {
2772 nsCOMPtr
<nsIDocShellTreeItem
> child
= do_QueryObject(childDocLoader
);
2773 NS_ENSURE_TRUE(child
, NS_ERROR_FAILURE
);
2775 if (child
->ItemType() == mItemType
) {
2776 child
->SetTreeOwner(aTreeOwner
);
2780 // If we're in the content process and have had a TreeOwner set on us, extract
2781 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2782 // that it hasn't changed.
2783 if (mTreeOwner
&& XRE_IsContentProcess()) {
2784 nsCOMPtr
<nsIBrowserChild
> newBrowserChild
= do_GetInterface(mTreeOwner
);
2785 MOZ_ASSERT(newBrowserChild
,
2786 "No BrowserChild actor for tree owner in Content!");
2788 if (mBrowserChild
) {
2789 nsCOMPtr
<nsIBrowserChild
> oldBrowserChild
=
2790 do_QueryReferent(mBrowserChild
);
2792 oldBrowserChild
== newBrowserChild
,
2793 "Cannot change BrowserChild during nsDocShell lifetime!");
2795 mBrowserChild
= do_GetWeakReference(newBrowserChild
);
2803 nsDocShell::GetHistoryID(nsID
& aID
) {
2804 aID
= mBrowsingContext
->GetHistoryID();
2808 const nsID
& nsDocShell::HistoryID() { return mBrowsingContext
->GetHistoryID(); }
2811 nsDocShell::GetIsInUnload(bool* aIsInUnload
) {
2812 *aIsInUnload
= mFiredUnloadEvent
;
2817 nsDocShell::GetInProcessChildCount(int32_t* aChildCount
) {
2818 NS_ENSURE_ARG_POINTER(aChildCount
);
2819 *aChildCount
= mChildList
.Length();
2824 nsDocShell::AddChild(nsIDocShellTreeItem
* aChild
) {
2825 NS_ENSURE_ARG_POINTER(aChild
);
2827 RefPtr
<nsDocLoader
> childAsDocLoader
= GetAsDocLoader(aChild
);
2828 NS_ENSURE_TRUE(childAsDocLoader
, NS_ERROR_UNEXPECTED
);
2830 // Make sure we're not creating a loop in the docshell tree
2831 nsDocLoader
* ancestor
= this;
2833 if (childAsDocLoader
== ancestor
) {
2834 return NS_ERROR_ILLEGAL_VALUE
;
2836 ancestor
= ancestor
->GetParent();
2839 // Make sure to remove the child from its current parent.
2840 nsDocLoader
* childsParent
= childAsDocLoader
->GetParent();
2842 nsresult rv
= childsParent
->RemoveChildLoader(childAsDocLoader
);
2843 NS_ENSURE_SUCCESS(rv
, rv
);
2846 // Make sure to clear the treeowner in case this child is a different type
2848 aChild
->SetTreeOwner(nullptr);
2850 nsresult res
= AddChildLoader(childAsDocLoader
);
2851 NS_ENSURE_SUCCESS(res
, res
);
2852 NS_ASSERTION(!mChildList
.IsEmpty(),
2853 "child list must not be empty after a successful add");
2855 /* Set the child's global history if the parent has one */
2856 if (mBrowsingContext
->GetUseGlobalHistory()) {
2857 // childDocShell->SetUseGlobalHistory(true);
2858 // this should be set through BC inherit
2859 MOZ_ASSERT(aChild
->GetBrowsingContext()->GetUseGlobalHistory());
2862 if (aChild
->ItemType() != mItemType
) {
2866 aChild
->SetTreeOwner(mTreeOwner
);
2868 nsCOMPtr
<nsIDocShell
> childAsDocShell(do_QueryInterface(aChild
));
2869 if (!childAsDocShell
) {
2873 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2875 // Now take this document's charset and set the child's parentCharset field
2876 // to it. We'll later use that field, in the loading process, for the
2877 // charset choosing algorithm.
2878 // If we fail, at any point, we just return NS_OK.
2879 // This code has some performance impact. But this will be reduced when
2880 // the current charset will finally be stored as an Atom, avoiding the
2881 // alias resolution extra look-up.
2883 // we are NOT going to propagate the charset is this Chrome's docshell
2884 if (mItemType
== nsIDocShellTreeItem::typeChrome
) {
2888 // get the parent's current charset
2889 if (!mDocumentViewer
) {
2892 Document
* doc
= mDocumentViewer
->GetDocument();
2897 const Encoding
* parentCS
= doc
->GetDocumentCharacterSet();
2898 int32_t charsetSource
= doc
->GetDocumentCharacterSetSource();
2899 // set the child's parentCharset
2900 childAsDocShell
->SetParentCharset(parentCS
, charsetSource
,
2901 doc
->NodePrincipal());
2903 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2904 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2910 nsDocShell::RemoveChild(nsIDocShellTreeItem
* aChild
) {
2911 NS_ENSURE_ARG_POINTER(aChild
);
2913 RefPtr
<nsDocLoader
> childAsDocLoader
= GetAsDocLoader(aChild
);
2914 NS_ENSURE_TRUE(childAsDocLoader
, NS_ERROR_UNEXPECTED
);
2916 nsresult rv
= RemoveChildLoader(childAsDocLoader
);
2917 NS_ENSURE_SUCCESS(rv
, rv
);
2919 aChild
->SetTreeOwner(nullptr);
2921 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader
);
2925 nsDocShell::GetInProcessChildAt(int32_t aIndex
, nsIDocShellTreeItem
** aChild
) {
2926 NS_ENSURE_ARG_POINTER(aChild
);
2928 RefPtr
<nsDocShell
> child
= GetInProcessChildAt(aIndex
);
2929 NS_ENSURE_TRUE(child
, NS_ERROR_UNEXPECTED
);
2931 child
.forget(aChild
);
2936 nsDocShell
* nsDocShell::GetInProcessChildAt(int32_t aIndex
) {
2939 NS_WARNING("Negative index passed to GetChildAt");
2940 } else if (static_cast<uint32_t>(aIndex
) >= mChildList
.Length()) {
2941 NS_WARNING("Too large an index passed to GetChildAt");
2945 nsIDocumentLoader
* child
= ChildAt(aIndex
);
2947 // child may be nullptr here.
2948 return static_cast<nsDocShell
*>(child
);
2951 nsresult
nsDocShell::AddChildSHEntry(nsISHEntry
* aCloneRef
,
2952 nsISHEntry
* aNewEntry
,
2953 int32_t aChildOffset
, uint32_t aLoadType
,
2954 bool aCloneChildren
) {
2955 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2956 nsresult rv
= NS_OK
;
2958 if (mLSHE
&& aLoadType
!= LOAD_PUSHSTATE
) {
2959 /* You get here if you are currently building a
2960 * hierarchy ie.,you just visited a frameset page
2962 if (NS_FAILED(mLSHE
->ReplaceChild(aNewEntry
))) {
2963 rv
= mLSHE
->AddChild(aNewEntry
, aChildOffset
);
2965 } else if (!aCloneRef
) {
2966 /* This is an initial load in some subframe. Just append it if we can */
2968 rv
= mOSHE
->AddChild(aNewEntry
, aChildOffset
, UseRemoteSubframes());
2971 RefPtr
<ChildSHistory
> shistory
= GetRootSessionHistory();
2973 rv
= shistory
->LegacySHistory()->AddChildSHEntryHelper(
2974 aCloneRef
, aNewEntry
, mBrowsingContext
->Top(), aCloneChildren
);
2980 nsresult
nsDocShell::AddChildSHEntryToParent(nsISHEntry
* aNewEntry
,
2981 int32_t aChildOffset
,
2982 bool aCloneChildren
) {
2983 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2984 /* You will get here when you are in a subframe and
2985 * a new url has been loaded on you.
2986 * The mOSHE in this subframe will be the previous url's
2987 * mOSHE. This mOSHE will be used as the identification
2988 * for this subframe in the CloneAndReplace function.
2991 // In this case, we will end up calling AddEntry, which increases the
2992 // current index by 1
2993 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
2995 mPreviousEntryIndex
= rootSH
->Index();
2999 // XXX(farre): this is not Fission safe, expect errors. This never
3000 // get's executed once session history in the parent is enabled.
3001 nsCOMPtr
<nsIDocShell
> parent
= do_QueryInterface(GetAsSupports(mParent
), &rv
);
3002 NS_WARNING_ASSERTION(
3003 parent
|| !UseRemoteSubframes(),
3004 "Failed to add child session history entry! This will be resolved once "
3005 "session history in the parent is enabled.");
3007 rv
= nsDocShell::Cast(parent
)->AddChildSHEntry(
3008 mOSHE
, aNewEntry
, aChildOffset
, mLoadType
, aCloneChildren
);
3012 mLoadedEntryIndex
= rootSH
->Index();
3014 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
3015 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
3016 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex
,
3017 mLoadedEntryIndex
));
3025 nsDocShell::GetCurrentSHEntry(nsISHEntry
** aEntry
, bool* aOSHE
) {
3029 NS_ADDREF(*aEntry
= mLSHE
);
3031 NS_ADDREF(*aEntry
= mOSHE
);
3037 NS_IMETHODIMP
nsDocShell::SynchronizeLayoutHistoryState() {
3038 if (mActiveEntry
&& mActiveEntry
->GetLayoutHistoryState() &&
3040 if (XRE_IsContentProcess()) {
3041 dom::ContentChild
* contentChild
= dom::ContentChild::GetSingleton();
3043 contentChild
->SendSynchronizeLayoutHistoryState(
3044 mBrowsingContext
, mActiveEntry
->GetLayoutHistoryState());
3047 SessionHistoryEntry
* entry
=
3048 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
3050 entry
->SetLayoutHistoryState(mActiveEntry
->GetLayoutHistoryState());
3053 if (mLoadingEntry
&&
3054 mLoadingEntry
->mInfo
.SharedId() == mActiveEntry
->SharedId()) {
3055 mLoadingEntry
->mInfo
.SetLayoutHistoryState(
3056 mActiveEntry
->GetLayoutHistoryState());
3063 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags
) {
3065 mLoadGroup
->SetDefaultLoadFlags(aLoadFlags
);
3068 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3069 "propagate the mode to");
3073 nsIScriptGlobalObject
* nsDocShell::GetScriptGlobalObject() {
3074 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3075 return mScriptGlobal
;
3078 Document
* nsDocShell::GetDocument() {
3079 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), nullptr);
3080 return mDocumentViewer
->GetDocument();
3083 Document
* nsDocShell::GetExtantDocument() {
3084 return mDocumentViewer
? mDocumentViewer
->GetDocument() : nullptr;
3087 nsPIDOMWindowOuter
* nsDocShell::GetWindow() {
3088 if (NS_FAILED(EnsureScriptEnvironment())) {
3091 return mScriptGlobal
;
3095 nsDocShell::GetDomWindow(mozIDOMWindowProxy
** aWindow
) {
3096 NS_ENSURE_ARG_POINTER(aWindow
);
3098 nsresult rv
= EnsureScriptEnvironment();
3099 NS_ENSURE_SUCCESS(rv
, rv
);
3101 RefPtr
<nsGlobalWindowOuter
> window
= mScriptGlobal
;
3102 window
.forget(aWindow
);
3107 nsDocShell::GetMessageManager(ContentFrameMessageManager
** aMessageManager
) {
3108 RefPtr
<ContentFrameMessageManager
> mm
;
3109 if (RefPtr
<BrowserChild
> browserChild
= BrowserChild::GetFrom(this)) {
3110 mm
= browserChild
->GetMessageManager();
3111 } else if (nsPIDOMWindowOuter
* win
= GetWindow()) {
3112 mm
= win
->GetMessageManager();
3114 mm
.forget(aMessageManager
);
3119 nsDocShell::GetIsNavigating(bool* aOut
) {
3120 *aOut
= mIsNavigating
;
3124 void nsDocShell::ClearFrameHistory(nsISHEntry
* aEntry
) {
3125 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3126 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3127 if (!rootSH
|| !aEntry
) {
3131 rootSH
->LegacySHistory()->RemoveFrameEntries(aEntry
);
3134 //-------------------------------------
3135 //-- Helper Method for Print discovery
3136 //-------------------------------------
3137 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog
) {
3138 if (!mBrowsingContext
->Top()->GetIsPrinting()) {
3141 if (aDisplayErrorDialog
) {
3142 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE
, nullptr, nullptr, nullptr);
3147 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog
,
3148 bool aCheckIfUnloadFired
) {
3149 bool isAllowed
= !NavigationBlockedByPrinting(aDisplayPrintErrorDialog
) &&
3150 (!aCheckIfUnloadFired
|| !mFiredUnloadEvent
);
3154 if (!mDocumentViewer
) {
3157 bool firingBeforeUnload
;
3158 mDocumentViewer
->GetBeforeUnloadFiring(&firingBeforeUnload
);
3159 return !firingBeforeUnload
;
3162 //*****************************************************************************
3163 // nsDocShell::nsIWebNavigation
3164 //*****************************************************************************
3167 nsDocShell::GetCanGoBack(bool* aCanGoBack
) {
3168 *aCanGoBack
= false;
3169 if (!IsNavigationAllowed(false)) {
3170 return NS_OK
; // JS may not handle returning of an error code
3172 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3174 *aCanGoBack
= rootSH
->CanGo(-1);
3175 MOZ_LOG(gSHLog
, LogLevel::Verbose
,
3176 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack
));
3180 return NS_ERROR_FAILURE
;
3184 nsDocShell::GetCanGoForward(bool* aCanGoForward
) {
3185 *aCanGoForward
= false;
3186 if (!IsNavigationAllowed(false)) {
3187 return NS_OK
; // JS may not handle returning of an error code
3189 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3191 *aCanGoForward
= rootSH
->CanGo(1);
3192 MOZ_LOG(gSHLog
, LogLevel::Verbose
,
3193 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward
));
3196 return NS_ERROR_FAILURE
;
3200 nsDocShell::GoBack(bool aRequireUserInteraction
, bool aUserActivation
) {
3201 if (!IsNavigationAllowed()) {
3202 return NS_OK
; // JS may not handle returning of an error code
3205 auto cleanupIsNavigating
= MakeScopeExit([&]() { mIsNavigating
= false; });
3206 mIsNavigating
= true;
3208 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3209 NS_ENSURE_TRUE(rootSH
, NS_ERROR_FAILURE
);
3211 rootSH
->Go(-1, aRequireUserInteraction
, aUserActivation
, rv
);
3212 return rv
.StealNSResult();
3216 nsDocShell::GoForward(bool aRequireUserInteraction
, bool aUserActivation
) {
3217 if (!IsNavigationAllowed()) {
3218 return NS_OK
; // JS may not handle returning of an error code
3221 auto cleanupIsNavigating
= MakeScopeExit([&]() { mIsNavigating
= false; });
3222 mIsNavigating
= true;
3224 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3225 NS_ENSURE_TRUE(rootSH
, NS_ERROR_FAILURE
);
3227 rootSH
->Go(1, aRequireUserInteraction
, aUserActivation
, rv
);
3228 return rv
.StealNSResult();
3231 // XXX(nika): We may want to stop exposing this API in the child process? Going
3232 // to a specific index from multiple different processes could definitely race.
3234 nsDocShell::GotoIndex(int32_t aIndex
, bool aUserActivation
) {
3235 if (!IsNavigationAllowed()) {
3236 return NS_OK
; // JS may not handle returning of an error code
3239 auto cleanupIsNavigating
= MakeScopeExit([&]() { mIsNavigating
= false; });
3240 mIsNavigating
= true;
3242 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3243 NS_ENSURE_TRUE(rootSH
, NS_ERROR_FAILURE
);
3246 rootSH
->GotoIndex(aIndex
, aIndex
- rootSH
->Index(), false, aUserActivation
,
3248 return rv
.StealNSResult();
3251 nsresult
nsDocShell::LoadURI(nsIURI
* aURI
,
3252 const LoadURIOptions
& aLoadURIOptions
) {
3253 if (!IsNavigationAllowed()) {
3254 return NS_OK
; // JS may not handle returning of an error code
3256 RefPtr
<nsDocShellLoadState
> loadState
;
3257 nsresult rv
= nsDocShellLoadState::CreateFromLoadURIOptions(
3258 mBrowsingContext
, aURI
, aLoadURIOptions
, getter_AddRefs(loadState
));
3259 MOZ_ASSERT(rv
!= NS_ERROR_MALFORMED_URI
);
3260 if (NS_FAILED(rv
) || !loadState
) {
3261 return NS_ERROR_FAILURE
;
3264 return LoadURI(loadState
, true);
3268 nsDocShell::LoadURIFromScript(nsIURI
* aURI
,
3269 JS::Handle
<JS::Value
> aLoadURIOptions
,
3271 // generate dictionary for aLoadURIOptions and forward call
3272 LoadURIOptions loadURIOptions
;
3273 if (!loadURIOptions
.Init(aCx
, aLoadURIOptions
)) {
3274 return NS_ERROR_INVALID_ARG
;
3276 return LoadURI(aURI
, loadURIOptions
);
3279 nsresult
nsDocShell::FixupAndLoadURIString(
3280 const nsAString
& aURIString
, const LoadURIOptions
& aLoadURIOptions
) {
3281 if (!IsNavigationAllowed()) {
3282 return NS_OK
; // JS may not handle returning of an error code
3285 RefPtr
<nsDocShellLoadState
> loadState
;
3286 nsresult rv
= nsDocShellLoadState::CreateFromLoadURIOptions(
3287 mBrowsingContext
, aURIString
, aLoadURIOptions
, getter_AddRefs(loadState
));
3289 uint32_t loadFlags
= aLoadURIOptions
.mLoadFlags
;
3290 if (NS_ERROR_MALFORMED_URI
== rv
) {
3291 MOZ_LOG(gSHLog
, LogLevel::Debug
,
3292 ("Creating an active entry on nsDocShell %p to %s (because "
3293 "we're showing an error page)",
3294 this, NS_ConvertUTF16toUTF8(aURIString
).get()));
3296 // We need to store a session history entry. We don't have a valid URI, so
3297 // we use about:blank instead.
3298 nsCOMPtr
<nsIURI
> uri
;
3299 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri
), "about:blank"_ns
));
3300 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
;
3301 if (aLoadURIOptions
.mTriggeringPrincipal
) {
3302 triggeringPrincipal
= aLoadURIOptions
.mTriggeringPrincipal
;
3304 triggeringPrincipal
= nsContentUtils::GetSystemPrincipal();
3306 if (mozilla::SessionHistoryInParent()) {
3307 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(
3308 uri
, triggeringPrincipal
, nullptr, nullptr, nullptr,
3309 nsLiteralCString("text/html"));
3310 mBrowsingContext
->SetActiveSessionHistoryEntry(
3311 Nothing(), mActiveEntry
.get(), MAKE_LOAD_TYPE(LOAD_NORMAL
, loadFlags
),
3312 /* aUpdatedCacheKey = */ 0);
3314 if (DisplayLoadError(rv
, nullptr, PromiseFlatString(aURIString
).get(),
3316 (loadFlags
& LOAD_FLAGS_ERROR_LOAD_CHANGES_RV
) != 0) {
3317 return NS_ERROR_LOAD_SHOWED_ERRORPAGE
;
3321 if (NS_FAILED(rv
) || !loadState
) {
3322 return NS_ERROR_FAILURE
;
3325 return LoadURI(loadState
, true);
3329 nsDocShell::FixupAndLoadURIStringFromScript(
3330 const nsAString
& aURIString
, JS::Handle
<JS::Value
> aLoadURIOptions
,
3332 // generate dictionary for aLoadURIOptions and forward call
3333 LoadURIOptions loadURIOptions
;
3334 if (!loadURIOptions
.Init(aCx
, aLoadURIOptions
)) {
3335 return NS_ERROR_INVALID_ARG
;
3337 return FixupAndLoadURIString(aURIString
, loadURIOptions
);
3340 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent
) {
3341 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3342 // such as the content-chrome boundary, don't fire the error event.
3343 if (mBrowsingContext
->IsTopContent() || mBrowsingContext
->IsChrome()) {
3347 // If embedder is same-process, then unblocking the load event is already
3348 // handled by nsDocLoader. Fire the error event on our embedder element if
3351 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3352 // more like the remote case when in-process.
3353 RefPtr
<Element
> element
= mBrowsingContext
->GetEmbedderElement();
3355 if (aFireFrameErrorEvent
) {
3356 if (RefPtr
<nsFrameLoaderOwner
> flo
= do_QueryObject(element
)) {
3357 if (RefPtr
<nsFrameLoader
> fl
= flo
->GetFrameLoader()) {
3358 fl
->FireErrorEvent();
3365 // If we have a cross-process parent document, we must notify it that we no
3366 // longer block its load event. This is necessary for OOP sub-documents
3367 // because error documents do not result in a call to
3368 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3369 // (Obviously, we must do this before any of the returns below.)
3370 RefPtr
<BrowserChild
> browserChild
= BrowserChild::GetFrom(this);
3372 !mBrowsingContext
->GetParentWindowContext()->IsInProcess()) {
3373 mozilla::Unused
<< browserChild
->SendMaybeFireEmbedderLoadEvents(
3374 aFireFrameErrorEvent
? EmbedderElementEventType::ErrorEvent
3375 : EmbedderElementEventType::NoEvent
);
3380 nsDocShell::DisplayLoadError(nsresult aError
, nsIURI
* aURI
,
3381 const char16_t
* aURL
, nsIChannel
* aFailedChannel
,
3382 bool* aDisplayedErrorPage
) {
3383 MOZ_LOG(gDocShellLeakLog
, LogLevel::Debug
,
3384 ("DOCSHELL %p DisplayLoadError %s\n", this,
3385 aURI
? aURI
->GetSpecOrDefault().get() : ""));
3387 *aDisplayedErrorPage
= false;
3388 // Get prompt and string bundle services
3389 nsCOMPtr
<nsIPrompt
> prompter
;
3390 nsCOMPtr
<nsIStringBundle
> stringBundle
;
3391 GetPromptAndStringBundle(getter_AddRefs(prompter
),
3392 getter_AddRefs(stringBundle
));
3394 NS_ENSURE_TRUE(stringBundle
, NS_ERROR_FAILURE
);
3395 NS_ENSURE_TRUE(prompter
, NS_ERROR_FAILURE
);
3397 const char* error
= nullptr;
3398 // The key used to select the appropriate error message from the properties
3400 const char* errorDescriptionID
= nullptr;
3401 AutoTArray
<nsString
, 3> formatStrs
;
3402 bool addHostPort
= false;
3403 bool isBadStsCertError
= false;
3404 nsresult rv
= NS_OK
;
3405 nsAutoString messageStr
;
3406 nsAutoCString cssClass
;
3407 nsAutoCString errorPage
;
3409 errorPage
.AssignLiteral("neterror");
3411 // Turn the error code into a human readable error message.
3412 if (NS_ERROR_UNKNOWN_PROTOCOL
== aError
) {
3413 NS_ENSURE_ARG_POINTER(aURI
);
3415 // Extract the schemes into a comma delimited list.
3416 nsAutoCString scheme
;
3417 aURI
->GetScheme(scheme
);
3418 CopyASCIItoUTF16(scheme
, *formatStrs
.AppendElement());
3419 nsCOMPtr
<nsINestedURI
> nestedURI
= do_QueryInterface(aURI
);
3421 nsCOMPtr
<nsIURI
> tempURI
;
3423 rv2
= nestedURI
->GetInnerURI(getter_AddRefs(tempURI
));
3424 if (NS_SUCCEEDED(rv2
) && tempURI
) {
3425 tempURI
->GetScheme(scheme
);
3426 formatStrs
[0].AppendLiteral(", ");
3427 AppendASCIItoUTF16(scheme
, formatStrs
[0]);
3429 nestedURI
= do_QueryInterface(tempURI
);
3431 error
= "unknownProtocolFound";
3432 } else if (NS_ERROR_FILE_NOT_FOUND
== aError
) {
3433 NS_ENSURE_ARG_POINTER(aURI
);
3434 error
= "fileNotFound";
3435 } else if (NS_ERROR_FILE_ACCESS_DENIED
== aError
) {
3436 NS_ENSURE_ARG_POINTER(aURI
);
3437 error
= "fileAccessDenied";
3438 } else if (NS_ERROR_UNKNOWN_HOST
== aError
) {
3439 NS_ENSURE_ARG_POINTER(aURI
);
3442 nsCOMPtr
<nsIURI
> innermostURI
= NS_GetInnermostURI(aURI
);
3443 innermostURI
->GetHost(host
);
3444 CopyUTF8toUTF16(host
, *formatStrs
.AppendElement());
3445 errorDescriptionID
= "dnsNotFound2";
3446 error
= "dnsNotFound";
3447 } else if (NS_ERROR_CONNECTION_REFUSED
== aError
||
3448 NS_ERROR_PROXY_BAD_GATEWAY
== aError
) {
3449 NS_ENSURE_ARG_POINTER(aURI
);
3451 error
= "connectionFailure";
3452 } else if (NS_ERROR_NET_INTERRUPT
== aError
) {
3453 NS_ENSURE_ARG_POINTER(aURI
);
3455 error
= "netInterrupt";
3456 } else if (NS_ERROR_NET_TIMEOUT
== aError
||
3457 NS_ERROR_PROXY_GATEWAY_TIMEOUT
== aError
||
3458 NS_ERROR_NET_TIMEOUT_EXTERNAL
== aError
) {
3459 NS_ENSURE_ARG_POINTER(aURI
);
3462 aURI
->GetHost(host
);
3463 CopyUTF8toUTF16(host
, *formatStrs
.AppendElement());
3464 error
= "netTimeout";
3465 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION
== aError
||
3466 NS_ERROR_CSP_FORM_ACTION_VIOLATION
== aError
) {
3468 cssClass
.AssignLiteral("neterror");
3469 error
= "cspBlocked";
3470 } else if (NS_ERROR_XFO_VIOLATION
== aError
) {
3472 cssClass
.AssignLiteral("neterror");
3473 error
= "xfoBlocked";
3474 } else if (NS_ERROR_GET_MODULE(aError
) == NS_ERROR_MODULE_SECURITY
) {
3475 nsCOMPtr
<nsINSSErrorsService
> nsserr
=
3476 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID
);
3478 uint32_t errorClass
;
3479 if (!nsserr
|| NS_FAILED(nsserr
->GetErrorClass(aError
, &errorClass
))) {
3480 errorClass
= nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL
;
3483 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
3484 if (aFailedChannel
) {
3485 aFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
3488 uint32_t securityState
;
3489 tsi
->GetSecurityState(&securityState
);
3490 if (securityState
& nsIWebProgressListener::STATE_USES_SSL_3
) {
3491 error
= "sslv3Used";
3493 } else if (securityState
&
3494 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO
) {
3495 error
= "weakCryptoUsed";
3499 // No channel, let's obtain the generic error message
3501 nsserr
->GetErrorMessage(aError
, messageStr
);
3504 // We don't have a message string here anymore but DisplayLoadError
3505 // requires a non-empty messageStr.
3506 messageStr
.Truncate();
3507 messageStr
.AssignLiteral(u
" ");
3508 if (errorClass
== nsINSSErrorsService::ERROR_CLASS_BAD_CERT
) {
3509 error
= "nssBadCert";
3511 // If this is an HTTP Strict Transport Security host or a pinned host
3512 // and the certificate is bad, don't allow overrides (RFC 6797 section
3514 bool isStsHost
= false;
3515 bool isPinnedHost
= false;
3516 OriginAttributes attrsForHSTS
;
3517 if (aFailedChannel
) {
3518 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel
,
3521 attrsForHSTS
= GetOriginAttributes();
3524 if (XRE_IsParentProcess()) {
3525 nsCOMPtr
<nsISiteSecurityService
> sss
=
3526 do_GetService(NS_SSSERVICE_CONTRACTID
, &rv
);
3527 NS_ENSURE_SUCCESS(rv
, rv
);
3528 rv
= sss
->IsSecureURI(aURI
, attrsForHSTS
, &isStsHost
);
3529 NS_ENSURE_SUCCESS(rv
, rv
);
3531 mozilla::dom::ContentChild
* cc
=
3532 mozilla::dom::ContentChild::GetSingleton();
3533 cc
->SendIsSecureURI(aURI
, attrsForHSTS
, &isStsHost
);
3535 nsCOMPtr
<nsIPublicKeyPinningService
> pkps
=
3536 do_GetService(NS_PKPSERVICE_CONTRACTID
, &rv
);
3537 NS_ENSURE_SUCCESS(rv
, rv
);
3538 rv
= pkps
->HostHasPins(aURI
, &isPinnedHost
);
3540 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3542 cssClass
.AssignLiteral("expertBadCert");
3545 // HSTS/pinning takes precedence over the expert bad cert pref. We
3546 // never want to show the "Add Exception" button for these sites.
3547 // In the future we should differentiate between an HSTS host and a
3548 // pinned host and display a more informative message to the user.
3549 if (isStsHost
|| isPinnedHost
) {
3550 isBadStsCertError
= true;
3551 cssClass
.AssignLiteral("badStsCert");
3554 errorPage
.Assign("certerror");
3556 error
= "nssFailure2";
3558 } else if (NS_ERROR_PHISHING_URI
== aError
||
3559 NS_ERROR_MALWARE_URI
== aError
||
3560 NS_ERROR_UNWANTED_URI
== aError
||
3561 NS_ERROR_HARMFUL_URI
== aError
) {
3563 aURI
->GetHost(host
);
3564 CopyUTF8toUTF16(host
, *formatStrs
.AppendElement());
3566 // Malware and phishing detectors may want to use an alternate error
3567 // page, but if the pref's not set, we'll fall back on the standard page
3568 nsAutoCString alternateErrorPage
;
3569 nsresult rv
= Preferences::GetCString("urlclassifier.alternate_error_page",
3570 alternateErrorPage
);
3571 if (NS_SUCCEEDED(rv
)) {
3572 errorPage
.Assign(alternateErrorPage
);
3575 if (NS_ERROR_PHISHING_URI
== aError
) {
3576 error
= "deceptiveBlocked";
3577 } else if (NS_ERROR_MALWARE_URI
== aError
) {
3578 error
= "malwareBlocked";
3579 } else if (NS_ERROR_UNWANTED_URI
== aError
) {
3580 error
= "unwantedBlocked";
3581 } else if (NS_ERROR_HARMFUL_URI
== aError
) {
3582 error
= "harmfulBlocked";
3585 cssClass
.AssignLiteral("blacklist");
3586 } else if (NS_ERROR_CONTENT_CRASHED
== aError
) {
3587 errorPage
.AssignLiteral("tabcrashed");
3588 error
= "tabcrashed";
3590 RefPtr
<EventTarget
> handler
= mChromeEventHandler
;
3592 nsCOMPtr
<Element
> element
= do_QueryInterface(handler
);
3593 element
->GetAttribute(u
"crashedPageTitle"_ns
, messageStr
);
3596 // DisplayLoadError requires a non-empty messageStr to proceed and call
3597 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3598 // space which will be trimmed and thus treated as empty by the front-end.
3599 if (messageStr
.IsEmpty()) {
3600 messageStr
.AssignLiteral(u
" ");
3602 } else if (NS_ERROR_FRAME_CRASHED
== aError
) {
3603 errorPage
.AssignLiteral("framecrashed");
3604 error
= "framecrashed";
3605 messageStr
.AssignLiteral(u
" ");
3606 } else if (NS_ERROR_BUILDID_MISMATCH
== aError
) {
3607 errorPage
.AssignLiteral("restartrequired");
3608 error
= "restartrequired";
3610 // DisplayLoadError requires a non-empty messageStr to proceed and call
3611 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3612 // space which will be trimmed and thus treated as empty by the front-end.
3613 if (messageStr
.IsEmpty()) {
3614 messageStr
.AssignLiteral(u
" ");
3617 // Errors requiring simple formatting
3619 case NS_ERROR_MALFORMED_URI
:
3621 error
= "malformedURI";
3622 errorDescriptionID
= "malformedURI2";
3624 case NS_ERROR_REDIRECT_LOOP
:
3625 // Doc failed to load because the server generated too many redirects
3626 error
= "redirectLoop";
3628 case NS_ERROR_UNKNOWN_SOCKET_TYPE
:
3629 // Doc failed to load because PSM is not installed
3630 error
= "unknownSocketType";
3632 case NS_ERROR_NET_RESET
:
3633 // Doc failed to load because the server kept reseting the connection
3634 // before we could read any data from it
3637 case NS_ERROR_DOCUMENT_NOT_CACHED
:
3638 // Doc failed to load because the cache does not contain a copy of
3640 error
= "notCached";
3642 case NS_ERROR_OFFLINE
:
3643 // Doc failed to load because we are offline.
3644 error
= "netOffline";
3646 case NS_ERROR_DOCUMENT_IS_PRINTMODE
:
3647 // Doc navigation attempted while Printing or Print Preview
3648 error
= "isprinting";
3650 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED
:
3651 // Port blocked for security reasons
3653 error
= "deniedPortAccess";
3655 case NS_ERROR_UNKNOWN_PROXY_HOST
:
3656 // Proxy hostname could not be resolved.
3657 error
= "proxyResolveFailure";
3659 case NS_ERROR_PROXY_CONNECTION_REFUSED
:
3660 case NS_ERROR_PROXY_FORBIDDEN
:
3661 case NS_ERROR_PROXY_NOT_IMPLEMENTED
:
3662 case NS_ERROR_PROXY_AUTHENTICATION_FAILED
:
3663 case NS_ERROR_PROXY_TOO_MANY_REQUESTS
:
3664 // Proxy connection was refused.
3665 error
= "proxyConnectFailure";
3667 case NS_ERROR_INVALID_CONTENT_ENCODING
:
3668 // Bad Content Encoding.
3669 error
= "contentEncodingError";
3671 case NS_ERROR_UNSAFE_CONTENT_TYPE
:
3672 // Channel refused to load from an unrecognized content type.
3673 error
= "unsafeContentType";
3675 case NS_ERROR_CORRUPTED_CONTENT
:
3676 // Broken Content Detected. e.g. Content-MD5 check failure.
3677 error
= "corruptedContentErrorv2";
3679 case NS_ERROR_INTERCEPTION_FAILED
:
3680 // ServiceWorker intercepted request, but something went wrong.
3681 error
= "corruptedContentErrorv2";
3683 case NS_ERROR_NET_INADEQUATE_SECURITY
:
3684 // Server negotiated bad TLS for HTTP/2.
3685 error
= "inadequateSecurityError";
3688 case NS_ERROR_BLOCKED_BY_POLICY
:
3689 case NS_ERROR_DOM_COOP_FAILED
:
3690 case NS_ERROR_DOM_COEP_FAILED
:
3691 // Page blocked by policy
3692 error
= "blockedByPolicy";
3694 case NS_ERROR_NET_HTTP2_SENT_GOAWAY
:
3695 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR
:
3696 // HTTP/2 or HTTP/3 stack detected a protocol error
3697 error
= "networkProtocolError";
3705 nsresult delegateErrorCode
= aError
;
3706 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3707 // caused this error, we replace the error-page with about:httpsonlyerror
3708 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel
, aError
)) {
3709 errorPage
.AssignLiteral("httpsonlyerror");
3710 delegateErrorCode
= NS_ERROR_HTTPS_ONLY
;
3711 } else if (isBadStsCertError
) {
3712 delegateErrorCode
= NS_ERROR_BAD_HSTS_CERT
;
3715 if (nsCOMPtr
<nsILoadURIDelegate
> loadURIDelegate
= GetLoadURIDelegate()) {
3716 nsCOMPtr
<nsIURI
> errorPageURI
;
3717 rv
= loadURIDelegate
->HandleLoadError(
3718 aURI
, delegateErrorCode
, NS_ERROR_GET_MODULE(delegateErrorCode
),
3719 getter_AddRefs(errorPageURI
));
3720 // If the docshell is going away there's no point in showing an error page.
3721 if (NS_FAILED(rv
) || mIsBeingDestroyed
) {
3722 *aDisplayedErrorPage
= false;
3727 *aDisplayedErrorPage
=
3728 NS_SUCCEEDED(LoadErrorPage(errorPageURI
, aURI
, aFailedChannel
));
3733 // Test if the error should be displayed
3738 if (!errorDescriptionID
) {
3739 errorDescriptionID
= error
;
3742 Telemetry::AccumulateCategoricalKeyed(
3743 IsSubframe() ? "frame"_ns
: "top"_ns
,
3744 mozilla::dom::LoadErrorToTelemetryLabel(aError
));
3746 // Test if the error needs to be formatted
3747 if (!messageStr
.IsEmpty()) {
3748 // already obtained message
3751 // Build up the host:port string.
3752 nsAutoCString hostport
;
3754 aURI
->GetHostPort(hostport
);
3756 hostport
.Assign('?');
3758 CopyUTF8toUTF16(hostport
, *formatStrs
.AppendElement());
3762 rv
= NS_ERROR_NOT_AVAILABLE
;
3763 auto& nextFormatStr
= *formatStrs
.AppendElement();
3765 // displaying "file://" is aesthetically unpleasing and could even be
3766 // confusing to the user
3767 if (SchemeIsFile(aURI
)) {
3768 aURI
->GetPathQueryRef(spec
);
3770 aURI
->GetSpec(spec
);
3773 nsCOMPtr
<nsITextToSubURI
> textToSubURI(
3774 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID
, &rv
));
3775 if (NS_SUCCEEDED(rv
)) {
3776 rv
= textToSubURI
->UnEscapeURIForUI(spec
, nextFormatStr
);
3781 if (NS_FAILED(rv
)) {
3782 CopyUTF8toUTF16(spec
, nextFormatStr
);
3788 stringBundle
->FormatStringFromName(errorDescriptionID
, formatStrs
, str
);
3789 NS_ENSURE_SUCCESS(rv
, rv
);
3790 messageStr
.Assign(str
);
3793 // Display the error as a page or an alert prompt
3794 NS_ENSURE_FALSE(messageStr
.IsEmpty(), NS_ERROR_FAILURE
);
3796 if ((NS_ERROR_NET_INTERRUPT
== aError
|| NS_ERROR_NET_RESET
== aError
) &&
3797 SchemeIsHTTPS(aURI
)) {
3798 // Maybe TLS intolerant. Treat this as an SSL error.
3799 error
= "nssFailure2";
3802 if (mBrowsingContext
->GetUseErrorPages()) {
3803 // Display an error page
3804 nsresult loadedPage
=
3805 LoadErrorPage(aURI
, aURL
, errorPage
.get(), error
, messageStr
.get(),
3806 cssClass
.get(), aFailedChannel
);
3807 *aDisplayedErrorPage
= NS_SUCCEEDED(loadedPage
);
3809 // The prompter reqires that our private window has a document (or it
3810 // asserts). Satisfy that assertion now since GetDoc will force
3811 // creation of one if it hasn't already been created.
3812 if (mScriptGlobal
) {
3813 Unused
<< mScriptGlobal
->GetDoc();
3816 // Display a message box
3817 prompter
->Alert(nullptr, messageStr
.get());
3823 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3825 nsresult
nsDocShell::LoadErrorPage(nsIURI
* aURI
, const char16_t
* aURL
,
3826 const char* aErrorPage
,
3827 const char* aErrorType
,
3828 const char16_t
* aDescription
,
3829 const char* aCSSClass
,
3830 nsIChannel
* aFailedChannel
) {
3831 if (mIsBeingDestroyed
) {
3832 return NS_ERROR_NOT_AVAILABLE
;
3836 if (MOZ_LOG_TEST(gDocShellLog
, LogLevel::Debug
)) {
3837 nsAutoCString chanName
;
3838 if (aFailedChannel
) {
3839 aFailedChannel
->GetName(chanName
);
3841 chanName
.AssignLiteral("<no channel>");
3844 MOZ_LOG(gDocShellLog
, LogLevel::Debug
,
3845 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3846 this, aURI
? aURI
->GetSpecOrDefault().get() : "",
3847 NS_ConvertUTF16toUTF8(aURL
).get(), chanName
.get()));
3853 nsresult rv
= aURI
->GetSpec(url
);
3854 NS_ENSURE_SUCCESS(rv
, rv
);
3856 CopyUTF16toUTF8(MakeStringSpan(aURL
), url
);
3858 return NS_ERROR_INVALID_POINTER
;
3861 // Create a URL to pass all the error information through to the page.
3864 #define SAFE_ESCAPE(output, input, params) \
3865 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3866 return NS_ERROR_OUT_OF_MEMORY; \
3869 nsCString escapedUrl
, escapedError
, escapedDescription
, escapedCSSClass
;
3870 SAFE_ESCAPE(escapedUrl
, url
, url_Path
);
3871 SAFE_ESCAPE(escapedError
, nsDependentCString(aErrorType
), url_Path
);
3872 SAFE_ESCAPE(escapedDescription
, NS_ConvertUTF16toUTF8(aDescription
),
3875 nsCString
cssClass(aCSSClass
);
3876 SAFE_ESCAPE(escapedCSSClass
, cssClass
, url_Path
);
3878 nsCString
errorPageUrl("about:");
3879 errorPageUrl
.AppendASCII(aErrorPage
);
3880 errorPageUrl
.AppendLiteral("?e=");
3882 errorPageUrl
.AppendASCII(escapedError
.get());
3883 errorPageUrl
.AppendLiteral("&u=");
3884 errorPageUrl
.AppendASCII(escapedUrl
.get());
3885 if ((strcmp(aErrorPage
, "blocked") == 0) &&
3886 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE
, true)) {
3887 errorPageUrl
.AppendLiteral("&o=1");
3889 if (!escapedCSSClass
.IsEmpty()) {
3890 errorPageUrl
.AppendLiteral("&s=");
3891 errorPageUrl
.AppendASCII(escapedCSSClass
.get());
3893 errorPageUrl
.AppendLiteral("&c=UTF-8");
3895 nsCOMPtr
<nsICaptivePortalService
> cps
= do_GetService(NS_CAPTIVEPORTAL_CID
);
3897 if (cps
&& NS_SUCCEEDED(cps
->GetState(&cpsState
)) &&
3898 cpsState
== nsICaptivePortalService::LOCKED_PORTAL
) {
3899 errorPageUrl
.AppendLiteral("&captive=true");
3902 errorPageUrl
.AppendLiteral("&d=");
3903 errorPageUrl
.AppendASCII(escapedDescription
.get());
3905 nsCOMPtr
<nsIURI
> errorPageURI
;
3906 nsresult rv
= NS_NewURI(getter_AddRefs(errorPageURI
), errorPageUrl
);
3907 NS_ENSURE_SUCCESS(rv
, rv
);
3909 return LoadErrorPage(errorPageURI
, aURI
, aFailedChannel
);
3912 nsresult
nsDocShell::LoadErrorPage(nsIURI
* aErrorURI
, nsIURI
* aFailedURI
,
3913 nsIChannel
* aFailedChannel
) {
3914 mFailedChannel
= aFailedChannel
;
3915 mFailedURI
= aFailedURI
;
3916 mFailedLoadType
= mLoadType
;
3919 // Abandon mLSHE's BFCache entry and create a new one. This way, if
3920 // we go back or forward to another SHEntry with the same doc
3921 // identifier, the error page won't persist.
3922 mLSHE
->AbandonBFCacheEntry();
3925 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(aErrorURI
);
3926 loadState
->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
3927 if (mBrowsingContext
) {
3928 loadState
->SetTriggeringSandboxFlags(mBrowsingContext
->GetSandboxFlags());
3929 loadState
->SetTriggeringWindowId(
3930 mBrowsingContext
->GetCurrentInnerWindowId());
3931 nsPIDOMWindowInner
* innerWin
= mScriptGlobal
->GetCurrentInnerWindow();
3933 loadState
->SetTriggeringStorageAccess(innerWin
->UsingStorageAccess());
3936 loadState
->SetLoadType(LOAD_ERROR_PAGE
);
3937 loadState
->SetFirstParty(true);
3938 loadState
->SetSourceBrowsingContext(mBrowsingContext
);
3939 if (mozilla::SessionHistoryInParent() && mLoadingEntry
) {
3940 // We keep the loading entry for the load that failed here. If the user
3941 // reloads we want to try to reload the original load, not the error page.
3942 loadState
->SetLoadingSessionHistoryInfo(
3943 MakeUnique
<LoadingSessionHistoryInfo
>(*mLoadingEntry
));
3945 return InternalLoad(loadState
);
3949 nsDocShell::Reload(uint32_t aReloadFlags
) {
3950 if (!IsNavigationAllowed()) {
3951 return NS_OK
; // JS may not handle returning of an error code
3954 NS_ASSERTION(((aReloadFlags
& INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS
) == 0),
3955 "Reload command not updated to use load flags!");
3956 NS_ASSERTION((aReloadFlags
& EXTRA_LOAD_FLAGS
) == 0,
3957 "Don't pass these flags to Reload");
3959 uint32_t loadType
= MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL
, aReloadFlags
);
3960 NS_ENSURE_TRUE(IsValidLoadType(loadType
), NS_ERROR_INVALID_ARG
);
3962 // Send notifications to the HistoryListener if any, about the impending
3964 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
3965 if (mozilla::SessionHistoryInParent()) {
3966 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("nsDocShell %p Reload", this));
3967 bool forceReload
= IsForceReloadType(loadType
);
3968 if (!XRE_IsParentProcess()) {
3969 ++mPendingReloadCount
;
3970 RefPtr
<nsDocShell
> docShell(this);
3971 nsCOMPtr
<nsIDocumentViewer
> viewer(mDocumentViewer
);
3972 NS_ENSURE_STATE(viewer
);
3974 bool okToUnload
= true;
3975 MOZ_TRY(viewer
->PermitUnload(&okToUnload
));
3980 RefPtr
<Document
> doc(GetDocument());
3981 RefPtr
<BrowsingContext
> browsingContext(mBrowsingContext
);
3982 nsCOMPtr
<nsIURI
> currentURI(mCurrentURI
);
3983 nsCOMPtr
<nsIReferrerInfo
> referrerInfo(mReferrerInfo
);
3984 RefPtr
<StopDetector
> stopDetector
= new StopDetector();
3985 nsCOMPtr
<nsILoadGroup
> loadGroup
;
3986 GetLoadGroup(getter_AddRefs(loadGroup
));
3988 // loadGroup may be null in theory. In that case stopDetector just
3989 // doesn't do anything.
3990 loadGroup
->AddRequest(stopDetector
, nullptr);
3993 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
3994 mBrowsingContext
, forceReload
,
3995 [docShell
, doc
, loadType
, browsingContext
, currentURI
, referrerInfo
,
3996 loadGroup
, stopDetector
](
3997 std::tuple
<bool, Maybe
<NotNull
<RefPtr
<nsDocShellLoadState
>>>,
3998 Maybe
<bool>>&& aResult
) {
3999 auto scopeExit
= MakeScopeExit([loadGroup
, stopDetector
]() {
4001 loadGroup
->RemoveRequest(stopDetector
, nullptr, NS_OK
);
4005 // Decrease mPendingReloadCount before any other early returns!
4006 if (--(docShell
->mPendingReloadCount
) > 0) {
4010 if (stopDetector
->Canceled()) {
4014 Maybe
<NotNull
<RefPtr
<nsDocShellLoadState
>>> loadState
;
4015 Maybe
<bool> reloadingActiveEntry
;
4017 std::tie(canReload
, loadState
, reloadingActiveEntry
) = aResult
;
4023 if (loadState
.isSome()) {
4025 gSHLog
, LogLevel::Debug
,
4026 ("nsDocShell %p Reload - LoadHistoryEntry", docShell
.get()));
4027 loadState
.ref()->SetNotifiedBeforeUnloadListeners(true);
4028 docShell
->LoadHistoryEntry(loadState
.ref(), loadType
,
4029 reloadingActiveEntry
.ref());
4031 MOZ_LOG(gSHLog
, LogLevel::Debug
,
4032 ("nsDocShell %p ReloadDocument", docShell
.get()));
4033 ReloadDocument(docShell
, doc
, loadType
, browsingContext
,
4034 currentURI
, referrerInfo
,
4035 /* aNotifiedBeforeUnloadListeners */ true);
4038 [](mozilla::ipc::ResponseRejectReason
) {});
4041 bool canReload
= false;
4042 Maybe
<NotNull
<RefPtr
<nsDocShellLoadState
>>> loadState
;
4043 Maybe
<bool> reloadingActiveEntry
;
4044 if (!mBrowsingContext
->IsDiscarded()) {
4045 mBrowsingContext
->Canonical()->NotifyOnHistoryReload(
4046 forceReload
, canReload
, loadState
, reloadingActiveEntry
);
4049 if (loadState
.isSome()) {
4050 MOZ_LOG(gSHLog
, LogLevel::Debug
,
4051 ("nsDocShell %p Reload - LoadHistoryEntry", this));
4052 LoadHistoryEntry(loadState
.ref(), loadType
,
4053 reloadingActiveEntry
.ref());
4055 MOZ_LOG(gSHLog
, LogLevel::Debug
,
4056 ("nsDocShell %p ReloadDocument", this));
4057 RefPtr
<Document
> doc
= GetDocument();
4058 RefPtr
<BrowsingContext
> bc
= mBrowsingContext
;
4059 nsCOMPtr
<nsIURI
> currentURI
= mCurrentURI
;
4060 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= mReferrerInfo
;
4061 ReloadDocument(this, doc
, loadType
, bc
, currentURI
, referrerInfo
);
4068 bool canReload
= true;
4070 rootSH
->LegacySHistory()->NotifyOnHistoryReload(&canReload
);
4077 /* If you change this part of code, make sure bug 45297 does not re-occur */
4079 nsCOMPtr
<nsISHEntry
> oshe
= mOSHE
;
4080 return LoadHistoryEntry(
4082 aReloadFlags
& nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION
);
4085 if (mLSHE
) { // In case a reload happened before the current load is done
4086 nsCOMPtr
<nsISHEntry
> lshe
= mLSHE
;
4087 return LoadHistoryEntry(
4089 aReloadFlags
& nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION
);
4092 RefPtr
<Document
> doc
= GetDocument();
4093 RefPtr
<BrowsingContext
> bc
= mBrowsingContext
;
4094 nsCOMPtr
<nsIURI
> currentURI
= mCurrentURI
;
4095 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= mReferrerInfo
;
4096 return ReloadDocument(this, doc
, loadType
, bc
, currentURI
, referrerInfo
);
4100 nsresult
nsDocShell::ReloadDocument(nsDocShell
* aDocShell
, Document
* aDocument
,
4102 BrowsingContext
* aBrowsingContext
,
4103 nsIURI
* aCurrentURI
,
4104 nsIReferrerInfo
* aReferrerInfo
,
4105 bool aNotifiedBeforeUnloadListeners
) {
4110 // Do not inherit owner from document
4111 uint32_t flags
= INTERNAL_LOAD_FLAGS_NONE
;
4112 nsAutoString srcdoc
;
4113 nsIURI
* baseURI
= nullptr;
4114 nsCOMPtr
<nsIURI
> originalURI
;
4115 nsCOMPtr
<nsIURI
> resultPrincipalURI
;
4116 bool loadReplace
= false;
4118 nsIPrincipal
* triggeringPrincipal
= aDocument
->NodePrincipal();
4119 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aDocument
->GetCsp();
4120 uint32_t triggeringSandboxFlags
= aDocument
->GetSandboxFlags();
4121 uint64_t triggeringWindowId
= aDocument
->InnerWindowID();
4122 bool triggeringStorageAccess
= aDocument
->UsingStorageAccess();
4124 nsAutoString contentTypeHint
;
4125 aDocument
->GetContentType(contentTypeHint
);
4127 if (aDocument
->IsSrcdocDocument()) {
4128 aDocument
->GetSrcdocData(srcdoc
);
4129 flags
|= INTERNAL_LOAD_FLAGS_IS_SRCDOC
;
4130 baseURI
= aDocument
->GetBaseURI();
4132 srcdoc
= VoidString();
4134 nsCOMPtr
<nsIChannel
> chan
= aDocument
->GetChannel();
4137 chan
->GetLoadFlags(&loadFlags
);
4138 loadReplace
= loadFlags
& nsIChannel::LOAD_REPLACE
;
4139 nsCOMPtr
<nsIHttpChannel
> httpChan(do_QueryInterface(chan
));
4141 httpChan
->GetOriginalURI(getter_AddRefs(originalURI
));
4144 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
4145 loadInfo
->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI
));
4148 if (!triggeringPrincipal
) {
4149 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4150 return NS_ERROR_FAILURE
;
4153 // Stack variables to ensure changes to the member variables don't affect to
4155 nsCOMPtr
<nsIURI
> currentURI
= aCurrentURI
;
4157 // Reload always rewrites result principal URI.
4158 Maybe
<nsCOMPtr
<nsIURI
>> emplacedResultPrincipalURI
;
4159 emplacedResultPrincipalURI
.emplace(std::move(resultPrincipalURI
));
4161 RefPtr
<WindowContext
> context
= aBrowsingContext
->GetCurrentWindowContext();
4162 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(currentURI
);
4163 loadState
->SetReferrerInfo(aReferrerInfo
);
4164 loadState
->SetOriginalURI(originalURI
);
4165 loadState
->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI
);
4166 loadState
->SetLoadReplace(loadReplace
);
4167 loadState
->SetTriggeringPrincipal(triggeringPrincipal
);
4168 loadState
->SetTriggeringSandboxFlags(triggeringSandboxFlags
);
4169 loadState
->SetTriggeringWindowId(triggeringWindowId
);
4170 loadState
->SetTriggeringStorageAccess(triggeringStorageAccess
);
4171 loadState
->SetPrincipalToInherit(triggeringPrincipal
);
4172 loadState
->SetCsp(csp
);
4173 loadState
->SetInternalLoadFlags(flags
);
4174 loadState
->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint
));
4175 loadState
->SetLoadType(aLoadType
);
4176 loadState
->SetFirstParty(true);
4177 loadState
->SetSrcdocData(srcdoc
);
4178 loadState
->SetSourceBrowsingContext(aBrowsingContext
);
4179 loadState
->SetBaseURI(baseURI
);
4180 loadState
->SetHasValidUserGestureActivation(
4181 context
&& context
->HasValidTransientUserGestureActivation());
4182 loadState
->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners
);
4183 return aDocShell
->InternalLoad(loadState
);
4187 nsDocShell::Stop(uint32_t aStopFlags
) {
4188 // Revoke any pending event related to content viewer restoration
4189 mRestorePresentationEvent
.Revoke();
4191 if (mLoadType
== LOAD_ERROR_PAGE
) {
4193 // Since error page loads never unset mLSHE, do so now
4194 SetHistoryEntryAndUpdateBC(Some(nullptr), Some
<nsISHEntry
*>(mLSHE
));
4196 mActiveEntryIsLoadingFromSessionHistory
= false;
4198 mFailedChannel
= nullptr;
4199 mFailedURI
= nullptr;
4202 if (nsIWebNavigation::STOP_CONTENT
& aStopFlags
) {
4203 // Stop the document loading and animations
4204 if (mDocumentViewer
) {
4205 nsCOMPtr
<nsIDocumentViewer
> viewer
= mDocumentViewer
;
4208 } else if (nsIWebNavigation::STOP_NETWORK
& aStopFlags
) {
4209 // Stop the document loading only
4210 if (mDocumentViewer
) {
4211 RefPtr
<Document
> doc
= mDocumentViewer
->GetDocument();
4213 doc
->StopDocumentLoad();
4218 if (nsIWebNavigation::STOP_NETWORK
& aStopFlags
) {
4219 // Suspend any timers that were set for this loader. We'll clear
4220 // them out for good in CreateDocumentViewer.
4221 if (mRefreshURIList
) {
4222 SuspendRefreshURIs();
4223 mSavedRefreshURIList
.swap(mRefreshURIList
);
4224 mRefreshURIList
= nullptr;
4227 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4228 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4232 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4233 // BFCache now, and the Stop above will have removed the DocumentChannel
4234 // from the loadgroup.
4235 mChannelToDisconnectOnPageHide
= 0;
4238 for (auto* child
: mChildList
.ForwardRange()) {
4239 nsCOMPtr
<nsIWebNavigation
> shellAsNav(do_QueryObject(child
));
4241 shellAsNav
->Stop(aStopFlags
);
4249 nsDocShell::GetDocument(Document
** aDocument
) {
4250 NS_ENSURE_ARG_POINTER(aDocument
);
4251 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), NS_ERROR_FAILURE
);
4253 RefPtr
<Document
> doc
= mDocumentViewer
->GetDocument();
4255 return NS_ERROR_NOT_AVAILABLE
;
4258 doc
.forget(aDocument
);
4263 nsDocShell::GetCurrentURI(nsIURI
** aURI
) {
4264 NS_ENSURE_ARG_POINTER(aURI
);
4266 nsCOMPtr
<nsIURI
> uri
= mCurrentURI
;
4272 nsDocShell::GetSessionHistoryXPCOM(nsISupports
** aSessionHistory
) {
4273 NS_ENSURE_ARG_POINTER(aSessionHistory
);
4274 RefPtr
<ChildSHistory
> shistory
= GetSessionHistory();
4275 shistory
.forget(aSessionHistory
);
4279 //*****************************************************************************
4280 // nsDocShell::nsIWebPageDescriptor
4281 //*****************************************************************************
4284 nsDocShell::LoadPageAsViewSource(nsIDocShell
* aOtherDocShell
,
4285 const nsAString
& aURI
) {
4286 if (!aOtherDocShell
) {
4287 return NS_ERROR_INVALID_POINTER
;
4289 nsCOMPtr
<nsIURI
> newURI
;
4290 nsresult rv
= NS_NewURI(getter_AddRefs(newURI
), aURI
);
4291 if (NS_FAILED(rv
)) {
4295 RefPtr
<nsDocShellLoadState
> loadState
;
4297 auto* otherDocShell
= nsDocShell::Cast(aOtherDocShell
);
4298 if (mozilla::SessionHistoryInParent()) {
4299 loadState
= new nsDocShellLoadState(newURI
);
4300 if (!otherDocShell
->FillLoadStateFromCurrentEntry(*loadState
)) {
4301 return NS_ERROR_INVALID_POINTER
;
4303 cacheKey
= otherDocShell
->GetCacheKeyFromCurrentEntry().valueOr(0);
4305 nsCOMPtr
<nsISHEntry
> entry
;
4307 otherDocShell
->GetCurrentSHEntry(getter_AddRefs(entry
), &isOriginalSHE
);
4309 return NS_ERROR_INVALID_POINTER
;
4311 rv
= entry
->CreateLoadInfo(getter_AddRefs(loadState
));
4312 NS_ENSURE_SUCCESS(rv
, rv
);
4313 entry
->GetCacheKey(&cacheKey
);
4314 loadState
->SetURI(newURI
);
4315 loadState
->SetSHEntry(nullptr);
4318 // We're doing a load of the page, via an API that
4319 // is only exposed to system code. The triggering principal for this load
4320 // should be the system principal.
4321 loadState
->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4322 loadState
->SetOriginalURI(nullptr);
4323 loadState
->SetResultPrincipalURI(nullptr);
4325 return InternalLoad(loadState
, Some(cacheKey
));
4329 nsDocShell::GetCurrentDescriptor(nsISupports
** aPageDescriptor
) {
4330 MOZ_ASSERT(aPageDescriptor
, "Null out param?");
4332 *aPageDescriptor
= nullptr;
4334 nsISHEntry
* src
= mOSHE
? mOSHE
: mLSHE
;
4336 nsCOMPtr
<nsISHEntry
> dest
;
4338 nsresult rv
= src
->Clone(getter_AddRefs(dest
));
4339 if (NS_FAILED(rv
)) {
4343 // null out inappropriate cloned attributes...
4344 dest
->SetParent(nullptr);
4345 dest
->SetIsSubFrame(false);
4347 return CallQueryInterface(dest
, aPageDescriptor
);
4350 return NS_ERROR_NOT_AVAILABLE
;
4353 already_AddRefed
<nsIInputStream
> nsDocShell::GetPostDataFromCurrentEntry()
4355 nsCOMPtr
<nsIInputStream
> postData
;
4356 if (mozilla::SessionHistoryInParent()) {
4358 postData
= mActiveEntry
->GetPostData();
4359 } else if (mLoadingEntry
) {
4360 postData
= mLoadingEntry
->mInfo
.GetPostData();
4364 postData
= mOSHE
->GetPostData();
4366 postData
= mLSHE
->GetPostData();
4370 return postData
.forget();
4373 Maybe
<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4374 if (mozilla::SessionHistoryInParent()) {
4376 return Some(mActiveEntry
->GetCacheKey());
4379 if (mLoadingEntry
) {
4380 return Some(mLoadingEntry
->mInfo
.GetCacheKey());
4384 return Some(mOSHE
->GetCacheKey());
4388 return Some(mLSHE
->GetCacheKey());
4395 bool nsDocShell::FillLoadStateFromCurrentEntry(
4396 nsDocShellLoadState
& aLoadState
) {
4397 if (mLoadingEntry
) {
4398 mLoadingEntry
->mInfo
.FillLoadInfo(aLoadState
);
4402 mActiveEntry
->FillLoadInfo(aLoadState
);
4408 //*****************************************************************************
4409 // nsDocShell::nsIBaseWindow
4410 //*****************************************************************************
4413 nsDocShell::InitWindow(nativeWindow aParentNativeWindow
,
4414 nsIWidget
* aParentWidget
, int32_t aX
, int32_t aY
,
4415 int32_t aWidth
, int32_t aHeight
) {
4416 SetParentWidget(aParentWidget
);
4417 SetPositionAndSize(aX
, aY
, aWidth
, aHeight
, 0);
4418 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE
);
4424 nsDocShell::Destroy() {
4425 // XXX: We allow this function to be called just once. If you are going to
4426 // reset new variables in this function, please make sure the variables will
4427 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4428 // in the setter functions for the variables would be enough.
4429 if (mIsBeingDestroyed
) {
4430 return NS_ERROR_DOCSHELL_DYING
;
4433 NS_ASSERTION(mItemType
== typeContent
|| mItemType
== typeChrome
,
4434 "Unexpected item type in docshell");
4436 nsCOMPtr
<nsIObserverService
> serv
= services::GetObserverService();
4438 const char* msg
= mItemType
== typeContent
4439 ? NS_WEBNAVIGATION_DESTROY
4440 : NS_CHROME_WEBNAVIGATION_DESTROY
;
4441 serv
->NotifyObservers(GetAsSupports(this), msg
, nullptr);
4444 mIsBeingDestroyed
= true;
4446 // Brak the cycle with the initial client, if present.
4447 mInitialClientSource
.reset();
4449 // Make sure to blow away our mLoadingURI just in case. No loads
4450 // from inside this pagehide.
4451 mLoadingURI
= nullptr;
4453 // Fire unload event before we blow anything away.
4454 (void)FirePageHideNotification(true);
4456 // Clear pointers to any detached nsEditorData that's lying
4457 // around in shistory entries. Breaks cycle. See bug 430921.
4459 mOSHE
->SetEditorData(nullptr);
4462 mLSHE
->SetEditorData(nullptr);
4465 // Note: mContentListener can be null if Init() failed and we're being
4466 // called from the destructor.
4467 if (mContentListener
) {
4468 mContentListener
->DropDocShellReference();
4469 mContentListener
->SetParentContentListener(nullptr);
4470 // Note that we do NOT set mContentListener to null here; that
4471 // way if someone tries to do a load in us after this point
4472 // the nsDSURIContentListener will block it. All of which
4473 // means that we should do this before calling Stop(), of
4477 // Stop any URLs that are currently being loaded...
4478 Stop(nsIWebNavigation::STOP_ALL
);
4480 mEditorData
= nullptr;
4482 // Save the state of the current document, before destroying the window.
4483 // This is needed to capture the state of a frameset when the new document
4484 // causes the frameset to be destroyed...
4485 PersistLayoutHistoryState();
4487 // Remove this docshell from its parent's child list
4488 nsCOMPtr
<nsIDocShellTreeItem
> docShellParentAsItem
=
4489 do_QueryInterface(GetAsSupports(mParent
));
4490 if (docShellParentAsItem
) {
4491 docShellParentAsItem
->RemoveChild(this);
4494 if (mDocumentViewer
) {
4495 mDocumentViewer
->Close(nullptr);
4496 mDocumentViewer
->Destroy();
4497 mDocumentViewer
= nullptr;
4500 nsDocLoader::Destroy();
4502 mParentWidget
= nullptr;
4503 SetCurrentURIInternal(nullptr);
4505 if (mScriptGlobal
) {
4506 mScriptGlobal
->DetachFromDocShell(!mWillChangeProcess
);
4507 mScriptGlobal
= nullptr;
4510 if (GetSessionHistory()) {
4511 // We want to destroy these content viewers now rather than
4512 // letting their destruction wait for the session history
4513 // entries to get garbage collected. (Bug 488394)
4514 GetSessionHistory()->EvictLocalDocumentViewers();
4517 if (mWillChangeProcess
&& !mBrowsingContext
->IsDiscarded()) {
4518 mBrowsingContext
->PrepareForProcessChange();
4521 SetTreeOwner(nullptr);
4523 mBrowserChild
= nullptr;
4525 mChromeEventHandler
= nullptr;
4527 // Cancel any timers that were set for this docshell; this is needed
4528 // to break the cycle between us and the timers.
4529 CancelRefreshURITimers();
4534 double nsDocShell::GetWidgetCSSToDeviceScale() {
4535 if (mParentWidget
) {
4536 return mParentWidget
->GetDefaultScale().scale
;
4538 if (nsCOMPtr
<nsIBaseWindow
> ownerWindow
= do_QueryInterface(mTreeOwner
)) {
4539 return ownerWindow
->GetWidgetCSSToDeviceScale();
4545 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale
) {
4546 if (mParentWidget
) {
4547 *aScale
= mParentWidget
->GetDesktopToDeviceScale().scale
;
4551 nsCOMPtr
<nsIBaseWindow
> ownerWindow(do_QueryInterface(mTreeOwner
));
4553 return ownerWindow
->GetDevicePixelsPerDesktopPixel(aScale
);
4561 nsDocShell::SetPosition(int32_t aX
, int32_t aY
) {
4562 mBounds
.MoveTo(aX
, aY
);
4564 if (mDocumentViewer
) {
4565 NS_ENSURE_SUCCESS(mDocumentViewer
->Move(aX
, aY
), NS_ERROR_FAILURE
);
4572 nsDocShell::SetPositionDesktopPix(int32_t aX
, int32_t aY
) {
4573 nsCOMPtr
<nsIBaseWindow
> ownerWindow(do_QueryInterface(mTreeOwner
));
4575 return ownerWindow
->SetPositionDesktopPix(aX
, aY
);
4579 GetDevicePixelsPerDesktopPixel(&scale
);
4580 return SetPosition(NSToIntRound(aX
* scale
), NSToIntRound(aY
* scale
));
4584 nsDocShell::GetPosition(int32_t* aX
, int32_t* aY
) {
4585 return GetPositionAndSize(aX
, aY
, nullptr, nullptr);
4589 nsDocShell::SetSize(int32_t aWidth
, int32_t aHeight
, bool aRepaint
) {
4590 int32_t x
= 0, y
= 0;
4591 GetPosition(&x
, &y
);
4592 return SetPositionAndSize(x
, y
, aWidth
, aHeight
,
4593 aRepaint
? nsIBaseWindow::eRepaint
: 0);
4597 nsDocShell::GetSize(int32_t* aWidth
, int32_t* aHeight
) {
4598 return GetPositionAndSize(nullptr, nullptr, aWidth
, aHeight
);
4602 nsDocShell::SetPositionAndSize(int32_t aX
, int32_t aY
, int32_t aWidth
,
4603 int32_t aHeight
, uint32_t aFlags
) {
4604 mBounds
.SetRect(aX
, aY
, aWidth
, aHeight
);
4606 // Hold strong ref, since SetBounds can make us null out mDocumentViewer
4607 nsCOMPtr
<nsIDocumentViewer
> viewer
= mDocumentViewer
;
4609 uint32_t cvflags
= (aFlags
& nsIBaseWindow::eDelayResize
)
4610 ? nsIDocumentViewer::eDelayResize
4612 // XXX Border figured in here or is that handled elsewhere?
4613 nsresult rv
= viewer
->SetBoundsWithFlags(mBounds
, cvflags
);
4614 NS_ENSURE_SUCCESS(rv
, NS_ERROR_FAILURE
);
4621 nsDocShell::GetPositionAndSize(int32_t* aX
, int32_t* aY
, int32_t* aWidth
,
4623 if (mParentWidget
) {
4624 // ensure size is up-to-date if window has changed resolution
4625 LayoutDeviceIntRect r
= mParentWidget
->GetClientBounds();
4626 SetPositionAndSize(mBounds
.X(), mBounds
.Y(), r
.Width(), r
.Height(), 0);
4629 // We should really consider just getting this information from
4630 // our window instead of duplicating the storage and code...
4631 if (aWidth
|| aHeight
) {
4632 // Caller wants to know our size; make sure to give them up to
4633 // date information.
4634 RefPtr
<Document
> doc(do_GetInterface(GetAsSupports(mParent
)));
4636 doc
->FlushPendingNotifications(FlushType::Layout
);
4640 DoGetPositionAndSize(aX
, aY
, aWidth
, aHeight
);
4644 void nsDocShell::DoGetPositionAndSize(int32_t* aX
, int32_t* aY
, int32_t* aWidth
,
4653 *aWidth
= mBounds
.Width();
4656 *aHeight
= mBounds
.Height();
4661 nsDocShell::SetDimensions(DimensionRequest
&& aRequest
) {
4662 return NS_ERROR_NOT_IMPLEMENTED
;
4666 nsDocShell::GetDimensions(DimensionKind aDimensionKind
, int32_t* aX
,
4667 int32_t* aY
, int32_t* aCX
, int32_t* aCY
) {
4668 return NS_ERROR_NOT_IMPLEMENTED
;
4672 nsDocShell::Repaint(bool aForce
) {
4673 PresShell
* presShell
= GetPresShell();
4674 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
4676 RefPtr
<nsViewManager
> viewManager
= presShell
->GetViewManager();
4677 NS_ENSURE_TRUE(viewManager
, NS_ERROR_FAILURE
);
4679 viewManager
->InvalidateAllViews();
4684 nsDocShell::GetParentWidget(nsIWidget
** aParentWidget
) {
4685 NS_ENSURE_ARG_POINTER(aParentWidget
);
4687 *aParentWidget
= mParentWidget
;
4688 NS_IF_ADDREF(*aParentWidget
);
4694 nsDocShell::SetParentWidget(nsIWidget
* aParentWidget
) {
4695 MOZ_ASSERT(!mIsBeingDestroyed
);
4696 mParentWidget
= aParentWidget
;
4702 nsDocShell::GetParentNativeWindow(nativeWindow
* aParentNativeWindow
) {
4703 NS_ENSURE_ARG_POINTER(aParentNativeWindow
);
4705 if (mParentWidget
) {
4706 *aParentNativeWindow
= mParentWidget
->GetNativeData(NS_NATIVE_WIDGET
);
4708 *aParentNativeWindow
= nullptr;
4715 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow
) {
4716 return NS_ERROR_NOT_IMPLEMENTED
;
4720 nsDocShell::GetNativeHandle(nsAString
& aNativeHandle
) {
4721 // the nativeHandle should be accessed from nsIAppWindow
4722 return NS_ERROR_NOT_IMPLEMENTED
;
4726 nsDocShell::GetVisibility(bool* aVisibility
) {
4727 NS_ENSURE_ARG_POINTER(aVisibility
);
4729 *aVisibility
= false;
4731 if (!mDocumentViewer
) {
4735 PresShell
* presShell
= GetPresShell();
4740 // get the view manager
4741 nsViewManager
* vm
= presShell
->GetViewManager();
4742 NS_ENSURE_TRUE(vm
, NS_ERROR_FAILURE
);
4744 // get the root view
4745 nsView
* view
= vm
->GetRootView(); // views are not ref counted
4746 NS_ENSURE_TRUE(view
, NS_ERROR_FAILURE
);
4748 // if our root view is hidden, we are not visible
4749 if (view
->GetVisibility() == ViewVisibility::Hide
) {
4753 // otherwise, we must walk up the document and view trees checking
4754 // for a hidden view, unless we're an off screen browser, which
4755 // would make this test meaningless.
4757 RefPtr
<nsDocShell
> docShell
= this;
4758 RefPtr
<nsDocShell
> parentItem
= docShell
->GetInProcessParentDocshell();
4759 while (parentItem
) {
4760 // Null-check for crash in bug 267804
4761 if (!parentItem
->GetPresShell()) {
4762 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4766 vm
= docShell
->GetPresShell()->GetViewManager();
4768 view
= vm
->GetRootView();
4772 view
= view
->GetParent(); // anonymous inner view
4774 view
= view
->GetParent(); // subdocumentframe's view
4778 nsIFrame
* frame
= view
? view
->GetFrame() : nullptr;
4779 if (frame
&& !frame
->IsVisibleConsideringAncestors(
4780 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY
)) {
4784 docShell
= parentItem
;
4785 parentItem
= docShell
->GetInProcessParentDocshell();
4788 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin(do_QueryInterface(mTreeOwner
));
4789 if (!treeOwnerAsWin
) {
4790 *aVisibility
= true;
4794 // Check with the tree owner as well to give embedders a chance to
4795 // expose visibility as well.
4796 nsresult rv
= treeOwnerAsWin
->GetVisibility(aVisibility
);
4797 if (rv
== NS_ERROR_NOT_IMPLEMENTED
) {
4798 // The tree owner had no opinion on our visibility.
4799 *aVisibility
= true;
4805 void nsDocShell::ActivenessMaybeChanged() {
4806 const bool isActive
= mBrowsingContext
->IsActive();
4807 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
4808 presShell
->ActivenessMaybeChanged();
4811 // Tell the window about it
4812 if (mScriptGlobal
) {
4813 mScriptGlobal
->SetIsBackground(!isActive
);
4814 if (RefPtr
<Document
> doc
= mScriptGlobal
->GetExtantDoc()) {
4815 // Update orientation when the top-level browsing context becomes active.
4816 if (isActive
&& mBrowsingContext
->IsTop()) {
4817 // We only care about the top-level browsing context.
4818 auto orientation
= mBrowsingContext
->GetOrientationLock();
4819 ScreenOrientation::UpdateActiveOrientationLock(orientation
);
4822 doc
->PostVisibilityUpdateEvent();
4826 // Tell the nsDOMNavigationTiming about it
4827 RefPtr
<nsDOMNavigationTiming
> timing
= mTiming
;
4828 if (!timing
&& mDocumentViewer
) {
4829 if (Document
* doc
= mDocumentViewer
->GetDocument()) {
4830 timing
= doc
->GetNavigationTiming();
4834 timing
->NotifyDocShellStateChanged(
4835 isActive
? nsDOMNavigationTiming::DocShellState::eActive
4836 : nsDOMNavigationTiming::DocShellState::eInactive
);
4839 // Restart or stop meta refresh timers if necessary
4840 if (mDisableMetaRefreshWhenInactive
) {
4842 ResumeRefreshURIs();
4844 SuspendRefreshURIs();
4848 if (InputTaskManager::CanSuspendInputEvent()) {
4849 mBrowsingContext
->Group()->UpdateInputTaskManagerIfNeeded(isActive
);
4854 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags
) {
4855 if (!mWillChangeProcess
) {
4856 // Intentionally ignoring handling discarded browsing contexts.
4857 Unused
<< mBrowsingContext
->SetDefaultLoadFlags(aDefaultLoadFlags
);
4859 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4860 // shutdown. Sorry DevTools, your DocShell is in another process.
4861 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4867 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags
) {
4868 *aDefaultLoadFlags
= mBrowsingContext
->GetDefaultLoadFlags();
4873 nsDocShell::GetFailedChannel(nsIChannel
** aFailedChannel
) {
4874 NS_ENSURE_ARG_POINTER(aFailedChannel
);
4875 Document
* doc
= GetDocument();
4877 *aFailedChannel
= nullptr;
4880 NS_IF_ADDREF(*aFailedChannel
= doc
->GetFailedChannel());
4885 nsDocShell::SetVisibility(bool aVisibility
) {
4886 // Show()/Hide() may change mDocumentViewer.
4887 nsCOMPtr
<nsIDocumentViewer
> viewer
= mDocumentViewer
;
4901 nsDocShell::GetEnabled(bool* aEnabled
) {
4902 NS_ENSURE_ARG_POINTER(aEnabled
);
4904 return NS_ERROR_NOT_IMPLEMENTED
;
4908 nsDocShell::SetEnabled(bool aEnabled
) { return NS_ERROR_NOT_IMPLEMENTED
; }
4911 nsDocShell::GetMainWidget(nsIWidget
** aMainWidget
) {
4912 // We don't create our own widget, so simply return the parent one.
4913 return GetParentWidget(aMainWidget
);
4917 nsDocShell::GetTitle(nsAString
& aTitle
) {
4923 nsDocShell::SetTitle(const nsAString
& aTitle
) {
4924 // Avoid unnecessary updates of the title if the URI and the title haven't
4926 if (mTitleValidForCurrentURI
&& mTitle
== aTitle
) {
4930 // Store local title
4932 mTitleValidForCurrentURI
= true;
4934 // When title is set on the top object it should then be passed to the
4936 if (mBrowsingContext
->IsTop()) {
4937 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin(do_QueryInterface(mTreeOwner
));
4938 if (treeOwnerAsWin
) {
4939 treeOwnerAsWin
->SetTitle(aTitle
);
4943 if (mCurrentURI
&& mLoadType
!= LOAD_ERROR_PAGE
) {
4944 UpdateGlobalHistoryTitle(mCurrentURI
);
4947 // Update SessionHistory with the document's title.
4948 if (mLoadType
!= LOAD_BYPASS_HISTORY
&& mLoadType
!= LOAD_ERROR_PAGE
) {
4949 SetTitleOnHistoryEntry(true);
4955 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory
) {
4957 mOSHE
->SetTitle(mTitle
);
4960 if (mActiveEntry
&& mBrowsingContext
) {
4961 mActiveEntry
->SetTitle(mTitle
);
4962 if (aUpdateEntryInSessionHistory
) {
4963 if (XRE_IsParentProcess()) {
4964 SessionHistoryEntry
* entry
=
4965 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
4967 entry
->SetTitle(mTitle
);
4971 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
4972 mBrowsingContext
, mTitle
);
4978 nsPoint
nsDocShell::GetCurScrollPos() {
4980 if (nsIScrollableFrame
* sf
= GetRootScrollFrame()) {
4981 scrollPos
= sf
->GetVisualViewportOffset();
4986 nsresult
nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos
,
4987 int32_t aCurVerticalPos
) {
4988 nsIScrollableFrame
* sf
= GetRootScrollFrame();
4989 NS_ENSURE_TRUE(sf
, NS_ERROR_FAILURE
);
4991 ScrollMode scrollMode
=
4992 sf
->IsSmoothScroll() ? ScrollMode::SmoothMsd
: ScrollMode::Instant
;
4994 nsPoint
targetPos(aCurHorizontalPos
, aCurVerticalPos
);
4995 sf
->ScrollTo(targetPos
, scrollMode
);
4997 // Set the visual viewport offset as well.
4999 RefPtr
<PresShell
> presShell
= GetPresShell();
5000 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
5002 nsPresContext
* presContext
= presShell
->GetPresContext();
5003 NS_ENSURE_TRUE(presContext
, NS_ERROR_FAILURE
);
5005 // Only the root content document can have a distinct visual viewport offset.
5006 if (!presContext
->IsRootContentDocumentCrossProcess()) {
5010 // Not on a platform with a distinct visual viewport - don't bother setting
5011 // the visual viewport offset.
5012 if (!presShell
->IsVisualViewportSizeSet()) {
5016 presShell
->ScrollToVisual(targetPos
, layers::FrameMetrics::eMainThread
,
5022 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref
) {
5023 if (mScrollbarPref
== aPref
) {
5026 mScrollbarPref
= aPref
;
5027 auto* ps
= GetPresShell();
5031 nsIFrame
* scrollFrame
= ps
->GetRootScrollFrame();
5035 ps
->FrameNeedsReflow(scrollFrame
,
5036 IntrinsicDirty::FrameAncestorsAndDescendants
,
5040 //*****************************************************************************
5041 // nsDocShell::nsIRefreshURI
5042 //*****************************************************************************
5045 nsDocShell::RefreshURI(nsIURI
* aURI
, nsIPrincipal
* aPrincipal
,
5047 MOZ_ASSERT(!mIsBeingDestroyed
);
5049 NS_ENSURE_ARG(aURI
);
5051 /* Check if Meta refresh/redirects are permitted. Some
5052 * embedded applications may not want to do this.
5053 * Must do this before sending out NOTIFY_REFRESH events
5054 * because listeners may have side effects (e.g. displaying a
5055 * button to manually trigger the refresh later).
5057 bool allowRedirects
= true;
5058 GetAllowMetaRedirects(&allowRedirects
);
5059 if (!allowRedirects
) {
5063 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5064 // give them a chance to block this refresh.
5066 nsresult rv
= aURI
->Equals(mCurrentURI
, &sameURI
);
5067 if (NS_FAILED(rv
)) {
5070 if (!RefreshAttempted(this, aURI
, aDelay
, sameURI
)) {
5074 nsCOMPtr
<nsITimerCallback
> refreshTimer
=
5075 new nsRefreshTimer(this, aURI
, aPrincipal
, aDelay
);
5077 BusyFlags busyFlags
= GetBusyFlags();
5079 if (!mRefreshURIList
) {
5080 mRefreshURIList
= nsArray::Create();
5083 if (busyFlags
& BUSY_FLAGS_BUSY
||
5084 (!mBrowsingContext
->IsActive() && mDisableMetaRefreshWhenInactive
)) {
5085 // We don't want to create the timer right now. Instead queue up the
5086 // request and trigger the timer in EndPageLoad() or whenever we become
5088 mRefreshURIList
->AppendElement(refreshTimer
);
5090 // There is no page loading going on right now. Create the
5091 // timer and fire it right away.
5092 nsCOMPtr
<nsPIDOMWindowOuter
> win
= GetWindow();
5093 NS_ENSURE_TRUE(win
, NS_ERROR_FAILURE
);
5095 nsCOMPtr
<nsITimer
> timer
;
5096 MOZ_TRY_VAR(timer
, NS_NewTimerWithCallback(refreshTimer
, aDelay
,
5097 nsITimer::TYPE_ONE_SHOT
));
5099 mRefreshURIList
->AppendElement(timer
); // owning timer ref
5104 nsresult
nsDocShell::ForceRefreshURIFromTimer(nsIURI
* aURI
,
5105 nsIPrincipal
* aPrincipal
,
5108 MOZ_ASSERT(aTimer
, "Must have a timer here");
5110 // Remove aTimer from mRefreshURIList if needed
5111 if (mRefreshURIList
) {
5113 mRefreshURIList
->GetLength(&n
);
5115 for (uint32_t i
= 0; i
< n
; ++i
) {
5116 nsCOMPtr
<nsITimer
> timer
= do_QueryElementAt(mRefreshURIList
, i
);
5117 if (timer
== aTimer
) {
5118 mRefreshURIList
->RemoveElementAt(i
);
5124 return ForceRefreshURI(aURI
, aPrincipal
, aDelay
);
5128 nsDocShell::ForceRefreshURI(nsIURI
* aURI
, nsIPrincipal
* aPrincipal
,
5130 NS_ENSURE_ARG(aURI
);
5132 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(aURI
);
5133 loadState
->SetOriginalURI(mCurrentURI
);
5134 loadState
->SetResultPrincipalURI(aURI
);
5135 loadState
->SetResultPrincipalURIIsSome(true);
5136 loadState
->SetKeepResultPrincipalURIIfSet(true);
5137 loadState
->SetIsMetaRefresh(true);
5139 // Set the triggering pricipal to aPrincipal if available, or current
5140 // document's principal otherwise.
5141 nsCOMPtr
<nsIPrincipal
> principal
= aPrincipal
;
5142 RefPtr
<Document
> doc
= GetDocument();
5145 return NS_ERROR_FAILURE
;
5147 principal
= doc
->NodePrincipal();
5149 loadState
->SetTriggeringPrincipal(principal
);
5151 loadState
->SetCsp(doc
->GetCsp());
5152 loadState
->SetHasValidUserGestureActivation(
5153 doc
->HasValidTransientUserGestureActivation());
5154 loadState
->SetTriggeringSandboxFlags(doc
->GetSandboxFlags());
5155 loadState
->SetTriggeringWindowId(doc
->InnerWindowID());
5156 loadState
->SetTriggeringStorageAccess(doc
->UsingStorageAccess());
5159 loadState
->SetPrincipalIsExplicit(true);
5161 /* Check if this META refresh causes a redirection
5164 bool equalUri
= false;
5165 nsresult rv
= aURI
->Equals(mCurrentURI
, &equalUri
);
5167 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
;
5168 if (NS_SUCCEEDED(rv
) && !equalUri
&& aDelay
<= REFRESH_REDIRECT_TIMER
) {
5169 /* It is a META refresh based redirection within the threshold time
5170 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5171 * Pass a REPLACE flag to LoadURI().
5173 loadState
->SetLoadType(LOAD_REFRESH_REPLACE
);
5175 /* For redirects we mimic HTTP, which passes the
5176 * original referrer.
5177 * We will pass in referrer but will not send to server
5179 if (mReferrerInfo
) {
5180 referrerInfo
= static_cast<ReferrerInfo
*>(mReferrerInfo
.get())
5181 ->CloneWithNewSendReferrer(false);
5184 loadState
->SetLoadType(LOAD_REFRESH
);
5185 /* We do need to pass in a referrer, but we don't want it to
5186 * be sent to the server.
5187 * For most refreshes the current URI is an appropriate
5188 * internal referrer.
5190 referrerInfo
= new ReferrerInfo(mCurrentURI
, ReferrerPolicy::_empty
, false);
5193 loadState
->SetReferrerInfo(referrerInfo
);
5194 loadState
->SetLoadFlags(
5195 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL
);
5196 loadState
->SetFirstParty(true);
5199 * LoadURI(...) will cancel all refresh timers... This causes the
5200 * Timer and its refreshData instance to be released...
5202 LoadURI(loadState
, false);
5207 static const char16_t
* SkipASCIIWhitespace(const char16_t
* aStart
,
5208 const char16_t
* aEnd
) {
5209 const char16_t
* iter
= aStart
;
5210 while (iter
!= aEnd
&& mozilla::IsAsciiWhitespace(*iter
)) {
5216 static std::tuple
<const char16_t
*, const char16_t
*> ExtractURLString(
5217 const char16_t
* aPosition
, const char16_t
* aEnd
) {
5218 MOZ_ASSERT(aPosition
!= aEnd
);
5220 // 1. Let urlString be the substring of input from the code point at
5221 // position to the end of the string.
5222 const char16_t
* urlStart
= aPosition
;
5223 const char16_t
* urlEnd
= aEnd
;
5225 // 2. If the code point in input pointed to by position is U+0055 (U) or
5226 // U+0075 (u), then advance position to the next code point.
5227 // Otherwise, jump to the step labeled skip quotes.
5228 if (*aPosition
== 'U' || *aPosition
== 'u') {
5231 // 3. If the code point in input pointed to by position is U+0052 (R) or
5232 // U+0072 (r), then advance position to the next code point.
5233 // Otherwise, jump to the step labeled parse.
5234 if (aPosition
== aEnd
|| (*aPosition
!= 'R' && *aPosition
!= 'r')) {
5235 return std::make_tuple(urlStart
, urlEnd
);
5240 // 4. If the code point in input pointed to by position is U+004C (L) or
5241 // U+006C (l), then advance position to the next code point.
5242 // Otherwise, jump to the step labeled parse.
5243 if (aPosition
== aEnd
|| (*aPosition
!= 'L' && *aPosition
!= 'l')) {
5244 return std::make_tuple(urlStart
, urlEnd
);
5249 // 5. Skip ASCII whitespace within input given position.
5250 aPosition
= SkipASCIIWhitespace(aPosition
, aEnd
);
5252 // 6. If the code point in input pointed to by position is U+003D (=),
5253 // then advance position to the next code point. Otherwise, jump to
5254 // the step labeled parse.
5255 if (aPosition
== aEnd
|| *aPosition
!= '=') {
5256 return std::make_tuple(urlStart
, urlEnd
);
5261 // 7. Skip ASCII whitespace within input given position.
5262 aPosition
= SkipASCIIWhitespace(aPosition
, aEnd
);
5265 // 8. Skip quotes: If the code point in input pointed to by position is
5266 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5267 // advance position to the next code point. Otherwise, let quote be
5268 // the empty string.
5270 if (aPosition
!= aEnd
&& (*aPosition
== '\'' || *aPosition
== '"')) {
5271 quote
.emplace(*aPosition
);
5275 // 9. Set urlString to the substring of input from the code point at
5276 // position to the end of the string.
5277 urlStart
= aPosition
;
5280 // 10. If quote is not the empty string, and there is a code point in
5281 // urlString equal to quote, then truncate urlString at that code
5282 // point, so that it and all subsequent code points are removed.
5283 const char16_t
* quotePos
;
5284 if (quote
.isSome() &&
5285 (quotePos
= nsCharTraits
<char16_t
>::find(
5286 urlStart
, std::distance(urlStart
, aEnd
), quote
.value()))) {
5290 return std::make_tuple(urlStart
, urlEnd
);
5293 void nsDocShell::SetupRefreshURIFromHeader(Document
* aDocument
,
5294 const nsAString
& aHeader
) {
5295 if (mIsBeingDestroyed
) {
5299 const char16_t
* position
= aHeader
.BeginReading();
5300 const char16_t
* end
= aHeader
.EndReading();
5303 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5305 // 3. Skip ASCII whitespace
5306 position
= SkipASCIIWhitespace(position
, end
);
5308 // 4. Let time be 0.
5309 CheckedInt
<uint32_t> milliSeconds
;
5311 // 5. Collect a sequence of code points that are ASCII digits
5312 const char16_t
* digitsStart
= position
;
5313 while (position
!= end
&& mozilla::IsAsciiDigit(*position
)) {
5317 if (position
== digitsStart
) {
5318 // 6. If timeString is the empty string, then:
5319 // 1. If the code point in input pointed to by position is not U+002E
5320 // (.), then return.
5321 if (position
== end
|| *position
!= '.') {
5325 // 7. Otherwise, set time to the result of parsing timeString using the
5326 // rules for parsing non-negative integers.
5327 nsContentUtils::ParseHTMLIntegerResultFlags result
;
5329 nsContentUtils::ParseHTMLInteger(digitsStart
, position
, &result
);
5330 MOZ_ASSERT(!(result
& nsContentUtils::eParseHTMLInteger_Negative
));
5331 if (result
& nsContentUtils::eParseHTMLInteger_Error
) {
5332 // The spec assumes no errors here (since we only pass ASCII digits in),
5333 // but we can still overflow, so this block should deal with that (and
5335 MOZ_ASSERT(!(result
& nsContentUtils::eParseHTMLInteger_ErrorOverflow
));
5339 !(result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
));
5341 milliSeconds
= seconds
;
5342 milliSeconds
*= 1000;
5343 if (!milliSeconds
.isValid()) {
5348 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5349 // STOP characters (.) from input given position. Ignore any collected
5351 while (position
!= end
&&
5352 (mozilla::IsAsciiDigit(*position
) || *position
== '.')) {
5356 // 9. Let urlRecord be document's URL.
5357 nsCOMPtr
<nsIURI
> urlRecord(aDocument
->GetDocumentURI());
5359 // 10. If position is not past the end of input
5360 if (position
!= end
) {
5361 // 1. If the code point in input pointed to by position is not U+003B (;),
5362 // U+002C (,), or ASCII whitespace, then return.
5363 if (*position
!= ';' && *position
!= ',' &&
5364 !mozilla::IsAsciiWhitespace(*position
)) {
5368 // 2. Skip ASCII whitespace within input given position.
5369 position
= SkipASCIIWhitespace(position
, end
);
5371 // 3. If the code point in input pointed to by position is U+003B (;) or
5372 // U+002C (,), then advance position to the next code point.
5373 if (position
!= end
&& (*position
== ';' || *position
== ',')) {
5376 // 4. Skip ASCII whitespace within input given position.
5377 position
= SkipASCIIWhitespace(position
, end
);
5380 // 11. If position is not past the end of input, then:
5381 if (position
!= end
) {
5382 const char16_t
* urlStart
;
5383 const char16_t
* urlEnd
;
5385 // 1-10. See ExtractURLString.
5386 std::tie(urlStart
, urlEnd
) = ExtractURLString(position
, end
);
5388 // 11. Parse: Parse urlString relative to document. If that fails, return.
5389 // Otherwise, set urlRecord to the resulting URL record.
5391 NS_NewURI(getter_AddRefs(urlRecord
),
5392 Substring(urlStart
, std::distance(urlStart
, urlEnd
)),
5393 /* charset = */ nullptr, aDocument
->GetDocBaseURI());
5394 NS_ENSURE_SUCCESS_VOID(rv
);
5398 nsIPrincipal
* principal
= aDocument
->NodePrincipal();
5399 nsCOMPtr
<nsIScriptSecurityManager
> securityManager
=
5400 nsContentUtils::GetSecurityManager();
5401 nsresult rv
= securityManager
->CheckLoadURIWithPrincipal(
5402 principal
, urlRecord
,
5403 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT
,
5404 aDocument
->InnerWindowID());
5405 NS_ENSURE_SUCCESS_VOID(rv
);
5408 rv
= NS_URIChainHasFlags(
5409 urlRecord
, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT
, &isjs
);
5410 NS_ENSURE_SUCCESS_VOID(rv
);
5416 RefreshURI(urlRecord
, principal
, milliSeconds
.value());
5419 static void DoCancelRefreshURITimers(nsIMutableArray
* aTimerList
) {
5425 aTimerList
->GetLength(&n
);
5428 nsCOMPtr
<nsITimer
> timer(do_QueryElementAt(aTimerList
, --n
));
5430 aTimerList
->RemoveElementAt(n
); // bye bye owning timer ref
5439 nsDocShell::CancelRefreshURITimers() {
5440 DoCancelRefreshURITimers(mRefreshURIList
);
5441 DoCancelRefreshURITimers(mSavedRefreshURIList
);
5442 DoCancelRefreshURITimers(mBFCachedRefreshURIList
);
5443 mRefreshURIList
= nullptr;
5444 mSavedRefreshURIList
= nullptr;
5445 mBFCachedRefreshURIList
= nullptr;
5451 nsDocShell::GetRefreshPending(bool* aResult
) {
5452 if (!mRefreshURIList
) {
5458 nsresult rv
= mRefreshURIList
->GetLength(&count
);
5459 if (NS_SUCCEEDED(rv
)) {
5460 *aResult
= (count
!= 0);
5465 void nsDocShell::RefreshURIToQueue() {
5466 if (mRefreshURIList
) {
5468 mRefreshURIList
->GetLength(&n
);
5470 for (uint32_t i
= 0; i
< n
; ++i
) {
5471 nsCOMPtr
<nsITimer
> timer
= do_QueryElementAt(mRefreshURIList
, i
);
5473 continue; // this must be a nsRefreshURI already
5476 // Replace this timer object with a nsRefreshTimer object.
5477 nsCOMPtr
<nsITimerCallback
> callback
;
5478 timer
->GetCallback(getter_AddRefs(callback
));
5482 mRefreshURIList
->ReplaceElementAt(callback
, i
);
5488 nsDocShell::SuspendRefreshURIs() {
5489 RefreshURIToQueue();
5491 // Suspend refresh URIs for our child shells as well.
5492 for (auto* child
: mChildList
.ForwardRange()) {
5493 nsCOMPtr
<nsIDocShell
> shell
= do_QueryObject(child
);
5495 shell
->SuspendRefreshURIs();
5503 nsDocShell::ResumeRefreshURIs() {
5504 RefreshURIFromQueue();
5506 // Resume refresh URIs for our child shells as well.
5507 for (auto* child
: mChildList
.ForwardRange()) {
5508 nsCOMPtr
<nsIDocShell
> shell
= do_QueryObject(child
);
5510 shell
->ResumeRefreshURIs();
5517 nsresult
nsDocShell::RefreshURIFromQueue() {
5518 if (!mRefreshURIList
) {
5522 mRefreshURIList
->GetLength(&n
);
5525 nsCOMPtr
<nsITimerCallback
> refreshInfo
=
5526 do_QueryElementAt(mRefreshURIList
, --n
);
5529 // This is the nsRefreshTimer object, waiting to be
5530 // setup in a timer object and fired.
5531 // Create the timer and trigger it.
5532 uint32_t delay
= static_cast<nsRefreshTimer
*>(
5533 static_cast<nsITimerCallback
*>(refreshInfo
))
5535 nsCOMPtr
<nsPIDOMWindowOuter
> win
= GetWindow();
5537 nsCOMPtr
<nsITimer
> timer
;
5538 NS_NewTimerWithCallback(getter_AddRefs(timer
), refreshInfo
, delay
,
5539 nsITimer::TYPE_ONE_SHOT
);
5542 // Replace the nsRefreshTimer element in the queue with
5543 // its corresponding timer object, so that in case another
5544 // load comes through before the timer can go off, the timer will
5545 // get cancelled in CancelRefreshURITimer()
5546 mRefreshURIList
->ReplaceElementAt(timer
, n
);
5555 static bool IsFollowupPartOfMultipart(nsIRequest
* aRequest
) {
5556 nsCOMPtr
<nsIMultiPartChannel
> multiPartChannel
= do_QueryInterface(aRequest
);
5557 bool firstPart
= false;
5558 return multiPartChannel
&&
5559 NS_SUCCEEDED(multiPartChannel
->GetIsFirstPart(&firstPart
)) &&
5563 nsresult
nsDocShell::Embed(nsIDocumentViewer
* aDocumentViewer
,
5564 WindowGlobalChild
* aWindowActor
,
5565 bool aIsTransientAboutBlank
, bool aPersist
,
5566 nsIRequest
* aRequest
, nsIURI
* aPreviousURI
) {
5567 // Save the LayoutHistoryState of the previous document, before
5568 // setting up new document
5569 PersistLayoutHistoryState();
5571 nsresult rv
= SetupNewViewer(aDocumentViewer
, aWindowActor
);
5572 NS_ENSURE_SUCCESS(rv
, rv
);
5574 // XXX What if SetupNewViewer fails?
5575 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry
: !!mLSHE
) {
5576 // Set history.state
5577 SetDocCurrentStateObj(mLSHE
,
5578 mLoadingEntry
? &mLoadingEntry
->mInfo
: nullptr);
5582 // Restore the editing state, if it's stored in session history.
5583 if (mLSHE
->HasDetachedEditor()) {
5584 ReattachEditorToWindow(mLSHE
);
5587 SetHistoryEntryAndUpdateBC(Nothing(), Some
<nsISHEntry
*>(mLSHE
));
5590 if (!aIsTransientAboutBlank
&& mozilla::SessionHistoryInParent() &&
5591 !IsFollowupPartOfMultipart(aRequest
)) {
5592 bool expired
= false;
5593 uint32_t cacheKey
= 0;
5594 nsCOMPtr
<nsICacheInfoChannel
> cacheChannel
= do_QueryInterface(aRequest
);
5596 // Check if the page has expired from cache
5597 uint32_t expTime
= 0;
5598 cacheChannel
->GetCacheTokenExpirationTime(&expTime
);
5599 uint32_t now
= PRTimeToSeconds(PR_Now());
5600 if (expTime
<= now
) {
5604 // The checks for updating cache key are similar to the old session
5605 // history in OnNewURI. Try to update the cache key if
5606 // - we should update session history and aren't doing a session
5608 // - we're doing a forced reload.
5609 if (((!mLoadingEntry
|| !mLoadingEntry
->mLoadIsFromSessionHistory
) &&
5610 mBrowsingContext
->ShouldUpdateSessionHistory(mLoadType
)) ||
5611 IsForceReloadType(mLoadType
)) {
5612 cacheChannel
->GetCacheKey(&cacheKey
);
5616 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("document %p Embed", this));
5617 MoveLoadingToActiveEntry(aPersist
, expired
, cacheKey
, aPreviousURI
);
5620 bool updateHistory
= true;
5622 // Determine if this type of load should update history
5623 switch (mLoadType
) {
5624 case LOAD_NORMAL_REPLACE
:
5625 case LOAD_REFRESH_REPLACE
:
5626 case LOAD_STOP_CONTENT_AND_REPLACE
:
5627 case LOAD_RELOAD_BYPASS_CACHE
:
5628 case LOAD_RELOAD_BYPASS_PROXY
:
5629 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
:
5630 case LOAD_REPLACE_BYPASS_CACHE
:
5631 updateHistory
= false;
5637 if (!updateHistory
) {
5638 SetLayoutHistoryState(nullptr);
5644 //*****************************************************************************
5645 // nsDocShell::nsIWebProgressListener
5646 //*****************************************************************************
5649 nsDocShell::OnProgressChange(nsIWebProgress
* aProgress
, nsIRequest
* aRequest
,
5650 int32_t aCurSelfProgress
, int32_t aMaxSelfProgress
,
5651 int32_t aCurTotalProgress
,
5652 int32_t aMaxTotalProgress
) {
5657 nsDocShell::OnStateChange(nsIWebProgress
* aProgress
, nsIRequest
* aRequest
,
5658 uint32_t aStateFlags
, nsresult aStatus
) {
5659 if ((~aStateFlags
& (STATE_START
| STATE_IS_NETWORK
)) == 0) {
5660 // Save timing statistics.
5661 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
5662 nsCOMPtr
<nsIURI
> uri
;
5663 channel
->GetURI(getter_AddRefs(uri
));
5665 uri
->GetAsciiSpec(aURI
);
5667 if (this == aProgress
) {
5668 mozilla::Unused
<< MaybeInitTiming();
5669 mTiming
->NotifyFetchStart(uri
,
5670 ConvertLoadTypeToNavigationType(mLoadType
));
5671 // If we are starting a DocumentChannel, we need to pass the timing
5672 // statistics so that should a process switch occur, the starting type can
5673 // be passed to the new DocShell running in the other content process.
5674 if (RefPtr
<DocumentChannel
> docChannel
= do_QueryObject(aRequest
)) {
5675 docChannel
->SetNavigationTiming(mTiming
);
5679 // Page has begun to load
5680 mBusyFlags
= (BusyFlags
)(BUSY_FLAGS_BUSY
| BUSY_FLAGS_BEFORE_PAGE_LOAD
);
5682 if ((aStateFlags
& STATE_RESTORING
) == 0) {
5683 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
5684 if (IsForceReloadType(mLoadType
)) {
5685 if (WindowContext
* windowContext
=
5686 mBrowsingContext
->GetCurrentWindowContext()) {
5687 SessionStoreChild::From(windowContext
->GetWindowGlobalChild())
5688 ->ResetSessionStore(mBrowsingContext
,
5689 mBrowsingContext
->GetSessionStoreEpoch());
5694 } else if ((~aStateFlags
& (STATE_TRANSFERRING
| STATE_IS_DOCUMENT
)) == 0) {
5696 mBusyFlags
= (BusyFlags
)(BUSY_FLAGS_BUSY
| BUSY_FLAGS_PAGE_LOADING
);
5697 } else if ((aStateFlags
& STATE_STOP
) && (aStateFlags
& STATE_IS_NETWORK
)) {
5698 // Page has finished loading
5699 mBusyFlags
= BUSY_FLAGS_NONE
;
5702 if ((~aStateFlags
& (STATE_IS_DOCUMENT
| STATE_STOP
)) == 0) {
5703 nsCOMPtr
<nsIWebProgress
> webProgress
=
5704 do_QueryInterface(GetAsSupports(this));
5705 // Is the document stop notification for this document?
5706 if (aProgress
== webProgress
.get()) {
5707 nsCOMPtr
<nsIChannel
> channel(do_QueryInterface(aRequest
));
5708 EndPageLoad(aProgress
, channel
, aStatus
);
5711 // note that redirect state changes will go through here as well, but it
5712 // is better to handle those in OnRedirectStateChange where more
5713 // information is available.
5718 nsDocShell::OnLocationChange(nsIWebProgress
* aProgress
, nsIRequest
* aRequest
,
5719 nsIURI
* aURI
, uint32_t aFlags
) {
5720 // Since we've now changed Documents, notify the BrowsingContext that we've
5721 // changed. Ideally we'd just let the BrowsingContext do this when it
5722 // changes the current window global, but that happens before this and we
5723 // have a lot of tests that depend on the specific ordering of messages.
5724 bool isTopLevel
= false;
5725 if (XRE_IsParentProcess() &&
5726 !(aFlags
& nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT
) &&
5727 NS_SUCCEEDED(aProgress
->GetIsTopLevel(&isTopLevel
)) && isTopLevel
) {
5728 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5733 void nsDocShell::OnRedirectStateChange(nsIChannel
* aOldChannel
,
5734 nsIChannel
* aNewChannel
,
5735 uint32_t aRedirectFlags
,
5736 uint32_t aStateFlags
) {
5737 NS_ASSERTION(aStateFlags
& STATE_REDIRECTING
,
5738 "Calling OnRedirectStateChange when there is no redirect");
5740 if (!(aStateFlags
& STATE_IS_DOCUMENT
)) {
5741 return; // not a toplevel document
5744 nsCOMPtr
<nsIURI
> oldURI
, newURI
;
5745 aOldChannel
->GetURI(getter_AddRefs(oldURI
));
5746 aNewChannel
->GetURI(getter_AddRefs(newURI
));
5747 if (!oldURI
|| !newURI
) {
5751 // DocumentChannel adds redirect chain to global history in the parent
5752 // process. The redirect chain can't be queried from the content process, so
5753 // there's no need to update global history here.
5754 RefPtr
<DocumentChannel
> docChannel
= do_QueryObject(aOldChannel
);
5756 // Below a URI visit is saved (see AddURIVisit method doc).
5757 // The visit chain looks something like:
5761 // (redirect to =>) Site N + 1 (we are here!)
5763 // Get N - 1 and transition type
5764 nsCOMPtr
<nsIURI
> previousURI
;
5765 uint32_t previousFlags
= 0;
5766 ExtractLastVisit(aOldChannel
, getter_AddRefs(previousURI
), &previousFlags
);
5768 if (aRedirectFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
||
5769 net::ChannelIsPost(aOldChannel
)) {
5770 // 1. Internal redirects are ignored because they are specific to the
5771 // channel implementation.
5772 // 2. POSTs are not saved by global history.
5774 // Regardless, we need to propagate the previous visit to the new
5776 SaveLastVisit(aNewChannel
, previousURI
, previousFlags
);
5778 // Get the HTTP response code, if available.
5779 uint32_t responseStatus
= 0;
5780 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aOldChannel
);
5782 Unused
<< httpChannel
->GetResponseStatus(&responseStatus
);
5785 // Add visit N -1 => N
5786 AddURIVisit(oldURI
, previousURI
, previousFlags
, responseStatus
);
5788 // Since N + 1 could be the final destination, we will not save N => N + 1
5789 // here. OnNewURI will do that, so we will cache it.
5790 SaveLastVisit(aNewChannel
, oldURI
, aRedirectFlags
);
5794 if (!(aRedirectFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
) &&
5795 mLoadType
& (LOAD_CMD_RELOAD
| LOAD_CMD_HISTORY
)) {
5796 mLoadType
= LOAD_NORMAL_REPLACE
;
5797 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5802 nsDocShell::OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
5803 nsresult aStatus
, const char16_t
* aMessage
) {
5804 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5809 nsDocShell::OnSecurityChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
5811 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5816 nsDocShell::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
5817 nsIRequest
* aRequest
, uint32_t aEvent
) {
5818 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5822 already_AddRefed
<nsIURIFixupInfo
> nsDocShell::KeywordToURI(
5823 const nsACString
& aKeyword
, bool aIsPrivateContext
) {
5824 nsCOMPtr
<nsIURIFixupInfo
> info
;
5825 if (!XRE_IsContentProcess()) {
5826 nsCOMPtr
<nsIURIFixup
> uriFixup
= components::URIFixup::Service();
5828 uriFixup
->KeywordToURI(aKeyword
, aIsPrivateContext
, getter_AddRefs(info
));
5831 return info
.forget();
5835 already_AddRefed
<nsIURI
> nsDocShell::MaybeFixBadCertDomainErrorURI(
5836 nsIChannel
* aChannel
, nsIURI
* aUrl
) {
5841 nsresult rv
= NS_OK
;
5843 rv
= aUrl
->GetAsciiHost(host
);
5844 if (NS_WARN_IF(NS_FAILED(rv
))) {
5848 // No point in going further if "www." is included in the hostname
5849 // already. That is the only hueristic we're applying in this function.
5850 if (StringBeginsWith(host
, "www."_ns
)) {
5854 // Return if fixup enable pref is turned off.
5855 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5859 // Return if scheme is not HTTPS.
5860 if (!SchemeIsHTTPS(aUrl
)) {
5864 nsCOMPtr
<nsILoadInfo
> info
= aChannel
->LoadInfo();
5869 // Skip doing the fixup if our channel was redirected, because we
5870 // shouldn't be guessing things about the post-redirect URI.
5871 if (!info
->RedirectChain().IsEmpty()) {
5876 rv
= aUrl
->GetPort(&port
);
5877 if (NS_WARN_IF(NS_FAILED(rv
))) {
5881 // Don't fix up hosts with ports.
5886 // Don't fix up localhost url.
5887 if (host
== "localhost") {
5891 // Don't fix up hostnames with IP address.
5892 if (net_IsValidIPv4Addr(host
) || net_IsValidIPv6Addr(host
)) {
5896 nsAutoCString userPass
;
5897 rv
= aUrl
->GetUserPass(userPass
);
5898 if (NS_WARN_IF(NS_FAILED(rv
))) {
5902 // Security - URLs with user / password info should NOT be modified.
5903 if (!userPass
.IsEmpty()) {
5907 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
5908 rv
= aChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
5909 if (NS_WARN_IF(NS_FAILED(rv
))) {
5913 if (NS_WARN_IF(!tsi
)) {
5917 nsCOMPtr
<nsIX509Cert
> cert
;
5918 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
5919 if (NS_WARN_IF(NS_FAILED(rv
) || !cert
)) {
5923 nsTArray
<uint8_t> certBytes
;
5924 rv
= cert
->GetRawDER(certBytes
);
5925 if (NS_FAILED(rv
)) {
5929 mozilla::pkix::Input serverCertInput
;
5930 mozilla::pkix::Result rv1
=
5931 serverCertInput
.Init(certBytes
.Elements(), certBytes
.Length());
5932 if (rv1
!= mozilla::pkix::Success
) {
5936 nsAutoCString
newHost("www."_ns
);
5937 newHost
.Append(host
);
5939 mozilla::pkix::Input newHostInput
;
5940 rv1
= newHostInput
.Init(
5941 BitwiseCast
<const uint8_t*, const char*>(newHost
.BeginReading()),
5943 if (rv1
!= mozilla::pkix::Success
) {
5947 // Check if adding a "www." prefix to the request's hostname will
5948 // cause the response's certificate to match.
5949 rv1
= mozilla::pkix::CheckCertHostname(serverCertInput
, newHostInput
);
5950 if (rv1
!= mozilla::pkix::Success
) {
5954 nsCOMPtr
<nsIURI
> newURI
;
5955 Unused
<< NS_MutateURI(aUrl
).SetHost(newHost
).Finalize(
5956 getter_AddRefs(newURI
));
5958 return newURI
.forget();
5962 already_AddRefed
<nsIURI
> nsDocShell::AttemptURIFixup(
5963 nsIChannel
* aChannel
, nsresult aStatus
,
5964 const mozilla::Maybe
<nsCString
>& aOriginalURIString
, uint32_t aLoadType
,
5965 bool aIsTopFrame
, bool aAllowKeywordFixup
, bool aUsePrivateBrowsing
,
5966 bool aNotifyKeywordSearchLoading
, nsIInputStream
** aNewPostData
,
5967 bool* outWasSchemelessInput
) {
5968 if (aStatus
!= NS_ERROR_UNKNOWN_HOST
&& aStatus
!= NS_ERROR_NET_RESET
&&
5969 aStatus
!= NS_ERROR_CONNECTION_REFUSED
&&
5971 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN
)) {
5975 if (!(aLoadType
== LOAD_NORMAL
&& aIsTopFrame
) && !aAllowKeywordFixup
) {
5979 nsCOMPtr
<nsIURI
> url
;
5980 nsresult rv
= aChannel
->GetURI(getter_AddRefs(url
));
5981 if (NS_FAILED(rv
)) {
5986 // Try and make an alternative URI from the old one
5988 nsCOMPtr
<nsIURI
> newURI
;
5989 nsCOMPtr
<nsIInputStream
> newPostData
;
5991 nsAutoCString oldSpec
;
5992 url
->GetSpec(oldSpec
);
5995 // First try keyword fixup
5997 nsAutoString keywordProviderName
, keywordAsSent
;
5998 if (aStatus
== NS_ERROR_UNKNOWN_HOST
&& aAllowKeywordFixup
) {
5999 // we should only perform a keyword search under the following
6001 // (0) Pref keyword.enabled is true
6002 // (1) the url scheme is http (or https)
6003 // (2) the url does not have a protocol scheme
6004 // If we don't enforce such a policy, then we end up doing
6005 // keyword searchs on urls we don't intend like imap, file,
6006 // mailbox, etc. This could lead to a security problem where we
6007 // send data to the keyword server that we shouldn't be.
6008 // Someone needs to clean up keywords in general so we can
6009 // determine on a per url basis if we want keywords
6010 // enabled...this is just a bandaid...
6011 nsAutoCString scheme
;
6012 Unused
<< url
->GetScheme(scheme
);
6013 if (Preferences::GetBool("keyword.enabled", false) &&
6014 StringBeginsWith(scheme
, "http"_ns
)) {
6015 bool attemptFixup
= false;
6017 Unused
<< url
->GetHost(host
);
6018 if (host
.FindChar('.') == kNotFound
) {
6019 attemptFixup
= true;
6021 // For domains with dots, we check the public suffix validity.
6022 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
6023 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
6025 nsAutoCString suffix
;
6027 NS_SUCCEEDED(tldService
->GetKnownPublicSuffix(url
, suffix
)) &&
6032 nsCOMPtr
<nsIURIFixupInfo
> info
;
6033 // only send non-qualified hosts to the keyword server
6034 if (aOriginalURIString
&& !aOriginalURIString
->IsEmpty()) {
6035 info
= KeywordToURI(*aOriginalURIString
, aUsePrivateBrowsing
);
6038 // If this string was passed through nsStandardURL by
6039 // chance, then it may have been converted from UTF-8 to
6040 // ACE, which would result in a completely bogus keyword
6041 // query. Here we try to recover the original Unicode
6042 // value, but this is not 100% correct since the value may
6043 // have been normalized per the IDN normalization rules.
6045 // Since we don't have access to the exact original string
6046 // that was entered by the user, this will just have to do.
6048 nsAutoCString utf8Host
;
6049 nsCOMPtr
<nsIIDNService
> idnSrv
=
6050 do_GetService(NS_IDNSERVICE_CONTRACTID
);
6051 if (idnSrv
&& NS_SUCCEEDED(idnSrv
->IsACE(host
, &isACE
)) && isACE
&&
6052 NS_SUCCEEDED(idnSrv
->ConvertACEtoUTF8(host
, utf8Host
))) {
6053 info
= KeywordToURI(utf8Host
, aUsePrivateBrowsing
);
6056 info
= KeywordToURI(host
, aUsePrivateBrowsing
);
6060 info
->GetPreferredURI(getter_AddRefs(newURI
));
6061 info
->GetWasSchemelessInput(outWasSchemelessInput
);
6063 info
->GetKeywordAsSent(keywordAsSent
);
6064 info
->GetKeywordProviderName(keywordProviderName
);
6065 info
->GetPostData(getter_AddRefs(newPostData
));
6073 // Now try change the address, e.g. turn http://foo into
6074 // http://www.foo.com, and if that doesn't work try https with
6075 // https://foo and https://www.foo.com.
6077 if (aStatus
== NS_ERROR_UNKNOWN_HOST
|| aStatus
== NS_ERROR_NET_RESET
) {
6078 // Skip fixup for anything except a normal document load
6079 // operation on the topframe.
6080 bool doCreateAlternate
= aLoadType
== LOAD_NORMAL
&& aIsTopFrame
;
6082 if (doCreateAlternate
) {
6083 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
6084 nsIPrincipal
* principal
= loadInfo
->TriggeringPrincipal();
6085 // Only do this if our channel was loaded directly by the user from the
6086 // URL bar or similar (system principal) and not redirected, because we
6087 // shouldn't be guessing things about links from other sites, or a
6088 // post-redirect URI.
6089 doCreateAlternate
= principal
&& principal
->IsSystemPrincipal() &&
6090 loadInfo
->RedirectChain().IsEmpty();
6092 // Test if keyword lookup produced a new URI or not
6093 if (doCreateAlternate
&& newURI
) {
6094 bool sameURI
= false;
6095 url
->Equals(newURI
, &sameURI
);
6097 // Keyword lookup made a new URI so no need to try
6098 // an alternate one.
6099 doCreateAlternate
= false;
6102 if (doCreateAlternate
) {
6104 newPostData
= nullptr;
6105 keywordProviderName
.Truncate();
6106 keywordAsSent
.Truncate();
6107 nsCOMPtr
<nsIURIFixup
> uriFixup
= components::URIFixup::Service();
6109 nsCOMPtr
<nsIURIFixupInfo
> fixupInfo
;
6110 uriFixup
->GetFixupURIInfo(oldSpec
, nsIURIFixup::FIXUP_FLAG_NONE
,
6111 getter_AddRefs(fixupInfo
));
6113 fixupInfo
->GetPreferredURI(getter_AddRefs(newURI
));
6117 } else if (aStatus
== NS_ERROR_CONNECTION_REFUSED
&&
6118 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6119 // Try HTTPS, since http didn't work
6120 if (SchemeIsHTTP(url
)) {
6122 url
->GetPort(&port
);
6124 // Fall back to HTTPS only if port is default
6127 newPostData
= nullptr;
6128 Unused
<< NS_MutateURI(url
)
6129 .SetScheme("https"_ns
)
6130 .Finalize(getter_AddRefs(newURI
));
6135 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try prefixing the domain name
6136 // with www. to see if we can avoid showing the cert error page. For example,
6137 // https://example.com -> https://www.example.com.
6139 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN
)) {
6140 newPostData
= nullptr;
6141 newURI
= MaybeFixBadCertDomainErrorURI(aChannel
, url
);
6144 // Did we make a new URI that is different to the old one? If so
6148 // Make sure the new URI is different from the old one,
6149 // otherwise there's little point trying to load it again.
6150 bool sameURI
= false;
6151 url
->Equals(newURI
, &sameURI
);
6154 newPostData
.forget(aNewPostData
);
6156 if (aNotifyKeywordSearchLoading
) {
6157 // This notification is meant for Firefox Health Report so it
6158 // can increment counts from the search engine
6159 MaybeNotifyKeywordSearchLoading(keywordProviderName
, keywordAsSent
);
6161 return newURI
.forget();
6168 nsresult
nsDocShell::FilterStatusForErrorPage(
6169 nsresult aStatus
, nsIChannel
* aChannel
, uint32_t aLoadType
,
6170 bool aIsTopFrame
, bool aUseErrorPages
, bool aIsInitialDocument
,
6171 bool* aSkippedUnknownProtocolNavigation
) {
6172 // Errors to be shown only on top-level frames
6173 if ((aStatus
== NS_ERROR_UNKNOWN_HOST
||
6174 aStatus
== NS_ERROR_CONNECTION_REFUSED
||
6175 aStatus
== NS_ERROR_UNKNOWN_PROXY_HOST
||
6176 aStatus
== NS_ERROR_PROXY_CONNECTION_REFUSED
||
6177 aStatus
== NS_ERROR_PROXY_FORBIDDEN
||
6178 aStatus
== NS_ERROR_PROXY_NOT_IMPLEMENTED
||
6179 aStatus
== NS_ERROR_PROXY_AUTHENTICATION_FAILED
||
6180 aStatus
== NS_ERROR_PROXY_TOO_MANY_REQUESTS
||
6181 aStatus
== NS_ERROR_MALFORMED_URI
||
6182 aStatus
== NS_ERROR_BLOCKED_BY_POLICY
||
6183 aStatus
== NS_ERROR_DOM_COOP_FAILED
||
6184 aStatus
== NS_ERROR_DOM_COEP_FAILED
) &&
6185 (aIsTopFrame
|| aUseErrorPages
)) {
6189 if (aStatus
== NS_ERROR_NET_TIMEOUT
||
6190 aStatus
== NS_ERROR_NET_TIMEOUT_EXTERNAL
||
6191 aStatus
== NS_ERROR_PROXY_GATEWAY_TIMEOUT
||
6192 aStatus
== NS_ERROR_REDIRECT_LOOP
||
6193 aStatus
== NS_ERROR_UNKNOWN_SOCKET_TYPE
||
6194 aStatus
== NS_ERROR_NET_INTERRUPT
|| aStatus
== NS_ERROR_NET_RESET
||
6195 aStatus
== NS_ERROR_PROXY_BAD_GATEWAY
|| aStatus
== NS_ERROR_OFFLINE
||
6196 aStatus
== NS_ERROR_MALWARE_URI
|| aStatus
== NS_ERROR_PHISHING_URI
||
6197 aStatus
== NS_ERROR_UNWANTED_URI
|| aStatus
== NS_ERROR_HARMFUL_URI
||
6198 aStatus
== NS_ERROR_UNSAFE_CONTENT_TYPE
||
6199 aStatus
== NS_ERROR_INTERCEPTION_FAILED
||
6200 aStatus
== NS_ERROR_NET_INADEQUATE_SECURITY
||
6201 aStatus
== NS_ERROR_NET_HTTP2_SENT_GOAWAY
||
6202 aStatus
== NS_ERROR_NET_HTTP3_PROTOCOL_ERROR
||
6203 aStatus
== NS_ERROR_DOM_BAD_URI
|| aStatus
== NS_ERROR_FILE_NOT_FOUND
||
6204 aStatus
== NS_ERROR_FILE_ACCESS_DENIED
||
6205 aStatus
== NS_ERROR_CORRUPTED_CONTENT
||
6206 aStatus
== NS_ERROR_INVALID_CONTENT_ENCODING
||
6207 NS_ERROR_GET_MODULE(aStatus
) == NS_ERROR_MODULE_SECURITY
) {
6208 // Errors to be shown for any frame
6212 if (aStatus
== NS_ERROR_UNKNOWN_PROTOCOL
) {
6213 // For unknown protocols we only display an error if the load is triggered
6214 // by the browser itself, or we're replacing the initial document (and
6215 // nothing else). Showing the error for page-triggered navigations causes
6216 // annoying behavior for users, see bug 1528305.
6218 // We could, maybe, try to detect if this is in response to some user
6219 // interaction (like clicking a link, or something else) and maybe show
6220 // the error page in that case. But this allows for ctrl+clicking and such
6221 // to see the error page.
6222 nsCOMPtr
<nsILoadInfo
> info
= aChannel
->LoadInfo();
6223 if (!info
->TriggeringPrincipal()->IsSystemPrincipal() &&
6224 StaticPrefs::dom_no_unknown_protocol_error_enabled() &&
6225 !aIsInitialDocument
) {
6226 if (aSkippedUnknownProtocolNavigation
) {
6227 *aSkippedUnknownProtocolNavigation
= true;
6234 if (aStatus
== NS_ERROR_DOCUMENT_NOT_CACHED
) {
6235 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6236 // Caching channels would have to look at their flags to work
6237 // out which error to return. Or we can fix up the error here.
6238 if (!(aLoadType
& LOAD_CMD_HISTORY
)) {
6239 return NS_ERROR_OFFLINE
;
6247 nsresult
nsDocShell::EndPageLoad(nsIWebProgress
* aProgress
,
6248 nsIChannel
* aChannel
, nsresult aStatus
) {
6249 MOZ_LOG(gDocShellLeakLog
, LogLevel::Debug
,
6250 ("DOCSHELL %p EndPageLoad status: %" PRIx32
"\n", this,
6251 static_cast<uint32_t>(aStatus
)));
6253 return NS_ERROR_NULL_POINTER
;
6256 // Make sure to discard the initial client if we never created the initial
6257 // about:blank document. Do this before possibly returning from the method
6259 mInitialClientSource
.reset();
6261 nsCOMPtr
<nsIConsoleReportCollector
> reporter
= do_QueryInterface(aChannel
);
6263 nsCOMPtr
<nsILoadGroup
> loadGroup
;
6264 aChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
6266 reporter
->FlushConsoleReports(loadGroup
);
6268 reporter
->FlushConsoleReports(GetDocument());
6272 nsCOMPtr
<nsIURI
> url
;
6273 nsresult rv
= aChannel
->GetURI(getter_AddRefs(url
));
6274 if (NS_FAILED(rv
)) {
6278 nsCOMPtr
<nsITimedChannel
> timingChannel
= do_QueryInterface(aChannel
);
6279 if (timingChannel
) {
6280 TimeStamp channelCreationTime
;
6281 rv
= timingChannel
->GetChannelCreation(&channelCreationTime
);
6282 if (NS_SUCCEEDED(rv
) && !channelCreationTime
.IsNull()) {
6283 Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME
,
6284 channelCreationTime
);
6288 // Timing is picked up by the window, we don't need it anymore
6291 // clean up reload state for meta charset
6292 if (eCharsetReloadRequested
== mCharsetReloadState
) {
6293 mCharsetReloadState
= eCharsetReloadStopOrigional
;
6295 mCharsetReloadState
= eCharsetReloadInit
;
6298 // Save a pointer to the currently-loading history entry.
6299 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6300 // entry further down in this method.
6301 nsCOMPtr
<nsISHEntry
> loadingSHE
= mLSHE
;
6302 mozilla::Unused
<< loadingSHE
; // XXX: Not sure if we need this anymore
6305 // one of many safeguards that prevent death and destruction if
6306 // someone is so very very rude as to bring this window down
6307 // during this load handler.
6309 nsCOMPtr
<nsIDocShell
> kungFuDeathGrip(this);
6311 // Notify the DocumentViewer that the Document has finished loading. This
6312 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6313 if (!mEODForCurrentDocument
&& mDocumentViewer
) {
6314 mIsExecutingOnLoadHandler
= true;
6315 nsCOMPtr
<nsIDocumentViewer
> viewer
= mDocumentViewer
;
6316 viewer
->LoadComplete(aStatus
);
6317 mIsExecutingOnLoadHandler
= false;
6319 mEODForCurrentDocument
= true;
6321 /* Check if the httpChannel has any cache-control related response headers,
6322 * like no-store, no-cache. If so, update SHEntry so that
6323 * when a user goes back/forward to this page, we appropriately do
6324 * form value restoration or load from server.
6326 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aChannel
));
6328 // HttpChannel could be hiding underneath a Multipart channel.
6329 GetHttpChannel(aChannel
, getter_AddRefs(httpChannel
));
6333 // figure out if SH should be saving layout state.
6334 bool discardLayoutState
= ShouldDiscardLayoutState(httpChannel
);
6335 if (mLSHE
&& discardLayoutState
&& (mLoadType
& LOAD_CMD_NORMAL
) &&
6336 (mLoadType
!= LOAD_BYPASS_HISTORY
) && (mLoadType
!= LOAD_ERROR_PAGE
)) {
6337 mLSHE
->SetSaveLayoutStateFlag(false);
6341 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6342 // onLoadHandler tries to load something different in
6343 // itself or one of its children, we can deal with it appropriately.
6345 mLSHE
->SetLoadType(LOAD_HISTORY
);
6347 // Clear the mLSHE reference to indicate document loading is done one
6349 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6351 mActiveEntryIsLoadingFromSessionHistory
= false;
6353 // if there's a refresh header in the channel, this method
6354 // will set it up for us.
6355 if (mBrowsingContext
->IsActive() || !mDisableMetaRefreshWhenInactive
)
6356 RefreshURIFromQueue();
6358 // Test whether this is the top frame or a subframe
6359 bool isTopFrame
= mBrowsingContext
->IsTop();
6361 bool hadErrorStatus
= false;
6362 // If status code indicates an error it means that DocumentChannel already
6363 // tried to fixup the uri and failed. Throw an error dialog box here.
6364 if (NS_FAILED(aStatus
)) {
6365 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6366 // the error event to our embedder, since tests are relying on this.
6367 // The error event is usually fired by the caller of InternalLoad, but
6368 // this particular error can happen asynchronously.
6369 // Bug 1629201 is filed for having much clearer decision making around
6370 // which cases need error events.
6371 bool fireFrameErrorEvent
= (aStatus
== NS_ERROR_CONTENT_BLOCKED_SHOW_ALT
||
6372 aStatus
== NS_ERROR_CONTENT_BLOCKED
);
6373 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent
);
6375 bool isInitialDocument
=
6376 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6377 bool skippedUnknownProtocolNavigation
= false;
6378 aStatus
= FilterStatusForErrorPage(aStatus
, aChannel
, mLoadType
, isTopFrame
,
6379 mBrowsingContext
->GetUseErrorPages(),
6381 &skippedUnknownProtocolNavigation
);
6382 hadErrorStatus
= true;
6383 if (NS_FAILED(aStatus
)) {
6384 if (!mIsBeingDestroyed
) {
6385 DisplayLoadError(aStatus
, url
, nullptr, aChannel
);
6387 } else if (skippedUnknownProtocolNavigation
) {
6388 nsTArray
<nsString
> params
;
6390 NS_GetSanitizedURIStringFromURI(url
, *params
.AppendElement()))) {
6391 params
.LastElement().AssignLiteral(u
"(unknown uri)");
6393 nsContentUtils::ReportToConsole(
6394 nsIScriptError::warningFlag
, "DOM"_ns
, GetExtantDocument(),
6395 nsContentUtils::eDOM_PROPERTIES
, "UnknownProtocolNavigationPrevented",
6399 // If we have a host
6400 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
6401 PredictorLearnRedirect(url
, aChannel
, loadInfo
->GetOriginAttributes());
6404 if (hadErrorStatus
) {
6405 // Don't send session store updates if the reason EndPageLoad was called is
6406 // because we are process switching. Sometimes the update takes too long and
6407 // incorrectly overrides session store data from the following load.
6410 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
6411 if (WindowContext
* windowContext
=
6412 mBrowsingContext
->GetCurrentWindowContext()) {
6413 using Change
= SessionStoreChangeListener::Change
;
6415 // We've finished loading the page and now we want to collect all the
6416 // session store state that the page is initialized with.
6417 SessionStoreChangeListener::CollectSessionStoreData(
6419 EnumSet
<Change
>(Change::Input
, Change::Scroll
, Change::SessionHistory
,
6420 Change::WireFrame
));
6427 //*****************************************************************************
6428 // nsDocShell: Content Viewer Management
6429 //*****************************************************************************
6431 nsresult
nsDocShell::EnsureDocumentViewer() {
6432 if (mDocumentViewer
) {
6435 if (mIsBeingDestroyed
) {
6436 return NS_ERROR_FAILURE
;
6439 nsCOMPtr
<nsIContentSecurityPolicy
> cspToInheritForAboutBlank
;
6440 nsCOMPtr
<nsIURI
> baseURI
;
6441 nsIPrincipal
* principal
= GetInheritedPrincipal(false);
6442 nsIPrincipal
* partitionedPrincipal
= GetInheritedPrincipal(false, true);
6444 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
6445 GetInProcessSameTypeParent(getter_AddRefs(parentItem
));
6447 if (nsCOMPtr
<nsPIDOMWindowOuter
> domWin
= GetWindow()) {
6448 nsCOMPtr
<Element
> parentElement
= domWin
->GetFrameElementInternal();
6449 if (parentElement
) {
6450 baseURI
= parentElement
->GetBaseURI();
6451 cspToInheritForAboutBlank
= parentElement
->GetCsp();
6456 nsresult rv
= CreateAboutBlankDocumentViewer(
6457 principal
, partitionedPrincipal
, cspToInheritForAboutBlank
, baseURI
,
6458 /* aIsInitialDocument */ true);
6460 NS_ENSURE_STATE(mDocumentViewer
);
6462 if (NS_SUCCEEDED(rv
)) {
6463 RefPtr
<Document
> doc(GetDocument());
6465 "Should have doc if CreateAboutBlankDocumentViewer "
6467 MOZ_ASSERT(doc
->IsInitialDocument(), "Document should be initial document");
6469 // Documents created using EnsureDocumentViewer may be transient
6470 // placeholders created by framescripts before content has a
6471 // chance to load. In some cases, window.open(..., "noopener")
6472 // will create such a document and then synchronously tear it
6473 // down, firing a "pagehide" event. Doing so violates our
6474 // assertions about DocGroups. It's easier to silence the
6475 // assertion here than to avoid creating the extra document.
6476 doc
->IgnoreDocGroupMismatches();
6482 nsresult
nsDocShell::CreateAboutBlankDocumentViewer(
6483 nsIPrincipal
* aPrincipal
, nsIPrincipal
* aPartitionedPrincipal
,
6484 nsIContentSecurityPolicy
* aCSP
, nsIURI
* aBaseURI
, bool aIsInitialDocument
,
6485 const Maybe
<nsILoadInfo::CrossOriginEmbedderPolicy
>& aCOEP
,
6486 bool aTryToSaveOldPresentation
, bool aCheckPermitUnload
,
6487 WindowGlobalChild
* aActor
) {
6488 RefPtr
<Document
> blankDoc
;
6489 nsCOMPtr
<nsIDocumentViewer
> viewer
;
6490 nsresult rv
= NS_ERROR_FAILURE
;
6492 MOZ_ASSERT_IF(aActor
, aActor
->DocumentPrincipal() == aPrincipal
);
6494 /* mCreatingDocument should never be true at this point. However, it's
6495 a theoretical possibility. We want to know about it and make it stop,
6496 and this sounds like a job for an assertion. */
6497 NS_ASSERTION(!mCreatingDocument
,
6498 "infinite(?) loop creating document averted");
6499 if (mCreatingDocument
) {
6500 return NS_ERROR_FAILURE
;
6503 if (!mBrowsingContext
->AncestorsAreCurrent() ||
6504 mBrowsingContext
->IsInBFCache()) {
6505 mBrowsingContext
->RemoveRootFromBFCacheSync();
6506 return NS_ERROR_NOT_AVAILABLE
;
6509 // mDocumentViewer->PermitUnload may release |this| docshell.
6510 nsCOMPtr
<nsIDocShell
> kungFuDeathGrip(this);
6512 AutoRestore
<bool> creatingDocument(mCreatingDocument
);
6513 mCreatingDocument
= true;
6515 if (aPrincipal
&& !aPrincipal
->IsSystemPrincipal() &&
6516 mItemType
!= typeChrome
) {
6517 MOZ_ASSERT(aPrincipal
->OriginAttributesRef() ==
6518 mBrowsingContext
->OriginAttributesRef());
6521 // Make sure timing is created. But first record whether we had it
6522 // already, so we don't clobber the timing for an in-progress load.
6523 bool hadTiming
= mTiming
;
6524 bool toBeReset
= MaybeInitTiming();
6525 if (mDocumentViewer
) {
6526 if (aCheckPermitUnload
) {
6527 // We've got a content viewer already. Make sure the user
6528 // permits us to discard the current document and replace it
6529 // with about:blank. And also ensure we fire the unload events
6530 // in the current document.
6532 // Unload gets fired first for
6533 // document loaded from the session history.
6534 mTiming
->NotifyBeforeUnload();
6537 rv
= mDocumentViewer
->PermitUnload(&okToUnload
);
6539 if (NS_SUCCEEDED(rv
) && !okToUnload
) {
6540 // The user chose not to unload the page, interrupt the load.
6541 MaybeResetInitTiming(toBeReset
);
6542 return NS_ERROR_FAILURE
;
6545 mTiming
->NotifyUnloadAccepted(mCurrentURI
);
6550 aTryToSaveOldPresentation
&&
6551 CanSavePresentation(LOAD_NORMAL
, nullptr, nullptr,
6552 /* aReportBFCacheComboTelemetry */ true);
6554 // Make sure to blow away our mLoadingURI just in case. No loads
6555 // from inside this pagehide.
6556 mLoadingURI
= nullptr;
6558 // Stop any in-progress loading, so that we don't accidentally trigger any
6559 // PageShow notifications from Embed() interrupting our loading below.
6562 // Notify the current document that it is about to be unloaded!!
6564 // It is important to fire the unload() notification *before* any state
6565 // is changed within the DocShell - otherwise, javascript will get the
6566 // wrong information :-(
6568 (void)FirePageHideNotification(!mSavingOldViewer
);
6569 // pagehide notification might destroy this docshell.
6570 if (mIsBeingDestroyed
) {
6571 return NS_ERROR_DOCSHELL_DYING
;
6575 // Now make sure we don't think we're in the middle of firing unload after
6576 // this point. This will make us fire unload when the about:blank document
6577 // unloads... but that's ok, more or less. Would be nice if it fired load
6579 mFiredUnloadEvent
= false;
6581 nsCOMPtr
<nsIDocumentLoaderFactory
> docFactory
=
6582 nsContentUtils::FindInternalDocumentViewer("text/html"_ns
);
6585 nsCOMPtr
<nsIPrincipal
> principal
, partitionedPrincipal
;
6586 const uint32_t sandboxFlags
=
6587 mBrowsingContext
->GetHasLoadedNonInitialDocument()
6588 ? mBrowsingContext
->GetSandboxFlags()
6589 : mBrowsingContext
->GetInitialSandboxFlags();
6590 // If we're sandboxed, then create a new null principal. We skip
6591 // this if we're being created from WindowGlobalChild, since in
6592 // that case we already have a null principal if required.
6593 // We can't compare againt the BrowsingContext sandbox flag, since
6594 // the value was taken when the load initiated and may have since
6596 if ((sandboxFlags
& SANDBOXED_ORIGIN
) && !aActor
) {
6598 principal
= NullPrincipal::CreateWithInheritedAttributes(aPrincipal
);
6600 principal
= NullPrincipal::Create(GetOriginAttributes());
6602 partitionedPrincipal
= principal
;
6604 principal
= aPrincipal
;
6605 partitionedPrincipal
= aPartitionedPrincipal
;
6608 // We cannot get the foreign partitioned prinicpal for the initial
6609 // about:blank page. So, we change to check if we need to use the
6610 // partitioned principal for the service worker here.
6611 MaybeCreateInitialClientSource(
6612 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6614 ? partitionedPrincipal
6617 // generate (about:blank) document to load
6618 blankDoc
= nsContentDLF::CreateBlankDocument(mLoadGroup
, principal
,
6619 partitionedPrincipal
, this);
6621 // Hack: manually set the CSP for the new document
6622 // Please create an actual copy of the CSP (do not share the same
6623 // reference) otherwise appending a new policy within the new
6624 // document will be incorrectly propagated to the opening doc.
6626 RefPtr
<nsCSPContext
> cspToInherit
= new nsCSPContext();
6627 cspToInherit
->InitFromOther(static_cast<nsCSPContext
*>(aCSP
));
6628 blankDoc
->SetCsp(cspToInherit
);
6631 blankDoc
->SetIsInitialDocument(aIsInitialDocument
);
6633 blankDoc
->SetEmbedderPolicy(aCOEP
);
6635 // Hack: set the base URI manually, since this document never
6636 // got Reset() with a channel.
6637 blankDoc
->SetBaseURI(aBaseURI
);
6639 // Copy our sandbox flags to the document. These are immutable
6640 // after being set here.
6641 blankDoc
->SetSandboxFlags(sandboxFlags
);
6643 blankDoc
->InitFeaturePolicy();
6645 // create a content viewer for us and the new document
6646 docFactory
->CreateInstanceForDocument(
6647 NS_ISUPPORTS_CAST(nsIDocShell
*, this), blankDoc
, "view",
6648 getter_AddRefs(viewer
));
6652 viewer
->SetContainer(this);
6653 rv
= Embed(viewer
, aActor
, true, false, nullptr, mCurrentURI
);
6654 NS_ENSURE_SUCCESS(rv
, rv
);
6656 SetCurrentURI(blankDoc
->GetDocumentURI(), nullptr,
6657 /* aFireLocationChange */ true,
6658 /* aIsInitialAboutBlank */ true,
6659 /* aLocationFlags */ 0);
6660 rv
= mIsBeingDestroyed
? NS_ERROR_NOT_AVAILABLE
: NS_OK
;
6665 // The transient about:blank viewer doesn't have a session history entry.
6666 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6668 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6669 // have one before entering this function.
6672 mBlankTiming
= true;
6679 nsDocShell::CreateAboutBlankDocumentViewer(nsIPrincipal
* aPrincipal
,
6680 nsIPrincipal
* aPartitionedPrincipal
,
6681 nsIContentSecurityPolicy
* aCSP
) {
6682 return CreateAboutBlankDocumentViewer(aPrincipal
, aPartitionedPrincipal
, aCSP
,
6684 /* aIsInitialDocument */ false);
6687 nsresult
nsDocShell::CreateDocumentViewerForActor(
6688 WindowGlobalChild
* aWindowActor
) {
6689 MOZ_ASSERT(aWindowActor
);
6691 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6692 // FIXME: We may want to support non-initial documents here.
6693 nsresult rv
= CreateAboutBlankDocumentViewer(
6694 aWindowActor
->DocumentPrincipal(), aWindowActor
->DocumentPrincipal(),
6696 /* aBaseURI */ nullptr,
6697 /* aIsInitialDocument */ true,
6698 /* aCOEP */ Nothing(),
6699 /* aTryToSaveOldPresentation */ true,
6700 /* aCheckPermitUnload */ true, aWindowActor
);
6702 if (NS_SUCCEEDED(rv
)) {
6703 RefPtr
<Document
> doc(GetDocument());
6706 "Should have a document if CreateAboutBlankDocumentViewer succeeded");
6707 MOZ_ASSERT(doc
->GetOwnerGlobal() == aWindowActor
->GetWindowGlobal(),
6708 "New document should be in the same global as our actor");
6709 MOZ_ASSERT(doc
->IsInitialDocument(),
6710 "New document should be an initial document");
6717 bool nsDocShell::CanSavePresentation(uint32_t aLoadType
,
6718 nsIRequest
* aNewRequest
,
6719 Document
* aNewDocument
,
6720 bool aReportBFCacheComboTelemetry
) {
6722 return false; // no entry to save into
6725 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6726 "mOSHE cannot be non-null with SHIP");
6727 nsCOMPtr
<nsIDocumentViewer
> viewer
= mOSHE
->GetDocumentViewer();
6729 NS_WARNING("mOSHE already has a content viewer!");
6733 // Only save presentation for "normal" loads and link loads. Anything else
6734 // probably wants to refetch the page, so caching the old presentation
6735 // would be incorrect.
6736 if (aLoadType
!= LOAD_NORMAL
&& aLoadType
!= LOAD_HISTORY
&&
6737 aLoadType
!= LOAD_LINK
&& aLoadType
!= LOAD_STOP_CONTENT
&&
6738 aLoadType
!= LOAD_STOP_CONTENT_AND_REPLACE
&&
6739 aLoadType
!= LOAD_ERROR_PAGE
) {
6743 // If the session history entry has the saveLayoutState flag set to false,
6744 // then we should not cache the presentation.
6745 if (!mOSHE
->GetSaveLayoutStateFlag()) {
6749 // If the document is not done loading, don't cache it.
6750 if (!mScriptGlobal
|| mScriptGlobal
->IsLoading()) {
6751 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
6752 ("Blocked due to document still loading"));
6756 if (mScriptGlobal
->WouldReuseInnerWindow(aNewDocument
)) {
6760 // Avoid doing the work of saving the presentation state in the case where
6761 // the content viewer cache is disabled.
6762 if (nsSHistory::GetMaxTotalViewers() == 0) {
6766 // Don't cache the content viewer if we're in a subframe.
6767 if (mBrowsingContext
->GetParent()) {
6768 return false; // this is a subframe load
6771 // If the document does not want its presentation cached, then don't.
6772 RefPtr
<Document
> doc
= mScriptGlobal
->GetExtantDoc();
6774 uint32_t bfCacheCombo
= 0;
6775 bool canSavePresentation
=
6776 doc
->CanSavePresentation(aNewRequest
, bfCacheCombo
, true);
6777 MOZ_ASSERT_IF(canSavePresentation
, bfCacheCombo
== 0);
6778 if (canSavePresentation
&& doc
->IsTopLevelContentDocument()) {
6779 auto* browsingContextGroup
= mBrowsingContext
->Group();
6780 nsTArray
<RefPtr
<BrowsingContext
>>& topLevelContext
=
6781 browsingContextGroup
->Toplevels();
6783 for (const auto& browsingContext
: topLevelContext
) {
6784 if (browsingContext
!= mBrowsingContext
) {
6785 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6786 canSavePresentation
= false;
6788 bfCacheCombo
|= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG
;
6794 if (aReportBFCacheComboTelemetry
) {
6795 ReportBFCacheComboTelemetry(bfCacheCombo
);
6797 return doc
&& canSavePresentation
;
6801 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo
) {
6802 // There are 11 possible reasons to make a request fails to use BFCache
6803 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6804 // the common combinations for reasons which make requests fail to use
6805 // BFCache. These combinations are generated based on some local browsings,
6806 // we need to adjust them when necessary.
6807 enum BFCacheStatusCombo
: uint32_t {
6809 NOT_ONLY_TOPLEVEL
= mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG
,
6810 // If both unload and beforeunload listeners are presented, it'll be
6811 // recorded as unload
6812 UNLOAD
= mozilla::dom::BFCacheStatus::UNLOAD_LISTENER
,
6813 UNLOAD_REQUEST
= mozilla::dom::BFCacheStatus::UNLOAD_LISTENER
|
6814 mozilla::dom::BFCacheStatus::REQUEST
,
6815 REQUEST
= mozilla::dom::BFCacheStatus::REQUEST
,
6816 UNLOAD_REQUEST_PEER
= mozilla::dom::BFCacheStatus::UNLOAD_LISTENER
|
6817 mozilla::dom::BFCacheStatus::REQUEST
|
6818 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION
,
6819 UNLOAD_REQUEST_PEER_MSE
=
6820 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER
|
6821 mozilla::dom::BFCacheStatus::REQUEST
|
6822 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION
|
6823 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT
,
6824 UNLOAD_REQUEST_MSE
= mozilla::dom::BFCacheStatus::UNLOAD_LISTENER
|
6825 mozilla::dom::BFCacheStatus::REQUEST
|
6826 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT
,
6827 SUSPENDED_UNLOAD_REQUEST_PEER
=
6828 mozilla::dom::BFCacheStatus::SUSPENDED
|
6829 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER
|
6830 mozilla::dom::BFCacheStatus::REQUEST
|
6831 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION
,
6832 REMOTE_SUBFRAMES
= mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES
,
6833 BEFOREUNLOAD
= mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER
,
6836 // Beforeunload is recorded as a blocker only if it is the only one to block
6838 if (aCombo
!= mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER
) {
6839 aCombo
&= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER
;
6842 case BFCACHE_SUCCESS
:
6843 Telemetry::AccumulateCategorical(
6844 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success
);
6846 case NOT_ONLY_TOPLEVEL
:
6847 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6848 Telemetry::AccumulateCategorical(
6849 Telemetry::LABELS_BFCACHE_COMBO::Other
);
6852 Telemetry::AccumulateCategorical(
6853 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success
);
6854 Telemetry::AccumulateCategorical(
6855 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel
);
6858 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload
);
6861 Telemetry::AccumulateCategorical(
6862 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload
);
6864 case UNLOAD_REQUEST
:
6865 Telemetry::AccumulateCategorical(
6866 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req
);
6869 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req
);
6871 case UNLOAD_REQUEST_PEER
:
6872 Telemetry::AccumulateCategorical(
6873 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer
);
6875 case UNLOAD_REQUEST_PEER_MSE
:
6876 Telemetry::AccumulateCategorical(
6877 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE
);
6879 case UNLOAD_REQUEST_MSE
:
6880 Telemetry::AccumulateCategorical(
6881 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE
);
6883 case SUSPENDED_UNLOAD_REQUEST_PEER
:
6884 Telemetry::AccumulateCategorical(
6885 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer
);
6887 case REMOTE_SUBFRAMES
:
6888 Telemetry::AccumulateCategorical(
6889 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes
);
6892 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other
);
6897 void nsDocShell::ReattachEditorToWindow(nsISHEntry
* aSHEntry
) {
6898 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6899 MOZ_ASSERT(!mIsBeingDestroyed
);
6901 NS_ASSERTION(!mEditorData
,
6902 "Why reattach an editor when we already have one?");
6903 NS_ASSERTION(aSHEntry
&& aSHEntry
->HasDetachedEditor(),
6904 "Reattaching when there's not a detached editor.");
6906 if (mEditorData
|| !aSHEntry
) {
6910 mEditorData
= WrapUnique(aSHEntry
->ForgetEditorData());
6915 mEditorData
->ReattachToWindow(this);
6916 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to reattach editing session");
6920 void nsDocShell::DetachEditorFromWindow() {
6921 if (!mEditorData
|| mEditorData
->WaitingForLoad()) {
6922 // If there's nothing to detach, or if the editor data is actually set
6923 // up for the _new_ page that's coming in, don't detach.
6927 NS_ASSERTION(!mOSHE
|| !mOSHE
->HasDetachedEditor(),
6928 "Detaching editor when it's already detached.");
6930 nsresult res
= mEditorData
->DetachFromWindow();
6931 NS_ASSERTION(NS_SUCCEEDED(res
), "Failed to detach editor");
6933 if (NS_SUCCEEDED(res
)) {
6934 // Make mOSHE hold the owning ref to the editor data.
6936 MOZ_ASSERT(!mIsBeingDestroyed
|| !mOSHE
->HasDetachedEditor(),
6937 "We should not set the editor data again once after we "
6938 "detached the editor data during destroying this docshell");
6939 mOSHE
->SetEditorData(mEditorData
.release());
6941 mEditorData
= nullptr;
6948 GetEditable(&isEditable
);
6949 NS_ASSERTION(!isEditable
,
6950 "Window is still editable after detaching editor.");
6955 nsresult
nsDocShell::CaptureState() {
6956 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6958 if (!mOSHE
|| mOSHE
== mLSHE
) {
6959 // No entry to save into, or we're replacing the existing entry.
6960 return NS_ERROR_FAILURE
;
6963 if (!mScriptGlobal
) {
6964 return NS_ERROR_FAILURE
;
6967 nsCOMPtr
<nsISupports
> windowState
= mScriptGlobal
->SaveWindowState();
6968 NS_ENSURE_TRUE(windowState
, NS_ERROR_FAILURE
);
6970 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Debug
))) {
6972 nsCOMPtr
<nsIURI
> uri
= mOSHE
->GetURI();
6976 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
6977 ("Saving presentation into session history, URI: %s", spec
.get()));
6980 mOSHE
->SetWindowState(windowState
);
6982 // Suspend refresh URIs and save off the timer queue
6983 mOSHE
->SetRefreshURIList(mSavedRefreshURIList
);
6985 // Capture the current content viewer bounds.
6986 if (mDocumentViewer
) {
6988 mDocumentViewer
->GetBounds(bounds
);
6989 mOSHE
->SetViewerBounds(bounds
);
6992 // Capture the docshell hierarchy.
6993 mOSHE
->ClearChildShells();
6995 uint32_t childCount
= mChildList
.Length();
6996 for (uint32_t i
= 0; i
< childCount
; ++i
) {
6997 nsCOMPtr
<nsIDocShellTreeItem
> childShell
= do_QueryInterface(ChildAt(i
));
6998 NS_ASSERTION(childShell
, "null child shell");
7000 mOSHE
->AddChildShell(childShell
);
7007 nsDocShell::RestorePresentationEvent::Run() {
7008 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7010 if (mDocShell
&& NS_FAILED(mDocShell
->RestoreFromHistory())) {
7011 NS_WARNING("RestoreFromHistory failed");
7017 nsDocShell::BeginRestore(nsIDocumentViewer
* aDocumentViewer
, bool aTop
) {
7018 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7021 if (!aDocumentViewer
) {
7022 rv
= EnsureDocumentViewer();
7023 NS_ENSURE_SUCCESS(rv
, rv
);
7025 aDocumentViewer
= mDocumentViewer
;
7028 // Dispatch events for restoring the presentation. We try to simulate
7029 // the progress notifications loading the document would cause, so we add
7030 // the document's channel to the loadgroup to initiate stateChange
7033 RefPtr
<Document
> doc
= aDocumentViewer
->GetDocument();
7035 nsIChannel
* channel
= doc
->GetChannel();
7037 mEODForCurrentDocument
= false;
7038 mIsRestoringDocument
= true;
7039 mLoadGroup
->AddRequest(channel
, nullptr);
7040 mIsRestoringDocument
= false;
7045 // This point corresponds to us having gotten OnStartRequest or
7046 // STATE_START, so do the same thing that CreateDocumentViewer does at
7047 // this point to ensure that unload/pagehide events for this document
7048 // will fire when it's unloaded again.
7049 mFiredUnloadEvent
= false;
7051 // For non-top frames, there is no notion of making sure that the
7052 // previous document is in the domwindow when STATE_START notifications
7053 // happen. We can just call BeginRestore for all of the child shells
7055 rv
= BeginRestoreChildren();
7056 NS_ENSURE_SUCCESS(rv
, rv
);
7062 nsresult
nsDocShell::BeginRestoreChildren() {
7063 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7065 for (auto* childDocLoader
: mChildList
.ForwardRange()) {
7066 nsCOMPtr
<nsIDocShell
> child
= do_QueryObject(childDocLoader
);
7068 nsresult rv
= child
->BeginRestore(nullptr, false);
7069 NS_ENSURE_SUCCESS(rv
, rv
);
7076 nsDocShell::FinishRestore() {
7077 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7079 // First we call finishRestore() on our children. In the simulated load,
7080 // all of the child frames finish loading before the main document.
7082 for (auto* childDocLoader
: mChildList
.ForwardRange()) {
7083 nsCOMPtr
<nsIDocShell
> child
= do_QueryObject(childDocLoader
);
7085 child
->FinishRestore();
7089 if (mOSHE
&& mOSHE
->HasDetachedEditor()) {
7090 ReattachEditorToWindow(mOSHE
);
7093 RefPtr
<Document
> doc
= GetDocument();
7095 // Finally, we remove the request from the loadgroup. This will
7096 // cause onStateChange(STATE_STOP) to fire, which will fire the
7097 // pageshow event to the chrome.
7099 nsIChannel
* channel
= doc
->GetChannel();
7101 mIsRestoringDocument
= true;
7102 mLoadGroup
->RemoveRequest(channel
, nullptr, NS_OK
);
7103 mIsRestoringDocument
= false;
7111 nsDocShell::GetRestoringDocument(bool* aRestoring
) {
7112 *aRestoring
= mIsRestoringDocument
;
7116 nsresult
nsDocShell::RestorePresentation(nsISHEntry
* aSHEntry
,
7118 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7119 MOZ_ASSERT(!mIsBeingDestroyed
);
7121 NS_ASSERTION(mLoadType
& LOAD_CMD_HISTORY
,
7122 "RestorePresentation should only be called for history loads");
7124 nsCOMPtr
<nsIDocumentViewer
> viewer
= aSHEntry
->GetDocumentViewer();
7127 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Debug
))) {
7128 nsCOMPtr
<nsIURI
> uri
= aSHEntry
->GetURI();
7134 *aRestoring
= false;
7137 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
7138 ("no saved presentation for uri: %s", spec
.get()));
7142 // We need to make sure the content viewer's container is this docshell.
7143 // In subframe navigation, it's possible for the docshell that the
7144 // content viewer was originally loaded into to be replaced with a
7145 // different one. We don't currently support restoring the presentation
7148 nsCOMPtr
<nsIDocShell
> container
;
7149 viewer
->GetContainer(getter_AddRefs(container
));
7150 if (!::SameCOMIdentity(container
, GetAsSupports(this))) {
7151 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
7152 ("No valid container, clearing presentation"));
7153 aSHEntry
->SetDocumentViewer(nullptr);
7154 return NS_ERROR_FAILURE
;
7157 NS_ASSERTION(mDocumentViewer
!= viewer
, "Restoring existing presentation");
7159 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
7160 ("restoring presentation from session history: %s", spec
.get()));
7162 SetHistoryEntryAndUpdateBC(Some(aSHEntry
), Nothing());
7164 // Post an event that will remove the request after we've returned
7165 // to the event loop. This mimics the way it is called by nsIChannel
7168 // Revoke any pending restore (just in case).
7169 NS_ASSERTION(!mRestorePresentationEvent
.IsPending(),
7170 "should only have one RestorePresentationEvent");
7171 mRestorePresentationEvent
.Revoke();
7173 RefPtr
<RestorePresentationEvent
> evt
= new RestorePresentationEvent(this);
7174 nsresult rv
= Dispatch(do_AddRef(evt
));
7175 if (NS_SUCCEEDED(rv
)) {
7176 mRestorePresentationEvent
= evt
.get();
7177 // The rest of the restore processing will happen on our event
7186 class MOZ_STACK_CLASS PresentationEventForgetter
{
7188 explicit PresentationEventForgetter(
7189 nsRevocableEventPtr
<nsDocShell::RestorePresentationEvent
>&
7190 aRestorePresentationEvent
)
7191 : mRestorePresentationEvent(aRestorePresentationEvent
),
7192 mEvent(aRestorePresentationEvent
.get()) {}
7194 ~PresentationEventForgetter() { Forget(); }
7197 if (mRestorePresentationEvent
.get() == mEvent
) {
7198 mRestorePresentationEvent
.Forget();
7204 nsRevocableEventPtr
<nsDocShell::RestorePresentationEvent
>&
7205 mRestorePresentationEvent
;
7206 RefPtr
<nsDocShell::RestorePresentationEvent
> mEvent
;
7211 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags
) {
7212 return (aSandboxFlags
& (SANDBOXED_ORIGIN
| SANDBOXED_SCRIPTS
)) == 0;
7215 nsresult
nsDocShell::RestoreFromHistory() {
7216 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7217 MOZ_ASSERT(mRestorePresentationEvent
.IsPending());
7218 PresentationEventForgetter
forgetter(mRestorePresentationEvent
);
7220 // This section of code follows the same ordering as CreateDocumentViewer.
7222 return NS_ERROR_FAILURE
;
7225 nsCOMPtr
<nsIDocumentViewer
> viewer
= mLSHE
->GetDocumentViewer();
7227 return NS_ERROR_FAILURE
;
7230 if (mSavingOldViewer
) {
7231 // We determined that it was safe to cache the document presentation
7232 // at the time we initiated the new load. We need to check whether
7233 // it's still safe to do so, since there may have been DOM mutations
7234 // or new requests initiated.
7235 RefPtr
<Document
> doc
= viewer
->GetDocument();
7236 nsIRequest
* request
= nullptr;
7238 request
= doc
->GetChannel();
7240 mSavingOldViewer
= CanSavePresentation(
7241 mLoadType
, request
, doc
, /* aReportBFCacheComboTelemetry */ false);
7244 // Protect against mLSHE going away via a load triggered from
7245 // pagehide or unload.
7246 nsCOMPtr
<nsISHEntry
> origLSHE
= mLSHE
;
7248 // Make sure to blow away our mLoadingURI just in case. No loads
7249 // from inside this pagehide.
7250 mLoadingURI
= nullptr;
7252 // Notify the old content viewer that it's being hidden.
7253 FirePageHideNotification(!mSavingOldViewer
);
7254 // pagehide notification might destroy this docshell.
7255 if (mIsBeingDestroyed
) {
7256 return NS_ERROR_DOCSHELL_DYING
;
7259 // If mLSHE was changed as a result of the pagehide event, then
7260 // something else was loaded. Don't finish restoring.
7261 if (mLSHE
!= origLSHE
) {
7265 // Add the request to our load group. We do this before swapping out
7266 // the content viewers so that consumers of STATE_START can access
7267 // the old document. We only deal with the toplevel load at this time --
7268 // to be consistent with normal document loading, subframes cannot start
7269 // loading until after data arrives, which is after STATE_START completes.
7271 RefPtr
<RestorePresentationEvent
> currentPresentationRestoration
=
7272 mRestorePresentationEvent
.get();
7274 // Make sure we're still restoring the same presentation.
7275 // If we aren't, docshell is in process doing another load already.
7276 NS_ENSURE_STATE(currentPresentationRestoration
==
7277 mRestorePresentationEvent
.get());
7278 BeginRestore(viewer
, true);
7279 NS_ENSURE_STATE(currentPresentationRestoration
==
7280 mRestorePresentationEvent
.get());
7283 // Set mFiredUnloadEvent = false so that the unload handler for the
7284 // *new* document will fire.
7285 mFiredUnloadEvent
= false;
7287 mURIResultedInDocument
= true;
7288 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
7290 mPreviousEntryIndex
= rootSH
->Index();
7291 rootSH
->LegacySHistory()->UpdateIndex();
7292 mLoadedEntryIndex
= rootSH
->Index();
7293 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
7294 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex
,
7295 mLoadedEntryIndex
));
7298 // Rather than call Embed(), we will retrieve the viewer from the session
7299 // history entry and swap it in.
7300 // XXX can we refactor this so that we can just call Embed()?
7301 PersistLayoutHistoryState();
7303 if (mDocumentViewer
) {
7304 if (mSavingOldViewer
&& NS_FAILED(CaptureState())) {
7306 mOSHE
->SyncPresentationState();
7308 mSavingOldViewer
= false;
7312 mSavedRefreshURIList
= nullptr;
7314 // In cases where we use a transient about:blank viewer between loads,
7315 // we never show the transient viewer, so _its_ previous viewer is never
7316 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7317 // before we grab the root view sibling, so that we don't grab a view
7318 // that's about to go away.
7320 if (mDocumentViewer
) {
7321 // Make sure to hold a strong ref to previousViewer here while we
7322 // drop the reference to it from mDocumentViewer.
7323 nsCOMPtr
<nsIDocumentViewer
> previousViewer
=
7324 mDocumentViewer
->GetPreviousViewer();
7325 if (previousViewer
) {
7326 mDocumentViewer
->SetPreviousViewer(nullptr);
7327 previousViewer
->Destroy();
7331 // Save off the root view's parent and sibling so that we can insert the
7332 // new content viewer's root view at the same position. Also save the
7333 // bounds of the root view's widget.
7335 nsView
* rootViewSibling
= nullptr;
7336 nsView
* rootViewParent
= nullptr;
7337 nsIntRect
newBounds(0, 0, 0, 0);
7339 PresShell
* oldPresShell
= GetPresShell();
7341 nsViewManager
* vm
= oldPresShell
->GetViewManager();
7343 nsView
* oldRootView
= vm
->GetRootView();
7346 rootViewSibling
= oldRootView
->GetNextSibling();
7347 rootViewParent
= oldRootView
->GetParent();
7349 mDocumentViewer
->GetBounds(newBounds
);
7354 nsCOMPtr
<nsIContent
> container
;
7355 RefPtr
<Document
> sibling
;
7356 if (rootViewParent
&& rootViewParent
->GetParent()) {
7357 nsIFrame
* frame
= rootViewParent
->GetParent()->GetFrame();
7358 container
= frame
? frame
->GetContent() : nullptr;
7360 if (rootViewSibling
) {
7361 nsIFrame
* frame
= rootViewSibling
->GetFrame();
7362 sibling
= frame
? frame
->PresShell()->GetDocument() : nullptr;
7365 // Transfer ownership to mDocumentViewer. By ensuring that either the
7366 // docshell or the session history, but not both, have references to the
7367 // content viewer, we prevent the viewer from being torn down after
7368 // Destroy() is called.
7370 if (mDocumentViewer
) {
7371 mDocumentViewer
->Close(mSavingOldViewer
? mOSHE
.get() : nullptr);
7372 viewer
->SetPreviousViewer(mDocumentViewer
);
7374 if (mOSHE
&& (!mDocumentViewer
|| !mSavingOldViewer
)) {
7375 // We don't plan to save a viewer in mOSHE; tell it to drop
7376 // any other state it's holding.
7377 mOSHE
->SyncPresentationState();
7380 // Order the mDocumentViewer setup just like Embed does.
7381 mDocumentViewer
= nullptr;
7383 // Now that we're about to switch documents, forget all of our children.
7384 // Note that we cached them as needed up in CaptureState above.
7387 mDocumentViewer
.swap(viewer
);
7389 // Grab all of the related presentation from the SHEntry now.
7390 // Clearing the viewer from the SHEntry will clear all of this state.
7391 nsCOMPtr
<nsISupports
> windowState
= mLSHE
->GetWindowState();
7392 mLSHE
->SetWindowState(nullptr);
7394 bool sticky
= mLSHE
->GetSticky();
7396 RefPtr
<Document
> document
= mDocumentViewer
->GetDocument();
7398 nsCOMArray
<nsIDocShellTreeItem
> childShells
;
7400 nsCOMPtr
<nsIDocShellTreeItem
> child
;
7401 while (NS_SUCCEEDED(mLSHE
->ChildShellAt(i
++, getter_AddRefs(child
))) &&
7403 childShells
.AppendObject(child
);
7406 // get the previous content viewer size
7407 nsIntRect
oldBounds(0, 0, 0, 0);
7408 mLSHE
->GetViewerBounds(oldBounds
);
7410 // Restore the refresh URI list. The refresh timers will be restarted
7411 // when EndPageLoad() is called.
7412 nsCOMPtr
<nsIMutableArray
> refreshURIList
= mLSHE
->GetRefreshURIList();
7414 // Reattach to the window object.
7415 mIsRestoringDocument
= true; // for MediaDocument::BecomeInteractive
7416 rv
= mDocumentViewer
->Open(windowState
, mLSHE
);
7417 mIsRestoringDocument
= false;
7419 // Hack to keep nsDocShellEditorData alive across the
7420 // SetContentViewer(nullptr) call below.
7421 UniquePtr
<nsDocShellEditorData
> data(mLSHE
->ForgetEditorData());
7423 // Now remove it from the cached presentation.
7424 mLSHE
->SetDocumentViewer(nullptr);
7425 mEODForCurrentDocument
= false;
7427 mLSHE
->SetEditorData(data
.release());
7431 nsCOMPtr
<nsIMutableArray
> refreshURIs
= mLSHE
->GetRefreshURIList();
7432 nsCOMPtr
<nsIDocShellTreeItem
> childShell
;
7433 mLSHE
->ChildShellAt(0, getter_AddRefs(childShell
));
7434 NS_ASSERTION(!refreshURIs
&& !childShell
,
7435 "SHEntry should have cleared presentation state");
7439 // Restore the sticky state of the viewer. The viewer has set this state
7440 // on the history entry in Destroy() just before marking itself non-sticky,
7441 // to avoid teardown of the presentation.
7442 mDocumentViewer
->SetSticky(sticky
);
7444 NS_ENSURE_SUCCESS(rv
, rv
);
7446 // mLSHE is now our currently-loaded document.
7447 SetHistoryEntryAndUpdateBC(Nothing(), Some
<nsISHEntry
*>(mLSHE
));
7449 // We aren't going to restore any items from the LayoutHistoryState,
7450 // but we don't want them to stay around in case the page is reloaded.
7451 SetLayoutHistoryState(nullptr);
7453 // This is the end of our Embed() replacement
7455 mSavingOldViewer
= false;
7456 mEODForCurrentDocument
= false;
7459 RefPtr
<nsDocShell
> parent
= GetInProcessParentDocshell();
7461 RefPtr
<Document
> d
= parent
->GetDocument();
7463 if (d
->EventHandlingSuppressed()) {
7464 document
->SuppressEventHandling(d
->EventHandlingSuppressed());
7469 // Use the uri from the mLSHE we had when we entered this function
7470 // (which need not match the document's URI if anchors are involved),
7471 // since that's the history entry we're loading. Note that if we use
7472 // origLSHE we don't have to worry about whether the entry in question
7473 // is still mLSHE or whether it's now mOSHE.
7474 nsCOMPtr
<nsIURI
> uri
= origLSHE
->GetURI();
7475 SetCurrentURI(uri
, document
->GetChannel(), /* aFireLocationChange */ true,
7476 /* aIsInitialAboutBlank */ false,
7477 /* aLocationFlags */ 0);
7480 // This is the end of our CreateDocumentViewer() replacement.
7481 // Now we simulate a load. First, we restore the state of the javascript
7483 nsCOMPtr
<nsPIDOMWindowOuter
> privWin
= GetWindow();
7484 NS_ASSERTION(privWin
, "could not get nsPIDOMWindow interface");
7486 // Now, dispatch a title change event which would happen as the
7487 // <head> is parsed.
7488 document
->NotifyPossibleTitleChange(false);
7490 // Now we simulate appending child docshells for subframes.
7491 for (i
= 0; i
< childShells
.Count(); ++i
) {
7492 nsIDocShellTreeItem
* childItem
= childShells
.ObjectAt(i
);
7493 nsCOMPtr
<nsIDocShell
> childShell
= do_QueryInterface(childItem
);
7495 // Make sure to not clobber the state of the child. Since AddChild
7496 // always clobbers it, save it off first.
7497 bool allowRedirects
;
7498 childShell
->GetAllowMetaRedirects(&allowRedirects
);
7500 bool allowSubframes
;
7501 childShell
->GetAllowSubframes(&allowSubframes
);
7504 childShell
->GetAllowImages(&allowImages
);
7506 bool allowMedia
= childShell
->GetAllowMedia();
7508 bool allowDNSPrefetch
;
7509 childShell
->GetAllowDNSPrefetch(&allowDNSPrefetch
);
7511 bool allowContentRetargeting
= childShell
->GetAllowContentRetargeting();
7512 bool allowContentRetargetingOnChildren
=
7513 childShell
->GetAllowContentRetargetingOnChildren();
7515 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7516 // the child inherits our state. Among other things, this means that the
7517 // child inherits our mPrivateBrowsingId, which is what we want.
7518 AddChild(childItem
);
7520 childShell
->SetAllowMetaRedirects(allowRedirects
);
7521 childShell
->SetAllowSubframes(allowSubframes
);
7522 childShell
->SetAllowImages(allowImages
);
7523 childShell
->SetAllowMedia(allowMedia
);
7524 childShell
->SetAllowDNSPrefetch(allowDNSPrefetch
);
7525 childShell
->SetAllowContentRetargeting(allowContentRetargeting
);
7526 childShell
->SetAllowContentRetargetingOnChildren(
7527 allowContentRetargetingOnChildren
);
7529 rv
= childShell
->BeginRestore(nullptr, false);
7530 NS_ENSURE_SUCCESS(rv
, rv
);
7533 // Make sure to restore the window state after adding the child shells back
7534 // to the tree. This is necessary for Thaw() and Resume() to propagate
7536 rv
= privWin
->RestoreWindowState(windowState
);
7537 NS_ENSURE_SUCCESS(rv
, rv
);
7539 RefPtr
<PresShell
> presShell
= GetPresShell();
7541 // We may be displayed on a different monitor (or in a different
7542 // HiDPI mode) than when we got into the history list. So we need
7543 // to check if this has happened. See bug 838239.
7545 // Because the prescontext normally handles resolution changes via
7546 // a runnable (see nsPresContext::UIResolutionChanged), its device
7547 // context won't be -immediately- updated as a result of calling
7548 // presShell->BackingScaleFactorChanged().
7550 // But we depend on that device context when adjusting the view size
7551 // via mDocumentViewer->SetBounds(newBounds) below. So we need to
7552 // explicitly tell it to check for changed resolution here.
7554 RefPtr
<nsPresContext
> pc
= presShell
->GetPresContext();
7555 if (pc
->DeviceContext()->CheckDPIChange()) {
7556 presShell
->BackingScaleFactorChanged();
7558 // Recompute zoom and text-zoom and such.
7559 pc
->RecomputeBrowsingContextDependentData();
7562 nsViewManager
* newVM
= presShell
? presShell
->GetViewManager() : nullptr;
7563 nsView
* newRootView
= newVM
? newVM
->GetRootView() : nullptr;
7565 // Insert the new root view at the correct location in the view tree.
7567 nsSubDocumentFrame
* subDocFrame
=
7568 do_QueryFrame(container
->GetPrimaryFrame());
7569 rootViewParent
= subDocFrame
? subDocFrame
->EnsureInnerView() : nullptr;
7571 rootViewParent
= nullptr;
7573 if (sibling
&& sibling
->GetPresShell() &&
7574 sibling
->GetPresShell()->GetViewManager()) {
7575 rootViewSibling
= sibling
->GetPresShell()->GetViewManager()->GetRootView();
7577 rootViewSibling
= nullptr;
7579 if (rootViewParent
&& newRootView
&&
7580 newRootView
->GetParent() != rootViewParent
) {
7581 nsViewManager
* parentVM
= rootViewParent
->GetViewManager();
7583 // InsertChild(parent, child, sib, true) inserts the child after
7584 // sib in content order, which is before sib in view order. BUT
7585 // when sib is null it inserts at the end of the the document
7586 // order, i.e., first in view order. But when oldRootSibling is
7587 // null, the old root as at the end of the view list --- last in
7588 // content order --- and we want to call InsertChild(parent, child,
7589 // nullptr, false) in that case.
7590 parentVM
->InsertChild(rootViewParent
, newRootView
, rootViewSibling
,
7591 rootViewSibling
? true : false);
7593 NS_ASSERTION(newRootView
->GetNextSibling() == rootViewSibling
,
7594 "error in InsertChild");
7598 nsCOMPtr
<nsPIDOMWindowInner
> privWinInner
= privWin
->GetCurrentInnerWindow();
7600 // If parent is suspended, increase suspension count.
7601 // This can't be done as early as event suppression since this
7602 // depends on docshell tree.
7603 privWinInner
->SyncStateFromParentWindow();
7605 // Now that all of the child docshells have been put into place, we can
7606 // restart the timers for the window and all of the child frames.
7607 privWinInner
->Resume();
7609 // Now that we have found the inner window of the page restored
7610 // from the history, we have to make sure that
7611 // performance.navigation.type is 2.
7612 Performance
* performance
= privWinInner
->GetPerformance();
7614 performance
->GetDOMTiming()->NotifyRestoreStart();
7617 // Restore the refresh URI list. The refresh timers will be restarted
7618 // when EndPageLoad() is called.
7619 mRefreshURIList
= refreshURIList
;
7621 // Meta-refresh timers have been restarted for this shell, but not
7622 // for our children. Walk the child shells and restart their timers.
7623 for (auto* childDocLoader
: mChildList
.ForwardRange()) {
7624 nsCOMPtr
<nsIDocShell
> child
= do_QueryObject(childDocLoader
);
7626 child
->ResumeRefreshURIs();
7630 // Make sure this presentation is the same size as the previous
7631 // presentation. If this is not the same size we showed it at last time,
7632 // then we need to resize the widget.
7634 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7635 // presentation had the infobar visible, then we will resize the new
7636 // presentation to that smaller size. However, firing the locationchanged
7637 // event will hide the infobar, which will immediately resize the window
7638 // back to the larger size. A future optimization might be to restore
7639 // the presentation at the "wrong" size, then fire the locationchanged
7640 // event and check whether the docshell's new size is the same as the
7641 // cached viewer size (skipping the resize if they are equal).
7644 if (!newBounds
.IsEmpty() && !newBounds
.IsEqualEdges(oldBounds
)) {
7645 MOZ_LOG(gPageCacheLog
, LogLevel::Debug
,
7646 ("resize widget(%d, %d, %d, %d)", newBounds
.x
, newBounds
.y
,
7647 newBounds
.width
, newBounds
.height
));
7648 mDocumentViewer
->SetBounds(newBounds
);
7650 nsIScrollableFrame
* rootScrollFrame
=
7651 presShell
->GetRootScrollFrameAsScrollable();
7652 if (rootScrollFrame
) {
7653 rootScrollFrame
->PostScrolledAreaEventForCurrentArea();
7658 // The FinishRestore call below can kill these, null them out so we don't
7659 // have invalid pointer lying around.
7660 newRootView
= rootViewSibling
= rootViewParent
= nullptr;
7663 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7665 if (oldPresShell
&& presShell
&&
7666 presShell
->IsUnderHiddenEmbedderElement() !=
7667 oldPresShell
->IsUnderHiddenEmbedderElement()) {
7668 presShell
->SetIsUnderHiddenEmbedderElement(
7669 oldPresShell
->IsUnderHiddenEmbedderElement());
7672 // Simulate the completion of the load.
7673 nsDocShell::FinishRestore();
7675 // Restart plugins, and paint the content.
7680 return privWin
->FireDelayedDOMEvents(true);
7683 nsresult
nsDocShell::CreateDocumentViewer(const nsACString
& aContentType
,
7684 nsIRequest
* aRequest
,
7685 nsIStreamListener
** aContentHandler
) {
7686 *aContentHandler
= nullptr;
7688 if (!mTreeOwner
|| mIsBeingDestroyed
) {
7689 // If we don't have a tree owner, then we're in the process of being
7690 // destroyed. Rather than continue trying to load something, just give up.
7691 return NS_ERROR_DOCSHELL_DYING
;
7694 if (!mBrowsingContext
->AncestorsAreCurrent() ||
7695 mBrowsingContext
->IsInBFCache()) {
7696 mBrowsingContext
->RemoveRootFromBFCacheSync();
7697 return NS_ERROR_NOT_AVAILABLE
;
7700 // Can we check the content type of the current content viewer
7701 // and reuse it without destroying it and re-creating it?
7703 NS_ASSERTION(mLoadGroup
, "Someone ignored return from Init()?");
7705 // Instantiate the content viewer object
7706 nsCOMPtr
<nsIDocumentViewer
> viewer
;
7707 nsresult rv
= NewDocumentViewerObj(aContentType
, aRequest
, mLoadGroup
,
7708 aContentHandler
, getter_AddRefs(viewer
));
7710 if (NS_FAILED(rv
)) {
7714 // Notify the current document that it is about to be unloaded!!
7716 // It is important to fire the unload() notification *before* any state
7717 // is changed within the DocShell - otherwise, javascript will get the
7718 // wrong information :-(
7721 if (mSavingOldViewer
) {
7722 // We determined that it was safe to cache the document presentation
7723 // at the time we initiated the new load. We need to check whether
7724 // it's still safe to do so, since there may have been DOM mutations
7725 // or new requests initiated.
7726 RefPtr
<Document
> doc
= viewer
->GetDocument();
7727 mSavingOldViewer
= CanSavePresentation(
7728 mLoadType
, aRequest
, doc
, /* aReportBFCacheComboTelemetry */ false);
7731 NS_ASSERTION(!mLoadingURI
, "Re-entering unload?");
7733 nsCOMPtr
<nsIChannel
> aOpenedChannel
= do_QueryInterface(aRequest
);
7734 if (aOpenedChannel
) {
7735 aOpenedChannel
->GetURI(getter_AddRefs(mLoadingURI
));
7738 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7739 // it before we do call Embed.
7740 nsCOMPtr
<nsIURI
> previousURI
= mCurrentURI
;
7742 FirePageHideNotification(!mSavingOldViewer
);
7743 if (mIsBeingDestroyed
) {
7744 // Force to stop the newly created orphaned viewer.
7746 return NS_ERROR_DOCSHELL_DYING
;
7748 mLoadingURI
= nullptr;
7750 // Set mFiredUnloadEvent = false so that the unload handler for the
7751 // *new* document will fire.
7752 mFiredUnloadEvent
= false;
7754 // we've created a new document so go ahead and call
7755 // OnNewURI(), but don't fire OnLocationChange()
7756 // notifications before we've called Embed(). See bug 284993.
7757 mURIResultedInDocument
= true;
7758 bool errorOnLocationChangeNeeded
= false;
7759 nsCOMPtr
<nsIChannel
> failedChannel
= mFailedChannel
;
7760 nsCOMPtr
<nsIURI
> failedURI
;
7762 if (mLoadType
== LOAD_ERROR_PAGE
) {
7763 // We need to set the SH entry and our current URI here and not
7764 // at the moment we load the page. We want the same behavior
7765 // of Stop() as for a normal page load. See bug 514232 for details.
7767 // Revert mLoadType to load type to state the page load failed,
7768 // following function calls need it.
7769 mLoadType
= mFailedLoadType
;
7771 Document
* doc
= viewer
->GetDocument();
7773 doc
->SetFailedChannel(failedChannel
);
7776 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
;
7777 if (failedChannel
) {
7778 // Make sure we have a URI to set currentURI.
7779 NS_GetFinalChannelURI(failedChannel
, getter_AddRefs(failedURI
));
7781 // if there is no failed channel we have to explicitly provide
7782 // a triggeringPrincipal for the history entry.
7783 triggeringPrincipal
= nsContentUtils::GetSystemPrincipal();
7787 failedURI
= mFailedURI
;
7790 // We need a URI object to store a session history entry, so make up a URI
7791 NS_NewURI(getter_AddRefs(failedURI
), "about:blank");
7794 // When we don't have failedURI, something wrong will happen. See
7796 MOZ_ASSERT(failedURI
, "We don't have a URI for history APIs.");
7798 mFailedChannel
= nullptr;
7799 mFailedURI
= nullptr;
7801 // Create an shistory entry for the old load.
7803 errorOnLocationChangeNeeded
=
7804 OnNewURI(failedURI
, failedChannel
, triggeringPrincipal
, nullptr,
7805 nullptr, nullptr, false, false);
7808 // Be sure to have a correct mLSHE, it may have been cleared by
7809 // EndPageLoad. See bug 302115.
7810 ChildSHistory
* shistory
= GetSessionHistory();
7811 if (!mozilla::SessionHistoryInParent() && shistory
&& !mLSHE
) {
7812 int32_t idx
= shistory
->LegacySHistory()->GetRequestedIndex();
7814 idx
= shistory
->Index();
7816 shistory
->LegacySHistory()->GetEntryAtIndex(idx
, getter_AddRefs(mLSHE
));
7819 mLoadType
= LOAD_ERROR_PAGE
;
7822 nsCOMPtr
<nsIURI
> finalURI
;
7823 // If this a redirect, use the final url (uri)
7824 // else use the original url
7826 // Note that this should match what documents do (see Document::Reset).
7827 NS_GetFinalChannelURI(aOpenedChannel
, getter_AddRefs(finalURI
));
7829 bool onLocationChangeNeeded
= false;
7831 // Pass false for aCloneSHChildren, since we're loading a new page here.
7832 onLocationChangeNeeded
= OnNewURI(finalURI
, aOpenedChannel
, nullptr,
7833 nullptr, nullptr, nullptr, true, false);
7836 // let's try resetting the load group if we need to...
7837 nsCOMPtr
<nsILoadGroup
> currentLoadGroup
;
7839 aOpenedChannel
->GetLoadGroup(getter_AddRefs(currentLoadGroup
)),
7842 if (currentLoadGroup
!= mLoadGroup
) {
7843 nsLoadFlags loadFlags
= 0;
7845 // Cancel any URIs that are currently loading...
7846 // XXX: Need to do this eventually Stop();
7848 // Retarget the document to this loadgroup...
7850 /* First attach the channel to the right loadgroup
7851 * and then remove from the old loadgroup. This
7852 * puts the notifications in the right order and
7853 * we don't null-out mLSHE in OnStateChange() for
7854 * all redirected urls
7856 aOpenedChannel
->SetLoadGroup(mLoadGroup
);
7858 // Mark the channel as being a document URI...
7859 aOpenedChannel
->GetLoadFlags(&loadFlags
);
7860 loadFlags
|= nsIChannel::LOAD_DOCUMENT_URI
;
7861 nsCOMPtr
<nsILoadInfo
> loadInfo
= aOpenedChannel
->LoadInfo();
7862 if (SandboxFlagsImplyCookies(loadInfo
->GetSandboxFlags())) {
7863 loadFlags
|= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE
;
7866 aOpenedChannel
->SetLoadFlags(loadFlags
);
7868 mLoadGroup
->AddRequest(aRequest
, nullptr);
7869 if (currentLoadGroup
) {
7870 currentLoadGroup
->RemoveRequest(aRequest
, nullptr, NS_BINDING_RETARGETED
);
7873 // Update the notification callbacks, so that progress and
7874 // status information are sent to the right docshell...
7875 aOpenedChannel
->SetNotificationCallbacks(this);
7878 NS_ENSURE_SUCCESS(Embed(viewer
, nullptr, false,
7879 ShouldAddToSessionHistory(finalURI
, aOpenedChannel
),
7880 aOpenedChannel
, previousURI
),
7883 if (!mBrowsingContext
->GetHasLoadedNonInitialDocument()) {
7884 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext
->SetHasLoadedNonInitialDocument(true));
7887 mSavedRefreshURIList
= nullptr;
7888 mSavingOldViewer
= false;
7889 mEODForCurrentDocument
= false;
7891 // if this document is part of a multipart document,
7892 // the ID can be used to distinguish it from the other parts.
7893 nsCOMPtr
<nsIMultiPartChannel
> multiPartChannel(do_QueryInterface(aRequest
));
7894 if (multiPartChannel
) {
7895 if (PresShell
* presShell
= GetPresShell()) {
7896 if (Document
* doc
= presShell
->GetDocument()) {
7898 multiPartChannel
->GetPartID(&partID
);
7899 doc
->SetPartID(partID
);
7904 if (errorOnLocationChangeNeeded
) {
7905 FireOnLocationChange(this, failedChannel
, failedURI
,
7906 LOCATION_CHANGE_ERROR_PAGE
);
7907 } else if (onLocationChangeNeeded
) {
7908 uint32_t locationFlags
=
7909 (mLoadType
& LOAD_CMD_RELOAD
) ? uint32_t(LOCATION_CHANGE_RELOAD
) : 0;
7910 FireOnLocationChange(this, aRequest
, mCurrentURI
, locationFlags
);
7916 nsresult
nsDocShell::NewDocumentViewerObj(const nsACString
& aContentType
,
7917 nsIRequest
* aRequest
,
7918 nsILoadGroup
* aLoadGroup
,
7919 nsIStreamListener
** aContentHandler
,
7920 nsIDocumentViewer
** aViewer
) {
7921 nsCOMPtr
<nsIChannel
> aOpenedChannel
= do_QueryInterface(aRequest
);
7923 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
7924 nsContentUtils::FindInternalDocumentViewer(aContentType
);
7925 if (!docLoaderFactory
) {
7926 return NS_ERROR_FAILURE
;
7929 // Now create an instance of the content viewer nsLayoutDLF makes the
7930 // determination if it should be a "view-source" instead of "view"
7931 nsresult rv
= docLoaderFactory
->CreateInstance(
7932 "view", aOpenedChannel
, aLoadGroup
, aContentType
, this, nullptr,
7933 aContentHandler
, aViewer
);
7934 NS_ENSURE_SUCCESS(rv
, rv
);
7936 (*aViewer
)->SetContainer(this);
7940 nsresult
nsDocShell::SetupNewViewer(nsIDocumentViewer
* aNewViewer
,
7941 WindowGlobalChild
* aWindowActor
) {
7942 MOZ_ASSERT(!mIsBeingDestroyed
);
7945 // Copy content viewer state from previous or parent content viewer.
7947 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7949 // Do NOT to maintain a reference to the old content viewer outside
7950 // of this "copying" block, or it will not be destroyed until the end of
7951 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7953 // In this block of code, if we get an error result, we return it
7954 // but if we get a null pointer, that's perfectly legal for parent
7955 // and parentContentViewer.
7963 // This will get the size from the current content viewer or from the
7965 DoGetPositionAndSize(&x
, &y
, &cx
, &cy
);
7967 nsCOMPtr
<nsIDocShellTreeItem
> parentAsItem
;
7968 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem
)),
7970 nsCOMPtr
<nsIDocShell
> parent(do_QueryInterface(parentAsItem
));
7972 const Encoding
* reloadEncoding
= nullptr;
7973 int32_t reloadEncodingSource
= kCharsetUninitialized
;
7974 // |newMUDV| also serves as a flag to set the data from the above vars
7975 nsCOMPtr
<nsIDocumentViewer
> newViewer
;
7977 if (mDocumentViewer
|| parent
) {
7978 nsCOMPtr
<nsIDocumentViewer
> oldViewer
;
7979 if (mDocumentViewer
) {
7980 // Get any interesting state from old content viewer
7981 // XXX: it would be far better to just reuse the document viewer ,
7982 // since we know we're just displaying the same document as before
7983 oldViewer
= mDocumentViewer
;
7985 // Tell the old content viewer to hibernate in session history when
7988 if (mSavingOldViewer
&& NS_FAILED(CaptureState())) {
7990 mOSHE
->SyncPresentationState();
7992 mSavingOldViewer
= false;
7995 // No old content viewer, so get state from parent's content viewer
7996 parent
->GetDocViewer(getter_AddRefs(oldViewer
));
8000 newViewer
= aNewViewer
;
8003 oldViewer
->GetReloadEncodingAndSource(&reloadEncodingSource
);
8008 nscolor bgcolor
= NS_RGBA(0, 0, 0, 0);
8009 bool isUnderHiddenEmbedderElement
= false;
8010 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8011 nsCOMPtr
<nsIDocumentViewer
> viewer
= mDocumentViewer
;
8013 // Stop any activity that may be happening in the old document before
8017 // Try to extract the canvas background color from the old
8018 // presentation shell, so we can use it for the next document.
8019 if (PresShell
* presShell
= viewer
->GetPresShell()) {
8020 bgcolor
= presShell
->GetCanvasBackground();
8021 isUnderHiddenEmbedderElement
= presShell
->IsUnderHiddenEmbedderElement();
8024 viewer
->Close(mSavingOldViewer
? mOSHE
.get() : nullptr);
8025 aNewViewer
->SetPreviousViewer(viewer
);
8027 if (mOSHE
&& (!mDocumentViewer
|| !mSavingOldViewer
)) {
8028 // We don't plan to save a viewer in mOSHE; tell it to drop
8029 // any other state it's holding.
8030 mOSHE
->SyncPresentationState();
8033 mDocumentViewer
= nullptr;
8035 // Now that we're about to switch documents, forget all of our children.
8036 // Note that we cached them as needed up in CaptureState above.
8039 mDocumentViewer
= aNewViewer
;
8041 nsCOMPtr
<nsIWidget
> widget
;
8042 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget
)), NS_ERROR_FAILURE
);
8044 nsIntRect
bounds(x
, y
, cx
, cy
);
8046 mDocumentViewer
->SetNavigationTiming(mTiming
);
8048 if (NS_FAILED(mDocumentViewer
->Init(widget
, bounds
, aWindowActor
))) {
8049 nsCOMPtr
<nsIDocumentViewer
> viewer
= mDocumentViewer
;
8050 viewer
->Close(nullptr);
8052 mDocumentViewer
= nullptr;
8053 SetCurrentURIInternal(nullptr);
8054 NS_WARNING("ContentViewer Initialization failed");
8055 return NS_ERROR_FAILURE
;
8058 // If we have old state to copy, set the old state onto the new content
8061 newViewer
->SetReloadEncodingAndSource(reloadEncoding
, reloadEncodingSource
);
8064 NS_ENSURE_TRUE(mDocumentViewer
, NS_ERROR_FAILURE
);
8066 // Stuff the bgcolor from the old pres shell into the new
8067 // pres shell. This improves page load continuity.
8068 if (RefPtr
<PresShell
> presShell
= mDocumentViewer
->GetPresShell()) {
8069 presShell
->SetCanvasBackground(bgcolor
);
8070 presShell
->ActivenessMaybeChanged();
8071 if (isUnderHiddenEmbedderElement
) {
8072 presShell
->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement
);
8076 // XXX: It looks like the LayoutState gets restored again in Embed()
8077 // right after the call to SetupNewViewer(...)
8079 // We don't show the mDocumentViewer yet, since we want to draw the old page
8080 // until we have enough of the new page to show. Just return with the new
8081 // viewer still set to hidden.
8086 void nsDocShell::SetDocCurrentStateObj(nsISHEntry
* aShEntry
,
8087 SessionHistoryInfo
* aInfo
) {
8088 NS_ENSURE_TRUE_VOID(mDocumentViewer
);
8090 RefPtr
<Document
> document
= GetDocument();
8091 NS_ENSURE_TRUE_VOID(document
);
8093 nsCOMPtr
<nsIStructuredCloneContainer
> scContainer
;
8094 if (mozilla::SessionHistoryInParent()) {
8095 // If aInfo is null, just set the document's state object to null.
8097 scContainer
= aInfo
->GetStateData();
8099 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8100 ("nsDocShell %p SetCurrentDocState %p", this, scContainer
.get()));
8103 scContainer
= aShEntry
->GetStateData();
8105 // If aShEntry is null, just set the document's state object to null.
8109 // It's OK for scContainer too be null here; that just means there's no
8110 // state data associated with this history entry.
8111 document
->SetStateObject(scContainer
);
8114 nsresult
nsDocShell::CheckLoadingPermissions() {
8115 // This method checks whether the caller may load content into
8116 // this docshell. Even though we've done our best to hide windows
8117 // from code that doesn't have the right to access them, it's
8118 // still possible for an evil site to open a window and access
8119 // frames in the new window through window.frames[] (which is
8120 // allAccess for historic reasons), so we still need to do this
8122 nsresult rv
= NS_OK
;
8124 if (!IsSubframe()) {
8125 // We're not a frame. Permit all loads.
8129 // Note - The check for a current JSContext here isn't necessarily sensical.
8130 // It's just designed to preserve the old semantics during a mass-conversion
8132 if (!nsContentUtils::GetCurrentJSContext()) {
8136 // Check if the caller is from the same origin as this docshell,
8137 // or any of its ancestors.
8138 for (RefPtr
<BrowsingContext
> bc
= mBrowsingContext
; bc
;
8139 bc
= bc
->GetParent()) {
8140 // If the BrowsingContext is not in process, then it
8141 // is true by construction that its principal will not
8142 // subsume the current docshell principal.
8143 if (!bc
->IsInProcess()) {
8147 nsCOMPtr
<nsIScriptGlobalObject
> sgo
=
8148 bc
->GetDocShell()->GetScriptGlobalObject();
8149 nsCOMPtr
<nsIScriptObjectPrincipal
> sop(do_QueryInterface(sgo
));
8152 if (!sop
|| !(p
= sop
->GetPrincipal())) {
8153 return NS_ERROR_UNEXPECTED
;
8156 if (nsContentUtils::SubjectPrincipal()->Subsumes(p
)) {
8157 // Same origin, permit load
8162 return NS_ERROR_DOM_PROP_ACCESS_DENIED
;
8165 //*****************************************************************************
8166 // nsDocShell: Site Loading
8167 //*****************************************************************************
8169 void nsDocShell::CopyFavicon(nsIURI
* aOldURI
, nsIURI
* aNewURI
,
8170 bool aInPrivateBrowsing
) {
8171 if (XRE_IsContentProcess()) {
8172 dom::ContentChild
* contentChild
= dom::ContentChild::GetSingleton();
8174 contentChild
->SendCopyFavicon(aOldURI
, aNewURI
, aInPrivateBrowsing
);
8180 nsCOMPtr
<nsIFaviconService
> favSvc
=
8181 do_GetService("@mozilla.org/browser/favicon-service;1");
8183 favSvc
->CopyFavicons(aOldURI
, aNewURI
,
8185 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8186 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE
,
8192 class InternalLoadEvent
: public Runnable
{
8194 InternalLoadEvent(nsDocShell
* aDocShell
, nsDocShellLoadState
* aLoadState
)
8195 : mozilla::Runnable("InternalLoadEvent"),
8196 mDocShell(aDocShell
),
8197 mLoadState(aLoadState
) {
8198 // For events, both target and filename should be the version of "null" they
8199 // expect. By the time the event is fired, both window targeting and file
8200 // downloading have been handled, so we should never have an internal load
8201 // event that retargets or had a download.
8202 mLoadState
->SetTarget(u
""_ns
);
8203 mLoadState
->SetFileName(VoidString());
8209 MOZ_ASSERT(mLoadState
->TriggeringPrincipal(),
8210 "InternalLoadEvent: Should always have a principal here");
8212 return mDocShell
->InternalLoad(mLoadState
);
8216 RefPtr
<nsDocShell
> mDocShell
;
8217 RefPtr
<nsDocShellLoadState
> mLoadState
;
8221 * Returns true if we started an asynchronous load (i.e., from the network), but
8222 * the document we're loading there hasn't yet become this docshell's active
8225 * When JustStartedNetworkLoad is true, you should be careful about modifying
8226 * mLoadType and mLSHE. These are both set when the asynchronous load first
8227 * starts, and the load expects that, when it eventually runs InternalLoad,
8228 * mLoadType and mLSHE will have their original values.
8230 bool nsDocShell::JustStartedNetworkLoad() {
8231 return mDocumentRequest
&& mDocumentRequest
!= GetCurrentDocChannel();
8234 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8235 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8238 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8239 // later when we call DoURILoad.
8240 nsContentPolicyType
nsDocShell::DetermineContentType() {
8241 if (!IsSubframe()) {
8242 return nsIContentPolicy::TYPE_DOCUMENT
;
8245 const auto& maybeEmbedderElementType
=
8246 GetBrowsingContext()->GetEmbedderElementType();
8247 if (!maybeEmbedderElementType
) {
8248 // If the EmbedderElementType hasn't been set yet, just assume we're
8249 // an iframe since that's more common.
8250 return nsIContentPolicy::TYPE_INTERNAL_IFRAME
;
8253 return maybeEmbedderElementType
->EqualsLiteral("iframe")
8254 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8255 : nsIContentPolicy::TYPE_INTERNAL_FRAME
;
8258 bool nsDocShell::NoopenerForceEnabled() {
8259 // If current's top-level browsing context's active document's
8260 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8261 // if currentDoc's origin is not same origin with currentDoc's top-level
8262 // origin, noopener is force enabled, and name is cleared to "_blank".
8263 auto topPolicy
= mBrowsingContext
->Top()->GetOpenerPolicy();
8264 return (topPolicy
== nsILoadInfo::OPENER_POLICY_SAME_ORIGIN
||
8267 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP
) &&
8268 !mBrowsingContext
->SameOriginWithTop();
8271 nsresult
nsDocShell::PerformRetargeting(nsDocShellLoadState
* aLoadState
) {
8272 MOZ_ASSERT(aLoadState
, "need a load state!");
8273 MOZ_ASSERT(!aLoadState
->Target().IsEmpty(), "should have a target here!");
8274 MOZ_ASSERT(aLoadState
->TargetBrowsingContext().IsNull(),
8275 "should not have picked target yet");
8277 nsresult rv
= NS_OK
;
8278 RefPtr
<BrowsingContext
> targetContext
;
8280 // Only _self, _parent, and _top are supported in noopener case. But we
8281 // have to be careful to not apply that to the noreferrer case. See bug
8283 bool allowNamedTarget
=
8284 !aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER
) ||
8285 aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER
);
8286 if (allowNamedTarget
||
8287 aLoadState
->Target().LowerCaseEqualsLiteral("_self") ||
8288 aLoadState
->Target().LowerCaseEqualsLiteral("_parent") ||
8289 aLoadState
->Target().LowerCaseEqualsLiteral("_top")) {
8290 Document
* document
= GetDocument();
8291 NS_ENSURE_TRUE(document
, NS_ERROR_FAILURE
);
8292 WindowGlobalChild
* wgc
= document
->GetWindowGlobalChild();
8293 NS_ENSURE_TRUE(wgc
, NS_ERROR_FAILURE
);
8294 targetContext
= wgc
->FindBrowsingContextWithName(
8295 aLoadState
->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8298 if (!targetContext
) {
8299 // If the targetContext doesn't exist, then this is a new docShell and we
8300 // should consider this a TYPE_DOCUMENT load
8302 // For example, when target="_blank"
8304 // If there's no targetContext, that means we are about to create a new
8305 // window. Perform a content policy check before creating the window. Please
8306 // note for all other docshell loads content policy checks are performed
8307 // within the contentSecurityManager when the channel is about to be
8309 nsISupports
* requestingContext
= nullptr;
8310 if (XRE_IsContentProcess()) {
8311 // In e10s the child process doesn't have access to the element that
8312 // contains the browsing context (because that element is in the chrome
8313 // process). So we just pass mScriptGlobal.
8314 requestingContext
= ToSupports(mScriptGlobal
);
8316 // This is for loading non-e10s tabs and toplevel windows of various
8318 // For the toplevel window cases, requestingElement will be null.
8319 nsCOMPtr
<Element
> requestingElement
=
8320 mScriptGlobal
->GetFrameElementInternal();
8321 requestingContext
= requestingElement
;
8324 // Ideally we should use the same loadinfo as within DoURILoad which
8325 // should match this one when both are applicable.
8326 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
=
8327 new LoadInfo(mScriptGlobal
, aLoadState
->URI(),
8328 aLoadState
->TriggeringPrincipal(), requestingContext
,
8329 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
, 0);
8331 // Since Content Policy checks are performed within docShell as well as
8332 // the ContentSecurityManager we need a reliable way to let certain
8333 // nsIContentPolicy consumers ignore duplicate calls.
8334 secCheckLoadInfo
->SetSkipContentPolicyCheckForWebRequest(true);
8336 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
8337 rv
= NS_CheckContentLoadPolicy(aLoadState
->URI(), secCheckLoadInfo
,
8340 if (NS_FAILED(rv
) || NS_CP_REJECTED(shouldLoad
)) {
8341 if (NS_SUCCEEDED(rv
)) {
8342 if (shouldLoad
== nsIContentPolicy::REJECT_TYPE
) {
8343 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT
;
8345 if (shouldLoad
== nsIContentPolicy::REJECT_POLICY
) {
8346 return NS_ERROR_BLOCKED_BY_POLICY
;
8350 return NS_ERROR_CONTENT_BLOCKED
;
8355 // Resolve the window target before going any further...
8356 // If the load has been targeted to another DocShell, then transfer the
8360 // We've already done our owner-inheriting. Mask out that bit, so we
8361 // don't try inheriting an owner from the target window if we came up
8362 // with a null owner above.
8363 aLoadState
->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL
);
8365 if (!targetContext
) {
8366 // If the docshell's document is sandboxed, only open a new window
8367 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8368 // (i.e. if allow-popups is specified)
8369 NS_ENSURE_TRUE(mDocumentViewer
, NS_ERROR_FAILURE
);
8370 Document
* doc
= mDocumentViewer
->GetDocument();
8372 const bool isDocumentAuxSandboxed
=
8373 doc
&& (doc
->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION
);
8375 if (isDocumentAuxSandboxed
) {
8376 return NS_ERROR_DOM_INVALID_ACCESS_ERR
;
8379 nsCOMPtr
<nsPIDOMWindowOuter
> win
= GetWindow();
8380 NS_ENSURE_TRUE(win
, NS_ERROR_NOT_AVAILABLE
);
8382 RefPtr
<BrowsingContext
> newBC
;
8384 aLoadState
->URI()->GetSpec(spec
);
8386 // If we are a noopener load, we just hand the whole thing over to our
8388 if (aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER
) ||
8389 NoopenerForceEnabled()) {
8390 // Various asserts that we know to hold because NO_OPENER loads can only
8391 // happen for links.
8392 MOZ_ASSERT(!aLoadState
->LoadReplace());
8393 MOZ_ASSERT(aLoadState
->PrincipalToInherit() ==
8394 aLoadState
->TriggeringPrincipal());
8395 MOZ_ASSERT(!(aLoadState
->InternalLoadFlags() &
8396 ~(INTERNAL_LOAD_FLAGS_NO_OPENER
|
8397 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER
)),
8398 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8399 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8400 MOZ_ASSERT_IF(aLoadState
->PostDataStream(),
8401 aLoadState
->IsFormSubmission());
8402 MOZ_ASSERT(!aLoadState
->HeadersStream());
8403 // If OnLinkClickSync was invoked inside the onload handler, the load
8404 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8406 MOZ_ASSERT(aLoadState
->LoadType() == LOAD_LINK
||
8407 aLoadState
->LoadType() == LOAD_NORMAL_REPLACE
);
8408 MOZ_ASSERT(!aLoadState
->LoadIsFromSessionHistory());
8409 MOZ_ASSERT(aLoadState
->FirstParty()); // Windowwatcher will assume this.
8411 RefPtr
<nsDocShellLoadState
> loadState
=
8412 new nsDocShellLoadState(aLoadState
->URI());
8414 // Set up our loadinfo so it will do the load as much like we would have
8416 loadState
->SetReferrerInfo(aLoadState
->GetReferrerInfo());
8417 loadState
->SetOriginalURI(aLoadState
->OriginalURI());
8419 Maybe
<nsCOMPtr
<nsIURI
>> resultPrincipalURI
;
8420 aLoadState
->GetMaybeResultPrincipalURI(resultPrincipalURI
);
8422 loadState
->SetMaybeResultPrincipalURI(resultPrincipalURI
);
8423 loadState
->SetKeepResultPrincipalURIIfSet(
8424 aLoadState
->KeepResultPrincipalURIIfSet());
8425 // LoadReplace will always be false due to asserts above, skip setting
8427 loadState
->SetTriggeringPrincipal(aLoadState
->TriggeringPrincipal());
8428 loadState
->SetTriggeringSandboxFlags(
8429 aLoadState
->TriggeringSandboxFlags());
8430 loadState
->SetTriggeringWindowId(aLoadState
->TriggeringWindowId());
8431 loadState
->SetTriggeringStorageAccess(
8432 aLoadState
->TriggeringStorageAccess());
8433 loadState
->SetCsp(aLoadState
->Csp());
8434 loadState
->SetInheritPrincipal(aLoadState
->HasInternalLoadFlags(
8435 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL
));
8436 // Explicit principal because we do not want any guesses as to what the
8437 // principal to inherit is: it should be aTriggeringPrincipal.
8438 loadState
->SetPrincipalIsExplicit(true);
8439 loadState
->SetLoadType(aLoadState
->LoadType());
8440 loadState
->SetForceAllowDataURI(aLoadState
->HasInternalLoadFlags(
8441 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI
));
8443 loadState
->SetHasValidUserGestureActivation(
8444 aLoadState
->HasValidUserGestureActivation());
8446 // Propagate POST data to the new load.
8447 loadState
->SetPostDataStream(aLoadState
->PostDataStream());
8448 loadState
->SetIsFormSubmission(aLoadState
->IsFormSubmission());
8450 rv
= win
->Open(NS_ConvertUTF8toUTF16(spec
),
8451 aLoadState
->Target(), // window name
8454 true, // aForceNoOpener
8455 getter_AddRefs(newBC
));
8460 rv
= win
->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec
),
8461 aLoadState
->Target(), // window name
8463 getter_AddRefs(newBC
));
8465 // In some cases the Open call doesn't actually result in a new
8466 // window being opened. We can detect these cases by examining the
8467 // document in |newBC|, if any.
8468 nsCOMPtr
<nsPIDOMWindowOuter
> piNewWin
=
8469 newBC
? newBC
->GetDOMWindow() : nullptr;
8471 RefPtr
<Document
> newDoc
= piNewWin
->GetExtantDoc();
8472 if (!newDoc
|| newDoc
->IsInitialDocument()) {
8473 aLoadState
->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD
);
8478 targetContext
= newBC
;
8481 NS_ENSURE_SUCCESS(rv
, rv
);
8482 NS_ENSURE_TRUE(targetContext
, rv
);
8484 // If our target BrowsingContext is still pending initialization, ignore the
8485 // navigation request targeting it.
8486 if (NS_WARN_IF(targetContext
->GetPendingInitialization())) {
8490 aLoadState
->SetTargetBrowsingContext(targetContext
);
8491 if (aLoadState
->IsFormSubmission()) {
8492 aLoadState
->SetLoadType(
8493 GetLoadTypeForFormSubmission(targetContext
, aLoadState
));
8497 // Transfer the load to the target BrowsingContext... Clear the window target
8498 // name to the empty string to prevent recursive retargeting!
8501 aLoadState
->SetTarget(u
""_ns
);
8502 // No forced download
8503 aLoadState
->SetFileName(VoidString());
8504 return targetContext
->InternalLoad(aLoadState
);
8507 static nsAutoCString
RefMaybeNull(nsIURI
* aURI
) {
8508 nsAutoCString result
;
8509 if (NS_FAILED(aURI
->GetRef(result
))) {
8510 result
.SetIsVoid(true);
8515 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI
* aNewURI
) {
8516 uint32_t flags
= LOCATION_CHANGE_SAME_DOCUMENT
;
8520 NS_SUCCEEDED(mCurrentURI
->EqualsExceptRef(aNewURI
, &equal
)) && equal
&&
8521 RefMaybeNull(mCurrentURI
) != RefMaybeNull(aNewURI
)) {
8522 flags
|= LOCATION_CHANGE_HASHCHANGE
;
8528 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState
* aLoadState
,
8529 SameDocumentNavigationState
& aState
) {
8530 MOZ_ASSERT(aLoadState
);
8531 if (!(aLoadState
->LoadType() == LOAD_NORMAL
||
8532 aLoadState
->LoadType() == LOAD_STOP_CONTENT
||
8533 LOAD_TYPE_HAS_FLAGS(aLoadState
->LoadType(),
8534 LOAD_FLAGS_REPLACE_HISTORY
) ||
8535 aLoadState
->LoadType() == LOAD_HISTORY
||
8536 aLoadState
->LoadType() == LOAD_LINK
)) {
8540 nsCOMPtr
<nsIURI
> currentURI
= mCurrentURI
;
8542 nsresult rvURINew
= aLoadState
->URI()->GetRef(aState
.mNewHash
);
8543 if (NS_SUCCEEDED(rvURINew
)) {
8544 rvURINew
= aLoadState
->URI()->GetHasRef(&aState
.mNewURIHasRef
);
8547 if (currentURI
&& NS_SUCCEEDED(rvURINew
)) {
8548 nsresult rvURIOld
= currentURI
->GetRef(aState
.mCurrentHash
);
8549 if (NS_SUCCEEDED(rvURIOld
)) {
8550 rvURIOld
= currentURI
->GetHasRef(&aState
.mCurrentURIHasRef
);
8552 if (NS_SUCCEEDED(rvURIOld
)) {
8553 if (NS_FAILED(currentURI
->EqualsExceptRef(aLoadState
->URI(),
8554 &aState
.mSameExceptHashes
))) {
8555 aState
.mSameExceptHashes
= false;
8560 if (!aState
.mSameExceptHashes
&& currentURI
&& NS_SUCCEEDED(rvURINew
)) {
8561 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8562 nsCOMPtr
<nsIURI
> currentExposableURI
=
8563 nsIOService::CreateExposableURI(currentURI
);
8564 nsresult rvURIOld
= currentExposableURI
->GetRef(aState
.mCurrentHash
);
8565 if (NS_SUCCEEDED(rvURIOld
)) {
8566 rvURIOld
= currentExposableURI
->GetHasRef(&aState
.mCurrentURIHasRef
);
8568 if (NS_SUCCEEDED(rvURIOld
)) {
8569 if (NS_FAILED(currentExposableURI
->EqualsExceptRef(
8570 aLoadState
->URI(), &aState
.mSameExceptHashes
))) {
8571 aState
.mSameExceptHashes
= false;
8573 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8574 // have to perform a special check here to avoid an actual navigation. If
8575 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8576 // fact that the new URI is currently http), then set mSameExceptHashes to
8577 // true and only perform a fragment navigation.
8578 if (!aState
.mSameExceptHashes
) {
8579 if (nsCOMPtr
<nsIChannel
> docChannel
= GetCurrentDocChannel()) {
8580 nsCOMPtr
<nsILoadInfo
> docLoadInfo
= docChannel
->LoadInfo();
8581 if (!docLoadInfo
->GetLoadErrorPage() &&
8582 nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8583 currentExposableURI
, aLoadState
->URI(), docLoadInfo
)) {
8584 uint32_t status
= docLoadInfo
->GetHttpsOnlyStatus();
8585 if (status
& (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED
|
8586 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST
)) {
8587 // At this point the requested URI is for sure a fragment
8588 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8589 // enabled. Also it is not interfering the upgrade order of
8590 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8591 // Since we are on an HTTPS site the fragment
8592 // navigation should also be an HTTPS.
8593 // For that reason we should upgrade the URI to HTTPS.
8594 aState
.mSecureUpgradeURI
= true;
8595 aState
.mSameExceptHashes
= true;
8603 if (mozilla::SessionHistoryInParent()) {
8604 if (mActiveEntry
&& aLoadState
->LoadIsFromSessionHistory()) {
8605 aState
.mHistoryNavBetweenSameDoc
= mActiveEntry
->SharesDocumentWith(
8606 aLoadState
->GetLoadingSessionHistoryInfo()->mInfo
);
8608 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8609 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8610 this, aState
.mHistoryNavBetweenSameDoc
));
8612 if (mOSHE
&& aLoadState
->LoadIsFromSessionHistory()) {
8613 // We're doing a history load.
8615 mOSHE
->SharesDocumentWith(aLoadState
->SHEntry(),
8616 &aState
.mHistoryNavBetweenSameDoc
);
8620 // A same document navigation happens when we navigate between two SHEntries
8621 // for the same document. We do a same document navigation under two
8622 // circumstances. Either
8624 // a) we're navigating between two different SHEntries which share a
8627 // b) we're navigating to a new shentry whose URI differs from the
8628 // current URI only in its hash, the new hash is non-empty, and
8629 // we're not doing a POST.
8631 // The restriction that the SHEntries in (a) must be different ensures
8632 // that history.go(0) and the like trigger full refreshes, rather than
8633 // same document navigations.
8634 if (!mozilla::SessionHistoryInParent()) {
8635 bool doSameDocumentNavigation
=
8636 (aState
.mHistoryNavBetweenSameDoc
&& mOSHE
!= aLoadState
->SHEntry()) ||
8637 (!aLoadState
->SHEntry() && !aLoadState
->PostDataStream() &&
8638 aState
.mSameExceptHashes
&& aState
.mNewURIHasRef
);
8639 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8640 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8641 aState
.mHistoryNavBetweenSameDoc
, doSameDocumentNavigation
));
8642 return doSameDocumentNavigation
;
8645 if (aState
.mHistoryNavBetweenSameDoc
&&
8646 !aLoadState
->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry
) {
8651 gSHLog
, LogLevel::Debug
,
8652 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8653 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8654 this, !aLoadState
->LoadIsFromSessionHistory() ? "true" : "false",
8655 !aLoadState
->PostDataStream() ? "true" : "false",
8656 aState
.mSameExceptHashes
? "true" : "false",
8657 aState
.mNewURIHasRef
? "true" : "false"));
8658 return !aLoadState
->LoadIsFromSessionHistory() &&
8659 !aLoadState
->PostDataStream() && aState
.mSameExceptHashes
&&
8660 aState
.mNewURIHasRef
;
8663 nsresult
nsDocShell::HandleSameDocumentNavigation(
8664 nsDocShellLoadState
* aLoadState
, SameDocumentNavigationState
& aState
,
8665 bool& aSameDocument
) {
8666 aSameDocument
= true;
8668 SameDocumentNavigationState state
;
8669 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState
, state
));
8672 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8673 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8674 mCurrentURI
->GetSpecOrDefault().get(),
8675 aLoadState
->URI()->GetSpecOrDefault().get()));
8677 RefPtr
<Document
> doc
= GetDocument();
8678 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
8679 doc
->DoNotifyPossibleTitleChange();
8681 nsCOMPtr
<nsIURI
> currentURI
= mCurrentURI
;
8683 // We need to upgrade the new URI from http: to https:
8684 nsCOMPtr
<nsIURI
> newURI
= aLoadState
->URI();
8685 if (aState
.mSecureUpgradeURI
) {
8686 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState
->URI(), getter_AddRefs(newURI
)));
8687 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8688 ("Upgraded URI to %s", newURI
->GetSpecOrDefault().get()));
8691 if (StaticPrefs::dom_security_setdocumenturi()) {
8692 // check if aLoadState->URI(), principalURI, mCurrentURI are same origin
8693 // skip handling otherwise
8694 nsCOMPtr
<nsIPrincipal
> origPrincipal
= doc
->NodePrincipal();
8695 nsCOMPtr
<nsIURI
> principalURI
= origPrincipal
->GetURI();
8696 if (origPrincipal
->GetIsNullPrincipal()) {
8697 nsCOMPtr
<nsIPrincipal
> precursor
= origPrincipal
->GetPrecursorPrincipal();
8699 principalURI
= precursor
->GetURI();
8703 auto isLoadableViaInternet
= [](nsIURI
* uri
) {
8704 return (uri
&& (net::SchemeIsHTTP(uri
) || net::SchemeIsHTTPS(uri
)));
8707 if (isLoadableViaInternet(principalURI
) &&
8708 isLoadableViaInternet(mCurrentURI
) && isLoadableViaInternet(newURI
)) {
8709 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
8711 ssm
->CheckSameOriginURI(newURI
, principalURI
, false, false)) ||
8712 !NS_SUCCEEDED(ssm
->CheckSameOriginURI(mCurrentURI
, principalURI
,
8714 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8715 ("nsDocShell[%p]: possible violation of the same origin policy "
8716 "during same document navigation",
8718 aSameDocument
= false;
8725 if (aState
.mSameExceptHashes
) {
8726 bool sameExceptHashes
= false;
8727 currentURI
->EqualsExceptRef(newURI
, &sameExceptHashes
);
8728 MOZ_ASSERT(sameExceptHashes
);
8732 // Save the position of the scrollers.
8733 nsPoint scrollPos
= GetCurScrollPos();
8735 // Reset mLoadType to its original value once we exit this block, because this
8736 // same document navigation might have started after a normal, network load,
8737 // and we don't want to clobber its load type. See bug 737307.
8738 AutoRestore
<uint32_t> loadTypeResetter(mLoadType
);
8740 // If a non-same-document-navigation (i.e., a network load) is pending, make
8741 // this a replacement load, so that we don't add a SHEntry here and the
8742 // network load goes into the SHEntry it expects to.
8743 if (JustStartedNetworkLoad() && (aLoadState
->LoadType() & LOAD_CMD_NORMAL
)) {
8744 mLoadType
= LOAD_NORMAL_REPLACE
;
8746 mLoadType
= aLoadState
->LoadType();
8749 mURIResultedInDocument
= true;
8751 nsCOMPtr
<nsISHEntry
> oldLSHE
= mLSHE
;
8753 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8754 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8755 // onLocationChange() notifications to the browser to update back/forward
8757 SetHistoryEntryAndUpdateBC(Some
<nsISHEntry
*>(aLoadState
->SHEntry()),
8759 UniquePtr
<mozilla::dom::LoadingSessionHistoryInfo
> oldLoadingEntry
;
8760 mLoadingEntry
.swap(oldLoadingEntry
);
8761 if (aLoadState
->GetLoadingSessionHistoryInfo()) {
8762 mLoadingEntry
= MakeUnique
<LoadingSessionHistoryInfo
>(
8763 *aLoadState
->GetLoadingSessionHistoryInfo());
8764 mNeedToReportActiveAfterLoadingBecomesActive
= false;
8767 // Set the doc's URI according to the new history entry's URI.
8768 doc
->SetDocumentURI(newURI
);
8770 /* This is a anchor traversal within the same page.
8771 * call OnNewURI() so that, this traversal will be
8772 * recorded in session and global history.
8774 nsCOMPtr
<nsIPrincipal
> newURITriggeringPrincipal
, newURIPrincipalToInherit
,
8775 newURIPartitionedPrincipalToInherit
;
8776 nsCOMPtr
<nsIContentSecurityPolicy
> newCsp
;
8777 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry
: !!mOSHE
) {
8778 if (mozilla::SessionHistoryInParent()) {
8779 newURITriggeringPrincipal
= mActiveEntry
->GetTriggeringPrincipal();
8780 newURIPrincipalToInherit
= mActiveEntry
->GetPrincipalToInherit();
8781 newURIPartitionedPrincipalToInherit
=
8782 mActiveEntry
->GetPartitionedPrincipalToInherit();
8783 newCsp
= mActiveEntry
->GetCsp();
8785 newURITriggeringPrincipal
= mOSHE
->GetTriggeringPrincipal();
8786 newURIPrincipalToInherit
= mOSHE
->GetPrincipalToInherit();
8787 newURIPartitionedPrincipalToInherit
=
8788 mOSHE
->GetPartitionedPrincipalToInherit();
8789 newCsp
= mOSHE
->GetCsp();
8792 newURITriggeringPrincipal
= aLoadState
->TriggeringPrincipal();
8793 newURIPrincipalToInherit
= doc
->NodePrincipal();
8794 newURIPartitionedPrincipalToInherit
= doc
->PartitionedPrincipal();
8795 newCsp
= doc
->GetCsp();
8798 uint32_t locationChangeFlags
= GetSameDocumentNavigationFlags(newURI
);
8800 // Pass true for aCloneSHChildren, since we're not
8801 // changing documents here, so all of our subframes are
8802 // still relevant to the new session history entry.
8804 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8805 // flag on firing onLocationChange(...).
8806 // Anyway, aCloneSHChildren param is simply reflecting
8807 // doSameDocumentNavigation in this scope.
8809 // Note: we'll actually fire onLocationChange later, in order to preserve
8810 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8812 bool locationChangeNeeded
= OnNewURI(
8813 newURI
, nullptr, newURITriggeringPrincipal
, newURIPrincipalToInherit
,
8814 newURIPartitionedPrincipalToInherit
, newCsp
, true, true);
8816 nsCOMPtr
<nsIInputStream
> postData
;
8817 uint32_t cacheKey
= 0;
8819 bool scrollRestorationIsManual
= false;
8820 if (!mozilla::SessionHistoryInParent()) {
8822 /* save current position of scroller(s) (bug 59774) */
8823 mOSHE
->SetScrollPosition(scrollPos
.x
, scrollPos
.y
);
8824 scrollRestorationIsManual
= mOSHE
->GetScrollRestorationIsManual();
8825 // Get the postdata and page ident from the current page, if
8826 // the new load is being done via normal means. Note that
8827 // "normal means" can be checked for just by checking for
8828 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8829 // above -- it filters out some LOAD_CMD_NORMAL cases that we
8830 // wouldn't want here.
8831 if (aLoadState
->LoadType() & LOAD_CMD_NORMAL
) {
8832 postData
= mOSHE
->GetPostData();
8833 cacheKey
= mOSHE
->GetCacheKey();
8836 // Link our new SHEntry to the old SHEntry's back/forward
8837 // cache data, since the two SHEntries correspond to the
8840 if (!aLoadState
->LoadIsFromSessionHistory()) {
8841 // If we're not doing a history load, scroll restoration
8842 // should be inherited from the previous session history entry.
8843 SetScrollRestorationIsManualOnHistoryEntry(mLSHE
,
8844 scrollRestorationIsManual
);
8846 mLSHE
->AdoptBFCacheEntry(mOSHE
);
8851 mActiveEntry
->SetScrollPosition(scrollPos
.x
, scrollPos
.y
);
8852 if (mBrowsingContext
) {
8854 if (XRE_IsParentProcess()) {
8855 SessionHistoryEntry
* entry
=
8856 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
8858 entry
->SetScrollPosition(scrollPos
.x
, scrollPos
.y
);
8861 mozilla::Unused
<< ContentChild::GetSingleton()
8862 ->SendSessionHistoryEntryScrollPosition(
8863 mBrowsingContext
, scrollPos
.x
,
8868 if (mLoadingEntry
) {
8869 if (!mLoadingEntry
->mLoadIsFromSessionHistory
) {
8870 // If we're not doing a history load, scroll restoration
8871 // should be inherited from the previous session history entry.
8872 // XXX This needs most probably tweaks once fragment navigation is
8873 // fixed to work with session-history-in-parent.
8874 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8875 scrollRestorationIsManual
);
8880 // If we're doing a history load, use its scroll restoration state.
8881 if (aLoadState
->LoadIsFromSessionHistory()) {
8882 if (mozilla::SessionHistoryInParent()) {
8883 scrollRestorationIsManual
= aLoadState
->GetLoadingSessionHistoryInfo()
8884 ->mInfo
.GetScrollRestorationIsManual();
8886 scrollRestorationIsManual
=
8887 aLoadState
->SHEntry()->GetScrollRestorationIsManual();
8891 /* Assign mLSHE to mOSHE. This will either be a new entry created
8892 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8895 if (!mozilla::SessionHistoryInParent()) {
8897 SetHistoryEntryAndUpdateBC(Nothing(), Some
<nsISHEntry
*>(mLSHE
));
8898 // Save the postData obtained from the previous page
8899 // in to the session history entry created for the
8900 // anchor page, so that any history load of the anchor
8901 // page will restore the appropriate postData.
8903 mOSHE
->SetPostData(postData
);
8906 // Make sure we won't just repost without hitting the
8908 if (cacheKey
!= 0) {
8909 mOSHE
->SetCacheKey(cacheKey
);
8913 /* Set the title for the SH entry for this target url so that
8914 * SH menus in go/back/forward buttons won't be empty for this.
8915 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
8917 * Note, when session history lives in the parent process, this does not
8918 * update the title there.
8920 SetTitleOnHistoryEntry(false);
8922 if (aLoadState
->LoadIsFromSessionHistory()) {
8924 gSHLog
, LogLevel::Debug
,
8925 ("Moving the loading entry to the active entry on nsDocShell %p to "
8927 this, mLoadingEntry
->mInfo
.GetURI()->GetSpecOrDefault().get()));
8929 nsCOMPtr
<nsILayoutHistoryState
> currentLayoutHistoryState
;
8931 currentLayoutHistoryState
= mActiveEntry
->GetLayoutHistoryState();
8934 UniquePtr
<SessionHistoryInfo
> previousActiveEntry(mActiveEntry
.release());
8935 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(mLoadingEntry
->mInfo
);
8936 if (currentLayoutHistoryState
) {
8937 // Restore the existing nsILayoutHistoryState object, since it is
8938 // possibly being used by the layout. When doing a new load, the
8939 // shared state is copied from the existing active entry, so this
8940 // special case is needed only with the history loads.
8941 mActiveEntry
->SetLayoutHistoryState(currentLayoutHistoryState
);
8944 if (cacheKey
!= 0) {
8945 mActiveEntry
->SetCacheKey(cacheKey
);
8947 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
8948 // does require a non-null uri if this is for a refresh load of the same
8949 // URI, but in that case mCurrentURI won't be null here.
8950 mBrowsingContext
->SessionHistoryCommit(
8951 *mLoadingEntry
, mLoadType
, mCurrentURI
, previousActiveEntry
.get(),
8953 /* No expiration update on the same document loads*/
8955 // FIXME Need to set postdata.
8957 // Set the title for the SH entry for this target url so that
8958 // SH menus in go/back/forward buttons won't be empty for this.
8959 // Note, when session history lives in the parent process, this does not
8960 // update the title there.
8961 SetTitleOnHistoryEntry(false);
8963 Maybe
<bool> scrollRestorationIsManual
;
8965 scrollRestorationIsManual
.emplace(
8966 mActiveEntry
->GetScrollRestorationIsManual());
8968 // Get the postdata and page ident from the current page, if the new
8969 // load is being done via normal means. Note that "normal means" can be
8970 // checked for just by checking for LOAD_CMD_NORMAL, given the loadType
8971 // and allowScroll check above -- it filters out some LOAD_CMD_NORMAL
8972 // cases that we wouldn't want here.
8973 if (aLoadState
->LoadType() & LOAD_CMD_NORMAL
) {
8974 postData
= mActiveEntry
->GetPostData();
8975 cacheKey
= mActiveEntry
->GetCacheKey();
8979 MOZ_LOG(gSHLog
, LogLevel::Debug
,
8980 ("Creating an active entry on nsDocShell %p to %s", this,
8981 newURI
->GetSpecOrDefault().get()));
8983 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(*mActiveEntry
, newURI
);
8985 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(
8986 newURI
, newURITriggeringPrincipal
, newURIPrincipalToInherit
,
8987 newURIPartitionedPrincipalToInherit
, newCsp
, mContentTypeHint
);
8990 // Save the postData obtained from the previous page in to the session
8991 // history entry created for the anchor page, so that any history load of
8992 // the anchor page will restore the appropriate postData.
8994 mActiveEntry
->SetPostData(postData
);
8997 // Make sure we won't just repost without hitting the
8999 if (cacheKey
!= 0) {
9000 mActiveEntry
->SetCacheKey(cacheKey
);
9003 // Set the title for the SH entry for this target url so that
9004 // SH menus in go/back/forward buttons won't be empty for this.
9005 mActiveEntry
->SetTitle(mTitle
);
9007 if (scrollRestorationIsManual
.isSome()) {
9008 mActiveEntry
->SetScrollRestorationIsManual(
9009 scrollRestorationIsManual
.value());
9012 if (LOAD_TYPE_HAS_FLAGS(mLoadType
, LOAD_FLAGS_REPLACE_HISTORY
)) {
9013 mBrowsingContext
->ReplaceActiveSessionHistoryEntry(mActiveEntry
.get());
9015 mBrowsingContext
->IncrementHistoryEntryCountForBrowsingContext();
9016 // FIXME We should probably just compute mChildOffset in the parent
9017 // instead of passing it over IPC here.
9018 mBrowsingContext
->SetActiveSessionHistoryEntry(
9019 Some(scrollPos
), mActiveEntry
.get(), mLoadType
, cacheKey
);
9020 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9025 if (locationChangeNeeded
) {
9026 FireOnLocationChange(this, nullptr, newURI
, locationChangeFlags
);
9029 /* Restore the original LSHE if we were loading something
9030 * while same document navigation was initiated.
9032 SetHistoryEntryAndUpdateBC(Some
<nsISHEntry
*>(oldLSHE
), Nothing());
9033 mLoadingEntry
.swap(oldLoadingEntry
);
9035 /* Set the title for the Global History entry for this anchor url.
9037 UpdateGlobalHistoryTitle(newURI
);
9039 SetDocCurrentStateObj(mOSHE
, mActiveEntry
.get());
9041 // Inform the favicon service that the favicon for oldURI also
9042 // applies to newURI.
9043 CopyFavicon(currentURI
, newURI
, UsePrivateBrowsing());
9045 RefPtr
<nsGlobalWindowOuter
> scriptGlobal
= mScriptGlobal
;
9046 nsCOMPtr
<nsPIDOMWindowInner
> win
=
9047 scriptGlobal
? scriptGlobal
->GetCurrentInnerWindow() : nullptr;
9049 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9050 // the function decides whether a scroll is appropriate based on the
9051 // arguments it receives. But even if we don't end up scrolling,
9052 // ScrollToAnchor performs other important tasks, such as informing
9053 // the presShell that we have a new hash. See bug 680257.
9054 nsresult rv
= ScrollToAnchor(aState
.mCurrentURIHasRef
, aState
.mNewURIHasRef
,
9055 aState
.mNewHash
, aLoadState
->LoadType());
9056 NS_ENSURE_SUCCESS(rv
, rv
);
9058 /* restore previous position of scroller(s), if we're moving
9059 * back in history (bug 59774)
9063 bool needsScrollPosUpdate
= false;
9064 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry
: !!mOSHE
) &&
9065 (aLoadState
->LoadType() == LOAD_HISTORY
||
9066 aLoadState
->LoadType() == LOAD_RELOAD_NORMAL
) &&
9067 !scrollRestorationIsManual
) {
9068 needsScrollPosUpdate
= true;
9069 if (mozilla::SessionHistoryInParent()) {
9070 mActiveEntry
->GetScrollPosition(&bx
, &by
);
9072 mOSHE
->GetScrollPosition(&bx
, &by
);
9076 // Dispatch the popstate and hashchange events, as appropriate.
9078 // The event dispatch below can cause us to re-enter script and
9079 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9080 // reference to avoid null derefs. See bug 914521.
9082 // Fire a hashchange event URIs differ, and only in their hashes.
9083 bool doHashchange
= aState
.mSameExceptHashes
&&
9084 (aState
.mCurrentURIHasRef
!= aState
.mNewURIHasRef
||
9085 !aState
.mCurrentHash
.Equals(aState
.mNewHash
));
9087 if (aState
.mHistoryNavBetweenSameDoc
|| doHashchange
) {
9088 win
->DispatchSyncPopState();
9091 if (needsScrollPosUpdate
&& win
->HasActiveDocument()) {
9092 SetCurScrollPosEx(bx
, by
);
9096 // Note that currentURI hasn't changed because it's on the
9097 // stack, so we can just use it directly as the old URI.
9098 win
->DispatchAsyncHashchange(currentURI
, newURI
);
9105 static bool NavigationShouldTakeFocus(nsDocShell
* aDocShell
,
9106 nsDocShellLoadState
* aLoadState
) {
9107 if (!aLoadState
->AllowFocusMove()) {
9110 if (!aLoadState
->HasValidUserGestureActivation()) {
9113 const auto& sourceBC
= aLoadState
->SourceBrowsingContext();
9114 if (!sourceBC
|| !sourceBC
->IsActive()) {
9115 // If the navigation didn't come from a foreground tab, then we don't steal
9119 auto* bc
= aDocShell
->GetBrowsingContext();
9120 if (sourceBC
.get() == bc
) {
9121 // If it comes from the same tab / frame, don't steal focus either.
9124 auto* fm
= nsFocusManager::GetFocusManager();
9125 if (fm
&& bc
->IsActive() && fm
->IsInActiveWindow(bc
)) {
9126 // If we're already on the foreground tab of the foreground window, then we
9127 // don't need to do this. This helps to e.g. not steal focus from the
9128 // browser chrome unnecessarily.
9131 if (auto* doc
= aDocShell
->GetExtantDocument()) {
9132 if (doc
->IsInitialDocument()) {
9133 // If we're the initial load for the browsing context, the browser
9134 // chrome determines what to focus. This is important because the
9135 // browser chrome may want to e.g focus the url-bar
9139 // Take loadDivertedInBackground into account so the behavior would be the
9140 // same as how the tab first opened.
9141 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9144 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
9145 BrowsingContext
* aTargetBC
, nsDocShellLoadState
* aLoadState
) {
9146 MOZ_ASSERT(aLoadState
->IsFormSubmission());
9148 // https://html.spec.whatwg.org/#form-submission-algorithm
9149 // 22. Let historyHandling be "push".
9150 // 23. If form document equals targetNavigable's active document, and
9151 // form document has not yet completely loaded, then set
9152 // historyHandling to "replace".
9153 return GetBrowsingContext() == aTargetBC
&& !mEODForCurrentDocument
9154 ? LOAD_NORMAL_REPLACE
9158 nsresult
nsDocShell::InternalLoad(nsDocShellLoadState
* aLoadState
,
9159 Maybe
<uint32_t> aCacheKey
) {
9160 MOZ_ASSERT(aLoadState
, "need a load state!");
9161 MOZ_ASSERT(aLoadState
->TriggeringPrincipal(),
9162 "need a valid TriggeringPrincipal");
9164 if (!aLoadState
->TriggeringPrincipal()) {
9165 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9166 return NS_ERROR_FAILURE
;
9168 if (NS_WARN_IF(mBrowsingContext
->GetPendingInitialization())) {
9169 return NS_ERROR_NOT_AVAILABLE
;
9172 const bool shouldTakeFocus
= NavigationShouldTakeFocus(this, aLoadState
);
9174 mOriginalUriString
.Truncate();
9176 MOZ_LOG(gDocShellLeakLog
, LogLevel::Debug
,
9177 ("DOCSHELL %p InternalLoad %s\n", this,
9178 aLoadState
->URI()->GetSpecOrDefault().get()));
9180 NS_ENSURE_TRUE(IsValidLoadType(aLoadState
->LoadType()), NS_ERROR_INVALID_ARG
);
9182 // Cancel loads coming from Docshells that are being destroyed.
9183 if (mIsBeingDestroyed
) {
9184 return NS_ERROR_NOT_AVAILABLE
;
9187 nsresult rv
= EnsureScriptEnvironment();
9188 if (NS_FAILED(rv
)) {
9192 // If we have a target to move to, do that now.
9193 if (!aLoadState
->Target().IsEmpty()) {
9194 return PerformRetargeting(aLoadState
);
9197 // This is the non-retargeting load path, we've already set the right loadtype
9198 // for form submissions in nsDocShell::OnLinkClickSync.
9199 if (aLoadState
->TargetBrowsingContext().IsNull()) {
9200 aLoadState
->SetTargetBrowsingContext(GetBrowsingContext());
9203 MOZ_DIAGNOSTIC_ASSERT(
9204 aLoadState
->TargetBrowsingContext() == GetBrowsingContext(),
9205 "Load must be targeting this BrowsingContext");
9207 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState
));
9209 // If we don't have a target, we're loading into ourselves, and our load
9210 // delegate may want to intercept that load.
9211 SameDocumentNavigationState sameDocumentNavigationState
;
9213 IsSameDocumentNavigation(aLoadState
, sameDocumentNavigationState
) &&
9214 !aLoadState
->GetPendingRedirectedChannel();
9216 // Note: We do this check both here and in BrowsingContext::
9217 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9218 // available in the process triggering the load, and we don't want the target
9219 // process to have to trust the triggering process to do the appropriate
9220 // checks for the BrowsingContext's sandbox flags.
9221 MOZ_TRY(mBrowsingContext
->CheckSandboxFlags(aLoadState
));
9223 NS_ENSURE_STATE(!HasUnloadedParent());
9225 rv
= CheckLoadingPermissions();
9226 if (NS_FAILED(rv
)) {
9230 if (mFiredUnloadEvent
) {
9231 if (IsOKToLoadURI(aLoadState
->URI())) {
9232 MOZ_ASSERT(aLoadState
->Target().IsEmpty(),
9233 "Shouldn't have a window target here!");
9235 // If this is a replace load, make whatever load triggered
9236 // the unload event also a replace load, so we don't
9237 // create extra history entries.
9238 if (LOAD_TYPE_HAS_FLAGS(aLoadState
->LoadType(),
9239 LOAD_FLAGS_REPLACE_HISTORY
)) {
9240 mLoadType
= LOAD_NORMAL_REPLACE
;
9243 // Do this asynchronously
9244 nsCOMPtr
<nsIRunnable
> ev
= new InternalLoadEvent(this, aLoadState
);
9245 return Dispatch(ev
.forget());
9248 // Just ignore this load attempt
9252 // If we are loading a URI that should inherit a security context (basically
9253 // javascript: at this point), and the caller has said that principal
9254 // inheritance is allowed, there are a few possible cases:
9256 // 1) We are provided with the principal to inherit. In that case, we just use
9259 // 2) The load is coming from some other application. In this case we don't
9260 // want to inherit from whatever document we have loaded now, since the
9261 // load is unrelated to it.
9263 // 3) It's a load from our application, but does not provide an explicit
9264 // principal to inherit. In that case, we want to inherit the principal of
9265 // our current document, or of our parent document (if any) if we don't
9266 // have a current document.
9270 if (!aLoadState
->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL
) &&
9271 !aLoadState
->PrincipalToInherit() &&
9272 (aLoadState
->HasInternalLoadFlags(
9273 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL
)) &&
9274 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9275 aLoadState
->URI(), &inherits
)) &&
9277 aLoadState
->SetPrincipalToInherit(GetInheritedPrincipal(true));
9279 // If principalToInherit is still null (e.g. if some of the conditions of
9280 // were not satisfied), then no inheritance of any sort will happen: the
9281 // load will just get a principal based on the URI being loaded.
9284 // If this docshell is owned by a frameloader, make sure to cancel
9285 // possible frameloader initialization before loading a new page.
9286 nsCOMPtr
<nsIDocShellTreeItem
> parent
= GetInProcessParentDocshell();
9288 RefPtr
<Document
> doc
= parent
->GetDocument();
9290 doc
->TryCancelFrameLoaderInitialization(this);
9294 // Before going any further vet loads initiated by external programs.
9295 if (aLoadState
->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL
)) {
9296 MOZ_DIAGNOSTIC_ASSERT(aLoadState
->LoadType() == LOAD_NORMAL
);
9298 // Disallow external chrome: loads targetted at content windows
9299 if (SchemeIsChrome(aLoadState
->URI())) {
9300 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9301 return NS_ERROR_FAILURE
;
9304 // clear the decks to prevent context bleed-through (bug 298255)
9305 rv
= CreateAboutBlankDocumentViewer(nullptr, nullptr, nullptr, nullptr,
9306 /* aIsInitialDocument */ false);
9307 if (NS_FAILED(rv
)) {
9308 return NS_ERROR_FAILURE
;
9312 mAllowKeywordFixup
= aLoadState
->HasInternalLoadFlags(
9313 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP
);
9314 mURIResultedInDocument
= false; // reset the clock...
9316 // See if this is actually a load between two history entries for the same
9317 // document. If the process fails, or if we successfully navigate within the
9318 // same document, return.
9320 nsresult rv
= HandleSameDocumentNavigation(
9321 aLoadState
, sameDocumentNavigationState
, sameDocument
);
9322 NS_ENSURE_SUCCESS(rv
, rv
);
9323 if (shouldTakeFocus
) {
9324 mBrowsingContext
->Focus(CallerType::System
, IgnoreErrors());
9331 // mDocumentViewer->PermitUnload can destroy |this| docShell, which
9332 // causes the next call of CanSavePresentation to crash.
9333 // Hold onto |this| until we return, to prevent a crash from happening.
9335 nsCOMPtr
<nsIDocShell
> kungFuDeathGrip(this);
9337 // Don't init timing for javascript:, since it generally doesn't
9338 // actually start a load or anything. If it does, we'll init
9339 // timing then, from OnStateChange.
9341 // XXXbz mTiming should know what channel it's for, so we don't
9342 // need this hackery.
9343 bool toBeReset
= false;
9344 bool isJavaScript
= SchemeIsJavascript(aLoadState
->URI());
9346 if (!isJavaScript
) {
9347 toBeReset
= MaybeInitTiming();
9349 bool isNotDownload
= aLoadState
->FileName().IsVoid();
9350 if (mTiming
&& isNotDownload
) {
9351 mTiming
->NotifyBeforeUnload();
9353 // Check if the page doesn't want to be unloaded. The javascript:
9354 // protocol handler deals with this for javascript: URLs.
9355 if (!isJavaScript
&& isNotDownload
&&
9356 !aLoadState
->NotifiedBeforeUnloadListeners() && mDocumentViewer
) {
9359 // Check if request is exempted from HTTPSOnlyMode and if https-first is
9360 // enabled, if so it means:
9361 // * https-first failed to upgrade request to https
9362 // * we already asked for permission to unload and the user accepted
9363 // otherwise we wouldn't be here.
9364 bool isPrivateWin
= GetOriginAttributes().mPrivateBrowsingId
> 0;
9365 bool isHistoryOrReload
= false;
9366 uint32_t loadType
= aLoadState
->LoadType();
9368 // Check if request is a reload.
9369 if (loadType
== LOAD_RELOAD_NORMAL
||
9370 loadType
== LOAD_RELOAD_BYPASS_CACHE
||
9371 loadType
== LOAD_RELOAD_BYPASS_PROXY
||
9372 loadType
== LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
||
9373 loadType
== LOAD_HISTORY
) {
9374 isHistoryOrReload
= true;
9377 // If it isn't a reload, the request already failed to be upgraded and
9378 // https-first is enabled then don't ask the user again for permission to
9379 // unload and just unload.
9380 if (!isHistoryOrReload
&& aLoadState
->IsExemptFromHTTPSFirstMode() &&
9381 nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin
)) {
9382 rv
= mDocumentViewer
->PermitUnload(
9383 nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload
,
9386 rv
= mDocumentViewer
->PermitUnload(&okToUnload
);
9389 if (NS_SUCCEEDED(rv
) && !okToUnload
) {
9390 // The user chose not to unload the page, interrupt the
9392 MaybeResetInitTiming(toBeReset
);
9397 if (mTiming
&& isNotDownload
) {
9398 mTiming
->NotifyUnloadAccepted(mCurrentURI
);
9401 // In e10s, in the parent process, we refuse to load anything other than
9402 // "safe" resources that we ship or trust enough to give "special" URLs.
9403 // Similar check will be performed by the ParentProcessDocumentChannel if in
9405 if (XRE_IsE10sParentProcess() &&
9406 !DocumentChannel::CanUseDocumentChannel(aLoadState
->URI()) &&
9407 !CanLoadInParentProcess(aLoadState
->URI())) {
9408 return NS_ERROR_FAILURE
;
9411 // Whenever a top-level browsing context is navigated, the user agent MUST
9412 // lock the orientation of the document to the document's default
9413 // orientation. We don't explicitly check for a top-level browsing context
9414 // here because orientation is only set on top-level browsing contexts.
9415 if (mBrowsingContext
->GetOrientationLock() != hal::ScreenOrientation::None
) {
9416 MOZ_ASSERT(mBrowsingContext
->IsTop());
9417 MOZ_ALWAYS_SUCCEEDS(
9418 mBrowsingContext
->SetOrientationLock(hal::ScreenOrientation::None
));
9419 if (mBrowsingContext
->IsActive()) {
9420 ScreenOrientation::UpdateActiveOrientationLock(
9421 hal::ScreenOrientation::None
);
9425 // Check for saving the presentation here, before calling Stop().
9426 // This is necessary so that we can catch any pending requests.
9427 // Since the new request has not been created yet, we pass null for the
9428 // new request parameter.
9429 // Also pass nullptr for the document, since it doesn't affect the return
9430 // value for our purposes here.
9431 bool savePresentation
=
9432 CanSavePresentation(aLoadState
->LoadType(), nullptr, nullptr,
9433 /* aReportBFCacheComboTelemetry */ true);
9435 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9436 // separate check for SHIP so that we know if there are ongoing requests
9437 // before calling Stop() below.
9438 if (mozilla::SessionHistoryInParent()) {
9439 Document
* document
= GetDocument();
9441 if (document
&& !document
->CanSavePresentation(nullptr, flags
, true)) {
9442 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9443 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9444 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9445 // Also, we want to report all the flags to the parent process here (and
9446 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9447 // telemetry data correctly.
9448 document
->DisallowBFCaching(flags
);
9452 // Don't stop current network activity for javascript: URL's since
9453 // they might not result in any data, and thus nothing should be
9454 // stopped in those cases. In the case where they do result in
9455 // data, the javascript: URL channel takes care of stopping
9456 // current network activity.
9457 if (!isJavaScript
&& isNotDownload
) {
9458 // Stop any current network activity.
9459 // Also stop content if this is a zombie doc. otherwise
9460 // the onload will be delayed by other loads initiated in the
9461 // background by the first document that
9462 // didn't fully load before the next load was initiated.
9463 // If not a zombie, don't stop content until data
9464 // starts arriving from the new URI...
9466 if ((mDocumentViewer
&& mDocumentViewer
->GetPreviousViewer()) ||
9467 LOAD_TYPE_HAS_FLAGS(aLoadState
->LoadType(), LOAD_FLAGS_STOP_CONTENT
)) {
9468 rv
= Stop(nsIWebNavigation::STOP_ALL
);
9470 rv
= Stop(nsIWebNavigation::STOP_NETWORK
);
9473 if (NS_FAILED(rv
)) {
9478 mLoadType
= aLoadState
->LoadType();
9480 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9481 // been called. But when loading an error page, do not clear the
9482 // mLSHE for the real page.
9483 if (mLoadType
!= LOAD_ERROR_PAGE
) {
9484 SetHistoryEntryAndUpdateBC(Some
<nsISHEntry
*>(aLoadState
->SHEntry()),
9486 if (aLoadState
->LoadIsFromSessionHistory() &&
9487 !mozilla::SessionHistoryInParent()) {
9488 // We're making history navigation or a reload. Make sure our history ID
9489 // points to the same ID as SHEntry's docshell ID.
9490 nsID historyID
= {};
9491 aLoadState
->SHEntry()->GetDocshellID(historyID
);
9493 Unused
<< mBrowsingContext
->SetHistoryID(historyID
);
9497 mSavingOldViewer
= savePresentation
;
9499 // If we have a saved content viewer in history, restore and show it now.
9500 if (aLoadState
->LoadIsFromSessionHistory() &&
9501 (mLoadType
& LOAD_CMD_HISTORY
)) {
9502 // https://html.spec.whatwg.org/#history-traversal:
9503 // To traverse the history
9504 // "If entry has a different Document object than the current entry, then
9505 // run the following substeps: Remove any tasks queued by the history
9506 // traversal task source..."
9507 // Same document object case was handled already above with
9508 // HandleSameDocumentNavigation call.
9509 RefPtr
<ChildSHistory
> shistory
= GetRootSessionHistory();
9511 shistory
->RemovePendingHistoryNavigations();
9513 if (!mozilla::SessionHistoryInParent()) {
9514 // It's possible that the previous viewer of mDocumentViewer is the
9515 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9516 // If that's the case, we need to go ahead and force it into its shentry
9517 // so we can restore it.
9518 if (mDocumentViewer
) {
9519 nsCOMPtr
<nsIDocumentViewer
> prevViewer
=
9520 mDocumentViewer
->GetPreviousViewer();
9523 nsCOMPtr
<nsIDocumentViewer
> prevPrevViewer
=
9524 prevViewer
->GetPreviousViewer();
9525 NS_ASSERTION(!prevPrevViewer
, "Should never have viewer chain here");
9527 nsCOMPtr
<nsISHEntry
> viewerEntry
;
9528 prevViewer
->GetHistoryEntry(getter_AddRefs(viewerEntry
));
9529 if (viewerEntry
== aLoadState
->SHEntry()) {
9530 // Make sure this viewer ends up in the right place
9531 mDocumentViewer
->SetPreviousViewer(nullptr);
9532 prevViewer
->Destroy();
9536 nsCOMPtr
<nsISHEntry
> oldEntry
= mOSHE
;
9538 rv
= RestorePresentation(aLoadState
->SHEntry(), &restoring
);
9540 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED
, true);
9543 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED
, false);
9545 // We failed to restore the presentation, so clean up.
9546 // Both the old and new history entries could potentially be in
9547 // an inconsistent state.
9548 if (NS_FAILED(rv
)) {
9550 oldEntry
->SyncPresentationState();
9553 aLoadState
->SHEntry()->SyncPresentationState();
9558 bool isTopLevelDoc
= mBrowsingContext
->IsTopContent();
9560 OriginAttributes attrs
= GetOriginAttributes();
9561 attrs
.SetFirstPartyDomain(isTopLevelDoc
, aLoadState
->URI());
9563 PredictorLearn(aLoadState
->URI(), nullptr,
9564 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL
, attrs
);
9565 PredictorPredict(aLoadState
->URI(), nullptr,
9566 nsINetworkPredictor::PREDICT_LOAD
, attrs
, nullptr);
9568 nsCOMPtr
<nsIRequest
> req
;
9569 rv
= DoURILoad(aLoadState
, aCacheKey
, getter_AddRefs(req
));
9571 if (NS_SUCCEEDED(rv
)) {
9572 if (shouldTakeFocus
) {
9573 mBrowsingContext
->Focus(CallerType::System
, IgnoreErrors());
9577 if (NS_FAILED(rv
)) {
9578 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(req
));
9579 UnblockEmbedderLoadEventForFailure();
9580 nsCOMPtr
<nsIURI
> uri
= aLoadState
->URI();
9581 if (DisplayLoadError(rv
, uri
, nullptr, chan
) &&
9582 // FIXME: At this point code was using internal load flags, but checking
9583 // non-internal load flags?
9584 aLoadState
->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV
)) {
9585 return NS_ERROR_LOAD_SHOWED_ERRORPAGE
;
9588 // We won't report any error if this is an unknown protocol error. The
9589 // reason behind this is that it will allow enumeration of external
9590 // protocols if we report an error for each unknown protocol.
9591 if (NS_ERROR_UNKNOWN_PROTOCOL
== rv
) {
9600 bool nsDocShell::CanLoadInParentProcess(nsIURI
* aURI
) {
9601 nsCOMPtr
<nsIURI
> uri
= aURI
;
9602 // In e10s, in the parent process, we refuse to load anything other than
9603 // "safe" resources that we ship or trust enough to give "special" URLs.
9604 bool canLoadInParent
= false;
9605 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9606 uri
, nsIProtocolHandler::URI_IS_UI_RESOURCE
, &canLoadInParent
)) &&
9608 // We allow UI resources.
9611 // For about: and extension-based URIs, which don't get
9612 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9613 while (uri
&& uri
->SchemeIs("view-source")) {
9614 nsCOMPtr
<nsINestedURI
> nested
= do_QueryInterface(uri
);
9616 nested
->GetInnerURI(getter_AddRefs(uri
));
9621 // Allow about: URIs, and allow moz-extension ones if we're running
9622 // extension content in the parent process.
9623 if (!uri
|| uri
->SchemeIs("about") ||
9624 (!StaticPrefs::extensions_webextensions_remote() &&
9625 uri
->SchemeIs("moz-extension"))) {
9628 #ifdef MOZ_THUNDERBIRD
9629 if (uri
->SchemeIs("imap") || uri
->SchemeIs("mailbox") ||
9630 uri
->SchemeIs("news") || uri
->SchemeIs("nntp") ||
9631 uri
->SchemeIs("snews")) {
9635 nsAutoCString scheme
;
9636 uri
->GetScheme(scheme
);
9637 // Allow ext+foo URIs (extension-registered custom protocols). See
9638 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9639 if (StringBeginsWith(scheme
, "ext+"_ns
) &&
9640 !StaticPrefs::extensions_webextensions_remote()) {
9643 // Final exception for some legacy automated tests:
9644 if (xpc::IsInAutomation() &&
9645 StaticPrefs::security_allow_unsafe_parent_loads()) {
9651 nsIPrincipal
* nsDocShell::GetInheritedPrincipal(
9652 bool aConsiderCurrentDocument
, bool aConsiderPartitionedPrincipal
) {
9653 RefPtr
<Document
> document
;
9654 bool inheritedFromCurrent
= false;
9656 if (aConsiderCurrentDocument
&& mDocumentViewer
) {
9657 document
= mDocumentViewer
->GetDocument();
9658 inheritedFromCurrent
= true;
9662 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
9663 GetInProcessSameTypeParent(getter_AddRefs(parentItem
));
9665 document
= parentItem
->GetDocument();
9670 if (!aConsiderCurrentDocument
) {
9674 // Make sure we end up with _something_ as the principal no matter
9675 // what.If this fails, we'll just get a null docViewer and bail.
9676 EnsureDocumentViewer();
9677 if (!mDocumentViewer
) {
9680 document
= mDocumentViewer
->GetDocument();
9683 //-- Get the document's principal
9685 nsIPrincipal
* docPrincipal
= aConsiderPartitionedPrincipal
9686 ? document
->PartitionedPrincipal()
9687 : document
->NodePrincipal();
9689 // Don't allow loads in typeContent docShells to inherit the system
9690 // principal from existing documents.
9691 if (inheritedFromCurrent
&& mItemType
== typeContent
&&
9692 docPrincipal
->IsSystemPrincipal()) {
9696 return docPrincipal
;
9702 /* static */ nsresult
nsDocShell::CreateRealChannelForDocument(
9703 nsIChannel
** aChannel
, nsIURI
* aURI
, nsILoadInfo
* aLoadInfo
,
9704 nsIInterfaceRequestor
* aCallbacks
, nsLoadFlags aLoadFlags
,
9705 const nsAString
& aSrcdoc
, nsIURI
* aBaseURI
) {
9706 nsCOMPtr
<nsIChannel
> channel
;
9707 if (aSrcdoc
.IsVoid()) {
9708 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel
), aURI
, aLoadInfo
,
9709 nullptr, // PerformanceStorage
9710 nullptr, // loadGroup
9711 aCallbacks
, aLoadFlags
));
9714 nsCOMPtr
<nsIViewSourceChannel
> vsc
= do_QueryInterface(channel
);
9716 MOZ_ALWAYS_SUCCEEDS(vsc
->SetBaseURI(aBaseURI
));
9719 } else if (SchemeIsViewSource(aURI
)) {
9720 // Instantiate view source handler protocol, if it doesn't exist already.
9721 nsCOMPtr
<nsIIOService
> io(do_GetIOService());
9723 nsCOMPtr
<nsIProtocolHandler
> handler
;
9725 io
->GetProtocolHandler("view-source", getter_AddRefs(handler
));
9726 if (NS_FAILED(rv
)) {
9730 nsViewSourceHandler
* vsh
= nsViewSourceHandler::GetInstance();
9732 return NS_ERROR_FAILURE
;
9735 MOZ_TRY(vsh
->NewSrcdocChannel(aURI
, aBaseURI
, aSrcdoc
, aLoadInfo
,
9736 getter_AddRefs(channel
)));
9738 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel
), aURI
,
9739 aSrcdoc
, "text/html"_ns
, aLoadInfo
,
9741 nsCOMPtr
<nsIInputStreamChannel
> isc
= do_QueryInterface(channel
);
9743 isc
->SetBaseURI(aBaseURI
);
9746 if (aLoadFlags
!= nsIRequest::LOAD_NORMAL
) {
9747 nsresult rv
= channel
->SetLoadFlags(aLoadFlags
);
9748 NS_ENSURE_SUCCESS(rv
, rv
);
9751 channel
.forget(aChannel
);
9755 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9756 BrowsingContext
* aBrowsingContext
, nsDocShellLoadState
* aLoadState
,
9757 LoadInfo
* aLoadInfo
, nsIInterfaceRequestor
* aCallbacks
,
9758 nsDocShell
* aDocShell
, const OriginAttributes
& aOriginAttributes
,
9759 nsLoadFlags aLoadFlags
, uint32_t aCacheKey
, nsresult
& aRv
,
9760 nsIChannel
** aChannel
) {
9761 MOZ_ASSERT(aLoadInfo
);
9763 nsString srcdoc
= VoidString();
9765 aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC
);
9767 srcdoc
= aLoadState
->SrcdocData();
9770 aLoadInfo
->SetTriggeringRemoteType(
9771 aLoadState
->GetEffectiveTriggeringRemoteType());
9773 if (aLoadState
->PrincipalToInherit()) {
9774 aLoadInfo
->SetPrincipalToInherit(aLoadState
->PrincipalToInherit());
9776 aLoadInfo
->SetLoadTriggeredFromExternal(
9777 aLoadState
->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL
));
9778 aLoadInfo
->SetForceAllowDataURI(aLoadState
->HasInternalLoadFlags(
9779 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI
));
9780 aLoadInfo
->SetOriginalFrameSrcLoad(
9781 aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC
));
9783 bool inheritAttrs
= false;
9784 if (aLoadState
->PrincipalToInherit()) {
9785 inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
9786 aLoadState
->PrincipalToInherit(), aLoadState
->URI(),
9787 true, // aInheritForAboutBlank
9791 // Strip the target query parameters before creating the channel.
9792 aLoadState
->MaybeStripTrackerQueryStrings(aBrowsingContext
);
9794 OriginAttributes attrs
;
9796 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9797 // true. Otherwise we just use the origin attributes from docshell.
9799 MOZ_ASSERT(aLoadState
->PrincipalToInherit(),
9800 "We should have PrincipalToInherit here.");
9801 attrs
= aLoadState
->PrincipalToInherit()->OriginAttributesRef();
9802 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9803 // have the same origin attributes with docshell.
9804 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9805 attrs
== aOriginAttributes
);
9807 attrs
= aOriginAttributes
;
9808 attrs
.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext
, aLoadInfo
),
9812 aRv
= aLoadInfo
->SetOriginAttributes(attrs
);
9813 if (NS_WARN_IF(NS_FAILED(aRv
))) {
9817 if (aLoadState
->GetIsFromProcessingFrameAttributes()) {
9818 aLoadInfo
->SetIsFromProcessingFrameAttributes();
9821 // Propagate the IsFormSubmission flag to the loadInfo.
9822 if (aLoadState
->IsFormSubmission()) {
9823 aLoadInfo
->SetIsFormSubmission(true);
9826 aLoadInfo
->SetUnstrippedURI(aLoadState
->GetUnstrippedURI());
9828 nsCOMPtr
<nsIChannel
> channel
;
9829 aRv
= CreateRealChannelForDocument(getter_AddRefs(channel
), aLoadState
->URI(),
9830 aLoadInfo
, aCallbacks
, aLoadFlags
, srcdoc
,
9831 aLoadState
->BaseURI());
9832 NS_ENSURE_SUCCESS(aRv
, false);
9838 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9839 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9840 // HTTPS_ONLY_EXEMPT.
9841 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel
);
9844 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
9845 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInternal(
9846 do_QueryInterface(channel
));
9847 nsCOMPtr
<nsIURI
> referrer
;
9848 nsIReferrerInfo
* referrerInfo
= aLoadState
->GetReferrerInfo();
9850 referrerInfo
->GetOriginalReferrer(getter_AddRefs(referrer
));
9852 if (httpChannelInternal
) {
9853 if (aLoadState
->HasInternalLoadFlags(
9854 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES
)) {
9855 aRv
= httpChannelInternal
->SetThirdPartyFlags(
9856 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW
);
9857 MOZ_ASSERT(NS_SUCCEEDED(aRv
));
9859 if (aLoadState
->FirstParty()) {
9860 aRv
= httpChannelInternal
->SetDocumentURI(aLoadState
->URI());
9861 MOZ_ASSERT(NS_SUCCEEDED(aRv
));
9863 aRv
= httpChannelInternal
->SetDocumentURI(referrer
);
9864 MOZ_ASSERT(NS_SUCCEEDED(aRv
));
9866 aRv
= httpChannelInternal
->SetRedirectMode(
9867 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL
);
9868 MOZ_ASSERT(NS_SUCCEEDED(aRv
));
9872 if (aLoadState
->HeadersStream()) {
9873 aRv
= AddHeadersToChannel(aLoadState
->HeadersStream(), httpChannel
);
9875 // Set the referrer explicitly
9876 // Referrer is currenly only set for link clicks here.
9878 aRv
= httpChannel
->SetReferrerInfo(referrerInfo
);
9879 MOZ_ASSERT(NS_SUCCEEDED(aRv
));
9882 // Mark the http channel as UrgentStart for top level document loading in
9884 if (IsUrgentStart(aBrowsingContext
, aLoadInfo
, aLoadState
->LoadType())) {
9885 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(channel
));
9887 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
9892 channel
->SetOriginalURI(aLoadState
->OriginalURI() ? aLoadState
->OriginalURI()
9893 : aLoadState
->URI());
9895 const nsACString
& typeHint
= aLoadState
->TypeHint();
9896 if (!typeHint
.IsVoid()) {
9897 channel
->SetContentType(typeHint
);
9900 const nsAString
& fileName
= aLoadState
->FileName();
9901 if (!fileName
.IsVoid()) {
9902 aRv
= channel
->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT
);
9903 NS_ENSURE_SUCCESS(aRv
, false);
9904 if (!fileName
.IsEmpty()) {
9905 aRv
= channel
->SetContentDispositionFilename(fileName
);
9906 NS_ENSURE_SUCCESS(aRv
, false);
9910 if (nsCOMPtr
<nsIWritablePropertyBag2
> props
= do_QueryInterface(channel
)) {
9911 nsCOMPtr
<nsIURI
> referrer
;
9912 nsIReferrerInfo
* referrerInfo
= aLoadState
->GetReferrerInfo();
9914 referrerInfo
->GetOriginalReferrer(getter_AddRefs(referrer
));
9916 // save true referrer for those who need it (e.g. xpinstall whitelisting)
9917 // Currently only http and ftp channels support this.
9918 props
->SetPropertyAsInterface(u
"docshell.internalReferrer"_ns
, referrer
);
9920 if (aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD
)) {
9921 props
->SetPropertyAsBool(u
"docshell.newWindowTarget"_ns
, true);
9925 nsCOMPtr
<nsICacheInfoChannel
> cacheChannel(do_QueryInterface(channel
));
9926 auto loadType
= aLoadState
->LoadType();
9928 if (loadType
== LOAD_RELOAD_NORMAL
&&
9930 browser_soft_reload_only_force_validate_top_level_document()) {
9931 nsCOMPtr
<nsICacheInfoChannel
> cachingChannel
= do_QueryInterface(channel
);
9932 if (cachingChannel
) {
9933 cachingChannel
->SetForceValidateCacheContent(true);
9937 // figure out if we need to set the post data stream on the channel...
9938 if (aLoadState
->PostDataStream()) {
9939 if (nsCOMPtr
<nsIFormPOSTActionChannel
> postChannel
=
9940 do_QueryInterface(channel
)) {
9941 // XXX it's a bit of a hack to rewind the postdata stream here but
9942 // it has to be done in case the post data is being reused multiple
9944 nsCOMPtr
<nsISeekableStream
> postDataSeekable
=
9945 do_QueryInterface(aLoadState
->PostDataStream());
9946 if (postDataSeekable
) {
9947 aRv
= postDataSeekable
->Seek(nsISeekableStream::NS_SEEK_SET
, 0);
9948 NS_ENSURE_SUCCESS(aRv
, false);
9951 // we really need to have a content type associated with this stream!!
9952 postChannel
->SetUploadStream(aLoadState
->PostDataStream(), ""_ns
, -1);
9954 // Ownership of the stream has transferred to the channel, clear our
9956 aLoadState
->SetPostDataStream(nullptr);
9959 /* If there is a valid postdata *and* it is a History Load,
9960 * set up the cache key on the channel, to retrieve the
9961 * data *only* from the cache. If it is a normal reload, the
9962 * cache is free to go to the server for updated postdata.
9964 if (cacheChannel
&& aCacheKey
!= 0) {
9965 if (loadType
== LOAD_HISTORY
|| loadType
== LOAD_RELOAD_CHARSET_CHANGE
) {
9966 cacheChannel
->SetCacheKey(aCacheKey
);
9968 if (NS_SUCCEEDED(channel
->GetLoadFlags(&loadFlags
))) {
9969 channel
->SetLoadFlags(loadFlags
|
9970 nsICachingChannel::LOAD_ONLY_FROM_CACHE
);
9972 } else if (loadType
== LOAD_RELOAD_NORMAL
) {
9973 cacheChannel
->SetCacheKey(aCacheKey
);
9977 /* If there is no postdata, set the cache key on the channel, and
9978 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
9979 * will be free to get it from net if it is not found in cache.
9980 * New cache may use it creatively on CGI pages with GET
9981 * method and even on those that say "no-cache"
9983 if (loadType
== LOAD_HISTORY
|| loadType
== LOAD_RELOAD_NORMAL
||
9984 loadType
== LOAD_RELOAD_CHARSET_CHANGE
||
9985 loadType
== LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE
||
9986 loadType
== LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE
) {
9987 if (cacheChannel
&& aCacheKey
!= 0) {
9988 cacheChannel
->SetCacheKey(aCacheKey
);
9993 if (nsCOMPtr
<nsIScriptChannel
> scriptChannel
= do_QueryInterface(channel
)) {
9994 // Allow execution against our context if the principals match
9995 scriptChannel
->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL
);
9998 if (nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(channel
)) {
9999 timedChannel
->SetTimingEnabled(true);
10001 nsString initiatorType
;
10002 switch (aLoadInfo
->InternalContentPolicyType()) {
10003 case nsIContentPolicy::TYPE_INTERNAL_EMBED
:
10004 initiatorType
= u
"embed"_ns
;
10006 case nsIContentPolicy::TYPE_INTERNAL_OBJECT
:
10007 initiatorType
= u
"object"_ns
;
10010 const auto& embedderElementType
=
10011 aBrowsingContext
->GetEmbedderElementType();
10012 if (embedderElementType
) {
10013 initiatorType
= *embedderElementType
;
10019 if (!initiatorType
.IsEmpty()) {
10020 timedChannel
->SetInitiatorType(initiatorType
);
10024 nsCOMPtr
<nsIURI
> rpURI
;
10025 aLoadInfo
->GetResultPrincipalURI(getter_AddRefs(rpURI
));
10026 Maybe
<nsCOMPtr
<nsIURI
>> originalResultPrincipalURI
;
10027 aLoadState
->GetMaybeResultPrincipalURI(originalResultPrincipalURI
);
10028 if (originalResultPrincipalURI
&&
10029 (!aLoadState
->KeepResultPrincipalURIIfSet() || !rpURI
)) {
10030 // Unconditionally override, we want the replay to be equal to what has
10032 aLoadInfo
->SetResultPrincipalURI(originalResultPrincipalURI
.ref());
10035 if (aLoadState
->OriginalURI() && aLoadState
->LoadReplace()) {
10036 // The LOAD_REPLACE flag and its handling here will be removed as part
10037 // of bug 1319110. For now preserve its restoration here to not break
10038 // any code expecting it being set specially on redirected channels.
10039 // If the flag has originally been set to change result of
10040 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10042 uint32_t loadFlags
;
10043 aRv
= channel
->GetLoadFlags(&loadFlags
);
10044 NS_ENSURE_SUCCESS(aRv
, false);
10045 channel
->SetLoadFlags(loadFlags
| nsIChannel::LOAD_REPLACE
);
10048 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aLoadState
->Csp();
10050 // Navigational requests that are same origin need to be upgraded in case
10051 // upgrade-insecure-requests is present. Please note that for document
10052 // navigations that bit is re-computed in case we encounter a server
10053 // side redirect so the navigation is not same-origin anymore.
10054 bool upgradeInsecureRequests
= false;
10055 csp
->GetUpgradeInsecureRequests(&upgradeInsecureRequests
);
10056 if (upgradeInsecureRequests
) {
10057 // only upgrade if the navigation is same origin
10058 nsCOMPtr
<nsIPrincipal
> resultPrincipal
;
10059 aRv
= nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10060 channel
, getter_AddRefs(resultPrincipal
));
10061 NS_ENSURE_SUCCESS(aRv
, false);
10062 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10063 aLoadState
->TriggeringPrincipal(), resultPrincipal
)) {
10064 aLoadInfo
->SetUpgradeInsecureRequests(true);
10068 // For document loads we store the CSP that potentially needs to
10069 // be inherited by the new document, e.g. in case we are loading
10070 // an opaque origin like a data: URI. The actual inheritance
10071 // check happens within Document::InitCSP().
10072 // Please create an actual copy of the CSP (do not share the same
10073 // reference) otherwise a Meta CSP of an opaque origin will
10074 // incorrectly be propagated to the embedding document.
10075 RefPtr
<nsCSPContext
> cspToInherit
= new nsCSPContext();
10076 cspToInherit
->InitFromOther(static_cast<nsCSPContext
*>(csp
.get()));
10077 aLoadInfo
->SetCSPToInherit(cspToInherit
);
10080 channel
.forget(aChannel
);
10084 bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
10085 nsIURI
* aURI
, bool aInheritPrincipal
, nsIPrincipal
* aPrincipalToInherit
) {
10086 return NS_IsAboutBlank(aURI
) && aInheritPrincipal
&&
10087 (aPrincipalToInherit
== GetInheritedPrincipal(false)) &&
10088 (!mDocumentViewer
|| !mDocumentViewer
->GetDocument() ||
10089 mDocumentViewer
->GetDocument()->IsInitialDocument());
10092 nsresult
nsDocShell::DoURILoad(nsDocShellLoadState
* aLoadState
,
10093 Maybe
<uint32_t> aCacheKey
,
10094 nsIRequest
** aRequest
) {
10095 // Double-check that we're still around to load this URI.
10096 if (mIsBeingDestroyed
) {
10097 // Return NS_OK despite not doing anything to avoid throwing exceptions
10098 // from nsLocation::SetHref if the unload handler of the existing page
10103 nsCOMPtr
<nsIURILoader
> uriLoader
= components::URILoader::Service();
10104 if (NS_WARN_IF(!uriLoader
)) {
10105 return NS_ERROR_UNEXPECTED
;
10108 // Persist and sync layout history state before we load a new uri, as this
10109 // might be our last chance to do so, in the content process.
10110 PersistLayoutHistoryState();
10111 SynchronizeLayoutHistoryState();
10114 nsContentPolicyType contentPolicyType
= DetermineContentType();
10116 if (IsSubframe()) {
10117 MOZ_ASSERT(contentPolicyType
== nsIContentPolicy::TYPE_INTERNAL_IFRAME
||
10118 contentPolicyType
== nsIContentPolicy::TYPE_INTERNAL_FRAME
,
10119 "DoURILoad thinks this is a frame and InternalLoad does not");
10121 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10122 // Only allow URLs able to return data in iframes.
10123 if (nsContentUtils::IsExternalProtocol(aLoadState
->URI())) {
10124 // The context to check user-interaction with for the purposes of
10127 // We generally want to check the context that initiated the navigation.
10128 WindowContext
* sourceWindowContext
= [&] {
10129 const MaybeDiscardedBrowsingContext
& sourceBC
=
10130 aLoadState
->SourceBrowsingContext();
10131 if (!sourceBC
.IsNullOrDiscarded()) {
10132 if (WindowContext
* wc
= sourceBC
.get()->GetCurrentWindowContext()) {
10136 return mBrowsingContext
->GetParentWindowContext();
10139 MOZ_ASSERT(sourceWindowContext
);
10140 // FIXME: We can't check user-interaction against an OOP window. This is
10141 // the next best thing we can really do. The load state keeps whether
10142 // the navigation had a user interaction in process
10143 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10144 // consume it, which we want to prevent popup-spamming from the same
10146 WindowContext
* context
=
10147 sourceWindowContext
->IsInProcess()
10148 ? sourceWindowContext
10149 : mBrowsingContext
->GetCurrentWindowContext();
10150 const bool popupBlocked
= [&] {
10151 const bool active
= mBrowsingContext
->IsActive();
10153 // For same-origin-with-top windows, we grant a single free popup
10154 // without user activation, see bug 1680721.
10156 // We consume the flag now even if there's no user activation.
10157 const bool hasFreePass
= [&] {
10159 !(context
->IsInProcess() && context
->SameOriginWithTop())) {
10162 nsGlobalWindowInner
* win
=
10163 context
->TopWindowContext()->GetInnerWindow();
10164 return win
&& win
->TryOpenExternalProtocolIframe();
10167 if (context
->IsInProcess() &&
10168 context
->ConsumeTransientUserGestureActivation()) {
10169 // If the user has interacted with the page, consume it.
10173 // TODO(emilio): Can we remove this check? It seems like what prompted
10174 // this code (bug 1514547) should be covered by transient user
10175 // activation, see bug 1514547.
10177 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10181 if (sourceWindowContext
->CanShowPopup()) {
10192 // No error must be returned when iframes are blocked.
10193 if (popupBlocked
) {
10194 nsAutoString message
;
10195 nsresult rv
= nsContentUtils::GetLocalizedString(
10196 nsContentUtils::eDOM_PROPERTIES
,
10197 "ExternalProtocolFrameBlockedNoUserActivation", message
);
10198 if (NS_SUCCEEDED(rv
)) {
10199 nsContentUtils::ReportToConsoleByWindowID(
10200 message
, nsIScriptError::warningFlag
, "DOM"_ns
,
10201 context
->InnerWindowId());
10208 // Only allow view-source scheme in top-level docshells. view-source is
10209 // the only scheme to which this applies at the moment due to potential
10210 // timing attacks to read data from cross-origin iframes. If this widens
10211 // we should add a protocol flag for whether the scheme is allowed in
10212 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10213 nsCOMPtr
<nsIURI
> tempURI
= aLoadState
->URI();
10214 nsCOMPtr
<nsINestedURI
> nestedURI
= do_QueryInterface(tempURI
);
10215 while (nestedURI
) {
10216 // view-source should always be an nsINestedURI, loop and check the
10217 // scheme on this and all inner URIs that are also nested URIs.
10218 if (SchemeIsViewSource(tempURI
)) {
10219 return NS_ERROR_UNKNOWN_PROTOCOL
;
10221 nestedURI
->GetInnerURI(getter_AddRefs(tempURI
));
10222 nestedURI
= do_QueryInterface(tempURI
);
10225 MOZ_ASSERT(contentPolicyType
== nsIContentPolicy::TYPE_DOCUMENT
,
10226 "DoURILoad thinks this is a document and InternalLoad does not");
10229 // We want to inherit aLoadState->PrincipalToInherit() when:
10230 // 1. ChannelShouldInheritPrincipal returns true.
10231 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10232 // configured as unique opaque origin.
10233 bool inheritPrincipal
= false;
10235 if (aLoadState
->PrincipalToInherit()) {
10237 aLoadState
->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC
);
10238 bool inheritAttrs
= nsContentUtils::ChannelShouldInheritPrincipal(
10239 aLoadState
->PrincipalToInherit(), aLoadState
->URI(),
10240 true, // aInheritForAboutBlank
10243 inheritPrincipal
= inheritAttrs
&& !SchemeIsData(aLoadState
->URI());
10246 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
10247 const bool isAboutBlankLoadOntoInitialAboutBlank
=
10248 IsAboutBlankLoadOntoInitialAboutBlank(aLoadState
->URI(), inheritPrincipal
,
10249 aLoadState
->PrincipalToInherit());
10251 // FIXME We still have a ton of codepaths that don't pass through
10252 // DocumentLoadListener, so probably need to create session history info
10254 if (aLoadState
->GetLoadingSessionHistoryInfo()) {
10255 SetLoadingSessionHistoryInfo(*aLoadState
->GetLoadingSessionHistoryInfo());
10256 } else if (isAboutBlankLoadOntoInitialAboutBlank
&&
10257 mozilla::SessionHistoryInParent()) {
10258 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
10259 // loads have it, and later history behavior depends on it existing.
10260 UniquePtr
<SessionHistoryInfo
> entry
= MakeUnique
<SessionHistoryInfo
>(
10261 aLoadState
->URI(), aLoadState
->TriggeringPrincipal(),
10262 aLoadState
->PrincipalToInherit(),
10263 aLoadState
->PartitionedPrincipalToInherit(), aLoadState
->Csp(),
10265 mozilla::dom::LoadingSessionHistoryInfo
info(*entry
);
10266 SetLoadingSessionHistoryInfo(info
, true);
10269 // open a channel for the url
10271 // If we have a pending channel, use the channel we've already created here.
10272 // We don't need to set up load flags for our channel, as it has already been
10275 if (nsCOMPtr
<nsIChannel
> channel
=
10276 aLoadState
->GetPendingRedirectedChannel()) {
10277 // If we have a request outparameter, shove our channel into it.
10279 nsCOMPtr
<nsIRequest
> outRequest
= channel
;
10280 outRequest
.forget(aRequest
);
10283 return OpenRedirectedChannel(aLoadState
);
10286 // There are two cases we care about:
10287 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10288 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10289 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10290 // element for the load. loadingPrincipal is the NodePrincipal of the
10292 nsCOMPtr
<nsINode
> loadingNode
;
10293 nsCOMPtr
<nsPIDOMWindowOuter
> loadingWindow
;
10294 nsCOMPtr
<nsIPrincipal
> loadingPrincipal
;
10295 nsCOMPtr
<nsISupports
> topLevelLoadingContext
;
10297 if (contentPolicyType
== nsIContentPolicy::TYPE_DOCUMENT
) {
10298 loadingNode
= nullptr;
10299 loadingPrincipal
= nullptr;
10300 loadingWindow
= mScriptGlobal
;
10301 if (XRE_IsContentProcess()) {
10302 // In e10s the child process doesn't have access to the element that
10303 // contains the browsing context (because that element is in the chrome
10305 nsCOMPtr
<nsIBrowserChild
> browserChild
= GetBrowserChild();
10306 topLevelLoadingContext
= ToSupports(browserChild
);
10308 // This is for loading non-e10s tabs and toplevel windows of various
10310 // For the toplevel window cases, requestingElement will be null.
10311 nsCOMPtr
<Element
> requestingElement
=
10312 loadingWindow
->GetFrameElementInternal();
10313 topLevelLoadingContext
= requestingElement
;
10316 loadingWindow
= nullptr;
10317 loadingNode
= mScriptGlobal
->GetFrameElementInternal();
10319 // If we have a loading node, then use that as our loadingPrincipal.
10320 loadingPrincipal
= loadingNode
->NodePrincipal();
10322 // Get the docshell type for requestingElement.
10323 RefPtr
<Document
> requestingDoc
= loadingNode
->OwnerDoc();
10324 nsCOMPtr
<nsIDocShell
> elementDocShell
= requestingDoc
->GetDocShell();
10325 // requestingElement docshell type = current docshell type.
10327 mItemType
== elementDocShell
->ItemType(),
10328 "subframes should have the same docshell type as their parent");
10331 if (mIsBeingDestroyed
) {
10332 // If this isn't a top-level load and mScriptGlobal's frame element is
10333 // null, then the element got removed from the DOM while we were trying
10334 // to load this resource. This docshell is scheduled for destruction
10335 // already, so bail out here.
10338 // If we are not being destroyed and we do not have access to the loading
10339 // node, then we are a remote subframe. Set the loading principal
10340 // to be a null principal and then set it correctly in the parent.
10341 loadingPrincipal
= NullPrincipal::Create(GetOriginAttributes(), nullptr);
10345 if (!aLoadState
->TriggeringPrincipal()) {
10346 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10347 return NS_ERROR_FAILURE
;
10350 uint32_t sandboxFlags
= mBrowsingContext
->GetSandboxFlags();
10351 nsSecurityFlags securityFlags
=
10352 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
;
10354 if (mLoadType
== LOAD_ERROR_PAGE
) {
10355 securityFlags
|= nsILoadInfo::SEC_LOAD_ERROR_PAGE
;
10358 if (inheritPrincipal
) {
10359 securityFlags
|= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
;
10362 // Must never have a parent for TYPE_DOCUMENT loads
10363 MOZ_ASSERT_IF(contentPolicyType
== nsIContentPolicy::TYPE_DOCUMENT
,
10364 !mBrowsingContext
->GetParent());
10365 // Subdocuments must have a parent
10366 MOZ_ASSERT_IF(contentPolicyType
== nsIContentPolicy::TYPE_SUBDOCUMENT
,
10367 mBrowsingContext
->GetParent());
10368 mBrowsingContext
->SetTriggeringAndInheritPrincipals(
10369 aLoadState
->TriggeringPrincipal(), aLoadState
->PrincipalToInherit(),
10370 aLoadState
->GetLoadIdentifier());
10371 RefPtr
<LoadInfo
> loadInfo
=
10372 (contentPolicyType
== nsIContentPolicy::TYPE_DOCUMENT
)
10373 ? new LoadInfo(loadingWindow
, aLoadState
->URI(),
10374 aLoadState
->TriggeringPrincipal(),
10375 topLevelLoadingContext
, securityFlags
, sandboxFlags
)
10376 : new LoadInfo(loadingPrincipal
, aLoadState
->TriggeringPrincipal(),
10377 loadingNode
, securityFlags
, contentPolicyType
,
10378 Maybe
<mozilla::dom::ClientInfo
>(),
10379 Maybe
<mozilla::dom::ServiceWorkerDescriptor
>(),
10381 RefPtr
<WindowContext
> context
= mBrowsingContext
->GetCurrentWindowContext();
10383 if (isAboutBlankLoadOntoInitialAboutBlank
) {
10384 // Match the DocumentChannel case where the default for third-partiness
10385 // differs from the default in LoadInfo construction here.
10386 // toolkit/components/antitracking/test/browser/browser_aboutblank.js
10387 // fails without this.
10388 BrowsingContext
* top
= mBrowsingContext
->Top();
10389 if (top
== mBrowsingContext
) {
10390 // If we're at the top, this must be a window.open()ed
10391 // window, and we can't be third-party relative to ourselves.
10392 loadInfo
->SetIsThirdPartyContextToTopWindow(false);
10394 if (Document
* topDoc
= top
->GetDocument()) {
10395 bool thirdParty
= false;
10396 mozilla::Unused
<< topDoc
->GetPrincipal()->IsThirdPartyPrincipal(
10397 aLoadState
->PrincipalToInherit(), &thirdParty
);
10398 loadInfo
->SetIsThirdPartyContextToTopWindow(thirdParty
);
10400 // If top is in a different process, we have to be third-party relative
10402 loadInfo
->SetIsThirdPartyContextToTopWindow(true);
10407 if (mLoadType
!= LOAD_ERROR_PAGE
&& context
&& context
->IsInProcess()) {
10408 if (context
->HasValidTransientUserGestureActivation()) {
10409 aLoadState
->SetHasValidUserGestureActivation(true);
10411 if (!aLoadState
->TriggeringWindowId()) {
10412 aLoadState
->SetTriggeringWindowId(context
->Id());
10414 if (!aLoadState
->TriggeringStorageAccess()) {
10415 Document
* contextDoc
= context
->GetExtantDoc();
10417 aLoadState
->SetTriggeringStorageAccess(
10418 contextDoc
->UsingStorageAccess());
10423 // in case this docshell load was triggered by a valid transient user gesture,
10424 // or also the load originates from external, then we pass that information on
10425 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10426 if (aLoadState
->HasValidUserGestureActivation() ||
10427 aLoadState
->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL
)) {
10428 loadInfo
->SetHasValidUserGestureActivation(true);
10431 loadInfo
->SetTriggeringWindowId(aLoadState
->TriggeringWindowId());
10432 loadInfo
->SetTriggeringStorageAccess(aLoadState
->TriggeringStorageAccess());
10433 loadInfo
->SetTriggeringSandboxFlags(aLoadState
->TriggeringSandboxFlags());
10434 loadInfo
->SetIsMetaRefresh(aLoadState
->IsMetaRefresh());
10436 uint32_t cacheKey
= 0;
10438 cacheKey
= *aCacheKey
;
10439 } else if (mozilla::SessionHistoryInParent()) {
10440 if (mLoadingEntry
) {
10441 cacheKey
= mLoadingEntry
->mInfo
.GetCacheKey();
10442 } else if (mActiveEntry
) { // for reload cases
10443 cacheKey
= mActiveEntry
->GetCacheKey();
10447 cacheKey
= mLSHE
->GetCacheKey();
10448 } else if (mOSHE
) { // for reload cases
10449 cacheKey
= mOSHE
->GetCacheKey();
10454 if (mLSHE
|| mLoadingEntry
) {
10455 if (mLoadingEntry
) {
10456 uriModified
= mLoadingEntry
->mInfo
.GetURIWasModified();
10458 uriModified
= mLSHE
->GetURIWasModified();
10461 uriModified
= false;
10464 bool isEmbeddingBlockedError
= false;
10465 if (mFailedChannel
) {
10467 mFailedChannel
->GetStatus(&status
);
10468 isEmbeddingBlockedError
= status
== NS_ERROR_XFO_VIOLATION
||
10469 status
== NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION
;
10472 nsLoadFlags loadFlags
= aLoadState
->CalculateChannelLoadFlags(
10473 mBrowsingContext
, Some(uriModified
), Some(isEmbeddingBlockedError
));
10475 nsCOMPtr
<nsIChannel
> channel
;
10476 if (DocumentChannel::CanUseDocumentChannel(aLoadState
->URI()) &&
10477 !isAboutBlankLoadOntoInitialAboutBlank
) {
10478 channel
= DocumentChannel::CreateForDocument(
10479 aLoadState
, loadInfo
, loadFlags
, this, cacheKey
, uriModified
,
10480 isEmbeddingBlockedError
);
10481 MOZ_ASSERT(channel
);
10483 // Disable keyword fixup when using DocumentChannel, since
10484 // DocumentLoadListener will handle this for us (in the parent process).
10485 mAllowKeywordFixup
= false;
10486 } else if (!CreateAndConfigureRealChannelForLoadState(
10487 mBrowsingContext
, aLoadState
, loadInfo
, this, this,
10488 GetOriginAttributes(), loadFlags
, cacheKey
, rv
,
10489 getter_AddRefs(channel
))) {
10493 // Make sure to give the caller a channel if we managed to create one
10494 // This is important for correct error page/session history interaction
10496 NS_ADDREF(*aRequest
= channel
);
10499 const nsACString
& typeHint
= aLoadState
->TypeHint();
10500 if (!typeHint
.IsVoid()) {
10501 mContentTypeHint
= typeHint
;
10503 mContentTypeHint
.Truncate();
10506 // Load attributes depend on load type...
10507 if (mLoadType
== LOAD_RELOAD_CHARSET_CHANGE
) {
10508 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10509 // only want to force cache load for this channel, not the whole
10511 nsCOMPtr
<nsICacheInfoChannel
> cachingChannel
= do_QueryInterface(channel
);
10512 if (cachingChannel
) {
10513 cachingChannel
->SetAllowStaleCacheContent(true);
10517 uint32_t openFlags
=
10518 nsDocShell::ComputeURILoaderFlags(mBrowsingContext
, mLoadType
);
10519 return OpenInitializedChannel(channel
, uriLoader
, openFlags
);
10522 static nsresult
AppendSegmentToString(nsIInputStream
* aIn
, void* aClosure
,
10523 const char* aFromRawSegment
,
10524 uint32_t aToOffset
, uint32_t aCount
,
10525 uint32_t* aWriteCount
) {
10526 // aFromSegment now contains aCount bytes of data.
10528 nsAutoCString
* buf
= static_cast<nsAutoCString
*>(aClosure
);
10529 buf
->Append(aFromRawSegment
, aCount
);
10531 // Indicate that we have consumed all of aFromSegment
10532 *aWriteCount
= aCount
;
10536 /* static */ nsresult
nsDocShell::AddHeadersToChannel(
10537 nsIInputStream
* aHeadersData
, nsIChannel
* aGenericChannel
) {
10538 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aGenericChannel
);
10539 NS_ENSURE_STATE(httpChannel
);
10542 nsAutoCString headersString
;
10543 nsresult rv
= aHeadersData
->ReadSegments(
10544 AppendSegmentToString
, &headersString
, UINT32_MAX
, &numRead
);
10545 NS_ENSURE_SUCCESS(rv
, rv
);
10547 // used during the manipulation of the String from the InputStream
10548 nsAutoCString headerName
;
10549 nsAutoCString headerValue
;
10554 // Iterate over the headersString: for each "\r\n" delimited chunk,
10555 // add the value as a header to the nsIHttpChannel
10558 static const char kWhitespace
[] = "\b\t\r\n ";
10560 crlf
= headersString
.Find("\r\n");
10561 if (crlf
== kNotFound
) {
10565 const nsACString
& oneHeader
= StringHead(headersString
, crlf
);
10567 colon
= oneHeader
.FindChar(':');
10568 if (colon
== kNotFound
) {
10569 return NS_ERROR_UNEXPECTED
;
10572 headerName
= StringHead(oneHeader
, colon
);
10573 headerValue
= Substring(oneHeader
, colon
+ 1);
10575 headerName
.Trim(kWhitespace
);
10576 headerValue
.Trim(kWhitespace
);
10578 headersString
.Cut(0, crlf
+ 2);
10581 // FINALLY: we can set the header!
10584 rv
= httpChannel
->SetRequestHeader(headerName
, headerValue
, true);
10585 NS_ENSURE_SUCCESS(rv
, rv
);
10588 MOZ_ASSERT_UNREACHABLE("oops");
10589 return NS_ERROR_UNEXPECTED
;
10592 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10593 BrowsingContext
* aBrowsingContext
, uint32_t aLoadType
) {
10594 MOZ_ASSERT(aBrowsingContext
);
10596 uint32_t openFlags
= 0;
10597 if (aLoadType
== LOAD_LINK
) {
10598 openFlags
|= nsIURILoader::IS_CONTENT_PREFERRED
;
10600 if (!aBrowsingContext
->GetAllowContentRetargeting()) {
10601 openFlags
|= nsIURILoader::DONT_RETARGET
;
10607 nsresult
nsDocShell::OpenInitializedChannel(nsIChannel
* aChannel
,
10608 nsIURILoader
* aURILoader
,
10609 uint32_t aOpenFlags
) {
10610 nsresult rv
= NS_OK
;
10612 // If anything fails here, make sure to clear our initial ClientSource.
10613 auto cleanupInitialClient
=
10614 MakeScopeExit([&] { mInitialClientSource
.reset(); });
10616 nsCOMPtr
<nsPIDOMWindowOuter
> win
= GetWindow();
10617 NS_ENSURE_TRUE(win
, NS_ERROR_FAILURE
);
10619 MaybeCreateInitialClientSource();
10621 // Let the client channel helper know if we are using DocumentChannel,
10622 // since redirects get handled in the parent process in that case.
10623 RefPtr
<net::DocumentChannel
> docChannel
= do_QueryObject(aChannel
);
10624 if (docChannel
&& XRE_IsContentProcess()) {
10625 // Tell the content process nsDocumentOpenInfo to not try to do
10626 // any sort of targeting.
10627 aOpenFlags
|= nsIURILoader::DONT_RETARGET
;
10630 // Since we are loading a document we need to make sure the proper reserved
10631 // and initial client data is stored on the nsILoadInfo. The
10632 // ClientChannelHelper does this and ensures that it is propagated properly
10633 // on redirects. We pass no reserved client here so that the helper will
10634 // create the reserved ClientSource if necessary.
10635 Maybe
<ClientInfo
> noReservedClient
;
10637 // When using DocumentChannel, all redirect handling is done in the parent,
10638 // so we just need the child variant to watch for the internal redirect
10639 // to the final channel.
10640 rv
= AddClientChannelHelperInChild(aChannel
,
10641 GetMainThreadSerialEventTarget());
10642 docChannel
->SetInitialClientInfo(GetInitialClientInfo());
10644 rv
= AddClientChannelHelper(aChannel
, std::move(noReservedClient
),
10645 GetInitialClientInfo(),
10646 GetMainThreadSerialEventTarget());
10648 NS_ENSURE_SUCCESS(rv
, rv
);
10650 rv
= aURILoader
->OpenURI(aChannel
, aOpenFlags
, this);
10651 NS_ENSURE_SUCCESS(rv
, rv
);
10653 // We're about to load a new page and it may take time before necko
10654 // gives back any data, so main thread might have a chance to process a
10656 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL
);
10658 // Success. Keep the initial ClientSource if it exists.
10659 cleanupInitialClient
.release();
10664 nsresult
nsDocShell::OpenRedirectedChannel(nsDocShellLoadState
* aLoadState
) {
10665 nsCOMPtr
<nsIChannel
> channel
= aLoadState
->GetPendingRedirectedChannel();
10666 MOZ_ASSERT(channel
);
10668 // If anything fails here, make sure to clear our initial ClientSource.
10669 auto cleanupInitialClient
=
10670 MakeScopeExit([&] { mInitialClientSource
.reset(); });
10672 nsCOMPtr
<nsPIDOMWindowOuter
> win
= GetWindow();
10673 NS_ENSURE_TRUE(win
, NS_ERROR_FAILURE
);
10675 MaybeCreateInitialClientSource();
10677 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
10679 LoadInfo
* li
= static_cast<LoadInfo
*>(loadInfo
.get());
10680 if (loadInfo
->GetExternalContentPolicyType() ==
10681 ExtContentPolicy::TYPE_DOCUMENT
) {
10682 li
->UpdateBrowsingContextID(mBrowsingContext
->Id());
10683 } else if (loadInfo
->GetExternalContentPolicyType() ==
10684 ExtContentPolicy::TYPE_SUBDOCUMENT
) {
10685 li
->UpdateFrameBrowsingContextID(mBrowsingContext
->Id());
10687 // TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
10689 // If we did a process switch, then we should have an existing allocated
10690 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10691 CreateReservedSourceIfNeeded(channel
, GetMainThreadSerialEventTarget());
10693 RefPtr
<nsDocumentOpenInfo
> loader
=
10694 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET
, nullptr);
10695 channel
->SetLoadGroup(mLoadGroup
);
10697 MOZ_ALWAYS_SUCCEEDS(loader
->Prepare());
10699 nsresult rv
= NS_OK
;
10700 if (XRE_IsParentProcess()) {
10701 // If we're in the parent, the we don't have an nsIChildChannel, just
10702 // the original channel, which is already open in this process.
10704 // DocumentLoadListener expects to get an nsIParentChannel, so
10705 // we create a wrapper around the channel and nsIStreamListener
10706 // that forwards functionality as needed, and then we register
10707 // it under the provided identifier.
10708 RefPtr
<ParentChannelWrapper
> wrapper
=
10709 new ParentChannelWrapper(channel
, loader
);
10710 wrapper
->Register(aLoadState
->GetPendingRedirectChannelRegistrarId());
10712 mLoadGroup
->AddRequest(channel
, nullptr);
10713 } else if (nsCOMPtr
<nsIChildChannel
> childChannel
=
10714 do_QueryInterface(channel
)) {
10715 // Our channel was redirected from another process, so doesn't need to
10716 // be opened again. However, it does need its listener hooked up
10718 rv
= childChannel
->CompleteRedirectSetup(loader
);
10720 // It's possible for the redirected channel to not implement
10721 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10722 // can just open the local instance and it will work.
10723 rv
= channel
->AsyncOpen(loader
);
10725 if (rv
== NS_ERROR_NO_CONTENT
) {
10728 NS_ENSURE_SUCCESS(rv
, rv
);
10730 // Success. Keep the initial ClientSource if it exists.
10731 cleanupInitialClient
.release();
10735 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
10736 nsresult
nsDocShell::ScrollToAnchor(bool aCurHasRef
, bool aNewHasRef
,
10737 nsACString
& aNewHash
, uint32_t aLoadType
) {
10738 if (!mCurrentURI
) {
10742 RefPtr
<PresShell
> presShell
= GetPresShell();
10744 // If we failed to get the shell, or if there is no shell,
10745 // nothing left to do here.
10749 nsIScrollableFrame
* rootScroll
= presShell
->GetRootScrollFrameAsScrollable();
10751 rootScroll
->ClearDidHistoryRestore();
10754 // If we have no new anchor, we do not want to scroll, unless there is a
10755 // current anchor and we are doing a history load. So return if we have no
10756 // new anchor, and there is no current anchor or the load is not a history
10758 if ((!aCurHasRef
|| aLoadType
!= LOAD_HISTORY
) && !aNewHasRef
) {
10762 // Both the new and current URIs refer to the same page. We can now
10763 // browse to the hash stored in the new URI.
10765 // If it's a load from history, we don't have any anchor jumping to do.
10766 // Scrollbar position will be restored by the caller based on positions stored
10767 // in session history.
10768 bool scroll
= aLoadType
!= LOAD_HISTORY
&& aLoadType
!= LOAD_RELOAD_NORMAL
;
10770 if (aNewHash
.IsEmpty()) {
10771 // 2. If fragment is the empty string, then return the special value top of
10774 // Tell the shell it's at an anchor without scrolling.
10775 presShell
->GoToAnchor(u
""_ns
, false);
10778 // Scroll to the top of the page. Ignore the return value; failure to
10779 // scroll here (e.g. if there is no root scrollframe) is not grounds for
10780 // canceling the load!
10781 SetCurScrollPosEx(0, 0);
10787 // 3. Let potentialIndicatedElement be the result of finding a potential
10788 // indicated element given document and fragment.
10789 NS_ConvertUTF8toUTF16
uStr(aNewHash
);
10790 auto rv
= presShell
->GoToAnchor(uStr
, scroll
, ScrollFlags::ScrollSmoothAuto
);
10792 // 4. If potentialIndicatedElement is not null, then return
10793 // potentialIndicatedElement.
10794 if (NS_SUCCEEDED(rv
)) {
10798 // 5. Let fragmentBytes be the result of percent-decoding fragment.
10799 nsAutoCString fragmentBytes
;
10800 const bool unescaped
= NS_UnescapeURL(aNewHash
.Data(), aNewHash
.Length(),
10801 /* aFlags = */ 0, fragmentBytes
);
10804 // Another attempt is only necessary if characters were unescaped.
10808 if (fragmentBytes
.IsEmpty()) {
10809 // When aNewHash contains "%00", the unescaped string may be empty, and
10810 // GoToAnchor asserts if we ask it to scroll to an empty ref.
10811 presShell
->GoToAnchor(u
""_ns
, false);
10815 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
10817 nsAutoString decodedFragment
;
10818 rv
= UTF_8_ENCODING
->DecodeWithoutBOMHandling(fragmentBytes
, decodedFragment
);
10819 NS_ENSURE_SUCCESS(rv
, rv
);
10821 // 7. Set potentialIndicatedElement to the result of finding a potential
10822 // indicated element given document and decodedFragment.
10824 // Ignore the return value of GoToAnchor, since it will return an error if
10825 // there is no such anchor in the document, which is actually a success
10826 // condition for us (we want to update the session history with the new URI no
10827 // matter whether we actually scrolled somewhere).
10828 presShell
->GoToAnchor(decodedFragment
, scroll
, ScrollFlags::ScrollSmoothAuto
);
10833 bool nsDocShell::OnNewURI(nsIURI
* aURI
, nsIChannel
* aChannel
,
10834 nsIPrincipal
* aTriggeringPrincipal
,
10835 nsIPrincipal
* aPrincipalToInherit
,
10836 nsIPrincipal
* aPartitionedPrincipalToInherit
,
10837 nsIContentSecurityPolicy
* aCsp
,
10838 bool aAddToGlobalHistory
, bool aCloneSHChildren
) {
10839 MOZ_ASSERT(aURI
, "uri is null");
10840 MOZ_ASSERT(!aChannel
|| !aTriggeringPrincipal
, "Shouldn't have both set");
10842 MOZ_ASSERT(!aPrincipalToInherit
||
10843 (aPrincipalToInherit
&& aTriggeringPrincipal
));
10846 if (MOZ_LOG_TEST(gDocShellLog
, LogLevel::Debug
)) {
10847 nsAutoCString chanName
;
10849 aChannel
->GetName(chanName
);
10851 chanName
.AssignLiteral("<no channel>");
10854 MOZ_LOG(gDocShellLog
, LogLevel::Debug
,
10855 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10856 aURI
->GetSpecOrDefault().get(), chanName
.get(), mLoadType
));
10860 bool equalUri
= false;
10862 // Get the post data and the HTTP response code from the channel.
10863 uint32_t responseStatus
= 0;
10864 nsCOMPtr
<nsIInputStream
> inputStream
;
10866 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aChannel
));
10868 // Check if the HTTPChannel is hiding under a multiPartChannel
10869 if (!httpChannel
) {
10870 GetHttpChannel(aChannel
, getter_AddRefs(httpChannel
));
10874 nsCOMPtr
<nsIUploadChannel
> uploadChannel(do_QueryInterface(httpChannel
));
10875 if (uploadChannel
) {
10876 uploadChannel
->GetUploadStream(getter_AddRefs(inputStream
));
10879 // If the response status indicates an error, unlink this session
10880 // history entry from any entries sharing its document.
10881 nsresult rv
= httpChannel
->GetResponseStatus(&responseStatus
);
10882 if (mLSHE
&& NS_SUCCEEDED(rv
) && responseStatus
>= 400) {
10883 mLSHE
->AbandonBFCacheEntry();
10884 // FIXME Do the same for mLoadingEntry
10889 // Determine if this type of load should update history.
10890 bool updateGHistory
= ShouldUpdateGlobalHistory(mLoadType
);
10892 // We don't update session history on reload unless we're loading
10893 // an iframe in shift-reload case.
10894 bool updateSHistory
= mBrowsingContext
->ShouldUpdateSessionHistory(mLoadType
);
10896 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10897 // root browsing context.
10898 // FIXME If session history in the parent is enabled then we only do this if
10899 // the session history object is in process, otherwise we can't really
10900 // use the mLSHE anyway. Once session history is only stored in the
10901 // parent then this code will probably be removed anyway.
10902 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
10904 updateSHistory
= false;
10905 updateGHistory
= false; // XXX Why global history too?
10908 // Check if the url to be loaded is the same as the one already loaded.
10910 aURI
->Equals(mCurrentURI
, &equalUri
);
10914 bool shAvailable
= (rootSH
!= nullptr);
10916 // XXX This log message is almost useless because |updateSHistory|
10917 // and |updateGHistory| are not correct at this point.
10919 MOZ_LOG(gDocShellLog
, LogLevel::Debug
,
10920 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10922 shAvailable
, updateSHistory
, updateGHistory
, equalUri
));
10925 /* If the url to be loaded is the same as the one already there,
10926 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10927 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10928 * AddToSessionHistory() won't mess with the current SHEntry and
10929 * if this page has any frame children, it also will be handled
10930 * properly. see bug 83684
10932 * NB: If mOSHE is null but we have a current URI, then it probably
10933 * means that we must be at the transient about:blank content viewer;
10934 * we should let the normal load continue, since there's nothing to
10935 * replace. Sometimes this happens after a session restore (eg process
10936 * switch) and mCurrentURI is not about:blank; we assume we can let the load
10937 * continue (Bug 1301399).
10939 * XXX Hopefully changing the loadType at this time will not hurt
10940 * anywhere. The other way to take care of sequentially repeating
10941 * frameset pages is to add new methods to nsIDocShellTreeItem.
10942 * Hopefully I don't have to do that.
10945 (mozilla::SessionHistoryInParent() ? !!mActiveEntry
: !!mOSHE
) &&
10946 (mLoadType
== LOAD_NORMAL
|| mLoadType
== LOAD_LINK
||
10947 mLoadType
== LOAD_STOP_CONTENT
) &&
10949 mLoadType
= LOAD_NORMAL_REPLACE
;
10952 // If this is a refresh to the currently loaded url, we don't
10953 // have to update session or global history.
10954 if (mLoadType
== LOAD_REFRESH
&& !inputStream
&& equalUri
) {
10955 SetHistoryEntryAndUpdateBC(Some
<nsISHEntry
*>(mOSHE
), Nothing());
10958 /* If the user pressed shift-reload, cache will create a new cache key
10959 * for the page. Save the new cacheKey in Session History.
10962 if (aChannel
&& IsForceReloadType(mLoadType
)) {
10963 MOZ_ASSERT(!updateSHistory
|| IsSubframe(),
10964 "We shouldn't be updating session history for forced"
10965 " reloads unless we're in a newly created iframe!");
10967 nsCOMPtr
<nsICacheInfoChannel
> cacheChannel(do_QueryInterface(aChannel
));
10968 uint32_t cacheKey
= 0;
10969 // Get the Cache Key and store it in SH.
10970 if (cacheChannel
) {
10971 cacheChannel
->GetCacheKey(&cacheKey
);
10973 // If we already have a loading history entry, store the new cache key
10974 // in it. Otherwise, since we're doing a reload and won't be updating
10975 // our history entry, store the cache key in our current history entry.
10976 SetCacheKeyOnHistoryEntry(mLSHE
? mLSHE
: mOSHE
, cacheKey
);
10978 if (!mozilla::SessionHistoryInParent()) {
10979 // Since we're force-reloading, clear all the sub frame history.
10980 ClearFrameHistory(mLSHE
);
10981 ClearFrameHistory(mOSHE
);
10985 if (!mozilla::SessionHistoryInParent()) {
10986 // Clear subframe history on refresh.
10987 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
10988 // this case. One should re-validate after bug 1331865 fixed.
10989 if (mLoadType
== LOAD_REFRESH
) {
10990 ClearFrameHistory(mLSHE
);
10991 ClearFrameHistory(mOSHE
);
10994 if (updateSHistory
) {
10995 // Update session history if necessary...
10996 if (!mLSHE
&& (mItemType
== typeContent
) && mURIResultedInDocument
) {
10997 /* This is a fresh page getting loaded for the first time
10998 *.Create a Entry for it and add it to SH, if this is the
11001 (void)AddToSessionHistory(aURI
, aChannel
, aTriggeringPrincipal
,
11002 aPrincipalToInherit
,
11003 aPartitionedPrincipalToInherit
, aCsp
,
11004 aCloneSHChildren
, getter_AddRefs(mLSHE
));
11006 } else if (GetSessionHistory() && mLSHE
&& mURIResultedInDocument
) {
11007 // Even if we don't add anything to SHistory, ensure the current index
11008 // points to the same SHEntry as our mLSHE.
11010 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11015 // If this is a POST request, we do not want to include this in global
11017 if (ShouldAddURIVisit(aChannel
) && updateGHistory
&& aAddToGlobalHistory
&&
11018 !net::ChannelIsPost(aChannel
)) {
11019 nsCOMPtr
<nsIURI
> previousURI
;
11020 uint32_t previousFlags
= 0;
11022 if (mLoadType
& LOAD_CMD_RELOAD
) {
11023 // On a reload request, we don't set redirecting flags.
11024 previousURI
= aURI
;
11026 ExtractLastVisit(aChannel
, getter_AddRefs(previousURI
), &previousFlags
);
11029 AddURIVisit(aURI
, previousURI
, previousFlags
, responseStatus
);
11032 // If this was a history load or a refresh, or it was a history load but
11033 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11034 // in session history.
11035 if (!mozilla::SessionHistoryInParent() && rootSH
&&
11036 ((mLoadType
& (LOAD_CMD_HISTORY
| LOAD_CMD_RELOAD
)) ||
11037 mLoadType
== LOAD_NORMAL_REPLACE
|| mLoadType
== LOAD_REFRESH_REPLACE
)) {
11038 mPreviousEntryIndex
= rootSH
->Index();
11039 if (!mozilla::SessionHistoryInParent()) {
11040 rootSH
->LegacySHistory()->UpdateIndex();
11042 mLoadedEntryIndex
= rootSH
->Index();
11043 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
11044 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex
,
11045 mLoadedEntryIndex
));
11048 // aCloneSHChildren exactly means "we are not loading a new document".
11049 uint32_t locationFlags
=
11050 aCloneSHChildren
? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT
) : 0;
11052 bool onLocationChangeNeeded
=
11053 SetCurrentURI(aURI
, aChannel
, false,
11054 /* aIsInitialAboutBlank */ false, locationFlags
);
11055 // Make sure to store the referrer from the channel, if any
11056 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aChannel
));
11058 mReferrerInfo
= httpChannel
->GetReferrerInfo();
11060 return onLocationChangeNeeded
;
11063 Maybe
<Wireframe
> nsDocShell::GetWireframe() {
11064 const bool collectWireFrame
=
11065 mozilla::SessionHistoryInParent() &&
11066 StaticPrefs::browser_history_collectWireframes() &&
11067 mBrowsingContext
->IsTopContent() && mActiveEntry
;
11069 if (!collectWireFrame
) {
11073 RefPtr
<Document
> doc
= mDocumentViewer
->GetDocument();
11074 Nullable
<Wireframe
> wireframe
;
11075 doc
->GetWireframeWithoutFlushing(false, wireframe
);
11076 if (wireframe
.IsNull()) {
11079 return Some(wireframe
.Value());
11082 bool nsDocShell::CollectWireframe() {
11083 Maybe
<Wireframe
> wireframe
= GetWireframe();
11084 if (wireframe
.isNothing()) {
11088 if (XRE_IsParentProcess()) {
11089 SessionHistoryEntry
* entry
=
11090 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
11092 entry
->SetWireframe(wireframe
);
11096 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11097 mBrowsingContext
, wireframe
.ref());
11103 //*****************************************************************************
11104 // nsDocShell: Session History
11105 //*****************************************************************************
11108 nsDocShell::AddState(JS::Handle
<JS::Value
> aData
, const nsAString
& aTitle
,
11109 const nsAString
& aURL
, bool aReplace
, JSContext
* aCx
) {
11110 MOZ_LOG(gSHLog
, LogLevel::Debug
,
11111 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11112 NS_ConvertUTF16toUTF8(aTitle
).get(),
11113 NS_ConvertUTF16toUTF8(aURL
).get(), aReplace
));
11114 // Implements History.pushState and History.replaceState
11116 // Here's what we do, roughly in the order specified by HTML5. The specific
11117 // steps we are executing are at
11118 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11120 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11121 // This function basically implements #dom-history-pushstate and
11122 // UpdateURLAndHistory implements #url-and-history-update-steps.
11124 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11126 // B. If the third argument is present, #dom-history-pushstate step 7.
11127 // 7.1. Resolve the url, relative to our document.
11128 // 7.2. If (a) fails, raise a SECURITY_ERR
11129 // 7.4. Compare the resulting absolute URL to the document's address. If
11130 // any part of the URLs difer other than the <path>, <query>, and
11131 // <fragment> components, raise a SECURITY_ERR and abort.
11132 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11133 // Remove from the session history all entries after the current entry,
11134 // as we would after a regular navigation, and save the current
11135 // entry's scroll position (bug 590573).
11136 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11137 // either add a state object entry to the session history after the
11138 // current entry with the following properties, or modify the current
11139 // session history entry to set
11140 // a. cloned data as the state object,
11141 // b. if the third argument was present, the absolute URL found in
11143 // Also clear the new history entry's POST data (see bug 580069).
11144 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11145 // replaceState), notify bfcache that we've navigated to a new page.
11146 // F. If the third argument is present, set the document's current address
11147 // to the absolute URL found in step B. This is
11148 // #url-and-history-update-steps step 4.
11150 // It's important that this function not run arbitrary scripts after step A
11151 // and before completing step E. For example, if a script called
11152 // history.back() before we completed step E, bfcache might destroy an
11153 // active content viewer. Since EvictOutOfRangeDocumentViewers at the end of
11154 // step E might run script, we can't just put a script blocker around the
11155 // critical section.
11157 // Note that we completely ignore the aTitle parameter.
11161 // Don't clobber the load type of an existing network load.
11162 AutoRestore
<uint32_t> loadTypeResetter(mLoadType
);
11164 // pushState effectively becomes replaceState when we've started a network
11165 // load but haven't adopted its document yet. This mirrors what we do with
11166 // changes to the hash at this stage of the game.
11167 if (JustStartedNetworkLoad()) {
11171 RefPtr
<Document
> document
= GetDocument();
11172 NS_ENSURE_TRUE(document
, NS_ERROR_FAILURE
);
11174 // Step A: Serialize aData using structured clone.
11175 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11177 nsCOMPtr
<nsIStructuredCloneContainer
> scContainer
;
11179 // scContainer->Init might cause arbitrary JS to run, and this code might
11180 // navigate the page we're on, potentially to a different origin! (bug
11181 // 634834) To protect against this, we abort if our principal changes due
11182 // to the InitFromJSVal() call.
11184 RefPtr
<Document
> origDocument
= GetDocument();
11185 if (!origDocument
) {
11186 return NS_ERROR_DOM_SECURITY_ERR
;
11188 nsCOMPtr
<nsIPrincipal
> origPrincipal
= origDocument
->NodePrincipal();
11190 scContainer
= new nsStructuredCloneContainer();
11191 rv
= scContainer
->InitFromJSVal(aData
, aCx
);
11192 NS_ENSURE_SUCCESS(rv
, rv
);
11194 RefPtr
<Document
> newDocument
= GetDocument();
11195 if (!newDocument
) {
11196 return NS_ERROR_DOM_SECURITY_ERR
;
11198 nsCOMPtr
<nsIPrincipal
> newPrincipal
= newDocument
->NodePrincipal();
11200 bool principalsEqual
= false;
11201 origPrincipal
->Equals(newPrincipal
, &principalsEqual
);
11202 NS_ENSURE_TRUE(principalsEqual
, NS_ERROR_DOM_SECURITY_ERR
);
11205 // Check that the state object isn't too long.
11206 int32_t maxStateObjSize
= StaticPrefs::browser_history_maxStateObjectSize();
11207 if (maxStateObjSize
< 0) {
11208 maxStateObjSize
= 0;
11212 rv
= scContainer
->GetSerializedNBytes(&scSize
);
11213 NS_ENSURE_SUCCESS(rv
, rv
);
11215 NS_ENSURE_TRUE(scSize
<= (uint32_t)maxStateObjSize
, NS_ERROR_ILLEGAL_VALUE
);
11217 // Step B: Resolve aURL.
11218 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11220 bool equalURIs
= true;
11221 nsCOMPtr
<nsIURI
> currentURI
;
11223 currentURI
= nsIOService::CreateExposableURI(mCurrentURI
);
11225 currentURI
= mCurrentURI
;
11227 nsCOMPtr
<nsIURI
> newURI
;
11228 if (aURL
.Length() == 0) {
11229 newURI
= currentURI
;
11231 // 7.1: Resolve aURL relative to mURI
11233 nsIURI
* docBaseURI
= document
->GetDocBaseURI();
11235 return NS_ERROR_FAILURE
;
11238 nsAutoCString spec
;
11239 docBaseURI
->GetSpec(spec
);
11241 rv
= NS_NewURI(getter_AddRefs(newURI
), aURL
,
11242 document
->GetDocumentCharacterSet(), docBaseURI
);
11244 // 7.2: If 2a fails, raise a SECURITY_ERR
11245 if (NS_FAILED(rv
)) {
11246 return NS_ERROR_DOM_SECURITY_ERR
;
11249 // 7.4 and 7.5: Same-origin check.
11250 if (!nsContentUtils::URIIsLocalFile(newURI
)) {
11251 // In addition to checking that the security manager says that
11252 // the new URI has the same origin as our current URI, we also
11253 // check that the two URIs have the same userpass. (The
11254 // security manager says that |http://foo.com| and
11255 // |http://me@foo.com| have the same origin.) currentURI
11256 // won't contain the password part of the userpass, so this
11257 // means that it's never valid to specify a password in a
11258 // pushState or replaceState URI.
11260 nsCOMPtr
<nsIScriptSecurityManager
> secMan
=
11261 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
);
11262 NS_ENSURE_TRUE(secMan
, NS_ERROR_FAILURE
);
11264 // It's very important that we check that newURI is of the same
11265 // origin as currentURI, not docBaseURI, because a page can
11266 // set docBaseURI arbitrarily to any domain.
11267 nsAutoCString currentUserPass
, newUserPass
;
11268 NS_ENSURE_SUCCESS(currentURI
->GetUserPass(currentUserPass
),
11270 NS_ENSURE_SUCCESS(newURI
->GetUserPass(newUserPass
), NS_ERROR_FAILURE
);
11271 bool isPrivateWin
=
11272 document
->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId
>
11274 if (NS_FAILED(secMan
->CheckSameOriginURI(currentURI
, newURI
, true,
11276 !currentUserPass
.Equals(newUserPass
)) {
11277 return NS_ERROR_DOM_SECURITY_ERR
;
11280 // It's a file:// URI
11281 nsCOMPtr
<nsIPrincipal
> principal
= document
->GetPrincipal();
11283 if (!principal
|| NS_FAILED(principal
->CheckMayLoadWithReporting(
11284 newURI
, false, document
->InnerWindowID()))) {
11285 return NS_ERROR_DOM_SECURITY_ERR
;
11290 currentURI
->Equals(newURI
, &equalURIs
);
11295 } // end of same-origin check
11297 // Step 8: call "URL and history update steps"
11298 rv
= UpdateURLAndHistory(document
, newURI
, scContainer
, aTitle
, aReplace
,
11299 currentURI
, equalURIs
);
11300 NS_ENSURE_SUCCESS(rv
, rv
);
11305 nsresult
nsDocShell::UpdateURLAndHistory(Document
* aDocument
, nsIURI
* aNewURI
,
11306 nsIStructuredCloneContainer
* aData
,
11307 const nsAString
& aTitle
, bool aReplace
,
11308 nsIURI
* aCurrentURI
, bool aEqualURIs
) {
11310 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11312 // If we have a pending title change, handle it before creating a new entry.
11313 aDocument
->DoNotifyPossibleTitleChange();
11315 // Step 2, if aReplace is false: Create a new entry in the session
11316 // history. This will erase all SHEntries after the new entry and make this
11317 // entry the current one. This operation may modify mOSHE, which we need
11318 // later, so we keep a reference here.
11319 NS_ENSURE_TRUE(mOSHE
|| mActiveEntry
|| aReplace
, NS_ERROR_FAILURE
);
11320 nsCOMPtr
<nsISHEntry
> oldOSHE
= mOSHE
;
11322 // If this push/replaceState changed the document's current URI and the new
11323 // URI differs from the old URI in more than the hash, or if the old
11324 // SHEntry's URI was modified in this way by a push/replaceState call
11325 // set URIWasModified to true for the current SHEntry (bug 669671).
11326 bool sameExceptHashes
= true;
11327 aNewURI
->EqualsExceptRef(aCurrentURI
, &sameExceptHashes
);
11328 bool uriWasModified
;
11329 if (sameExceptHashes
) {
11330 if (mozilla::SessionHistoryInParent()) {
11331 uriWasModified
= mActiveEntry
&& mActiveEntry
->GetURIWasModified();
11333 uriWasModified
= oldOSHE
&& oldOSHE
->GetURIWasModified();
11336 uriWasModified
= true;
11339 mLoadType
= LOAD_PUSHSTATE
;
11341 nsCOMPtr
<nsISHEntry
> newSHEntry
;
11345 // Step 2.2, "Remove any tasks queued by the history traversal task
11346 // source that are associated with any Document objects in the
11347 // top-level browsing context's document family." This is very hard in
11348 // SessionHistoryInParent since we can't synchronously access the
11349 // pending navigations that are already sent to the parent. We can
11350 // abort any AsyncGo navigations that are waiting to be sent. If we
11351 // send a message to the parent, it would be processed after any
11352 // navigations previously sent. So long as we consider the "history
11353 // traversal task source" to be the list in this process we match the
11354 // spec. If we move the entire list to the parent, we can handle the
11355 // aborting of loads there, but we don't have a way to synchronously
11356 // remove entries as we do here for non-SHIP.
11357 RefPtr
<ChildSHistory
> shistory
= GetRootSessionHistory();
11359 shistory
->RemovePendingHistoryNavigations();
11362 nsPoint scrollPos
= GetCurScrollPos();
11364 bool scrollRestorationIsManual
;
11365 if (mozilla::SessionHistoryInParent()) {
11366 // FIXME Need to save the current scroll position on mActiveEntry.
11367 scrollRestorationIsManual
= mActiveEntry
->GetScrollRestorationIsManual();
11369 // Save the current scroll position (bug 590573). Step 2.3.
11370 mOSHE
->SetScrollPosition(scrollPos
.x
, scrollPos
.y
);
11372 scrollRestorationIsManual
= mOSHE
->GetScrollRestorationIsManual();
11375 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aDocument
->GetCsp();
11377 if (mozilla::SessionHistoryInParent()) {
11378 MOZ_LOG(gSHLog
, LogLevel::Debug
,
11379 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11380 nsString
title(mActiveEntry
->GetTitle());
11381 UpdateActiveEntry(false,
11382 /* aPreviousScrollPos = */ Some(scrollPos
), aNewURI
,
11383 /* aOriginalURI = */ nullptr,
11384 /* aReferrerInfo = */ nullptr,
11385 /* aTriggeringPrincipal = */ aDocument
->NodePrincipal(),
11386 csp
, title
, scrollRestorationIsManual
, aData
,
11389 // Since we're not changing which page we have loaded, pass
11390 // true for aCloneChildren.
11391 nsresult rv
= AddToSessionHistory(
11393 aDocument
->NodePrincipal(), // triggeringPrincipal
11394 nullptr, nullptr, csp
, true, getter_AddRefs(newSHEntry
));
11395 NS_ENSURE_SUCCESS(rv
, rv
);
11397 NS_ENSURE_TRUE(newSHEntry
, NS_ERROR_FAILURE
);
11399 // Session history entries created by pushState inherit scroll restoration
11400 // mode from the current entry.
11401 newSHEntry
->SetScrollRestorationIsManual(scrollRestorationIsManual
);
11404 mOSHE
->GetTitle(title
);
11406 // Set the new SHEntry's title (bug 655273).
11407 newSHEntry
->SetTitle(title
);
11409 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11410 // two entries correspond to the same document.
11411 NS_ENSURE_SUCCESS(newSHEntry
->AdoptBFCacheEntry(oldOSHE
),
11414 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11415 // we'll just set mOSHE here.
11416 mOSHE
= newSHEntry
;
11418 } else if (mozilla::SessionHistoryInParent()) {
11419 MOZ_LOG(gSHLog
, LogLevel::Debug
,
11420 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11421 this, mActiveEntry
.get()));
11422 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11423 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11424 // in our case. We could also set it to aNewURI, with the same result.
11425 // We don't use aTitle here, see bug 544535.
11427 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
;
11428 if (mActiveEntry
) {
11429 title
= mActiveEntry
->GetTitle();
11430 referrerInfo
= mActiveEntry
->GetReferrerInfo();
11432 referrerInfo
= nullptr;
11435 true, /* aPreviousScrollPos = */ Nothing(), aNewURI
, aNewURI
,
11436 /* aReferrerInfo = */ referrerInfo
, aDocument
->NodePrincipal(),
11437 aDocument
->GetCsp(), title
,
11438 mActiveEntry
&& mActiveEntry
->GetScrollRestorationIsManual(), aData
,
11442 newSHEntry
= mOSHE
;
11444 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("nsDocShell %p step 3", this));
11445 // Since we're not changing which page we have loaded, pass
11446 // true for aCloneChildren.
11448 nsresult rv
= AddToSessionHistory(
11450 aDocument
->NodePrincipal(), // triggeringPrincipal
11451 nullptr, nullptr, aDocument
->GetCsp(), true,
11452 getter_AddRefs(newSHEntry
));
11453 NS_ENSURE_SUCCESS(rv
, rv
);
11454 mOSHE
= newSHEntry
;
11457 newSHEntry
->SetURI(aNewURI
);
11458 newSHEntry
->SetOriginalURI(aNewURI
);
11459 // We replaced the URI of the entry, clear the unstripped URI as it
11460 // shouldn't be used for reloads anymore.
11461 newSHEntry
->SetUnstrippedURI(nullptr);
11462 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11463 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11464 // in our case. We could also set it to aNewURI, with the same result.
11465 newSHEntry
->SetResultPrincipalURI(nullptr);
11466 newSHEntry
->SetLoadReplace(false);
11469 if (!mozilla::SessionHistoryInParent()) {
11470 // Step 2.4 and 3: Modify new/original session history entry and clear its
11471 // POST data, if there is any.
11472 newSHEntry
->SetStateData(aData
);
11473 newSHEntry
->SetPostData(nullptr);
11475 newSHEntry
->SetURIWasModified(uriWasModified
);
11477 // Step E as described at the top of AddState: If aReplace is false,
11478 // indicating that we're doing a pushState rather than a replaceState,
11479 // notify bfcache that we've added a page to the history so it can evict
11480 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11481 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11482 // root session history if this call is coming from a document.open() in a
11483 // docshell subtree that disables session history.
11484 RefPtr
<ChildSHistory
> rootSH
= GetRootSessionHistory();
11486 rootSH
->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry
,
11491 // Step 4: If the document's URI changed, update document's URI and update
11494 // We need to call FireOnLocationChange so that the browser's address bar
11495 // gets updated and the back button is enabled, but we only need to
11496 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11497 // since SetCurrentURI will call FireOnLocationChange for us.
11499 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11500 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11501 // notification is allowed only when we know docshell is not loading a new
11502 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11503 // FireOnLocationChange(...) breaks security UI.
11505 // If the docshell is shutting down, don't update the document URI, as we
11506 // can't load into a docshell that is being destroyed.
11507 if (!aEqualURIs
&& !mIsBeingDestroyed
) {
11508 aDocument
->SetDocumentURI(aNewURI
);
11509 SetCurrentURI(aNewURI
, nullptr, /* aFireLocationChange */ true,
11510 /* aIsInitialAboutBlank */ false,
11511 GetSameDocumentNavigationFlags(aNewURI
));
11513 AddURIVisit(aNewURI
, aCurrentURI
, 0);
11515 // AddURIVisit doesn't set the title for the new URI in global history,
11516 // so do that here.
11517 UpdateGlobalHistoryTitle(aNewURI
);
11519 // Inform the favicon service that our old favicon applies to this new
11521 CopyFavicon(aCurrentURI
, aNewURI
, UsePrivateBrowsing());
11523 FireDummyOnLocationChange();
11525 aDocument
->SetStateObject(aData
);
11531 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual
) {
11532 if (mozilla::SessionHistoryInParent()) {
11533 *aIsManual
= mActiveEntry
&& mActiveEntry
->GetScrollRestorationIsManual();
11537 *aIsManual
= false;
11539 return mOSHE
->GetScrollRestorationIsManual(aIsManual
);
11546 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual
) {
11547 SetScrollRestorationIsManualOnHistoryEntry(mOSHE
, aIsManual
);
11552 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11553 nsISHEntry
* aSHEntry
, bool aIsManual
) {
11555 aSHEntry
->SetScrollRestorationIsManual(aIsManual
);
11558 if (mActiveEntry
&& mBrowsingContext
) {
11559 mActiveEntry
->SetScrollRestorationIsManual(aIsManual
);
11560 if (XRE_IsParentProcess()) {
11561 SessionHistoryEntry
* entry
=
11562 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
11564 entry
->SetScrollRestorationIsManual(aIsManual
);
11567 mozilla::Unused
<< ContentChild::GetSingleton()
11568 ->SendSessionHistoryEntryScrollRestorationIsManual(
11569 mBrowsingContext
, aIsManual
);
11574 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry
* aSHEntry
,
11575 uint32_t aCacheKey
) {
11577 aSHEntry
->SetCacheKey(aCacheKey
);
11580 if (mActiveEntry
&& mBrowsingContext
) {
11581 mActiveEntry
->SetCacheKey(aCacheKey
);
11582 if (XRE_IsParentProcess()) {
11583 SessionHistoryEntry
* entry
=
11584 mBrowsingContext
->Canonical()->GetActiveSessionHistoryEntry();
11586 entry
->SetCacheKey(aCacheKey
);
11590 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11591 mBrowsingContext
, aCacheKey
);
11597 bool nsDocShell::ShouldAddToSessionHistory(nsIURI
* aURI
, nsIChannel
* aChannel
) {
11598 // I believe none of the about: urls should go in the history. But then
11599 // that could just be me... If the intent is only deny about:blank then we
11600 // should just do a spec compare, rather than two gets of the scheme and
11601 // then the path. -Gagan
11605 rv
= aURI
->GetScheme(buf
);
11606 if (NS_FAILED(rv
)) {
11610 if (buf
.EqualsLiteral("about")) {
11611 rv
= aURI
->GetPathQueryRef(buf
);
11612 if (NS_FAILED(rv
)) {
11616 if (buf
.EqualsLiteral("blank")) {
11619 // We only want to add about:newtab if it's not privileged, and
11620 // if it is not configured to show the blank page.
11621 if (buf
.EqualsLiteral("newtab")) {
11622 if (!StaticPrefs::browser_newtabpage_enabled()) {
11626 NS_ENSURE_TRUE(aChannel
, false);
11627 nsCOMPtr
<nsIPrincipal
> resultPrincipal
;
11628 rv
= nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11629 aChannel
, getter_AddRefs(resultPrincipal
));
11630 NS_ENSURE_SUCCESS(rv
, false);
11631 return !resultPrincipal
->IsSystemPrincipal();
11638 nsresult
nsDocShell::AddToSessionHistory(
11639 nsIURI
* aURI
, nsIChannel
* aChannel
, nsIPrincipal
* aTriggeringPrincipal
,
11640 nsIPrincipal
* aPrincipalToInherit
,
11641 nsIPrincipal
* aPartitionedPrincipalToInherit
,
11642 nsIContentSecurityPolicy
* aCsp
, bool aCloneChildren
,
11643 nsISHEntry
** aNewEntry
) {
11644 MOZ_ASSERT(aURI
, "uri is null");
11645 MOZ_ASSERT(!aChannel
|| !aTriggeringPrincipal
, "Shouldn't have both set");
11646 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11649 if (MOZ_LOG_TEST(gDocShellLog
, LogLevel::Debug
)) {
11650 nsAutoCString chanName
;
11652 aChannel
->GetName(chanName
);
11654 chanName
.AssignLiteral("<no channel>");
11657 MOZ_LOG(gDocShellLog
, LogLevel::Debug
,
11658 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11659 aURI
->GetSpecOrDefault().get(), chanName
.get()));
11663 nsresult rv
= NS_OK
;
11664 nsCOMPtr
<nsISHEntry
> entry
;
11667 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11668 * the existing SH entry in the page and replace the url and
11669 * other vitalities.
11671 if (LOAD_TYPE_HAS_FLAGS(mLoadType
, LOAD_FLAGS_REPLACE_HISTORY
) &&
11672 !mBrowsingContext
->IsTop()) {
11673 // This is a subframe
11676 entry
->ClearEntry();
11680 // Create a new entry if necessary.
11682 entry
= new nsSHEntry();
11685 // Get the post data & referrer
11686 nsCOMPtr
<nsIInputStream
> inputStream
;
11687 nsCOMPtr
<nsIURI
> originalURI
;
11688 nsCOMPtr
<nsIURI
> resultPrincipalURI
;
11689 nsCOMPtr
<nsIURI
> unstrippedURI
;
11690 bool loadReplace
= false;
11691 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
;
11692 uint32_t cacheKey
= 0;
11693 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
= aTriggeringPrincipal
;
11694 nsCOMPtr
<nsIPrincipal
> principalToInherit
= aPrincipalToInherit
;
11695 nsCOMPtr
<nsIPrincipal
> partitionedPrincipalToInherit
=
11696 aPartitionedPrincipalToInherit
;
11697 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aCsp
;
11698 bool expired
= false; // by default the page is not expired
11699 bool discardLayoutState
= false;
11700 nsCOMPtr
<nsICacheInfoChannel
> cacheChannel
;
11701 bool userActivation
= false;
11704 cacheChannel
= do_QueryInterface(aChannel
);
11706 /* If there is a caching channel, get the Cache Key and store it
11709 if (cacheChannel
) {
11710 cacheChannel
->GetCacheKey(&cacheKey
);
11712 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aChannel
));
11714 // Check if the httpChannel is hiding under a multipartChannel
11715 if (!httpChannel
) {
11716 GetHttpChannel(aChannel
, getter_AddRefs(httpChannel
));
11719 nsCOMPtr
<nsIUploadChannel
> uploadChannel(do_QueryInterface(httpChannel
));
11720 if (uploadChannel
) {
11721 uploadChannel
->GetUploadStream(getter_AddRefs(inputStream
));
11723 httpChannel
->GetOriginalURI(getter_AddRefs(originalURI
));
11724 uint32_t loadFlags
;
11725 aChannel
->GetLoadFlags(&loadFlags
);
11726 loadReplace
= loadFlags
& nsIChannel::LOAD_REPLACE
;
11727 rv
= httpChannel
->GetReferrerInfo(getter_AddRefs(referrerInfo
));
11728 MOZ_ASSERT(NS_SUCCEEDED(rv
));
11730 discardLayoutState
= ShouldDiscardLayoutState(httpChannel
);
11733 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
11734 if (!triggeringPrincipal
) {
11735 triggeringPrincipal
= loadInfo
->TriggeringPrincipal();
11738 csp
= loadInfo
->GetCspToInherit();
11741 loadInfo
->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI
));
11743 loadInfo
->GetUnstrippedURI(getter_AddRefs(unstrippedURI
));
11745 userActivation
= loadInfo
->GetHasValidUserGestureActivation();
11747 // For now keep storing just the principal in the SHEntry.
11748 if (!principalToInherit
) {
11749 if (loadInfo
->GetLoadingSandboxed()) {
11750 if (loadInfo
->GetLoadingPrincipal()) {
11751 principalToInherit
= NullPrincipal::CreateWithInheritedAttributes(
11752 loadInfo
->GetLoadingPrincipal());
11754 // get the OriginAttributes
11755 OriginAttributes attrs
;
11756 loadInfo
->GetOriginAttributes(&attrs
);
11757 principalToInherit
= NullPrincipal::Create(attrs
);
11760 principalToInherit
= loadInfo
->PrincipalToInherit();
11764 if (!partitionedPrincipalToInherit
) {
11765 // XXXehsan is it correct to fall back to the principal to inherit in all
11766 // cases? For example, what about the cases where we are using the load
11767 // info's principal to inherit? Do we need to add a similar concept to
11768 // load info for partitioned principal?
11769 partitionedPrincipalToInherit
= principalToInherit
;
11773 nsAutoString srcdoc
;
11774 bool srcdocEntry
= false;
11775 nsCOMPtr
<nsIURI
> baseURI
;
11777 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(aChannel
);
11779 bool isSrcdocChannel
;
11780 inStrmChan
->GetIsSrcdocChannel(&isSrcdocChannel
);
11781 if (isSrcdocChannel
) {
11782 inStrmChan
->GetSrcdocData(srcdoc
);
11783 srcdocEntry
= true;
11784 inStrmChan
->GetBaseURI(getter_AddRefs(baseURI
));
11786 srcdoc
.SetIsVoid(true);
11789 /* If cache got a 'no-store', ask SH not to store
11790 * HistoryLayoutState. By default, SH will set this
11791 * flag to true and save HistoryLayoutState.
11793 bool saveLayoutState
= !discardLayoutState
;
11795 if (cacheChannel
) {
11796 // Check if the page has expired from cache
11797 uint32_t expTime
= 0;
11798 cacheChannel
->GetCacheTokenExpirationTime(&expTime
);
11799 uint32_t now
= PRTimeToSeconds(PR_Now());
11800 if (expTime
<= now
) {
11805 // Title is set in nsDocShell::SetTitle()
11806 entry
->Create(aURI
, // uri
11808 inputStream
, // Post data stream
11809 cacheKey
, // CacheKey
11810 mContentTypeHint
, // Content-type
11811 triggeringPrincipal
, // Channel or provided principal
11812 principalToInherit
, partitionedPrincipalToInherit
, csp
,
11813 HistoryID(), GetCreatedDynamically(), originalURI
,
11814 resultPrincipalURI
, unstrippedURI
, loadReplace
, referrerInfo
,
11815 srcdoc
, srcdocEntry
, baseURI
, saveLayoutState
, expired
,
11818 if (mBrowsingContext
->IsTop() && GetSessionHistory()) {
11819 bool shouldPersist
= ShouldAddToSessionHistory(aURI
, aChannel
);
11820 Maybe
<int32_t> previousEntryIndex
;
11821 Maybe
<int32_t> loadedEntryIndex
;
11822 rv
= GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11823 aCloneChildren
, mOSHE
, mBrowsingContext
, entry
, mLoadType
,
11824 shouldPersist
, &previousEntryIndex
, &loadedEntryIndex
);
11826 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Could not add entry to root session history");
11827 if (previousEntryIndex
.isSome()) {
11828 mPreviousEntryIndex
= previousEntryIndex
.value();
11830 if (loadedEntryIndex
.isSome()) {
11831 mLoadedEntryIndex
= loadedEntryIndex
.value();
11834 // aCloneChildren implies that we are retaining the same document, thus we
11835 // need to signal to the top WC that the new SHEntry may receive a fresh
11836 // user interaction flag.
11837 if (aCloneChildren
) {
11838 WindowContext
* topWc
= mBrowsingContext
->GetTopWindowContext();
11839 if (topWc
&& !topWc
->IsDiscarded()) {
11840 MOZ_ALWAYS_SUCCEEDS(topWc
->SetSHEntryHasUserInteraction(false));
11844 // This is a subframe, make sure that this new SHEntry will be
11845 // marked with user interaction.
11846 WindowContext
* topWc
= mBrowsingContext
->GetTopWindowContext();
11847 if (topWc
&& !topWc
->IsDiscarded()) {
11848 MOZ_ALWAYS_SUCCEEDS(topWc
->SetSHEntryHasUserInteraction(false));
11850 if (!mOSHE
|| !LOAD_TYPE_HAS_FLAGS(mLoadType
, LOAD_FLAGS_REPLACE_HISTORY
)) {
11851 rv
= AddChildSHEntryToParent(entry
, mBrowsingContext
->ChildOffset(),
11856 // Return the new SH entry...
11858 *aNewEntry
= nullptr;
11859 if (NS_SUCCEEDED(rv
)) {
11860 entry
.forget(aNewEntry
);
11867 void nsDocShell::UpdateActiveEntry(
11868 bool aReplace
, const Maybe
<nsPoint
>& aPreviousScrollPos
, nsIURI
* aURI
,
11869 nsIURI
* aOriginalURI
, nsIReferrerInfo
* aReferrerInfo
,
11870 nsIPrincipal
* aTriggeringPrincipal
, nsIContentSecurityPolicy
* aCsp
,
11871 const nsAString
& aTitle
, bool aScrollRestorationIsManual
,
11872 nsIStructuredCloneContainer
* aData
, bool aURIWasModified
) {
11873 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11874 MOZ_ASSERT(aURI
, "uri is null");
11875 MOZ_ASSERT(mLoadType
== LOAD_PUSHSTATE
,
11876 "This code only deals with pushState");
11877 MOZ_ASSERT_IF(aPreviousScrollPos
.isSome(), !aReplace
);
11879 MOZ_LOG(gSHLog
, LogLevel::Debug
,
11880 ("Creating an active entry on nsDocShell %p to %s", this,
11881 aURI
->GetSpecOrDefault().get()));
11883 // Even if we're replacing an existing entry we create new a
11884 // SessionHistoryInfo. In the parent process we'll keep the existing
11885 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11886 // entry keeps identity but its data is replaced.
11887 bool replace
= aReplace
&& mActiveEntry
;
11890 CollectWireframe();
11893 if (mActiveEntry
) {
11894 // Link this entry to the previous active entry.
11895 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(*mActiveEntry
, aURI
);
11897 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(
11898 aURI
, aTriggeringPrincipal
, nullptr, nullptr, aCsp
, mContentTypeHint
);
11900 mActiveEntry
->SetOriginalURI(aOriginalURI
);
11901 mActiveEntry
->SetUnstrippedURI(nullptr);
11902 mActiveEntry
->SetReferrerInfo(aReferrerInfo
);
11903 mActiveEntry
->SetTitle(aTitle
);
11904 mActiveEntry
->SetStateData(static_cast<nsStructuredCloneContainer
*>(aData
));
11905 mActiveEntry
->SetURIWasModified(aURIWasModified
);
11906 mActiveEntry
->SetScrollRestorationIsManual(aScrollRestorationIsManual
);
11909 mBrowsingContext
->ReplaceActiveSessionHistoryEntry(mActiveEntry
.get());
11911 mBrowsingContext
->IncrementHistoryEntryCountForBrowsingContext();
11912 // FIXME We should probably just compute mChildOffset in the parent
11913 // instead of passing it over IPC here.
11914 mBrowsingContext
->SetActiveSessionHistoryEntry(
11915 aPreviousScrollPos
, mActiveEntry
.get(), mLoadType
,
11916 /* aCacheKey = */ 0);
11917 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
11921 nsresult
nsDocShell::LoadHistoryEntry(nsISHEntry
* aEntry
, uint32_t aLoadType
,
11922 bool aUserActivation
) {
11923 NS_ENSURE_TRUE(aEntry
, NS_ERROR_FAILURE
);
11926 RefPtr
<nsDocShellLoadState
> loadState
;
11927 rv
= aEntry
->CreateLoadInfo(getter_AddRefs(loadState
));
11928 NS_ENSURE_SUCCESS(rv
, rv
);
11930 // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if
11931 // that's the only thing holding a ref to aEntry that will cause aEntry to
11932 // die while we're loading it. So hold a strong ref to aEntry here, just
11934 nsCOMPtr
<nsISHEntry
> kungFuDeathGrip(aEntry
);
11936 loadState
->SetHasValidUserGestureActivation(
11937 loadState
->HasValidUserGestureActivation() || aUserActivation
);
11939 return LoadHistoryEntry(loadState
, aLoadType
, aEntry
== mOSHE
);
11942 nsresult
nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo
& aEntry
,
11943 uint32_t aLoadType
,
11944 bool aUserActivation
) {
11945 RefPtr
<nsDocShellLoadState
> loadState
= aEntry
.CreateLoadInfo();
11946 loadState
->SetHasValidUserGestureActivation(
11947 loadState
->HasValidUserGestureActivation() || aUserActivation
);
11949 return LoadHistoryEntry(loadState
, aLoadType
, aEntry
.mLoadingCurrentEntry
);
11952 nsresult
nsDocShell::LoadHistoryEntry(nsDocShellLoadState
* aLoadState
,
11953 uint32_t aLoadType
,
11954 bool aLoadingCurrentEntry
) {
11955 if (!IsNavigationAllowed()) {
11959 // We are setting load type afterwards so we don't have to
11960 // send it in an IPC message
11961 aLoadState
->SetLoadType(aLoadType
);
11964 if (SchemeIsJavascript(aLoadState
->URI())) {
11965 // We're loading a URL that will execute script from inside asyncOpen.
11966 // Replace the current document with about:blank now to prevent
11967 // anything from the current document from leaking into any JavaScript
11968 // code in the URL.
11969 // Don't cache the presentation if we're going to just reload the
11970 // current entry. Caching would lead to trying to save the different
11971 // content viewers in the same nsISHEntry object.
11972 rv
= CreateAboutBlankDocumentViewer(
11973 aLoadState
->PrincipalToInherit(),
11974 aLoadState
->PartitionedPrincipalToInherit(), nullptr, nullptr,
11975 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry
);
11977 if (NS_FAILED(rv
)) {
11978 // The creation of the intermittent about:blank content
11979 // viewer failed for some reason (potentially because the
11980 // user prevented it). Interrupt the history load.
11984 if (!aLoadState
->TriggeringPrincipal()) {
11985 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
11986 // URIs will pick it up from the about:blank page we just loaded,
11987 // and we don't really want even that in this case.
11988 nsCOMPtr
<nsIPrincipal
> principal
=
11989 NullPrincipal::Create(GetOriginAttributes());
11990 aLoadState
->SetTriggeringPrincipal(principal
);
11994 /* If there is a valid postdata *and* the user pressed
11995 * reload or shift-reload, take user's permission before we
11996 * repost the data to the server.
11998 if ((aLoadType
& LOAD_CMD_RELOAD
) && aLoadState
->PostDataStream()) {
12000 rv
= ConfirmRepost(&repost
);
12001 if (NS_FAILED(rv
)) {
12005 // If the user pressed cancel in the dialog, return. We're done here.
12007 return NS_BINDING_ABORTED
;
12011 // If there is no valid triggeringPrincipal, we deny the load
12012 MOZ_ASSERT(aLoadState
->TriggeringPrincipal(),
12013 "need a valid triggeringPrincipal to load from history");
12014 if (!aLoadState
->TriggeringPrincipal()) {
12015 return NS_ERROR_FAILURE
;
12018 return InternalLoad(aLoadState
); // No nsIRequest
12022 nsDocShell::PersistLayoutHistoryState() {
12023 nsresult rv
= NS_OK
;
12025 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry
: !!mOSHE
) {
12026 bool scrollRestorationIsManual
;
12027 if (mozilla::SessionHistoryInParent()) {
12028 scrollRestorationIsManual
= mActiveEntry
->GetScrollRestorationIsManual();
12030 scrollRestorationIsManual
= mOSHE
->GetScrollRestorationIsManual();
12032 nsCOMPtr
<nsILayoutHistoryState
> layoutState
;
12033 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
12034 rv
= presShell
->CaptureHistoryState(getter_AddRefs(layoutState
));
12035 } else if (scrollRestorationIsManual
) {
12036 // Even if we don't have layout anymore, we may want to reset the
12037 // current scroll state in layout history.
12038 GetLayoutHistoryState(getter_AddRefs(layoutState
));
12041 if (scrollRestorationIsManual
&& layoutState
) {
12042 layoutState
->ResetScrollState();
12049 void nsDocShell::SwapHistoryEntries(nsISHEntry
* aOldEntry
,
12050 nsISHEntry
* aNewEntry
) {
12051 if (aOldEntry
== mOSHE
) {
12055 if (aOldEntry
== mLSHE
) {
12060 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe
<nsISHEntry
*>& aLSHE
,
12061 const Maybe
<nsISHEntry
*>& aOSHE
) {
12062 // We want to hold on to the reference in mLSHE before we update it.
12063 // Otherwise, SetHistoryEntry could release the last reference to
12064 // the entry while aOSHE is pointing to it.
12065 nsCOMPtr
<nsISHEntry
> deathGripOldLSHE
;
12066 if (aLSHE
.isSome()) {
12067 deathGripOldLSHE
= SetHistoryEntry(&mLSHE
, aLSHE
.value());
12068 MOZ_ASSERT(mLSHE
.get() == aLSHE
.value());
12070 nsCOMPtr
<nsISHEntry
> deathGripOldOSHE
;
12071 if (aOSHE
.isSome()) {
12072 deathGripOldOSHE
= SetHistoryEntry(&mOSHE
, aOSHE
.value());
12073 MOZ_ASSERT(mOSHE
.get() == aOSHE
.value());
12077 already_AddRefed
<nsISHEntry
> nsDocShell::SetHistoryEntry(
12078 nsCOMPtr
<nsISHEntry
>* aPtr
, nsISHEntry
* aEntry
) {
12079 // We need to sync up the docshell and session history trees for
12080 // subframe navigation. If the load was in a subframe, we forward up to
12081 // the root docshell, which will then recursively sync up all docshells
12082 // to their corresponding entries in the new session history tree.
12083 // If we don't do this, then we can cache a content viewer on the wrong
12084 // cloned entry, and subsequently restore it at the wrong time.
12085 RefPtr
<BrowsingContext
> topBC
= mBrowsingContext
->Top();
12086 if (topBC
->IsDiscarded()) {
12089 RefPtr
<BrowsingContext
> currBC
=
12090 mBrowsingContext
->IsDiscarded() ? nullptr : mBrowsingContext
;
12091 if (topBC
&& *aPtr
) {
12092 (*aPtr
)->SyncTreesForSubframeNavigation(aEntry
, topBC
, currBC
);
12094 nsCOMPtr
<nsISHEntry
> entry(aEntry
);
12096 return entry
.forget();
12099 already_AddRefed
<ChildSHistory
> nsDocShell::GetRootSessionHistory() {
12100 RefPtr
<ChildSHistory
> childSHistory
=
12101 mBrowsingContext
->Top()->GetChildSessionHistory();
12102 return childSHistory
.forget();
12105 nsresult
nsDocShell::GetHttpChannel(nsIChannel
* aChannel
,
12106 nsIHttpChannel
** aReturn
) {
12107 NS_ENSURE_ARG_POINTER(aReturn
);
12109 return NS_ERROR_FAILURE
;
12112 nsCOMPtr
<nsIMultiPartChannel
> multiPartChannel(do_QueryInterface(aChannel
));
12113 if (multiPartChannel
) {
12114 nsCOMPtr
<nsIChannel
> baseChannel
;
12115 multiPartChannel
->GetBaseChannel(getter_AddRefs(baseChannel
));
12116 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(baseChannel
));
12117 *aReturn
= httpChannel
;
12118 NS_IF_ADDREF(*aReturn
);
12123 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel
* aChannel
) {
12124 // By default layout State will be saved.
12129 // figure out if SH should be saving layout state
12130 bool noStore
= false;
12131 Unused
<< aChannel
->IsNoStoreResponse(&noStore
);
12136 nsDocShell::GetEditor(nsIEditor
** aEditor
) {
12137 NS_ENSURE_ARG_POINTER(aEditor
);
12138 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditorInternal();
12139 htmlEditor
.forget(aEditor
);
12144 nsDocShell::SetEditor(nsIEditor
* aEditor
) {
12145 HTMLEditor
* htmlEditor
= aEditor
? aEditor
->GetAsHTMLEditor() : nullptr;
12146 // If TextEditor comes, throw an error.
12147 if (aEditor
&& !htmlEditor
) {
12148 return NS_ERROR_INVALID_ARG
;
12150 return SetHTMLEditorInternal(htmlEditor
);
12153 HTMLEditor
* nsDocShell::GetHTMLEditorInternal() {
12154 return mEditorData
? mEditorData
->GetHTMLEditor() : nullptr;
12157 nsresult
nsDocShell::SetHTMLEditorInternal(HTMLEditor
* aHTMLEditor
) {
12158 if (!aHTMLEditor
&& !mEditorData
) {
12162 nsresult rv
= EnsureEditorData();
12163 if (NS_FAILED(rv
)) {
12167 return mEditorData
->SetHTMLEditor(aHTMLEditor
);
12171 nsDocShell::GetEditable(bool* aEditable
) {
12172 NS_ENSURE_ARG_POINTER(aEditable
);
12173 *aEditable
= mEditorData
&& mEditorData
->GetEditable();
12178 nsDocShell::GetHasEditingSession(bool* aHasEditingSession
) {
12179 NS_ENSURE_ARG_POINTER(aHasEditingSession
);
12182 *aHasEditingSession
= !!mEditorData
->GetEditingSession();
12184 *aHasEditingSession
= false;
12191 nsDocShell::MakeEditable(bool aInWaitForUriLoad
) {
12192 nsresult rv
= EnsureEditorData();
12193 if (NS_FAILED(rv
)) {
12197 return mEditorData
->MakeEditable(aInWaitForUriLoad
);
12200 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel
* aChannel
) {
12201 bool needToAddURIVisit
= true;
12202 nsCOMPtr
<nsIPropertyBag2
> props(do_QueryInterface(aChannel
));
12204 mozilla::Unused
<< props
->GetPropertyAsBool(
12205 u
"docshell.needToAddURIVisit"_ns
, &needToAddURIVisit
);
12208 return needToAddURIVisit
;
12211 /* static */ void nsDocShell::ExtractLastVisit(
12212 nsIChannel
* aChannel
, nsIURI
** aURI
, uint32_t* aChannelRedirectFlags
) {
12213 nsCOMPtr
<nsIPropertyBag2
> props(do_QueryInterface(aChannel
));
12219 nsCOMPtr
<nsIURI
> uri(do_GetProperty(props
, u
"docshell.previousURI"_ns
, &rv
));
12220 if (NS_SUCCEEDED(rv
)) {
12223 rv
= props
->GetPropertyAsUint32(u
"docshell.previousFlags"_ns
,
12224 aChannelRedirectFlags
);
12226 NS_WARNING_ASSERTION(
12228 "Could not fetch previous flags, URI will be treated like referrer");
12231 // There is no last visit for this channel, so this must be the first
12232 // link. Link the visit to the referrer of this request, if any.
12233 // Treat referrer as null if there is an error getting it.
12234 NS_GetReferrerFromChannel(aChannel
, aURI
);
12238 void nsDocShell::SaveLastVisit(nsIChannel
* aChannel
, nsIURI
* aURI
,
12239 uint32_t aChannelRedirectFlags
) {
12240 nsCOMPtr
<nsIWritablePropertyBag2
> props(do_QueryInterface(aChannel
));
12241 if (!props
|| !aURI
) {
12245 props
->SetPropertyAsInterface(u
"docshell.previousURI"_ns
, aURI
);
12246 props
->SetPropertyAsUint32(u
"docshell.previousFlags"_ns
,
12247 aChannelRedirectFlags
);
12250 /* static */ void nsDocShell::InternalAddURIVisit(
12251 nsIURI
* aURI
, nsIURI
* aPreviousURI
, uint32_t aChannelRedirectFlags
,
12252 uint32_t aResponseStatus
, BrowsingContext
* aBrowsingContext
,
12253 nsIWidget
* aWidget
, uint32_t aLoadType
, bool aWasUpgraded
) {
12254 MOZ_ASSERT(aURI
, "Visited URI is null!");
12255 MOZ_ASSERT(aLoadType
!= LOAD_ERROR_PAGE
&& aLoadType
!= LOAD_BYPASS_HISTORY
,
12256 "Do not add error or bypass pages to global history");
12258 bool usePrivateBrowsing
= false;
12259 aBrowsingContext
->GetUsePrivateBrowsing(&usePrivateBrowsing
);
12261 // Only content-type docshells save URI visits. Also don't do
12262 // anything here if we're not supposed to use global history.
12263 if (!aBrowsingContext
->IsContent() ||
12264 !aBrowsingContext
->GetUseGlobalHistory() || usePrivateBrowsing
) {
12268 nsCOMPtr
<IHistory
> history
= components::History::Service();
12271 uint32_t visitURIFlags
= 0;
12273 if (aBrowsingContext
->IsTop()) {
12274 visitURIFlags
|= IHistory::TOP_LEVEL
;
12277 if (aChannelRedirectFlags
& nsIChannelEventSink::REDIRECT_TEMPORARY
) {
12278 visitURIFlags
|= IHistory::REDIRECT_TEMPORARY
;
12279 } else if (aChannelRedirectFlags
&
12280 nsIChannelEventSink::REDIRECT_PERMANENT
) {
12281 visitURIFlags
|= IHistory::REDIRECT_PERMANENT
;
12283 MOZ_ASSERT(!aChannelRedirectFlags
,
12284 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12285 "if any flags in aChannelRedirectFlags is set.");
12288 if (aResponseStatus
>= 300 && aResponseStatus
< 400) {
12289 visitURIFlags
|= IHistory::REDIRECT_SOURCE
;
12290 if (aResponseStatus
== 301 || aResponseStatus
== 308) {
12291 visitURIFlags
|= IHistory::REDIRECT_SOURCE_PERMANENT
;
12294 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12295 // simple retry attempt by the user is unlikely to solve them.
12296 // 408 is special cased, since may actually indicate a temporary
12297 // connection problem.
12298 else if (aResponseStatus
!= 408 &&
12299 ((aResponseStatus
>= 400 && aResponseStatus
<= 501) ||
12300 aResponseStatus
== 505)) {
12301 visitURIFlags
|= IHistory::UNRECOVERABLE_ERROR
;
12304 if (aWasUpgraded
) {
12306 IHistory::REDIRECT_SOURCE
| IHistory::REDIRECT_SOURCE_UPGRADED
;
12309 mozilla::Unused
<< history
->VisitURI(aWidget
, aURI
, aPreviousURI
,
12311 aBrowsingContext
->BrowserId());
12315 void nsDocShell::AddURIVisit(nsIURI
* aURI
, nsIURI
* aPreviousURI
,
12316 uint32_t aChannelRedirectFlags
,
12317 uint32_t aResponseStatus
) {
12318 nsPIDOMWindowOuter
* outer
= GetWindow();
12319 nsCOMPtr
<nsIWidget
> widget
= widget::WidgetUtils::DOMWindowToWidget(outer
);
12321 InternalAddURIVisit(aURI
, aPreviousURI
, aChannelRedirectFlags
,
12322 aResponseStatus
, mBrowsingContext
, widget
, mLoadType
,
12326 //*****************************************************************************
12327 // nsDocShell: Helper Routines
12328 //*****************************************************************************
12331 nsDocShell::SetLoadType(uint32_t aLoadType
) {
12332 mLoadType
= aLoadType
;
12337 nsDocShell::GetLoadType(uint32_t* aLoadType
) {
12338 *aLoadType
= mLoadType
;
12342 nsresult
nsDocShell::ConfirmRepost(bool* aRepost
) {
12343 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12348 nsCOMPtr
<nsIPromptCollection
> prompter
=
12349 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12351 return NS_ERROR_NOT_AVAILABLE
;
12354 return prompter
->ConfirmRepost(mBrowsingContext
, aRepost
);
12357 nsresult
nsDocShell::GetPromptAndStringBundle(nsIPrompt
** aPrompt
,
12358 nsIStringBundle
** aStringBundle
) {
12359 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt
), (void**)aPrompt
),
12362 nsCOMPtr
<nsIStringBundleService
> stringBundleService
=
12363 mozilla::components::StringBundle::Service();
12364 NS_ENSURE_TRUE(stringBundleService
, NS_ERROR_FAILURE
);
12367 stringBundleService
->CreateBundle(kAppstringsBundleURL
, aStringBundle
),
12373 nsIScrollableFrame
* nsDocShell::GetRootScrollFrame() {
12374 PresShell
* presShell
= GetPresShell();
12375 NS_ENSURE_TRUE(presShell
, nullptr);
12377 return presShell
->GetRootScrollFrameAsScrollable();
12380 nsresult
nsDocShell::EnsureScriptEnvironment() {
12381 if (mScriptGlobal
) {
12385 if (mIsBeingDestroyed
) {
12386 return NS_ERROR_NOT_AVAILABLE
;
12390 NS_ASSERTION(!mInEnsureScriptEnv
,
12391 "Infinite loop! Calling EnsureScriptEnvironment() from "
12392 "within EnsureScriptEnvironment()!");
12394 // Yeah, this isn't re-entrant safe, but that's ok since if we
12395 // re-enter this method, we'll infinitely loop...
12396 AutoRestore
<bool> boolSetter(mInEnsureScriptEnv
);
12397 mInEnsureScriptEnv
= true;
12400 nsCOMPtr
<nsIWebBrowserChrome
> browserChrome(do_GetInterface(mTreeOwner
));
12401 NS_ENSURE_TRUE(browserChrome
, NS_ERROR_NOT_AVAILABLE
);
12403 uint32_t chromeFlags
;
12404 browserChrome
->GetChromeFlags(&chromeFlags
);
12406 // If our window is modal and we're not opened as chrome, make
12407 // this window a modal content window.
12408 mScriptGlobal
= nsGlobalWindowOuter::Create(this, mItemType
== typeChrome
);
12409 MOZ_ASSERT(mScriptGlobal
);
12411 // Ensure the script object is set up to run script.
12412 return mScriptGlobal
->EnsureScriptEnvironment();
12415 nsresult
nsDocShell::EnsureEditorData() {
12416 MOZ_ASSERT(!mIsBeingDestroyed
);
12418 bool openDocHasDetachedEditor
= mOSHE
&& mOSHE
->HasDetachedEditor();
12419 if (!mEditorData
&& !mIsBeingDestroyed
&& !openDocHasDetachedEditor
) {
12420 // We shouldn't recreate the editor data if it already exists, or
12421 // we're shutting down, or we already have a detached editor data
12422 // stored in the session history. We should only have one editordata
12424 mEditorData
= MakeUnique
<nsDocShellEditorData
>(this);
12427 return mEditorData
? NS_OK
: NS_ERROR_NOT_AVAILABLE
;
12430 nsresult
nsDocShell::EnsureFind() {
12432 mFind
= new nsWebBrowserFind();
12435 // we promise that the nsIWebBrowserFind that we return has been set
12436 // up to point to the focused, or content window, so we have to
12437 // set that up each time.
12439 nsIScriptGlobalObject
* scriptGO
= GetScriptGlobalObject();
12440 NS_ENSURE_TRUE(scriptGO
, NS_ERROR_UNEXPECTED
);
12442 // default to our window
12443 nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
= do_QueryInterface(scriptGO
);
12444 nsCOMPtr
<nsPIDOMWindowOuter
> windowToSearch
;
12445 nsFocusManager::GetFocusedDescendant(ourWindow
,
12446 nsFocusManager::eIncludeAllDescendants
,
12447 getter_AddRefs(windowToSearch
));
12449 nsCOMPtr
<nsIWebBrowserFindInFrames
> findInFrames
= do_QueryInterface(mFind
);
12450 if (!findInFrames
) {
12451 return NS_ERROR_NO_INTERFACE
;
12454 nsresult rv
= findInFrames
->SetRootSearchFrame(ourWindow
);
12455 if (NS_FAILED(rv
)) {
12458 rv
= findInFrames
->SetCurrentSearchFrame(windowToSearch
);
12459 if (NS_FAILED(rv
)) {
12467 nsDocShell::IsBeingDestroyed(bool* aDoomed
) {
12468 NS_ENSURE_ARG(aDoomed
);
12469 *aDoomed
= mIsBeingDestroyed
;
12474 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult
) {
12475 NS_ENSURE_ARG(aResult
);
12476 *aResult
= mIsExecutingOnLoadHandler
;
12481 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState
** aLayoutHistoryState
) {
12482 nsCOMPtr
<nsILayoutHistoryState
> state
;
12483 if (mozilla::SessionHistoryInParent()) {
12484 if (mActiveEntry
) {
12485 state
= mActiveEntry
->GetLayoutHistoryState();
12489 state
= mOSHE
->GetLayoutHistoryState();
12492 state
.forget(aLayoutHistoryState
);
12497 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState
* aLayoutHistoryState
) {
12499 mOSHE
->SetLayoutHistoryState(aLayoutHistoryState
);
12501 if (mActiveEntry
) {
12502 mActiveEntry
->SetLayoutHistoryState(aLayoutHistoryState
);
12507 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12508 nsIInterfaceRequestor
* aRequestor
) {
12510 mWeakPtr
= do_GetWeakReference(aRequestor
);
12514 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12515 mWeakPtr
= nullptr;
12518 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy
, nsIInterfaceRequestor
)
12521 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID
& aIID
,
12523 NS_ENSURE_ARG_POINTER(aSink
);
12524 nsCOMPtr
<nsIInterfaceRequestor
> ifReq
= do_QueryReferent(mWeakPtr
);
12526 return ifReq
->GetInterface(aIID
, aSink
);
12529 return NS_NOINTERFACE
;
12532 //*****************************************************************************
12533 // nsDocShell::nsIAuthPromptProvider
12534 //*****************************************************************************
12537 nsDocShell::GetAuthPrompt(uint32_t aPromptReason
, const nsIID
& aIID
,
12539 // a priority prompt request will override a false mAllowAuth setting
12540 bool priorityPrompt
= (aPromptReason
== PROMPT_PROXY
);
12542 if (!mAllowAuth
&& !priorityPrompt
) {
12543 return NS_ERROR_NOT_AVAILABLE
;
12546 // we're either allowing auth, or it's a proxy request
12548 nsCOMPtr
<nsIPromptFactory
> wwatch
=
12549 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
12550 NS_ENSURE_SUCCESS(rv
, rv
);
12552 rv
= EnsureScriptEnvironment();
12553 NS_ENSURE_SUCCESS(rv
, rv
);
12555 // Get the an auth prompter for our window so that the parenting
12556 // of the dialogs works as it should when using tabs.
12558 return wwatch
->GetPrompt(mScriptGlobal
, aIID
,
12559 reinterpret_cast<void**>(aResult
));
12562 //*****************************************************************************
12563 // nsDocShell::nsILoadContext
12564 //*****************************************************************************
12567 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy
** aWindow
) {
12568 CallGetInterface(this, aWindow
);
12573 nsDocShell::GetTopWindow(mozIDOMWindowProxy
** aWindow
) {
12574 return mBrowsingContext
->GetTopWindow(aWindow
);
12578 nsDocShell::GetTopFrameElement(Element
** aElement
) {
12579 return mBrowsingContext
->GetTopFrameElement(aElement
);
12583 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection
) {
12584 return mBrowsingContext
->GetUseTrackingProtection(aUseTrackingProtection
);
12588 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection
) {
12589 return mBrowsingContext
->SetUseTrackingProtection(aUseTrackingProtection
);
12593 nsDocShell::GetIsContent(bool* aIsContent
) {
12594 *aIsContent
= (mItemType
== typeContent
);
12598 bool nsDocShell::IsOKToLoadURI(nsIURI
* aURI
) {
12599 MOZ_ASSERT(aURI
, "Must have a URI!");
12601 if (!mFiredUnloadEvent
) {
12605 if (!mLoadingURI
) {
12609 bool isPrivateWin
= false;
12610 Document
* doc
= GetDocument();
12613 doc
->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId
> 0;
12616 nsCOMPtr
<nsIScriptSecurityManager
> secMan
=
12617 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID
);
12618 return secMan
&& NS_SUCCEEDED(secMan
->CheckSameOriginURI(
12619 aURI
, mLoadingURI
, false, isPrivateWin
));
12623 // Routines for selection and clipboard
12625 nsresult
nsDocShell::GetControllerForCommand(const char* aCommand
,
12626 nsIController
** aResult
) {
12627 NS_ENSURE_ARG_POINTER(aResult
);
12628 *aResult
= nullptr;
12630 NS_ENSURE_TRUE(mScriptGlobal
, NS_ERROR_FAILURE
);
12632 nsCOMPtr
<nsPIWindowRoot
> root
= mScriptGlobal
->GetTopWindowRoot();
12633 NS_ENSURE_TRUE(root
, NS_ERROR_FAILURE
);
12635 return root
->GetControllerForCommand(aCommand
, false /* for any window */,
12640 nsDocShell::IsCommandEnabled(const char* aCommand
, bool* aResult
) {
12641 NS_ENSURE_ARG_POINTER(aResult
);
12644 nsresult rv
= NS_ERROR_FAILURE
;
12646 nsCOMPtr
<nsIController
> controller
;
12647 rv
= GetControllerForCommand(aCommand
, getter_AddRefs(controller
));
12649 rv
= controller
->IsCommandEnabled(aCommand
, aResult
);
12656 nsDocShell::DoCommand(const char* aCommand
) {
12657 nsresult rv
= NS_ERROR_FAILURE
;
12659 nsCOMPtr
<nsIController
> controller
;
12660 rv
= GetControllerForCommand(aCommand
, getter_AddRefs(controller
));
12662 rv
= controller
->DoCommand(aCommand
);
12669 nsDocShell::DoCommandWithParams(const char* aCommand
,
12670 nsICommandParams
* aParams
) {
12671 nsCOMPtr
<nsIController
> controller
;
12672 nsresult rv
= GetControllerForCommand(aCommand
, getter_AddRefs(controller
));
12673 if (NS_WARN_IF(NS_FAILED(rv
))) {
12677 nsCOMPtr
<nsICommandController
> commandController
=
12678 do_QueryInterface(controller
, &rv
);
12679 if (NS_WARN_IF(NS_FAILED(rv
))) {
12683 return commandController
->DoCommandWithParams(aCommand
, aParams
);
12686 nsresult
nsDocShell::EnsureCommandHandler() {
12687 if (!mCommandManager
) {
12688 if (nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
= GetWindow()) {
12689 mCommandManager
= new nsCommandManager(domWindow
);
12692 return mCommandManager
? NS_OK
: NS_ERROR_FAILURE
;
12697 class OnLinkClickEvent
: public Runnable
{
12699 OnLinkClickEvent(nsDocShell
* aHandler
, nsIContent
* aContent
,
12700 nsDocShellLoadState
* aLoadState
, bool aNoOpenerImplied
,
12701 bool aIsTrusted
, nsIPrincipal
* aTriggeringPrincipal
);
12703 NS_IMETHOD
Run() override
{
12704 AutoPopupStatePusher
popupStatePusher(mPopupState
);
12706 // We need to set up an AutoJSAPI here for the following reason: When we
12707 // do OnLinkClickSync we'll eventually end up in
12708 // nsGlobalWindow::OpenInternal which only does popup blocking if
12709 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12710 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12713 if (mIsTrusted
|| jsapi
.Init(mContent
->OwnerDoc()->GetScopeObject())) {
12714 mHandler
->OnLinkClickSync(mContent
, mLoadState
, mNoOpenerImplied
,
12715 mTriggeringPrincipal
);
12721 RefPtr
<nsDocShell
> mHandler
;
12722 nsCOMPtr
<nsIContent
> mContent
;
12723 RefPtr
<nsDocShellLoadState
> mLoadState
;
12724 nsCOMPtr
<nsIPrincipal
> mTriggeringPrincipal
;
12725 PopupBlocker::PopupControlState mPopupState
;
12726 bool mNoOpenerImplied
;
12730 OnLinkClickEvent::OnLinkClickEvent(nsDocShell
* aHandler
, nsIContent
* aContent
,
12731 nsDocShellLoadState
* aLoadState
,
12732 bool aNoOpenerImplied
, bool aIsTrusted
,
12733 nsIPrincipal
* aTriggeringPrincipal
)
12734 : mozilla::Runnable("OnLinkClickEvent"),
12735 mHandler(aHandler
),
12736 mContent(aContent
),
12737 mLoadState(aLoadState
),
12738 mTriggeringPrincipal(aTriggeringPrincipal
),
12739 mPopupState(PopupBlocker::GetPopupControlState()),
12740 mNoOpenerImplied(aNoOpenerImplied
),
12741 mIsTrusted(aIsTrusted
) {}
12743 nsresult
nsDocShell::OnLinkClick(
12744 nsIContent
* aContent
, nsIURI
* aURI
, const nsAString
& aTargetSpec
,
12745 const nsAString
& aFileName
, nsIInputStream
* aPostDataStream
,
12746 nsIInputStream
* aHeadersDataStream
, bool aIsUserTriggered
, bool aIsTrusted
,
12747 nsIPrincipal
* aTriggeringPrincipal
, nsIContentSecurityPolicy
* aCsp
) {
12749 MOZ_ASSERT(aTriggeringPrincipal
, "Need a valid triggeringPrincipal");
12751 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12753 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI
)) {
12757 // On history navigation through Back/Forward buttons, don't execute
12758 // automatic JavaScript redirection such as |anchorElement.click()| or
12759 // |formElement.submit()|.
12761 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12762 // nsDocShell::OnLinkClickSync(...) instead.
12763 if (ShouldBlockLoadingForBackButton()) {
12767 if (aContent
->IsEditable()) {
12771 Document
* ownerDoc
= aContent
->OwnerDoc();
12772 if (nsContentUtils::IsExternalProtocol(aURI
)) {
12773 ownerDoc
->EnsureNotEnteringAndExitFullscreen();
12776 bool noOpenerImplied
= false;
12777 nsAutoString
target(aTargetSpec
);
12778 if (aFileName
.IsVoid() &&
12779 ShouldOpenInBlankTarget(aTargetSpec
, aURI
, aContent
, aIsUserTriggered
)) {
12780 target
= u
"_blank";
12781 if (!aTargetSpec
.Equals(target
)) {
12782 noOpenerImplied
= true;
12786 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(aURI
);
12787 loadState
->SetTarget(target
);
12788 loadState
->SetFileName(aFileName
);
12789 loadState
->SetPostDataStream(aPostDataStream
);
12790 loadState
->SetHeadersStream(aHeadersDataStream
);
12791 loadState
->SetFirstParty(true);
12792 loadState
->SetTriggeringPrincipal(
12793 aTriggeringPrincipal
? aTriggeringPrincipal
: aContent
->NodePrincipal());
12794 loadState
->SetPrincipalToInherit(aContent
->NodePrincipal());
12795 loadState
->SetCsp(aCsp
? aCsp
: aContent
->GetCsp());
12796 loadState
->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12798 nsCOMPtr
<nsIRunnable
> ev
=
12799 new OnLinkClickEvent(this, aContent
, loadState
, noOpenerImplied
,
12800 aIsTrusted
, aTriggeringPrincipal
);
12801 return Dispatch(ev
.forget());
12804 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString
& aOriginalTarget
,
12805 nsIURI
* aLinkURI
, nsIContent
* aContent
,
12806 bool aIsUserTriggered
) {
12807 if (net::SchemeIsJavascript(aLinkURI
)) {
12811 // External links from within app tabs should always open in new tabs
12812 // instead of replacing the app tab's page (Bug 575561)
12813 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12814 // get either host, just return false to use the original target.
12815 nsAutoCString linkHost
;
12816 if (NS_FAILED(aLinkURI
->GetHost(linkHost
))) {
12820 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12821 // privileged code to change the default targeting behaviour. In particular,
12822 // if a user-initiated link click for the (or targetting the) top-level frame
12823 // is detected, we default the target to "_blank" to give it a new
12824 // top-level BrowsingContext.
12825 if (mBrowsingContext
->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered
&&
12826 ((aOriginalTarget
.IsEmpty() && mBrowsingContext
->IsTop()) ||
12827 aOriginalTarget
== u
"_top"_ns
)) {
12831 // Don't modify non-default targets.
12832 if (!aOriginalTarget
.IsEmpty()) {
12836 // Only check targets that are in extension panels or app tabs.
12837 // (isAppTab will be false for app tab subframes).
12838 nsString mmGroup
= mBrowsingContext
->Top()->GetMessageManagerGroup();
12839 if (!mmGroup
.EqualsLiteral("webext-browsers") &&
12840 !mBrowsingContext
->IsAppTab()) {
12844 nsCOMPtr
<nsIURI
> docURI
= aContent
->OwnerDoc()->GetDocumentURIObject();
12849 nsAutoCString docHost
;
12850 if (NS_FAILED(docURI
->GetHost(docHost
))) {
12854 if (linkHost
.Equals(docHost
)) {
12858 // Special case: ignore "www" prefix if it is part of host string
12859 return linkHost
.Length() < docHost
.Length()
12860 ? !docHost
.Equals("www."_ns
+ linkHost
)
12861 : !linkHost
.Equals("www."_ns
+ docHost
);
12864 static bool ElementCanHaveNoopener(nsIContent
* aContent
) {
12865 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12866 // the HTML, XHTML, or SVG namespace.
12867 return aContent
->IsAnyOfHTMLElements(nsGkAtoms::a
, nsGkAtoms::area
,
12868 nsGkAtoms::form
) ||
12869 aContent
->IsSVGElement(nsGkAtoms::a
);
12872 nsresult
nsDocShell::OnLinkClickSync(nsIContent
* aContent
,
12873 nsDocShellLoadState
* aLoadState
,
12874 bool aNoOpenerImplied
,
12875 nsIPrincipal
* aTriggeringPrincipal
) {
12876 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState
->URI())) {
12880 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12881 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12882 // but |HTMLFormElement::SubmitSubmission(...)|.
12883 if (aContent
->IsHTMLElement(nsGkAtoms::form
) &&
12884 ShouldBlockLoadingForBackButton()) {
12888 if (aContent
->IsEditable()) {
12892 // if the triggeringPrincipal is not passed explicitly, then we
12893 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12894 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
=
12895 aTriggeringPrincipal
? aTriggeringPrincipal
: aContent
->NodePrincipal();
12898 // defer to an external protocol handler if necessary...
12899 nsCOMPtr
<nsIExternalProtocolService
> extProtService
=
12900 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID
);
12901 if (extProtService
) {
12902 nsAutoCString scheme
;
12903 aLoadState
->URI()->GetScheme(scheme
);
12904 if (!scheme
.IsEmpty()) {
12905 // if the URL scheme does not correspond to an exposed protocol, then
12906 // we need to hand this link click over to the external protocol
12910 extProtService
->IsExposedProtocol(scheme
.get(), &isExposed
);
12911 if (NS_SUCCEEDED(rv
) && !isExposed
) {
12912 return extProtService
->LoadURI(
12913 aLoadState
->URI(), triggeringPrincipal
, nullptr, mBrowsingContext
,
12914 /* aTriggeredExternally */
12916 /* aHasValidUserGestureActivation */
12917 aContent
->OwnerDoc()->HasValidTransientUserGestureActivation());
12922 uint32_t triggeringSandboxFlags
= 0;
12923 uint64_t triggeringWindowId
= 0;
12924 bool triggeringStorageAccess
= false;
12925 if (mBrowsingContext
) {
12926 triggeringSandboxFlags
= aContent
->OwnerDoc()->GetSandboxFlags();
12927 triggeringWindowId
= aContent
->OwnerDoc()->InnerWindowID();
12928 triggeringStorageAccess
= aContent
->OwnerDoc()->UsingStorageAccess();
12931 uint32_t flags
= INTERNAL_LOAD_FLAGS_NONE
;
12932 bool elementCanHaveNoopener
= ElementCanHaveNoopener(aContent
);
12933 bool triggeringPrincipalIsSystemPrincipal
=
12934 aLoadState
->TriggeringPrincipal()->IsSystemPrincipal();
12935 if (elementCanHaveNoopener
) {
12936 MOZ_ASSERT(aContent
->IsHTMLElement() || aContent
->IsSVGElement());
12937 nsAutoString relString
;
12938 aContent
->AsElement()->GetAttr(nsGkAtoms::rel
, relString
);
12939 nsWhitespaceTokenizerTemplate
<nsContentUtils::IsHTMLWhitespace
> tok(
12942 bool targetBlank
= aLoadState
->Target().LowerCaseEqualsLiteral("_blank");
12943 bool explicitOpenerSet
= false;
12945 // The opener behaviour follows a hierarchy, such that if a higher
12946 // priority behaviour is specified, it always takes priority. That
12947 // priority is currently: norefrerer > noopener > opener > default
12949 while (tok
.hasMoreTokens()) {
12950 const nsAString
& token
= tok
.nextToken();
12951 if (token
.LowerCaseEqualsLiteral("noreferrer")) {
12952 flags
|= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER
|
12953 INTERNAL_LOAD_FLAGS_NO_OPENER
;
12954 // noreferrer cannot be overwritten by a 'rel=opener'.
12955 explicitOpenerSet
= true;
12959 if (token
.LowerCaseEqualsLiteral("noopener")) {
12960 flags
|= INTERNAL_LOAD_FLAGS_NO_OPENER
;
12961 explicitOpenerSet
= true;
12964 if (targetBlank
&& StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12965 token
.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet
) {
12966 explicitOpenerSet
= true;
12970 if (targetBlank
&& StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12971 !explicitOpenerSet
&& !triggeringPrincipalIsSystemPrincipal
) {
12972 flags
|= INTERNAL_LOAD_FLAGS_NO_OPENER
;
12975 if (aNoOpenerImplied
) {
12976 flags
|= INTERNAL_LOAD_FLAGS_NO_OPENER
;
12980 // Get the owner document of the link that was clicked, this will be
12981 // the document that the link is in, or the last document that the
12982 // link was in. From that document, we'll get the URI to use as the
12983 // referrer, since the current URI in this docshell may be a
12984 // new document that we're in the process of loading.
12985 RefPtr
<Document
> referrerDoc
= aContent
->OwnerDoc();
12987 // Now check that the referrerDoc's inner window is the current inner
12988 // window for mScriptGlobal. If it's not, then we don't want to
12989 // follow this link.
12990 nsPIDOMWindowInner
* referrerInner
= referrerDoc
->GetInnerWindow();
12991 if (!mScriptGlobal
|| !referrerInner
||
12992 mScriptGlobal
->GetCurrentInnerWindow() != referrerInner
) {
12993 // We're no longer the current inner window
12997 // referrer could be null here in some odd cases, but that's ok,
12998 // we'll just load the link w/o sending a referrer in those cases.
13000 // If this is an anchor element, grab its type property to use as a hint
13001 nsAutoString typeHint
;
13002 RefPtr
<HTMLAnchorElement
> anchor
= HTMLAnchorElement::FromNode(aContent
);
13004 anchor
->GetType(typeHint
);
13005 NS_ConvertUTF16toUTF8
utf8Hint(typeHint
);
13006 nsAutoCString type
, dummy
;
13007 NS_ParseRequestContentType(utf8Hint
, type
, dummy
);
13008 CopyUTF8toUTF16(type
, typeHint
);
13011 uint32_t loadType
= LOAD_LINK
;
13012 if (aLoadState
->IsFormSubmission()) {
13013 if (aLoadState
->Target().IsEmpty()) {
13014 // We set the right load type here for form submissions with an empty
13015 // target. Form submission with a non-empty target are handled in
13016 // nsDocShell::PerformRetargeting after we've selected the correct target
13018 loadType
= GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState
);
13021 // Link click can be triggered inside an onload handler, and we don't want
13022 // to add history entry in this case.
13023 bool inOnLoadHandler
= false;
13024 GetIsExecutingOnLoadHandler(&inOnLoadHandler
);
13025 if (inOnLoadHandler
) {
13026 loadType
= LOAD_NORMAL_REPLACE
;
13030 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
13031 elementCanHaveNoopener
? new ReferrerInfo(*aContent
->AsElement())
13032 : new ReferrerInfo(*referrerDoc
);
13033 RefPtr
<WindowContext
> context
= mBrowsingContext
->GetCurrentWindowContext();
13035 aLoadState
->SetTriggeringSandboxFlags(triggeringSandboxFlags
);
13036 aLoadState
->SetTriggeringWindowId(triggeringWindowId
);
13037 aLoadState
->SetTriggeringStorageAccess(triggeringStorageAccess
);
13038 aLoadState
->SetReferrerInfo(referrerInfo
);
13039 aLoadState
->SetInternalLoadFlags(flags
);
13040 aLoadState
->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint
));
13041 aLoadState
->SetLoadType(loadType
);
13042 aLoadState
->SetSourceBrowsingContext(mBrowsingContext
);
13043 aLoadState
->SetHasValidUserGestureActivation(
13044 context
&& context
->HasValidTransientUserGestureActivation());
13046 nsresult rv
= InternalLoad(aLoadState
);
13048 if (NS_SUCCEEDED(rv
)) {
13049 nsPingListener::DispatchPings(this, aContent
, aLoadState
->URI(),
13056 nsresult
nsDocShell::OnOverLink(nsIContent
* aContent
, nsIURI
* aURI
,
13057 const nsAString
& aTargetSpec
) {
13058 if (aContent
->IsEditable()) {
13062 nsresult rv
= NS_ERROR_FAILURE
;
13064 nsCOMPtr
<nsIWebBrowserChrome
> browserChrome
= do_GetInterface(mTreeOwner
);
13065 if (!browserChrome
) {
13069 nsCOMPtr
<nsIURI
> exposableURI
= nsIOService::CreateExposableURI(aURI
);
13070 nsAutoCString spec
;
13071 rv
= exposableURI
->GetDisplaySpec(spec
);
13072 NS_ENSURE_SUCCESS(rv
, rv
);
13074 NS_ConvertUTF8toUTF16
uStr(spec
);
13076 PredictorPredict(aURI
, mCurrentURI
, nsINetworkPredictor::PREDICT_LINK
,
13077 aContent
->NodePrincipal()->OriginAttributesRef(), nullptr);
13079 rv
= browserChrome
->SetLinkStatus(uStr
);
13083 nsresult
nsDocShell::OnLeaveLink() {
13084 nsCOMPtr
<nsIWebBrowserChrome
> browserChrome(do_GetInterface(mTreeOwner
));
13085 nsresult rv
= NS_ERROR_FAILURE
;
13087 if (browserChrome
) {
13088 rv
= browserChrome
->SetLinkStatus(u
""_ns
);
13093 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13094 if (!(mLoadType
& LOAD_CMD_HISTORY
) ||
13095 UserActivation::IsHandlingUserInput() ||
13096 !Preferences::GetBool("accessibility.blockjsredirection")) {
13100 bool canGoForward
= false;
13101 GetCanGoForward(&canGoForward
);
13102 return canGoForward
;
13105 bool nsDocShell::PluginsAllowedInCurrentDoc() {
13106 if (!mDocumentViewer
) {
13110 Document
* doc
= mDocumentViewer
->GetDocument();
13115 return doc
->GetAllowPlugins();
13118 //----------------------------------------------------------------------
13119 // Web Shell Services API
13121 // This functions is only called when a new charset is detected in loading a
13123 nsresult
nsDocShell::CharsetChangeReloadDocument(
13124 mozilla::NotNull
<const mozilla::Encoding
*> aEncoding
, int32_t aSource
) {
13125 // XXX hack. keep the aCharset and aSource wait to pick it up
13126 nsCOMPtr
<nsIDocumentViewer
> viewer
;
13127 NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer
)), NS_ERROR_FAILURE
);
13130 Unused
<< viewer
->GetReloadEncodingAndSource(&source
);
13131 if (aSource
> source
) {
13132 viewer
->SetReloadEncodingAndSource(aEncoding
, aSource
);
13133 if (eCharsetReloadRequested
!= mCharsetReloadState
) {
13134 mCharsetReloadState
= eCharsetReloadRequested
;
13135 switch (mLoadType
) {
13136 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
:
13137 return Reload(LOAD_FLAGS_CHARSET_CHANGE
| LOAD_FLAGS_BYPASS_CACHE
|
13138 LOAD_FLAGS_BYPASS_PROXY
);
13139 case LOAD_RELOAD_BYPASS_CACHE
:
13140 return Reload(LOAD_FLAGS_CHARSET_CHANGE
| LOAD_FLAGS_BYPASS_CACHE
);
13142 return Reload(LOAD_FLAGS_CHARSET_CHANGE
);
13147 // return failure if this request is not accepted due to mCharsetReloadState
13148 return NS_ERROR_DOCSHELL_REQUEST_REJECTED
;
13151 nsresult
nsDocShell::CharsetChangeStopDocumentLoad() {
13152 if (eCharsetReloadRequested
!= mCharsetReloadState
) {
13153 Stop(nsIWebNavigation::STOP_ALL
);
13156 // return failer if this request is not accepted due to mCharsetReloadState
13157 return NS_ERROR_DOCSHELL_REQUEST_REJECTED
;
13160 NS_IMETHODIMP
nsDocShell::ExitPrintPreview() {
13161 #if NS_PRINT_PREVIEW
13162 nsCOMPtr
<nsIWebBrowserPrint
> viewer
= do_QueryInterface(mDocumentViewer
);
13163 return viewer
->ExitPrintPreview();
13170 NS_IMETHODIMP
nsDocShell::GetIsTopLevelContentDocShell(
13171 bool* aIsTopLevelContentDocShell
) {
13172 *aIsTopLevelContentDocShell
= false;
13174 if (mItemType
== typeContent
) {
13175 *aIsTopLevelContentDocShell
= mBrowsingContext
->IsTopContent();
13181 // Implements nsILoadContext.originAttributes
13183 nsDocShell::GetScriptableOriginAttributes(JSContext
* aCx
,
13184 JS::MutableHandle
<JS::Value
> aVal
) {
13185 return mBrowsingContext
->GetScriptableOriginAttributes(aCx
, aVal
);
13188 // Implements nsIDocShell.GetOriginAttributes()
13190 nsDocShell::GetOriginAttributes(JSContext
* aCx
,
13191 JS::MutableHandle
<JS::Value
> aVal
) {
13192 return mBrowsingContext
->GetScriptableOriginAttributes(aCx
, aVal
);
13195 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal
* aPrincipal
,
13197 MOZ_ASSERT(aPrincipal
);
13200 if (UsePrivateBrowsing() || mBrowsingContext
->GetSandboxFlags()) {
13204 nsCOMPtr
<nsIDocShellTreeItem
> parent
;
13205 GetInProcessSameTypeParent(getter_AddRefs(parent
));
13206 nsPIDOMWindowOuter
* parentOuter
= parent
? parent
->GetWindow() : nullptr;
13207 nsPIDOMWindowInner
* parentInner
=
13208 parentOuter
? parentOuter
->GetCurrentInnerWindow() : nullptr;
13210 StorageAccess storage
=
13211 StorageAllowedForNewWindow(aPrincipal
, aURI
, parentInner
);
13213 // If the partitioned service worker is enabled, service worker is allowed to
13214 // control the window if partition is enabled.
13215 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner
) {
13216 RefPtr
<Document
> doc
= parentInner
->GetExtantDoc();
13218 if (doc
&& StoragePartitioningEnabled(storage
, doc
->CookieJarSettings())) {
13223 return storage
== StorageAccess::eAllow
;
13226 nsresult
nsDocShell::SetOriginAttributes(const OriginAttributes
& aAttrs
) {
13227 MOZ_ASSERT(!mIsBeingDestroyed
);
13228 return mBrowsingContext
->SetOriginAttributes(aAttrs
);
13232 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier
, int32_t aHistoryIndex
) {
13233 RefPtr
<nsDocShell
> self
= this;
13234 RefPtr
<ChildProcessChannelListener
> cpcl
=
13235 ChildProcessChannelListener::GetSingleton();
13237 // Call into InternalLoad with the pending channel when it is received.
13238 cpcl
->RegisterCallback(
13239 aIdentifier
, [self
, aHistoryIndex
](
13240 nsDocShellLoadState
* aLoadState
,
13241 nsTArray
<Endpoint
<extensions::PStreamFilterParent
>>&&
13242 aStreamFilterEndpoints
,
13243 nsDOMNavigationTiming
* aTiming
) {
13244 MOZ_ASSERT(aLoadState
->GetPendingRedirectedChannel());
13245 if (NS_WARN_IF(self
->mIsBeingDestroyed
)) {
13246 aLoadState
->GetPendingRedirectedChannel()->CancelWithReason(
13247 NS_BINDING_ABORTED
, "nsDocShell::mIsBeingDestroyed"_ns
);
13248 return NS_BINDING_ABORTED
;
13251 self
->mLoadType
= aLoadState
->LoadType();
13252 nsCOMPtr
<nsIURI
> previousURI
;
13253 uint32_t previousFlags
= 0;
13254 ExtractLastVisit(aLoadState
->GetPendingRedirectedChannel(),
13255 getter_AddRefs(previousURI
), &previousFlags
);
13256 self
->SaveLastVisit(aLoadState
->GetPendingRedirectedChannel(),
13257 previousURI
, previousFlags
);
13260 self
->mTiming
= new nsDOMNavigationTiming(self
, aTiming
);
13261 self
->mBlankTiming
= false;
13264 // If we're performing a history load, locate the correct history entry,
13265 // and set the relevant bits on our loadState.
13266 if (aHistoryIndex
>= 0 && self
->GetSessionHistory() &&
13267 !mozilla::SessionHistoryInParent()) {
13268 nsCOMPtr
<nsISHistory
> legacySHistory
=
13269 self
->GetSessionHistory()->LegacySHistory();
13271 nsCOMPtr
<nsISHEntry
> entry
;
13272 nsresult rv
= legacySHistory
->GetEntryAtIndex(aHistoryIndex
,
13273 getter_AddRefs(entry
));
13274 if (NS_SUCCEEDED(rv
)) {
13275 legacySHistory
->InternalSetRequestedIndex(aHistoryIndex
);
13276 aLoadState
->SetLoadType(LOAD_HISTORY
);
13277 aLoadState
->SetSHEntry(entry
);
13281 self
->InternalLoad(aLoadState
);
13283 if (aLoadState
->GetOriginalURIString().isSome()) {
13284 // Save URI string in case it's needed later when
13285 // sending to search engine service in EndPageLoad()
13286 self
->mOriginalUriString
= *aLoadState
->GetOriginalURIString();
13289 for (auto& endpoint
: aStreamFilterEndpoints
) {
13290 extensions::StreamFilterParent::Attach(
13291 aLoadState
->GetPendingRedirectedChannel(), std::move(endpoint
));
13294 // If the channel isn't pending, then it means that InternalLoad
13295 // never connected it, and we shouldn't try to continue. This
13296 // can happen even if InternalLoad returned NS_OK.
13297 bool pending
= false;
13298 aLoadState
->GetPendingRedirectedChannel()->IsPending(&pending
);
13299 NS_ASSERTION(pending
, "We should have connected the pending channel!");
13301 return NS_BINDING_ABORTED
;
13309 nsDocShell::SetOriginAttributes(JS::Handle
<JS::Value
> aOriginAttributes
,
13311 OriginAttributes attrs
;
13312 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
13313 return NS_ERROR_INVALID_ARG
;
13316 return SetOriginAttributes(attrs
);
13320 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut
) {
13321 if (PresShell
* presShell
= GetPresShell()) {
13322 *aOut
= presShell
->AsyncPanZoomEnabled();
13326 // If we don't have a presShell, fall back to the default platform value of
13327 // whether or not APZ is enabled.
13328 *aOut
= gfxPlatform::AsyncPanZoomEnabled();
13332 bool nsDocShell::HasUnloadedParent() {
13333 for (WindowContext
* wc
= GetBrowsingContext()->GetParentWindowContext(); wc
;
13334 wc
= wc
->GetParentWindowContext()) {
13335 if (!wc
->IsCurrent() || wc
->IsDiscarded() ||
13336 wc
->GetBrowsingContext()->IsDiscarded()) {
13337 // If a parent is OOP and the parent WindowContext is no
13338 // longer current, we can assume the parent was unloaded.
13342 if (wc
->GetBrowsingContext()->IsInProcess() &&
13343 (!wc
->GetBrowsingContext()->GetDocShell() ||
13344 wc
->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13352 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType
) {
13353 return !(aLoadType
== LOAD_BYPASS_HISTORY
|| aLoadType
== LOAD_ERROR_PAGE
||
13354 aLoadType
& LOAD_CMD_HISTORY
);
13357 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI
* aURI
) {
13358 if (!mBrowsingContext
->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13362 // Global history is interested into sub-frame visits only for link-coloring
13363 // purposes, thus title updates are skipped for those.
13365 // Moreover, some iframe documents (such as the ones created via
13366 // document.open()) inherit the document uri of the caller, which would cause
13367 // us to override a previously set page title with one from the subframe.
13368 if (IsSubframe()) {
13372 if (nsCOMPtr
<IHistory
> history
= components::History::Service()) {
13373 history
->SetURITitle(aURI
, mTitle
);
13377 bool nsDocShell::IsInvisible() { return mInvisible
; }
13379 void nsDocShell::SetInvisible(bool aInvisible
) { mInvisible
= aInvisible
; }
13382 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString
& aProvider
,
13383 const nsString
& aKeyword
) {
13384 if (aProvider
.IsEmpty()) {
13388 nsCOMPtr
<nsISupportsString
> isupportsString
=
13389 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID
, &rv
);
13390 NS_ENSURE_SUCCESS_VOID(rv
);
13392 rv
= isupportsString
->SetData(aProvider
);
13393 NS_ENSURE_SUCCESS_VOID(rv
);
13395 nsCOMPtr
<nsIObserverService
> obsSvc
= services::GetObserverService();
13397 // Note that "keyword-search" refers to a search via the url
13398 // bar, not a bookmarks keyword search.
13399 obsSvc
->NotifyObservers(isupportsString
, "keyword-search", aKeyword
.get());
13404 nsDocShell::ShouldPrepareForIntercept(nsIURI
* aURI
, nsIChannel
* aChannel
,
13405 bool* aShouldIntercept
) {
13406 return mInterceptController
->ShouldPrepareForIntercept(aURI
, aChannel
,
13411 nsDocShell::ChannelIntercepted(nsIInterceptedChannel
* aChannel
) {
13412 return mInterceptController
->ChannelIntercepted(aChannel
);
13415 bool nsDocShell::InFrameSwap() {
13416 RefPtr
<nsDocShell
> shell
= this;
13418 if (shell
->mInFrameSwap
) {
13421 shell
= shell
->GetInProcessParentDocshell();
13426 UniquePtr
<ClientSource
> nsDocShell::TakeInitialClientSource() {
13427 return std::move(mInitialClientSource
);
13431 nsDocShell::GetEditingSession(nsIEditingSession
** aEditSession
) {
13432 if (!NS_SUCCEEDED(EnsureEditorData())) {
13433 return NS_ERROR_FAILURE
;
13436 *aEditSession
= do_AddRef(mEditorData
->GetEditingSession()).take();
13437 return *aEditSession
? NS_OK
: NS_ERROR_FAILURE
;
13441 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild
** aBrowserChild
) {
13442 *aBrowserChild
= GetBrowserChild().take();
13443 return *aBrowserChild
? NS_OK
: NS_ERROR_FAILURE
;
13446 already_AddRefed
<nsIBrowserChild
> nsDocShell::GetBrowserChild() {
13447 nsCOMPtr
<nsIBrowserChild
> tc
= do_QueryReferent(mBrowserChild
);
13448 return tc
.forget();
13451 nsCommandManager
* nsDocShell::GetCommandManager() {
13452 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13453 return mCommandManager
;
13456 NS_IMETHODIMP_(void)
13457 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes
& aAttrs
) {
13458 mBrowsingContext
->GetOriginAttributes(aAttrs
);
13461 HTMLEditor
* nsIDocShell::GetHTMLEditor() {
13462 nsDocShell
* docShell
= static_cast<nsDocShell
*>(this);
13463 return docShell
->GetHTMLEditorInternal();
13466 nsresult
nsIDocShell::SetHTMLEditor(HTMLEditor
* aHTMLEditor
) {
13467 nsDocShell
* docShell
= static_cast<nsDocShell
*>(this);
13468 return docShell
->SetHTMLEditorInternal(aHTMLEditor
);
13471 #define MATRIX_LENGTH 20
13474 nsDocShell::SetColorMatrix(const nsTArray
<float>& aMatrix
) {
13475 if (aMatrix
.Length() == MATRIX_LENGTH
) {
13476 mColorMatrix
.reset(new gfx::Matrix5x4());
13478 MATRIX_LENGTH
* sizeof(float) == sizeof(mColorMatrix
->components
),
13479 "Size mismatch for our memcpy");
13480 memcpy(mColorMatrix
->components
, aMatrix
.Elements(),
13481 sizeof(mColorMatrix
->components
));
13482 } else if (aMatrix
.Length() == 0) {
13483 mColorMatrix
.reset();
13485 return NS_ERROR_INVALID_ARG
;
13488 PresShell
* presShell
= GetPresShell();
13490 return NS_ERROR_FAILURE
;
13493 nsIFrame
* frame
= presShell
->GetRootFrame();
13495 return NS_ERROR_FAILURE
;
13498 frame
->SchedulePaint();
13504 nsDocShell::GetColorMatrix(nsTArray
<float>& aMatrix
) {
13505 if (mColorMatrix
) {
13506 aMatrix
.SetLength(MATRIX_LENGTH
);
13508 MATRIX_LENGTH
* sizeof(float) == sizeof(mColorMatrix
->components
),
13509 "Size mismatch for our memcpy");
13510 memcpy(aMatrix
.Elements(), mColorMatrix
->components
,
13511 MATRIX_LENGTH
* sizeof(float));
13517 #undef MATRIX_LENGTH
13520 nsDocShell::GetIsForceReloading(bool* aForceReload
) {
13521 *aForceReload
= IsForceReloading();
13525 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType
); }
13528 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext
** aBrowsingContext
) {
13529 *aBrowsingContext
= do_AddRef(mBrowsingContext
).take();
13533 BrowsingContext
* nsDocShell::GetBrowsingContext() { return mBrowsingContext
; }
13535 bool nsDocShell::GetIsAttemptingToNavigate() {
13536 // XXXbz the document.open spec says to abort even if there's just a
13537 // queued navigation task, sort of. It's not clear whether browsers
13538 // actually do that, and we didn't use to do it, so for now let's
13540 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13541 if (mDocumentRequest
) {
13542 // There's definitely a navigation in progress.
13546 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13547 // until the script runs, which means they're not sending loadgroup
13548 // notifications and hence not getting set as mDocumentRequest. Look through
13549 // our loadgroup for document-level javascript: loads.
13554 nsCOMPtr
<nsISimpleEnumerator
> requests
;
13555 mLoadGroup
->GetRequests(getter_AddRefs(requests
));
13556 bool hasMore
= false;
13557 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
13558 nsCOMPtr
<nsISupports
> elem
;
13559 requests
->GetNext(getter_AddRefs(elem
));
13560 nsCOMPtr
<nsIScriptChannel
> scriptChannel(do_QueryInterface(elem
));
13561 if (!scriptChannel
) {
13565 if (scriptChannel
->GetIsDocumentLoad()) {
13566 // This is a javascript: load that might lead to a new document,
13567 // hence a navigation.
13572 return mCheckingSessionHistory
;
13575 void nsDocShell::SetLoadingSessionHistoryInfo(
13576 const mozilla::dom::LoadingSessionHistoryInfo
& aLoadingInfo
,
13577 bool aNeedToReportActiveAfterLoadingBecomesActive
) {
13578 // FIXME Would like to assert this, but can't yet.
13579 // MOZ_ASSERT(!mLoadingEntry);
13580 MOZ_LOG(gSHLog
, LogLevel::Debug
,
13581 ("Setting the loading entry on nsDocShell %p to %s", this,
13582 aLoadingInfo
.mInfo
.GetURI()->GetSpecOrDefault().get()));
13583 mLoadingEntry
= MakeUnique
<LoadingSessionHistoryInfo
>(aLoadingInfo
);
13584 mNeedToReportActiveAfterLoadingBecomesActive
=
13585 aNeedToReportActiveAfterLoadingBecomesActive
;
13588 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist
, bool aExpired
,
13589 uint32_t aCacheKey
,
13590 nsIURI
* aPreviousURI
) {
13591 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13593 MOZ_LOG(gSHLog
, LogLevel::Debug
,
13594 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13596 UniquePtr
<SessionHistoryInfo
> previousActiveEntry(mActiveEntry
.release());
13597 mozilla::UniquePtr
<mozilla::dom::LoadingSessionHistoryInfo
> loadingEntry
;
13598 mActiveEntryIsLoadingFromSessionHistory
=
13599 mLoadingEntry
&& mLoadingEntry
->mLoadIsFromSessionHistory
;
13600 if (mLoadingEntry
) {
13601 MOZ_LOG(gSHLog
, LogLevel::Debug
,
13602 ("Moving the loading entry to the active entry on nsDocShell %p "
13604 this, mLoadingEntry
->mInfo
.GetURI()->GetSpecOrDefault().get()));
13605 mActiveEntry
= MakeUnique
<SessionHistoryInfo
>(mLoadingEntry
->mInfo
);
13606 mLoadingEntry
.swap(loadingEntry
);
13607 if (!mActiveEntryIsLoadingFromSessionHistory
) {
13608 if (mNeedToReportActiveAfterLoadingBecomesActive
) {
13609 // Needed to pass various history length WPTs.
13610 mBrowsingContext
->SetActiveSessionHistoryEntry(
13611 mozilla::Nothing(), mActiveEntry
.get(), mLoadType
,
13612 /* aUpdatedCacheKey = */ 0, false);
13614 mBrowsingContext
->IncrementHistoryEntryCountForBrowsingContext();
13617 mNeedToReportActiveAfterLoadingBecomesActive
= false;
13619 if (mActiveEntry
) {
13620 if (aCacheKey
!= 0) {
13621 mActiveEntry
->SetCacheKey(aCacheKey
);
13623 MOZ_ASSERT(loadingEntry
);
13624 uint32_t loadType
=
13625 mLoadType
== LOAD_ERROR_PAGE
? mFailedLoadType
: mLoadType
;
13627 if (loadingEntry
->mLoadId
!= UINT64_MAX
) {
13628 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13629 // does require a non-null uri if this is for a refresh load of the same
13630 // URI, but in that case mCurrentURI won't be null here.
13631 mBrowsingContext
->SessionHistoryCommit(
13632 *loadingEntry
, loadType
, aPreviousURI
, previousActiveEntry
.get(),
13633 aPersist
, false, aExpired
, aCacheKey
);
13638 static bool IsFaviconLoad(nsIRequest
* aRequest
) {
13639 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
13644 nsCOMPtr
<nsILoadInfo
> li
= channel
->LoadInfo();
13645 return li
&& li
->InternalContentPolicyType() ==
13646 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON
;
13649 void nsDocShell::RecordSingleChannelId(bool aStartRequest
,
13650 nsIRequest
* aRequest
) {
13651 // Ignore favicon loads, they don't need to block caching.
13652 if (IsFaviconLoad(aRequest
)) {
13656 MOZ_ASSERT_IF(!aStartRequest
, mRequestForBlockingFromBFCacheCount
> 0);
13658 mRequestForBlockingFromBFCacheCount
+= aStartRequest
? 1 : -1;
13660 if (mBrowsingContext
->GetCurrentWindowContext()) {
13661 // We have three states: no request, one request with an id and
13662 // eiher one request without an id or multiple requests. Nothing() is no
13663 // request, Some(non-zero) is one request with an id and Some(0) is one
13664 // request without an id or multiple requests.
13665 Maybe
<uint64_t> singleChannelId
;
13666 if (mRequestForBlockingFromBFCacheCount
> 1) {
13667 singleChannelId
= Some(0);
13668 } else if (mRequestForBlockingFromBFCacheCount
== 1) {
13669 nsCOMPtr
<nsIIdentChannel
> identChannel
;
13670 if (aStartRequest
) {
13671 identChannel
= do_QueryInterface(aRequest
);
13673 // aChannel is the channel that's being removed, but we need to check if
13674 // the remaining channel in the loadgroup has an id.
13675 nsCOMPtr
<nsISimpleEnumerator
> requests
;
13676 mLoadGroup
->GetRequests(getter_AddRefs(requests
));
13677 for (const auto& request
: SimpleEnumerator
<nsIRequest
>(requests
)) {
13678 if (!IsFaviconLoad(request
) &&
13679 !!(identChannel
= do_QueryInterface(request
))) {
13685 if (identChannel
) {
13686 singleChannelId
= Some(identChannel
->ChannelId());
13688 singleChannelId
= Some(0);
13691 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount
== 0);
13692 singleChannelId
= Nothing();
13695 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Verbose
))) {
13696 nsAutoCString
uri("[no uri]");
13698 uri
= mCurrentURI
->GetSpecOrDefault();
13700 if (singleChannelId
.isNothing()) {
13701 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Verbose
,
13702 ("Loadgroup for %s doesn't have any requests relevant for "
13703 "blocking BFCache",
13705 } else if (singleChannelId
.value() == 0) {
13706 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Verbose
,
13707 ("Loadgroup for %s has multiple requests relevant for blocking "
13711 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Verbose
,
13712 ("Loadgroup for %s has one request with id %" PRIu64
13713 " relevant for blocking BFCache",
13714 uri
.get(), singleChannelId
.value()));
13718 if (mSingleChannelId
!= singleChannelId
) {
13719 mSingleChannelId
= singleChannelId
;
13720 WindowGlobalChild
* wgc
=
13721 mBrowsingContext
->GetCurrentWindowContext()->GetWindowGlobalChild();
13723 wgc
->SendSetSingleChannelId(singleChannelId
);
13730 nsDocShell::OnStartRequest(nsIRequest
* aRequest
) {
13731 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Verbose
))) {
13732 nsAutoCString
uri("[no uri]");
13734 uri
= mCurrentURI
->GetSpecOrDefault();
13736 nsAutoCString name
;
13737 aRequest
->GetName(name
);
13738 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Verbose
,
13739 ("Adding request %s to loadgroup for %s", name
.get(), uri
.get()));
13741 RecordSingleChannelId(true, aRequest
);
13742 return nsDocLoader::OnStartRequest(aRequest
);
13746 nsDocShell::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
13747 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Verbose
))) {
13748 nsAutoCString
uri("[no uri]");
13750 uri
= mCurrentURI
->GetSpecOrDefault();
13752 nsAutoCString name
;
13753 aRequest
->GetName(name
);
13755 gSHIPBFCacheLog
, LogLevel::Verbose
,
13756 ("Removing request %s from loadgroup for %s", name
.get(), uri
.get()));
13758 RecordSingleChannelId(false, aRequest
);
13759 return nsDocLoader::OnStopRequest(aRequest
, aStatusCode
);
13762 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13763 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13765 if (mChannelToDisconnectOnPageHide
!= 0 && mLoadGroup
) {
13766 nsCOMPtr
<nsISimpleEnumerator
> requests
;
13767 mLoadGroup
->GetRequests(getter_AddRefs(requests
));
13768 for (const auto& request
: SimpleEnumerator
<nsIRequest
>(requests
)) {
13769 RefPtr
<DocumentChannel
> channel
= do_QueryObject(request
);
13770 if (channel
&& channel
->ChannelId() == mChannelToDisconnectOnPageHide
) {
13771 static_cast<DocumentChannelChild
*>(channel
.get())
13772 ->DisconnectChildListeners(NS_BINDING_ABORTED
, NS_BINDING_ABORTED
);
13775 mChannelToDisconnectOnPageHide
= 0;