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