Bug 1751497 - adjust wpt test-verify and test-coverage tasks to be fission only....
[gecko.git] / docshell / base / nsDocShell.cpp
blob9d191bc43d79a926acff242b0d67036f28e477ea
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDocShell.h"
9 #include <algorithm>
11 #ifdef XP_WIN
12 # include <process.h>
13 # define getpid _getpid
14 #else
15 # include <unistd.h> // for getpid()
16 #endif
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/AutoRestore.h"
21 #include "mozilla/BasePrincipal.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/CheckedInt.h"
24 #include "mozilla/Components.h"
25 #include "mozilla/DebugOnly.h"
26 #include "mozilla/Encoding.h"
27 #include "mozilla/EventStateManager.h"
28 #include "mozilla/HTMLEditor.h"
29 #include "mozilla/InputTaskManager.h"
30 #include "mozilla/LoadInfo.h"
31 #include "mozilla/Logging.h"
32 #include "mozilla/MediaFeatureChange.h"
33 #include "mozilla/ObservedDocShell.h"
34 #include "mozilla/Preferences.h"
35 #include "mozilla/PresShell.h"
36 #include "mozilla/ResultExtensions.h"
37 #include "mozilla/SchedulerGroup.h"
38 #include "mozilla/ScopeExit.h"
39 #include "mozilla/ScrollTypes.h"
40 #include "mozilla/SimpleEnumerator.h"
41 #include "mozilla/StaticPrefs_browser.h"
42 #include "mozilla/StaticPrefs_docshell.h"
43 #include "mozilla/StaticPrefs_dom.h"
44 #include "mozilla/StaticPrefs_extensions.h"
45 #include "mozilla/StaticPrefs_privacy.h"
46 #include "mozilla/StaticPrefs_security.h"
47 #include "mozilla/StaticPrefs_ui.h"
48 #include "mozilla/StaticPrefs_fission.h"
49 #include "mozilla/StartupTimeline.h"
50 #include "mozilla/StorageAccess.h"
51 #include "mozilla/StoragePrincipalHelper.h"
52 #include "mozilla/Telemetry.h"
53 #include "mozilla/Tuple.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/SessionStoreDataCollector.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 "nsIAppShell.h"
100 #include "nsIAuthPrompt.h"
101 #include "nsIAuthPrompt2.h"
102 #include "nsICachingChannel.h"
103 #include "nsICaptivePortalService.h"
104 #include "nsIChannel.h"
105 #include "nsIChannelEventSink.h"
106 #include "nsIClassOfService.h"
107 #include "nsIConsoleReportCollector.h"
108 #include "nsIContent.h"
109 #include "nsIContentInlines.h"
110 #include "nsIContentSecurityPolicy.h"
111 #include "nsIContentViewer.h"
112 #include "nsIController.h"
113 #include "nsIDocShellTreeItem.h"
114 #include "nsIDocShellTreeOwner.h"
115 #include "mozilla/dom/Document.h"
116 #include "nsIDocumentLoaderFactory.h"
117 #include "nsIDOMWindow.h"
118 #include "nsIEditingSession.h"
119 #include "nsIEffectiveTLDService.h"
120 #include "nsIExternalProtocolService.h"
121 #include "nsIFormPOSTActionChannel.h"
122 #include "nsIFrame.h"
123 #include "nsIGlobalObject.h"
124 #include "nsIHttpChannel.h"
125 #include "nsIHttpChannelInternal.h"
126 #include "nsIIDNService.h"
127 #include "nsIInputStreamChannel.h"
128 #include "nsIInterfaceRequestorUtils.h"
129 #include "nsILayoutHistoryState.h"
130 #include "nsILoadInfo.h"
131 #include "nsILoadURIDelegate.h"
132 #include "nsIMultiPartChannel.h"
133 #include "nsINestedURI.h"
134 #include "nsINetworkPredictor.h"
135 #include "nsINode.h"
136 #include "nsINSSErrorsService.h"
137 #include "nsIObserverService.h"
138 #include "nsIOService.h"
139 #include "nsIPrincipal.h"
140 #include "nsIPrivacyTransitionObserver.h"
141 #include "nsIPrompt.h"
142 #include "nsIPromptCollection.h"
143 #include "nsIPromptFactory.h"
144 #include "nsIPublicKeyPinningService.h"
145 #include "nsIReflowObserver.h"
146 #include "nsIScriptChannel.h"
147 #include "nsIScriptObjectPrincipal.h"
148 #include "nsIScriptSecurityManager.h"
149 #include "nsIScrollableFrame.h"
150 #include "nsIScrollObserver.h"
151 #include "nsISupportsPrimitives.h"
152 #include "nsISecureBrowserUI.h"
153 #include "nsISeekableStream.h"
154 #include "nsISelectionDisplay.h"
155 #include "nsISHEntry.h"
156 #include "nsISiteSecurityService.h"
157 #include "nsISocketProvider.h"
158 #include "nsIStringBundle.h"
159 #include "nsIStructuredCloneContainer.h"
160 #include "nsIBrowserChild.h"
161 #include "nsITextToSubURI.h"
162 #include "nsITimedChannel.h"
163 #include "nsITimer.h"
164 #include "nsITransportSecurityInfo.h"
165 #include "nsIUploadChannel.h"
166 #include "nsIURIFixup.h"
167 #include "nsIURIMutator.h"
168 #include "nsIURILoader.h"
169 #include "nsIViewSourceChannel.h"
170 #include "nsIWebBrowserChrome.h"
171 #include "nsIWebBrowserChromeFocus.h"
172 #include "nsIWebBrowserFind.h"
173 #include "nsIWebProgress.h"
174 #include "nsIWidget.h"
175 #include "nsIWindowWatcher.h"
176 #include "nsIWritablePropertyBag2.h"
177 #include "nsIX509Cert.h"
178 #include "nsIXULRuntime.h"
180 #include "nsCommandManager.h"
181 #include "nsPIDOMWindow.h"
182 #include "nsPIWindowRoot.h"
184 #include "IHistory.h"
185 #include "IUrlClassifierUITelemetry.h"
187 #include "nsArray.h"
188 #include "nsArrayUtils.h"
189 #include "nsCExternalHandlerService.h"
190 #include "nsContentDLF.h"
191 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
192 #include "nsContentSecurityManager.h"
193 #include "nsContentSecurityUtils.h"
194 #include "nsContentUtils.h"
195 #include "nsCURILoader.h"
196 #include "nsDocShellCID.h"
197 #include "nsDocShellEditorData.h"
198 #include "nsDocShellEnumerator.h"
199 #include "nsDocShellLoadState.h"
200 #include "nsDocShellLoadTypes.h"
201 #include "nsDOMCID.h"
202 #include "nsDOMNavigationTiming.h"
203 #include "nsDSURIContentListener.h"
204 #include "nsEditingSession.h"
205 #include "nsError.h"
206 #include "nsEscape.h"
207 #include "nsFocusManager.h"
208 #include "nsGlobalWindow.h"
209 #include "nsJSEnvironment.h"
210 #include "nsNetCID.h"
211 #include "nsNetUtil.h"
212 #include "nsObjectLoadingContent.h"
213 #include "nsPingListener.h"
214 #include "nsPoint.h"
215 #include "nsQueryObject.h"
216 #include "nsQueryActor.h"
217 #include "nsRect.h"
218 #include "nsRefreshTimer.h"
219 #include "nsSandboxFlags.h"
220 #include "nsSHEntry.h"
221 #include "nsSHistory.h"
222 #include "nsSHEntry.h"
223 #include "nsStructuredCloneContainer.h"
224 #include "nsSubDocumentFrame.h"
225 #include "nsURILoader.h"
226 #include "nsURLHelper.h"
227 #include "nsView.h"
228 #include "nsViewManager.h"
229 #include "nsViewSourceHandler.h"
230 #include "nsWebBrowserFind.h"
231 #include "nsWhitespaceTokenizer.h"
232 #include "nsWidgetsCID.h"
233 #include "nsXULAppAPI.h"
235 #include "BRNameMatchingPolicy.h"
236 #include "GeckoProfiler.h"
237 #include "mozilla/NullPrincipal.h"
238 #include "Navigator.h"
239 #include "prenv.h"
240 #include "mozilla/ipc/URIUtils.h"
241 #include "sslerr.h"
242 #include "mozpkix/pkix.h"
243 #include "NSSErrorsService.h"
245 #include "timeline/JavascriptTimelineMarker.h"
246 #include "nsDocShellTelemetryUtils.h"
248 #ifdef MOZ_PLACES
249 # include "nsIFaviconService.h"
250 # include "mozIPlacesPendingOperation.h"
251 #endif
253 #if NS_PRINT_PREVIEW
254 # include "nsIDocumentViewerPrint.h"
255 # include "nsIWebBrowserPrint.h"
256 #endif
258 using namespace mozilla;
259 using namespace mozilla::dom;
260 using namespace mozilla::net;
262 using mozilla::ipc::Endpoint;
264 // Threshold value in ms for META refresh based redirects
265 #define REFRESH_REDIRECT_TIMER 15000
267 // Hint for native dispatch of events on how long to delay after
268 // all documents have loaded in milliseconds before favoring normal
269 // native event dispatch priorites over performance
270 // Can be overridden with docshell.event_starvation_delay_hint pref.
271 #define NS_EVENT_STARVATION_DELAY_HINT 2000
273 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
275 // Number of documents currently loading
276 static int32_t gNumberOfDocumentsLoading = 0;
278 static mozilla::LazyLogModule gCharsetMenuLog("CharsetMenu");
280 #define LOGCHARSETMENU(args) \
281 MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args)
283 #ifdef DEBUG
284 unsigned long nsDocShell::gNumberOfDocShells = 0;
285 static uint64_t gDocshellIDCounter = 0;
287 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
288 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
289 "DocShellAndDOMWindowLeak");
290 #endif
291 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
292 extern mozilla::LazyLogModule gPageCacheLog;
293 mozilla::LazyLogModule gSHLog("SessionHistory");
294 extern mozilla::LazyLogModule gSHIPBFCacheLog;
296 const char kAppstringsBundleURL[] =
297 "chrome://global/locale/appstrings.properties";
299 static void FavorPerformanceHint(bool aPerfOverStarvation) {
300 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
301 if (appShell) {
302 appShell->FavorPerformanceHint(
303 aPerfOverStarvation,
304 Preferences::GetUint("docshell.event_starvation_delay_hint",
305 NS_EVENT_STARVATION_DELAY_HINT));
309 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext,
310 nsILoadInfo* aLoadInfo) {
311 MOZ_ASSERT(aBrowsingContext);
312 MOZ_ASSERT(aLoadInfo);
314 if (aLoadInfo->GetExternalContentPolicyType() !=
315 ExtContentPolicy::TYPE_DOCUMENT) {
316 return false;
319 return aBrowsingContext->IsTopContent();
322 // True if loading for top level document loading in active tab.
323 static bool IsUrgentStart(BrowsingContext* aBrowsingContext,
324 nsILoadInfo* aLoadInfo, uint32_t aLoadType) {
325 MOZ_ASSERT(aBrowsingContext);
326 MOZ_ASSERT(aLoadInfo);
328 if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) {
329 return false;
332 if (aLoadType &
333 (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) {
334 return true;
337 return aBrowsingContext->IsActive();
340 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
341 uint64_t aContentWindowID)
342 : nsDocLoader(true),
343 mContentWindowID(aContentWindowID),
344 mBrowsingContext(aBrowsingContext),
345 mParentCharset(nullptr),
346 mTreeOwner(nullptr),
347 mScrollbarPref(ScrollbarPreference::Auto),
348 mCharsetReloadState(eCharsetReloadInit),
349 mParentCharsetSource(0),
350 mFrameMargins(-1, -1),
351 mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome),
352 mPreviousEntryIndex(-1),
353 mLoadedEntryIndex(-1),
354 mBusyFlags(BUSY_FLAGS_NONE),
355 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
356 mLoadType(0),
357 mFailedLoadType(0),
358 mJSRunToCompletionDepth(0),
359 mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
360 mChannelToDisconnectOnPageHide(0),
361 mCreatingDocument(false),
362 #ifdef DEBUG
363 mInEnsureScriptEnv(false),
364 #endif
365 mInitialized(false),
366 mAllowSubframes(true),
367 mAllowMetaRedirects(true),
368 mAllowImages(true),
369 mAllowMedia(true),
370 mAllowDNSPrefetch(true),
371 mAllowWindowControl(true),
372 mCSSErrorReportingEnabled(false),
373 mAllowAuth(mItemType == typeContent),
374 mAllowKeywordFixup(false),
375 mDisableMetaRefreshWhenInactive(false),
376 mIsAppTab(false),
377 mDeviceSizeIsPageSize(false),
378 mWindowDraggingAllowed(false),
379 mInFrameSwap(false),
380 mFiredUnloadEvent(false),
381 mEODForCurrentDocument(false),
382 mURIResultedInDocument(false),
383 mIsBeingDestroyed(false),
384 mIsExecutingOnLoadHandler(false),
385 mSavingOldViewer(false),
386 mInvisible(false),
387 mHasLoadedNonBlankURI(false),
388 mBlankTiming(false),
389 mTitleValidForCurrentURI(false),
390 mWillChangeProcess(false),
391 mIsNavigating(false),
392 mSuspendMediaWhenInactive(false),
393 mForcedAutodetection(false),
394 mCheckingSessionHistory(false) {
395 // If no outer window ID was provided, generate a new one.
396 if (aContentWindowID == 0) {
397 mContentWindowID = nsContentUtils::GenerateWindowId();
400 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
402 #ifdef DEBUG
403 mDocShellID = gDocshellIDCounter++;
404 // We're counting the number of |nsDocShells| to help find leaks
405 ++gNumberOfDocShells;
406 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
407 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this,
408 gNumberOfDocShells, getpid(), mDocShellID));
409 #endif
412 nsDocShell::~nsDocShell() {
413 MOZ_ASSERT(!mObserved);
415 // Avoid notifying observers while we're in the dtor.
416 mIsBeingDestroyed = true;
418 Destroy();
420 if (mContentViewer) {
421 mContentViewer->Close(nullptr);
422 mContentViewer->Destroy();
423 mContentViewer = nullptr;
426 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
428 #ifdef DEBUG
429 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
430 nsAutoCString url;
431 if (mLastOpenedURI) {
432 url = mLastOpenedURI->GetSpecOrDefault();
434 // Data URLs can be very long, so truncate to avoid flooding the log.
435 const uint32_t maxURLLength = 1000;
436 if (url.Length() > maxURLLength) {
437 url.Truncate(maxURLLength);
441 // We're counting the number of |nsDocShells| to help find leaks
442 --gNumberOfDocShells;
443 MOZ_LOG(
444 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
445 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n",
446 (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get()));
448 #endif
451 bool nsDocShell::Initialize() {
452 if (mInitialized) {
453 // We've already been initialized.
454 return true;
457 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
458 "Unexpected item type in docshell");
460 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
461 mInitialized = true;
463 mDisableMetaRefreshWhenInactive =
464 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
465 mDisableMetaRefreshWhenInactive);
467 mDeviceSizeIsPageSize = Preferences::GetBool(
468 "docshell.device_size_is_page_size", mDeviceSizeIsPageSize);
470 if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) {
471 const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
472 : NS_CHROME_WEBNAVIGATION_CREATE;
473 serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr);
476 return true;
479 /* static */
480 already_AddRefed<nsDocShell> nsDocShell::Create(
481 BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
482 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
484 nsresult rv;
485 RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
487 // Initialize the underlying nsDocLoader.
488 rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
489 if (NS_WARN_IF(NS_FAILED(rv))) {
490 return nullptr;
493 // Create our ContentListener
494 ds->mContentListener = new nsDSURIContentListener(ds);
496 // We enable if we're in the parent process in order to support non-e10s
497 // configurations.
498 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
499 // constructor.
500 if (XRE_IsParentProcess()) {
501 ds->mInterceptController = new ServiceWorkerInterceptController();
504 // We want to hold a strong ref to the loadgroup, so it better hold a weak
505 // ref to us... use an InterfaceRequestorProxy to do this.
506 nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
507 ds->mLoadGroup->SetNotificationCallbacks(proxy);
509 // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
510 // It could be nice to directly set up our DocLoader tree?
511 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
512 if (NS_WARN_IF(NS_FAILED(rv))) {
513 return nullptr;
516 // Add |ds| as a progress listener to itself. A little weird, but simpler
517 // than reproducing all the listener-notification logic in overrides of the
518 // various methods via which nsDocLoader can be notified. Note that this
519 // holds an nsWeakPtr to |ds|, so it's ok.
520 rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
521 nsIWebProgress::NOTIFY_STATE_NETWORK |
522 nsIWebProgress::NOTIFY_LOCATION);
523 if (NS_WARN_IF(NS_FAILED(rv))) {
524 return nullptr;
527 // If our BrowsingContext has private browsing enabled, update the number of
528 // private browsing docshells.
529 if (aBrowsingContext->UsePrivateBrowsing()) {
530 ds->NotifyPrivateBrowsingChanged();
533 // If our parent window is present in this process, set up our parent now.
534 RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext();
535 if (parentWC && parentWC->IsInProcess()) {
536 // If we don't have a parent element anymore, we can't finish this load!
537 // How'd we get here?
538 RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement();
539 if (!parentElement) {
540 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
541 return nullptr;
544 // We have an in-process parent window, but don't have a parent nsDocShell?
545 // How'd we get here!
546 nsCOMPtr<nsIDocShell> parentShell =
547 parentElement->OwnerDoc()->GetDocShell();
548 if (!parentShell) {
549 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
550 return nullptr;
552 parentShell->AddChild(ds);
555 // Make |ds| the primary DocShell for the given context.
556 aBrowsingContext->SetDocShell(ds);
558 // Set |ds| default load flags on load group.
559 ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
561 if (XRE_IsParentProcess()) {
562 aBrowsingContext->Canonical()->MaybeAddAsProgressListener(ds);
565 return ds.forget();
568 void nsDocShell::DestroyChildren() {
569 for (auto* child : mChildList.ForwardRange()) {
570 nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child);
571 NS_ASSERTION(shell, "docshell has null child");
573 if (shell) {
574 shell->SetTreeOwner(nullptr);
578 nsDocLoader::DestroyChildren();
581 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
582 mScriptGlobal, mInitialClientSource,
583 mBrowsingContext,
584 mChromeEventHandler)
586 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
587 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
589 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
590 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
591 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
592 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
593 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
594 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
595 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
596 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
597 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
598 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
599 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
600 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
601 mInterceptController)
602 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
604 NS_IMETHODIMP
605 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
606 MOZ_ASSERT(aSink, "null out param");
608 *aSink = nullptr;
610 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
611 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
612 *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
613 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
614 *aSink = mContentListener;
615 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
616 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
617 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
618 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
619 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
620 NS_SUCCEEDED(EnsureScriptEnvironment())) {
621 return mScriptGlobal->QueryInterface(aIID, aSink);
622 } else if (aIID.Equals(NS_GET_IID(Document)) &&
623 NS_SUCCEEDED(EnsureContentViewer())) {
624 RefPtr<Document> doc = mContentViewer->GetDocument();
625 doc.forget(aSink);
626 return *aSink ? NS_OK : NS_NOINTERFACE;
627 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
628 NS_SUCCEEDED(EnsureScriptEnvironment())) {
629 nsresult rv;
630 nsCOMPtr<nsIWindowWatcher> wwatch =
631 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
632 NS_ENSURE_SUCCESS(rv, rv);
634 // Get the an auth prompter for our window so that the parenting
635 // of the dialogs works as it should when using tabs.
636 nsIPrompt* prompt;
637 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
638 NS_ENSURE_SUCCESS(rv, rv);
640 *aSink = prompt;
641 return NS_OK;
642 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
643 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
644 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
645 ? NS_OK
646 : NS_NOINTERFACE;
647 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
648 // This is deprecated, you should instead directly get
649 // ChildSHistory from the browsing context.
650 MOZ_DIAGNOSTIC_ASSERT(
651 false, "Do not try to get a nsISHistory interface from nsIDocShell");
652 return NS_NOINTERFACE;
653 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
654 nsresult rv = EnsureFind();
655 if (NS_FAILED(rv)) {
656 return rv;
659 *aSink = mFind;
660 NS_ADDREF((nsISupports*)*aSink);
661 return NS_OK;
662 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
663 if (PresShell* presShell = GetPresShell()) {
664 return presShell->QueryInterface(aIID, aSink);
666 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
667 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
668 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
669 if (NS_SUCCEEDED(rv) && treeOwner) {
670 return treeOwner->QueryInterface(aIID, aSink);
672 } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
673 *aSink = GetBrowserChild().take();
674 return *aSink ? NS_OK : NS_ERROR_FAILURE;
675 } else {
676 return nsDocLoader::GetInterface(aIID, aSink);
679 NS_IF_ADDREF(((nsISupports*)*aSink));
680 return *aSink ? NS_OK : NS_NOINTERFACE;
683 NS_IMETHODIMP
684 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
685 // Note: this gets called fairly early (before a pageload actually starts).
686 // We could probably defer this even longer.
687 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
688 static_cast<BrowserChild*>(browserChild.get())
689 ->SetCancelContentJSEpoch(aEpoch);
690 return NS_OK;
693 nsresult nsDocShell::CheckDisallowedJavascriptLoad(
694 nsDocShellLoadState* aLoadState) {
695 if (!net::SchemeIsJavascript(aLoadState->URI())) {
696 return NS_OK;
699 if (nsCOMPtr<nsIPrincipal> targetPrincipal =
700 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
701 if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) {
702 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
704 return NS_OK;
706 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
709 NS_IMETHODIMP
710 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
711 return LoadURI(aLoadState, aSetNavigating, false);
714 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
715 bool aSetNavigating,
716 bool aContinueHandlingSubframeHistory) {
717 MOZ_ASSERT(aLoadState, "Must have a valid load state!");
718 // NOTE: This comparison between what appears to be internal/external load
719 // flags is intentional, as it's ensuring that the caller isn't using any of
720 // the flags reserved for implementations by the `nsIWebNavigation` interface.
721 // In the future, this check may be dropped.
722 MOZ_ASSERT(
723 (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
724 "Should not have these flags set");
725 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
726 "Targeting doesn't occur until InternalLoad");
728 if (!aLoadState->TriggeringPrincipal()) {
729 MOZ_ASSERT(false, "LoadURI must have a triggering principal");
730 return NS_ERROR_FAILURE;
733 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
735 bool oldIsNavigating = mIsNavigating;
736 auto cleanupIsNavigating =
737 MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
738 if (aSetNavigating) {
739 mIsNavigating = true;
742 PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden;
743 if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) {
744 popupState = PopupBlocker::openAllowed;
745 // If we allow popups as part of the navigation, ensure we fake a user
746 // interaction, so that popups can, in fact, be allowed to open.
747 if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) {
748 wc->NotifyUserGestureActivation();
752 AutoPopupStatePusher statePusher(popupState);
754 if (aLoadState->GetCancelContentJSEpoch().isSome()) {
755 SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
758 // Note: we allow loads to get through here even if mFiredUnloadEvent is
759 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
760 // so we pass false as the second parameter to IsNavigationAllowed.
761 // However, we don't allow the page to change location *in the middle of*
762 // firing beforeunload, so we do need to check if *beforeunload* is currently
763 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
764 if (!IsNavigationAllowed(true, false)) {
765 return NS_OK; // JS may not handle returning of an error code
768 nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
769 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) {
770 defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
771 } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) {
772 defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
775 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags));
777 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
778 mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
779 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
782 // LoadType used to be set to a default value here, if no LoadInfo/LoadState
783 // object was passed in. That functionality has been removed as of bug
784 // 1492648. LoadType should now be set up by the caller at the time they
785 // create their nsDocShellLoadState object to pass into LoadURI.
787 MOZ_LOG(
788 gDocShellLeakLog, LogLevel::Debug,
789 ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
790 aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
792 // Always clear mCheckingSessionHistory. MaybeHandleSubframeHistory uses it
793 // internally when querying session history information from the parent
794 // process.
795 mCheckingSessionHistory = false;
797 if ((!aLoadState->LoadIsFromSessionHistory() &&
798 !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
799 LOAD_FLAGS_REPLACE_HISTORY)) ||
800 aContinueHandlingSubframeHistory) {
801 // This is possibly a subframe, so handle it accordingly.
803 // If history exists, it will be loaded into the aLoadState object, and the
804 // LoadType will be changed.
805 if (MaybeHandleSubframeHistory(aLoadState,
806 aContinueHandlingSubframeHistory)) {
807 // MaybeHandleSubframeHistory returns true if we need to continue loading
808 // asynchronously.
809 return NS_OK;
813 if (aLoadState->LoadIsFromSessionHistory()) {
814 MOZ_LOG(gSHLog, LogLevel::Debug,
815 ("nsDocShell[%p]: loading from session history", this));
817 if (!mozilla::SessionHistoryInParent()) {
818 return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType(),
819 aLoadState->HasValidUserGestureActivation());
822 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
823 return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(),
824 aLoadState->LoadType(),
825 aLoadState->HasValidUserGestureActivation());
828 // On history navigation via Back/Forward buttons, don't execute
829 // automatic JavaScript redirection such as |location.href = ...| or
830 // |window.open()|
832 // LOAD_NORMAL: window.open(...) etc.
833 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
834 if ((aLoadState->LoadType() == LOAD_NORMAL ||
835 aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
836 ShouldBlockLoadingForBackButton()) {
837 return NS_OK;
840 BrowsingContext::Type bcType = mBrowsingContext->GetType();
842 // Set up the inheriting principal in LoadState.
843 nsresult rv = aLoadState->SetupInheritingPrincipal(
844 bcType, mBrowsingContext->OriginAttributesRef());
845 NS_ENSURE_SUCCESS(rv, rv);
847 rv = aLoadState->SetupTriggeringPrincipal(
848 mBrowsingContext->OriginAttributesRef());
849 NS_ENSURE_SUCCESS(rv, rv);
851 aLoadState->CalculateLoadURIFlags();
853 MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
854 "Typehint should be null when calling InternalLoad from LoadURI");
855 MOZ_ASSERT(aLoadState->FileName().IsVoid(),
856 "FileName should be null when calling InternalLoad from LoadURI");
857 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(),
858 "Shouldn't be loading from an entry when calling InternalLoad "
859 "from LoadURI");
861 // If we have a system triggering principal, we can assume that this load was
862 // triggered by some UI in the browser chrome, such as the URL bar or
863 // bookmark bar. This should count as a user interaction for the current sh
864 // entry, so that the user may navigate back to the current entry, from the
865 // entry that is going to be added as part of this load.
866 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
867 aLoadState->TriggeringPrincipal();
868 if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) {
869 if (mozilla::SessionHistoryInParent()) {
870 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
871 if (topWc && !topWc->IsDiscarded()) {
872 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
874 } else {
875 bool oshe = false;
876 nsCOMPtr<nsISHEntry> currentSHEntry;
877 GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe);
878 if (currentSHEntry) {
879 currentSHEntry->SetHasUserInteraction(true);
884 rv = InternalLoad(aLoadState);
885 NS_ENSURE_SUCCESS(rv, rv);
887 if (aLoadState->GetOriginalURIString().isSome()) {
888 // Save URI string in case it's needed later when
889 // sending to search engine service in EndPageLoad()
890 mOriginalUriString = *aLoadState->GetOriginalURIString();
893 return NS_OK;
896 bool nsDocShell::IsLoadingFromSessionHistory() {
897 return mActiveEntryIsLoadingFromSessionHistory;
900 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
901 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
902 // detect Cancel calls.
903 class StopDetector final : public nsIRequest {
904 public:
905 StopDetector() = default;
907 NS_DECL_ISUPPORTS
908 NS_DECL_NSIREQUEST
910 bool Canceled() { return mCanceled; }
912 private:
913 ~StopDetector() = default;
915 bool mCanceled = false;
918 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
920 NS_IMETHODIMP
921 StopDetector::GetName(nsACString& aResult) {
922 aResult.AssignLiteral("about:stop-detector");
923 return NS_OK;
926 NS_IMETHODIMP
927 StopDetector::IsPending(bool* aRetVal) {
928 *aRetVal = true;
929 return NS_OK;
932 NS_IMETHODIMP
933 StopDetector::GetStatus(nsresult* aStatus) {
934 *aStatus = NS_OK;
935 return NS_OK;
938 NS_IMETHODIMP
939 StopDetector::Cancel(nsresult aStatus) {
940 mCanceled = true;
941 return NS_OK;
944 NS_IMETHODIMP
945 StopDetector::Suspend(void) { return NS_OK; }
946 NS_IMETHODIMP
947 StopDetector::Resume(void) { return NS_OK; }
949 NS_IMETHODIMP
950 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
951 *aLoadGroup = nullptr;
952 return NS_OK;
955 NS_IMETHODIMP
956 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
958 NS_IMETHODIMP
959 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
960 *aLoadFlags = nsIRequest::LOAD_NORMAL;
961 return NS_OK;
964 NS_IMETHODIMP
965 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
966 return GetTRRModeImpl(aTRRMode);
969 NS_IMETHODIMP
970 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
971 return SetTRRModeImpl(aTRRMode);
974 NS_IMETHODIMP
975 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
977 bool nsDocShell::MaybeHandleSubframeHistory(
978 nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
979 // First, verify if this is a subframe.
980 // Note, it is ok to rely on docshell here and not browsing context since when
981 // an iframe is created, it has first in-process docshell.
982 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
983 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
984 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
986 if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
987 if (mBrowsingContext && mBrowsingContext->IsTop()) {
988 // This is the root docshell. If we got here while
989 // executing an onLoad Handler,this load will not go
990 // into session history.
991 // XXX Why is this code in a method which deals with iframes!
992 bool inOnLoadHandler = false;
993 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
994 if (inOnLoadHandler) {
995 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
998 return false;
1001 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
1002 * loaded through a history mechanism, then get the SH entry for the child
1003 * from the parent. This is done to restore frameset navigation while going
1004 * back/forward. If the parent was loaded through any other loadType, set the
1005 * child's loadType too accordingly, so that session history does not get
1006 * confused.
1009 // Get the parent's load type
1010 uint32_t parentLoadType;
1011 parentDS->GetLoadType(&parentLoadType);
1013 if (!aContinueHandlingSubframeHistory) {
1014 if (mozilla::SessionHistoryInParent()) {
1015 if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
1016 !GetCreatedDynamically()) {
1017 if (XRE_IsContentProcess()) {
1018 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
1019 nsCOMPtr<nsILoadGroup> loadGroup;
1020 GetLoadGroup(getter_AddRefs(loadGroup));
1021 if (contentChild && loadGroup && !mCheckingSessionHistory) {
1022 RefPtr<Document> parentDoc = parentDS->GetDocument();
1023 parentDoc->BlockOnload();
1024 RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
1025 Maybe<uint64_t> currentLoadIdentifier =
1026 mBrowsingContext->GetCurrentLoadIdentifier();
1027 RefPtr<nsDocShellLoadState> loadState = aLoadState;
1028 bool isNavigating = mIsNavigating;
1029 RefPtr<StopDetector> stopDetector = new StopDetector();
1030 loadGroup->AddRequest(stopDetector, nullptr);
1031 // Need to set mCheckingSessionHistory so that
1032 // GetIsAttemptingToNavigate() returns true.
1033 mCheckingSessionHistory = true;
1035 auto resolve =
1036 [currentLoadIdentifier, browsingContext, parentDoc, loadState,
1037 isNavigating, loadGroup, stopDetector](
1038 mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
1039 RefPtr<nsDocShell> docShell =
1040 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1041 auto unblockParent = MakeScopeExit(
1042 [loadGroup, stopDetector, parentDoc, docShell]() {
1043 if (docShell) {
1044 docShell->mCheckingSessionHistory = false;
1046 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1047 parentDoc->UnblockOnload(false);
1050 if (!docShell || !docShell->mCheckingSessionHistory) {
1051 return;
1054 if (stopDetector->Canceled()) {
1055 return;
1057 if (currentLoadIdentifier ==
1058 browsingContext->GetCurrentLoadIdentifier() &&
1059 aResult.isSome()) {
1060 loadState->SetLoadingSessionHistoryInfo(aResult.value());
1061 // This is an initial subframe load from the session
1062 // history, index doesn't need to be updated.
1063 loadState->SetLoadIsFromSessionHistory(0, false);
1066 // We got the results back from the parent process, call
1067 // LoadURI again with the possibly updated data.
1068 docShell->LoadURI(loadState, isNavigating, true);
1070 auto reject = [loadGroup, stopDetector, browsingContext,
1071 parentDoc](mozilla::ipc::ResponseRejectReason) {
1072 RefPtr<nsDocShell> docShell =
1073 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1074 if (docShell) {
1075 docShell->mCheckingSessionHistory = false;
1077 // In practise reject shouldn't be called ever.
1078 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1079 parentDoc->UnblockOnload(false);
1081 contentChild->SendGetLoadingSessionHistoryInfoFromParent(
1082 mBrowsingContext, std::move(resolve), std::move(reject));
1083 return true;
1085 } else {
1086 Maybe<LoadingSessionHistoryInfo> info;
1087 mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
1088 info);
1089 if (info.isSome()) {
1090 aLoadState->SetLoadingSessionHistoryInfo(info.value());
1091 // This is an initial subframe load from the session
1092 // history, index doesn't need to be updated.
1093 aLoadState->SetLoadIsFromSessionHistory(0, false);
1097 } else {
1098 // Get the ShEntry for the child from the parent
1099 nsCOMPtr<nsISHEntry> currentSH;
1100 bool oshe = false;
1101 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1102 bool dynamicallyAddedChild = GetCreatedDynamically();
1104 if (!dynamicallyAddedChild && !oshe && currentSH) {
1105 // Only use the old SHEntry, if we're sure enough that
1106 // it wasn't originally for some other frame.
1107 nsCOMPtr<nsISHEntry> shEntry;
1108 currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
1109 mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry));
1110 if (shEntry) {
1111 aLoadState->SetSHEntry(shEntry);
1117 // Make some decisions on the child frame's loadType based on the
1118 // parent's loadType, if the subframe hasn't loaded anything into it.
1120 // In some cases privileged scripts may try to get the DOMWindow
1121 // reference of this docshell before the loading starts, causing the
1122 // initial about:blank content viewer being created and mCurrentURI being
1123 // set. To handle this case we check if mCurrentURI is about:blank and
1124 // currentSHEntry is null.
1125 bool oshe = false;
1126 nsCOMPtr<nsISHEntry> currentChildEntry;
1127 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
1129 if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry ||
1130 mLoadingEntry || mActiveEntry)) {
1131 // This is a pre-existing subframe. If
1132 // 1. The load of this frame was not originally initiated by session
1133 // history directly (i.e. (!shEntry) condition succeeded, but it can
1134 // still be a history load on parent which causes this frame being
1135 // loaded), which we checked with the above assert, and
1136 // 2. mCurrentURI is not null, nor the initial about:blank,
1137 // it is possible that a parent's onLoadHandler or even self's
1138 // onLoadHandler is loading a new page in this child. Check parent's and
1139 // self's busy flag and if it is set, we don't want this onLoadHandler
1140 // load to get in to session history.
1141 BusyFlags parentBusy = parentDS->GetBusyFlags();
1142 BusyFlags selfBusy = GetBusyFlags();
1144 if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
1145 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1146 aLoadState->ClearLoadIsFromSessionHistory();
1148 return false;
1151 // This is a newly created frame. Check for exception cases first.
1152 // By default the subframe will inherit the parent's loadType.
1153 if (aLoadState->LoadIsFromSessionHistory() &&
1154 (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) {
1155 // The parent was loaded normally. In this case, this *brand new*
1156 // child really shouldn't have a SHEntry. If it does, it could be
1157 // because the parent is replacing an existing frame with a new frame,
1158 // in the onLoadHandler. We don't want this url to get into session
1159 // history. Clear off shEntry, and set load type to
1160 // LOAD_BYPASS_HISTORY.
1161 bool inOnLoadHandler = false;
1162 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1163 if (inOnLoadHandler) {
1164 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1165 aLoadState->ClearLoadIsFromSessionHistory();
1167 } else if (parentLoadType == LOAD_REFRESH) {
1168 // Clear shEntry. For refresh loads, we have to load
1169 // what comes through the pipe, not what's in history.
1170 aLoadState->ClearLoadIsFromSessionHistory();
1171 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1172 (aLoadState->LoadIsFromSessionHistory() &&
1173 ((parentLoadType & LOAD_CMD_HISTORY) ||
1174 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1175 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
1176 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
1177 (parentLoadType ==
1178 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
1179 // If the parent url, bypassed history or was loaded from
1180 // history, pass on the parent's loadType to the new child
1181 // frame too, so that the child frame will also
1182 // avoid getting into history.
1183 aLoadState->SetLoadType(parentLoadType);
1184 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1185 // If the parent document is an error page, we don't
1186 // want to update global/session history. However,
1187 // this child frame is not an error page.
1188 aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
1189 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1190 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1191 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1192 // the new frame should inherit the parent's load type so that it also
1193 // bypasses the cache and/or proxy
1194 aLoadState->SetLoadType(parentLoadType);
1197 return false;
1201 * Reset state to a new content model within the current document and the
1202 * document viewer. Called by the document before initiating an out of band
1203 * document.write().
1205 NS_IMETHODIMP
1206 nsDocShell::PrepareForNewContentModel() {
1207 // Clear out our form control state, because the state of controls
1208 // in the pre-open() document should not affect the state of
1209 // controls that are now going to be written.
1210 SetLayoutHistoryState(nullptr);
1211 mEODForCurrentDocument = false;
1212 return NS_OK;
1215 NS_IMETHODIMP
1216 nsDocShell::FirePageHideNotification(bool aIsUnload) {
1217 FirePageHideNotificationInternal(aIsUnload, false);
1218 return NS_OK;
1221 void nsDocShell::FirePageHideNotificationInternal(
1222 bool aIsUnload, bool aSkipCheckingDynEntries) {
1223 if (mContentViewer && !mFiredUnloadEvent) {
1224 // Keep an explicit reference since calling PageHide could release
1225 // mContentViewer
1226 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1227 mFiredUnloadEvent = true;
1229 if (mTiming) {
1230 mTiming->NotifyUnloadEventStart();
1233 contentViewer->PageHide(aIsUnload);
1235 if (mTiming) {
1236 mTiming->NotifyUnloadEventEnd();
1239 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1240 uint32_t n = mChildList.Length();
1241 kids.SetCapacity(n);
1242 for (uint32_t i = 0; i < n; i++) {
1243 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1246 n = kids.Length();
1247 for (uint32_t i = 0; i < n; ++i) {
1248 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1249 if (child) {
1250 // Skip checking dynamic subframe entries in our children.
1251 child->FirePageHideNotificationInternal(aIsUnload, true);
1255 // If the document is unloading, remove all dynamic subframe entries.
1256 if (aIsUnload && !aSkipCheckingDynEntries) {
1257 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
1258 if (rootSH) {
1259 MOZ_LOG(
1260 gSHLog, LogLevel::Debug,
1261 ("nsDocShell %p unloading, remove dynamic subframe entries", this));
1262 if (mozilla::SessionHistoryInParent()) {
1263 if (mActiveEntry) {
1264 mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry();
1266 MOZ_LOG(gSHLog, LogLevel::Debug,
1267 ("nsDocShell %p unloading, no active entries", this));
1268 } else if (mOSHE) {
1269 int32_t index = rootSH->Index();
1270 rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
1275 // Now make sure our editor, if any, is detached before we go
1276 // any farther.
1277 DetachEditorFromWindow();
1281 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) {
1282 MOZ_ASSERT(mozilla::BFCacheInParent());
1284 if (!mScriptGlobal) {
1285 return;
1288 RefPtr<nsGlobalWindowInner> inner =
1289 mScriptGlobal->GetCurrentInnerWindowInternal();
1290 if (inner) {
1291 if (aThaw) {
1292 inner->Thaw(false);
1293 } else {
1294 inner->Freeze(false);
1299 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
1300 MOZ_ASSERT(mozilla::BFCacheInParent());
1302 if (!mContentViewer) {
1303 return;
1306 // Emulate what non-SHIP BFCache does too. In pageshow case
1307 // add and remove a request and before that call SetCurrentURI to get
1308 // the location change notification.
1309 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
1310 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1311 if (aShow) {
1312 contentViewer->SetIsHidden(false);
1313 mRefreshURIList = std::move(mBFCachedRefreshURIList);
1314 RefreshURIFromQueue();
1315 mFiredUnloadEvent = false;
1316 RefPtr<Document> doc = contentViewer->GetDocument();
1317 if (doc) {
1318 doc->NotifyActivityChanged();
1319 RefPtr<nsGlobalWindowInner> inner =
1320 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal()
1321 : nullptr;
1322 if (mBrowsingContext->IsTop()) {
1323 doc->NotifyPossibleTitleChange(false);
1324 if (inner) {
1325 // Now that we have found the inner window of the page restored
1326 // from the history, we have to make sure that
1327 // performance.navigation.type is 2.
1328 // Traditionally this type change has been done to the top level page
1329 // only.
1330 inner->GetPerformance()->GetDOMTiming()->NotifyRestoreStart();
1334 nsCOMPtr<nsIChannel> channel = doc->GetChannel();
1335 if (channel) {
1336 SetLoadType(LOAD_HISTORY);
1337 mEODForCurrentDocument = false;
1338 mIsRestoringDocument = true;
1339 mLoadGroup->AddRequest(channel, nullptr);
1340 SetCurrentURI(doc->GetDocumentURI(), channel,
1341 /* aFireOnLocationChange */ true,
1342 /* aIsInitialAboutBlank */ false, /* aLocationFlags */ 0);
1343 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
1344 mIsRestoringDocument = false;
1346 RefPtr<PresShell> presShell = GetPresShell();
1347 if (presShell) {
1348 presShell->Thaw(false);
1351 if (inner) {
1352 inner->FireDelayedDOMEvents(false);
1355 } else if (!mFiredUnloadEvent) {
1356 // XXXBFCache check again that the page can enter bfcache.
1357 // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
1359 if (mRefreshURIList) {
1360 RefreshURIToQueue();
1361 mBFCachedRefreshURIList = std::move(mRefreshURIList);
1362 } else {
1363 // If Stop was called, the list was moved to mSavedRefreshURIList after
1364 // calling SuspendRefreshURIs, which calls RefreshURIToQueue.
1365 mBFCachedRefreshURIList = std::move(mSavedRefreshURIList);
1368 mFiredUnloadEvent = true;
1369 contentViewer->PageHide(false);
1371 RefPtr<PresShell> presShell = GetPresShell();
1372 if (presShell) {
1373 presShell->Freeze(false);
1378 nsresult nsDocShell::Dispatch(TaskCategory aCategory,
1379 already_AddRefed<nsIRunnable>&& aRunnable) {
1380 nsCOMPtr<nsIRunnable> runnable(aRunnable);
1381 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1382 if (NS_WARN_IF(!win)) {
1383 // Window should only be unavailable after destroyed.
1384 MOZ_ASSERT(mIsBeingDestroyed);
1385 return NS_ERROR_FAILURE;
1388 if (win->GetDocGroup()) {
1389 return win->GetDocGroup()->Dispatch(aCategory, runnable.forget());
1392 return SchedulerGroup::Dispatch(aCategory, runnable.forget());
1395 NS_IMETHODIMP
1396 nsDocShell::DispatchLocationChangeEvent() {
1397 return Dispatch(
1398 TaskCategory::Other,
1399 NewRunnableMethod("nsDocShell::FireDummyOnLocationChange", this,
1400 &nsDocShell::FireDummyOnLocationChange));
1403 NS_IMETHODIMP
1404 nsDocShell::StartDelayedAutoplayMediaComponents() {
1405 RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow();
1406 if (outerWindow) {
1407 outerWindow->ActivateMediaComponents();
1409 return NS_OK;
1412 bool nsDocShell::MaybeInitTiming() {
1413 if (mTiming && !mBlankTiming) {
1414 return false;
1417 bool canBeReset = false;
1419 if (mScriptGlobal && mBlankTiming) {
1420 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
1421 if (innerWin && innerWin->GetPerformance()) {
1422 mTiming = innerWin->GetPerformance()->GetDOMTiming();
1423 mBlankTiming = false;
1427 if (!mTiming) {
1428 mTiming = new nsDOMNavigationTiming(this);
1429 canBeReset = true;
1432 mTiming->NotifyNavigationStart(
1433 mBrowsingContext->IsActive()
1434 ? nsDOMNavigationTiming::DocShellState::eActive
1435 : nsDOMNavigationTiming::DocShellState::eInactive);
1437 return canBeReset;
1440 void nsDocShell::MaybeResetInitTiming(bool aReset) {
1441 if (aReset) {
1442 mTiming = nullptr;
1446 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
1447 return mTiming;
1451 // Bug 13871: Prevent frameset spoofing
1453 // This routine answers: 'Is origin's document from same domain as
1454 // target's document?'
1456 // file: uris are considered the same domain for the purpose of
1457 // frame navigation regardless of script accessibility (bug 420425)
1459 /* static */
1460 bool nsDocShell::ValidateOrigin(BrowsingContext* aOrigin,
1461 BrowsingContext* aTarget) {
1462 nsIDocShell* originDocShell = aOrigin->GetDocShell();
1463 MOZ_ASSERT(originDocShell, "originDocShell must not be null");
1464 Document* originDocument = originDocShell->GetDocument();
1465 NS_ENSURE_TRUE(originDocument, false);
1467 nsIDocShell* targetDocShell = aTarget->GetDocShell();
1468 MOZ_ASSERT(targetDocShell, "targetDocShell must not be null");
1469 Document* targetDocument = targetDocShell->GetDocument();
1470 NS_ENSURE_TRUE(targetDocument, false);
1472 bool equal;
1473 nsresult rv = originDocument->NodePrincipal()->Equals(
1474 targetDocument->NodePrincipal(), &equal);
1475 if (NS_SUCCEEDED(rv) && equal) {
1476 return true;
1478 // Not strictly equal, special case if both are file: uris
1479 nsCOMPtr<nsIURI> originURI;
1480 nsCOMPtr<nsIURI> targetURI;
1481 nsCOMPtr<nsIURI> innerOriginURI;
1482 nsCOMPtr<nsIURI> innerTargetURI;
1484 // Casting to BasePrincipal, as we can't get InnerMost URI otherwise
1485 auto* originDocumentBasePrincipal =
1486 BasePrincipal::Cast(originDocument->NodePrincipal());
1488 rv = originDocumentBasePrincipal->GetURI(getter_AddRefs(originURI));
1489 if (NS_SUCCEEDED(rv) && originURI) {
1490 innerOriginURI = NS_GetInnermostURI(originURI);
1493 auto* targetDocumentBasePrincipal =
1494 BasePrincipal::Cast(targetDocument->NodePrincipal());
1496 rv = targetDocumentBasePrincipal->GetURI(getter_AddRefs(targetURI));
1497 if (NS_SUCCEEDED(rv) && targetURI) {
1498 innerTargetURI = NS_GetInnermostURI(targetURI);
1501 return innerOriginURI && innerTargetURI && SchemeIsFile(innerOriginURI) &&
1502 SchemeIsFile(innerTargetURI);
1505 nsPresContext* nsDocShell::GetEldestPresContext() {
1506 nsIContentViewer* viewer = mContentViewer;
1507 while (viewer) {
1508 nsIContentViewer* prevViewer = viewer->GetPreviousViewer();
1509 if (!prevViewer) {
1510 return viewer->GetPresContext();
1512 viewer = prevViewer;
1515 return nullptr;
1518 nsPresContext* nsDocShell::GetPresContext() {
1519 if (!mContentViewer) {
1520 return nullptr;
1523 return mContentViewer->GetPresContext();
1526 PresShell* nsDocShell::GetPresShell() {
1527 nsPresContext* presContext = GetPresContext();
1528 return presContext ? presContext->GetPresShell() : nullptr;
1531 PresShell* nsDocShell::GetEldestPresShell() {
1532 nsPresContext* presContext = GetEldestPresContext();
1534 if (presContext) {
1535 return presContext->GetPresShell();
1538 return nullptr;
1541 NS_IMETHODIMP
1542 nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer) {
1543 NS_ENSURE_ARG_POINTER(aContentViewer);
1545 *aContentViewer = mContentViewer;
1546 NS_IF_ADDREF(*aContentViewer);
1547 return NS_OK;
1550 NS_IMETHODIMP
1551 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) {
1552 *aWindowID = mContentWindowID;
1553 return NS_OK;
1556 NS_IMETHODIMP
1557 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) {
1558 mChromeEventHandler = aChromeEventHandler;
1560 if (mScriptGlobal) {
1561 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1564 return NS_OK;
1567 NS_IMETHODIMP
1568 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) {
1569 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1570 RefPtr<EventTarget> handler = mChromeEventHandler;
1571 handler.forget(aChromeEventHandler);
1572 return NS_OK;
1575 NS_IMETHODIMP
1576 nsDocShell::SetCurrentURI(nsIURI* aURI) {
1577 // Note that securityUI will set STATE_IS_INSECURE, even if
1578 // the scheme of |aURI| is "https".
1579 SetCurrentURI(aURI, nullptr, /* aFireOnLocationChange */ true,
1580 /* aIsInitialAboutBlank */ false, /* aLocationFlags */ 0);
1581 return NS_OK;
1584 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1585 bool aFireOnLocationChange,
1586 bool aIsInitialAboutBlank,
1587 uint32_t aLocationFlags) {
1588 MOZ_ASSERT(!mIsBeingDestroyed);
1590 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1591 ("DOCSHELL %p SetCurrentURI %s\n", this,
1592 aURI ? aURI->GetSpecOrDefault().get() : ""));
1594 // We don't want to send a location change when we're displaying an error
1595 // page, and we don't want to change our idea of "current URI" either
1596 if (mLoadType == LOAD_ERROR_PAGE) {
1597 return false;
1600 bool uriIsEqual = false;
1601 if (!mCurrentURI || !aURI ||
1602 NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
1603 mTitleValidForCurrentURI = false;
1606 mCurrentURI = aURI;
1608 #ifdef DEBUG
1609 mLastOpenedURI = aURI;
1610 #endif
1612 if (!NS_IsAboutBlank(mCurrentURI)) {
1613 mHasLoadedNonBlankURI = true;
1616 // Don't fire onLocationChange when creating a subframe's initial about:blank
1617 // document, as this can happen when it's not safe for us to run script.
1618 if (aIsInitialAboutBlank && !mHasLoadedNonBlankURI &&
1619 !mBrowsingContext->IsTop()) {
1620 MOZ_ASSERT(!aRequest && aLocationFlags == 0);
1621 return false;
1624 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1626 if (aFireOnLocationChange) {
1627 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1629 return !aFireOnLocationChange;
1632 NS_IMETHODIMP
1633 nsDocShell::GetCharset(nsACString& aCharset) {
1634 aCharset.Truncate();
1636 PresShell* presShell = GetPresShell();
1637 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1638 Document* doc = presShell->GetDocument();
1639 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1640 doc->GetDocumentCharacterSet()->Name(aCharset);
1641 return NS_OK;
1644 NS_IMETHODIMP
1645 nsDocShell::ForceEncodingDetection() {
1646 nsCOMPtr<nsIContentViewer> viewer;
1647 GetContentViewer(getter_AddRefs(viewer));
1648 if (!viewer) {
1649 return NS_OK;
1652 Document* doc = viewer->GetDocument();
1653 if (!doc || doc->WillIgnoreCharsetOverride()) {
1654 return NS_OK;
1657 mForcedAutodetection = true;
1659 LOGCHARSETMENU(("ENCODING_OVERRIDE_USED_AUTOMATIC"));
1660 Telemetry::ScalarSet(Telemetry::ScalarID::ENCODING_OVERRIDE_USED_AUTOMATIC,
1661 true);
1663 nsIURI* url = doc->GetOriginalURI();
1664 bool isFileURL = url && SchemeIsFile(url);
1666 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1667 auto encoding = doc->GetDocumentCharacterSet();
1668 switch (charsetSource) {
1669 case kCharsetFromInitialUserForcedAutoDetection:
1670 case kCharsetFromFinalUserForcedAutoDetection:
1671 LOGCHARSETMENU(("AutoOverridden"));
1672 Telemetry::AccumulateCategorical(
1673 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::AutoOverridden);
1674 break;
1675 case kCharsetFromInitialAutoDetectionASCII:
1676 // Deliberately no final version
1677 LOGCHARSETMENU(("UnlabeledAscii"));
1678 Telemetry::AccumulateCategorical(
1679 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::UnlabeledAscii);
1680 break;
1681 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1682 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1683 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1684 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1685 LOGCHARSETMENU(("UnlabeledNonUtf8"));
1686 Telemetry::AccumulateCategorical(
1687 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::UnlabeledNonUtf8);
1688 break;
1689 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1690 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1691 LOGCHARSETMENU(("UnlabeledNonUtf8TLD"));
1692 Telemetry::AccumulateCategorical(
1693 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::UnlabeledNonUtf8TLD);
1694 break;
1695 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1696 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8:
1697 LOGCHARSETMENU(("UnlabeledUtf8"));
1698 Telemetry::AccumulateCategorical(
1699 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::UnlabeledUtf8);
1700 break;
1701 case kCharsetFromChannel:
1702 if (encoding == UTF_8_ENCODING) {
1703 LOGCHARSETMENU(("ChannelUtf8"));
1704 Telemetry::AccumulateCategorical(
1705 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::ChannelUtf8);
1706 } else {
1707 LOGCHARSETMENU(("ChannelNonUtf8"));
1708 Telemetry::AccumulateCategorical(
1709 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::ChannelNonUtf8);
1711 break;
1712 case kCharsetFromXmlDeclaration:
1713 case kCharsetFromMetaTag:
1714 if (isFileURL) {
1715 LOGCHARSETMENU(("LocalLabeled"));
1716 Telemetry::AccumulateCategorical(
1717 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::LocalLabeled);
1718 } else if (encoding == UTF_8_ENCODING) {
1719 LOGCHARSETMENU(("MetaUtf8"));
1720 Telemetry::AccumulateCategorical(
1721 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::MetaUtf8);
1722 } else {
1723 LOGCHARSETMENU(("MetaNonUtf8"));
1724 Telemetry::AccumulateCategorical(
1725 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::MetaNonUtf8);
1727 break;
1728 case kCharsetFromFinalAutoDetectionFile:
1729 if (isFileURL) {
1730 LOGCHARSETMENU(("LocalUnlabeled"));
1731 Telemetry::AccumulateCategorical(
1732 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::LocalUnlabeled);
1733 } else {
1734 LOGCHARSETMENU(("Bug"));
1735 Telemetry::AccumulateCategorical(
1736 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::Bug);
1738 break;
1739 default:
1740 LOGCHARSETMENU(("Bug"));
1741 Telemetry::AccumulateCategorical(
1742 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_2::Bug);
1743 break;
1745 return NS_OK;
1748 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1749 int32_t aCharsetSource,
1750 nsIPrincipal* aPrincipal) {
1751 mParentCharset = aCharset;
1752 mParentCharsetSource = aCharsetSource;
1753 mParentCharsetPrincipal = aPrincipal;
1756 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1757 int32_t* aCharsetSource,
1758 nsIPrincipal** aPrincipal) {
1759 aCharset = mParentCharset;
1760 *aCharsetSource = mParentCharsetSource;
1761 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1764 NS_IMETHODIMP
1765 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
1766 MOZ_ASSERT(aPromise);
1768 ErrorResult rv;
1769 RefPtr<Document> doc(GetDocument());
1770 RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
1771 if (NS_WARN_IF(rv.Failed())) {
1772 return rv.StealNSResult();
1775 // Retrieve the document's content blocking events from the parent process.
1776 RefPtr<Document::GetContentBlockingEventsPromise> promise =
1777 doc->GetContentBlockingEvents();
1778 if (promise) {
1779 promise->Then(
1780 GetCurrentSerialEventTarget(), __func__,
1781 [retPromise](const Document::GetContentBlockingEventsPromise::
1782 ResolveOrRejectValue& aValue) {
1783 if (aValue.IsResolve()) {
1784 bool has = aValue.ResolveValue() &
1785 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
1786 retPromise->MaybeResolve(has);
1787 } else {
1788 retPromise->MaybeResolve(false);
1791 } else {
1792 retPromise->MaybeResolve(false);
1795 retPromise.forget(aPromise);
1796 return NS_OK;
1799 NS_IMETHODIMP
1800 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
1801 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1803 *aAllowPlugins = mBrowsingContext->GetAllowPlugins();
1804 return NS_OK;
1807 NS_IMETHODIMP
1808 nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
1809 // XXX should enable or disable a plugin host
1810 return mBrowsingContext->SetAllowPlugins(aAllowPlugins);
1813 NS_IMETHODIMP
1814 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
1815 MOZ_ASSERT(aEnabled);
1816 *aEnabled = mCSSErrorReportingEnabled;
1817 return NS_OK;
1820 NS_IMETHODIMP
1821 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1822 mCSSErrorReportingEnabled = aEnabled;
1823 return NS_OK;
1826 NS_IMETHODIMP
1827 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1828 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1829 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1832 void nsDocShell::NotifyPrivateBrowsingChanged() {
1833 MOZ_ASSERT(!mIsBeingDestroyed);
1835 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1836 while (iter.HasMore()) {
1837 nsWeakPtr ref = iter.GetNext();
1838 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1839 if (!obs) {
1840 iter.Remove();
1841 } else {
1842 obs->PrivateModeChanged(UsePrivateBrowsing());
1847 NS_IMETHODIMP
1848 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1849 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1852 NS_IMETHODIMP
1853 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1854 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1857 NS_IMETHODIMP
1858 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1859 NS_ENSURE_ARG_POINTER(aResult);
1861 *aResult = mHasLoadedNonBlankURI;
1862 return NS_OK;
1865 NS_IMETHODIMP
1866 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1867 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1868 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1871 NS_IMETHODIMP
1872 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1873 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1876 NS_IMETHODIMP
1877 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1878 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1879 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1882 NS_IMETHODIMP
1883 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1884 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1887 NS_IMETHODIMP
1888 nsDocShell::AddWeakPrivacyTransitionObserver(
1889 nsIPrivacyTransitionObserver* aObserver) {
1890 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1891 if (!weakObs) {
1892 return NS_ERROR_NOT_AVAILABLE;
1894 mPrivacyObservers.AppendElement(weakObs);
1895 return NS_OK;
1898 NS_IMETHODIMP
1899 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1900 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1901 if (!weakObs) {
1902 return NS_ERROR_FAILURE;
1904 mReflowObservers.AppendElement(weakObs);
1905 return NS_OK;
1908 NS_IMETHODIMP
1909 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1910 nsWeakPtr obs = do_GetWeakReference(aObserver);
1911 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1914 NS_IMETHODIMP
1915 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1916 DOMHighResTimeStamp aStart,
1917 DOMHighResTimeStamp aEnd) {
1918 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1919 while (iter.HasMore()) {
1920 nsWeakPtr ref = iter.GetNext();
1921 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1922 if (!obs) {
1923 iter.Remove();
1924 } else if (aInterruptible) {
1925 obs->ReflowInterruptible(aStart, aEnd);
1926 } else {
1927 obs->Reflow(aStart, aEnd);
1930 return NS_OK;
1933 NS_IMETHODIMP
1934 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1935 NS_ENSURE_ARG_POINTER(aReturn);
1937 *aReturn = mAllowMetaRedirects;
1938 return NS_OK;
1941 NS_IMETHODIMP
1942 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1943 mAllowMetaRedirects = aValue;
1944 return NS_OK;
1947 NS_IMETHODIMP
1948 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1949 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1951 *aAllowSubframes = mAllowSubframes;
1952 return NS_OK;
1955 NS_IMETHODIMP
1956 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1957 mAllowSubframes = aAllowSubframes;
1958 return NS_OK;
1961 NS_IMETHODIMP
1962 nsDocShell::GetAllowImages(bool* aAllowImages) {
1963 NS_ENSURE_ARG_POINTER(aAllowImages);
1965 *aAllowImages = mAllowImages;
1966 return NS_OK;
1969 NS_IMETHODIMP
1970 nsDocShell::SetAllowImages(bool aAllowImages) {
1971 mAllowImages = aAllowImages;
1972 return NS_OK;
1975 NS_IMETHODIMP
1976 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1977 *aAllowMedia = mAllowMedia;
1978 return NS_OK;
1981 NS_IMETHODIMP
1982 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1983 mAllowMedia = aAllowMedia;
1985 // Mute or unmute audio contexts attached to the inner window.
1986 if (mScriptGlobal) {
1987 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1988 if (aAllowMedia) {
1989 innerWin->UnmuteAudioContexts();
1990 } else {
1991 innerWin->MuteAudioContexts();
1996 return NS_OK;
1999 NS_IMETHODIMP
2000 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
2001 *aAllowDNSPrefetch = mAllowDNSPrefetch;
2002 return NS_OK;
2005 NS_IMETHODIMP
2006 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
2007 mAllowDNSPrefetch = aAllowDNSPrefetch;
2008 return NS_OK;
2011 NS_IMETHODIMP
2012 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
2013 *aAllowWindowControl = mAllowWindowControl;
2014 return NS_OK;
2017 NS_IMETHODIMP
2018 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
2019 mAllowWindowControl = aAllowWindowControl;
2020 return NS_OK;
2023 NS_IMETHODIMP
2024 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
2025 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
2026 return NS_OK;
2029 NS_IMETHODIMP
2030 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
2031 BrowsingContext::Transaction txn;
2032 txn.SetAllowContentRetargeting(aAllowContentRetargeting);
2033 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
2034 return txn.Commit(mBrowsingContext);
2037 NS_IMETHODIMP
2038 nsDocShell::GetAllowContentRetargetingOnChildren(
2039 bool* aAllowContentRetargetingOnChildren) {
2040 *aAllowContentRetargetingOnChildren =
2041 mBrowsingContext->GetAllowContentRetargetingOnChildren();
2042 return NS_OK;
2045 NS_IMETHODIMP
2046 nsDocShell::SetAllowContentRetargetingOnChildren(
2047 bool aAllowContentRetargetingOnChildren) {
2048 return mBrowsingContext->SetAllowContentRetargetingOnChildren(
2049 aAllowContentRetargetingOnChildren);
2052 NS_IMETHODIMP
2053 nsDocShell::GetMayEnableCharacterEncodingMenu(
2054 bool* aMayEnableCharacterEncodingMenu) {
2055 *aMayEnableCharacterEncodingMenu = false;
2056 if (!mContentViewer) {
2057 return NS_OK;
2059 Document* doc = mContentViewer->GetDocument();
2060 if (!doc) {
2061 return NS_OK;
2063 if (doc->WillIgnoreCharsetOverride()) {
2064 return NS_OK;
2067 *aMayEnableCharacterEncodingMenu = true;
2068 return NS_OK;
2071 NS_IMETHODIMP
2072 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
2073 DocShellEnumeratorDirection aDirection,
2074 nsTArray<RefPtr<nsIDocShell>>& aResult) {
2075 aResult.Clear();
2077 nsDocShellEnumerator docShellEnum(
2078 (aDirection == ENUMERATE_FORWARDS)
2079 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2080 : nsDocShellEnumerator::EnumerationDirection::Backwards,
2081 aItemType, *this);
2083 nsresult rv = docShellEnum.BuildDocShellArray(aResult);
2084 if (NS_FAILED(rv)) {
2085 return rv;
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 nsDocShell::GetAppType(AppType* aAppType) {
2093 *aAppType = mAppType;
2094 return NS_OK;
2097 NS_IMETHODIMP
2098 nsDocShell::SetAppType(AppType aAppType) {
2099 mAppType = aAppType;
2100 return NS_OK;
2103 NS_IMETHODIMP
2104 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2105 *aAllowAuth = mAllowAuth;
2106 return NS_OK;
2109 NS_IMETHODIMP
2110 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2111 mAllowAuth = aAllowAuth;
2112 return NS_OK;
2115 NS_IMETHODIMP
2116 nsDocShell::GetZoom(float* aZoom) {
2117 NS_ENSURE_ARG_POINTER(aZoom);
2118 *aZoom = 1.0f;
2119 return NS_OK;
2122 NS_IMETHODIMP
2123 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2125 NS_IMETHODIMP
2126 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
2127 NS_ENSURE_ARG_POINTER(aBusyFlags);
2129 *aBusyFlags = mBusyFlags;
2130 return NS_OK;
2133 NS_IMETHODIMP
2134 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
2135 bool* aTookFocus) {
2136 NS_ENSURE_ARG_POINTER(aTookFocus);
2138 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2139 if (chromeFocus) {
2140 if (aForward) {
2141 *aTookFocus =
2142 NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2143 } else {
2144 *aTookFocus =
2145 NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2147 } else {
2148 *aTookFocus = false;
2151 return NS_OK;
2154 NS_IMETHODIMP
2155 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2156 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
2157 delegate.forget(aLoadURIDelegate);
2158 return NS_OK;
2161 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
2162 if (nsCOMPtr<nsILoadURIDelegate> result =
2163 do_QueryActor("LoadURIDelegate", GetDocument())) {
2164 return result.forget();
2167 return nullptr;
2170 NS_IMETHODIMP
2171 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2172 *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
2173 return NS_OK;
2176 NS_IMETHODIMP
2177 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2178 return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
2181 NS_IMETHODIMP
2182 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
2183 *aPreviousEntryIndex = mPreviousEntryIndex;
2184 return NS_OK;
2187 NS_IMETHODIMP
2188 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
2189 *aLoadedEntryIndex = mLoadedEntryIndex;
2190 return NS_OK;
2193 NS_IMETHODIMP
2194 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2195 // These indices are used for fastback cache eviction, to determine
2196 // which session history entries are candidates for content viewer
2197 // eviction. We need to adjust by the number of entries that we
2198 // just purged from history, so that we look at the right session history
2199 // entries during eviction.
2200 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
2201 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
2203 for (auto* child : mChildList.ForwardRange()) {
2204 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2205 if (shell) {
2206 shell->HistoryPurged(aNumEntries);
2210 return NS_OK;
2213 void nsDocShell::TriggerParentCheckDocShellIsEmpty() {
2214 if (RefPtr<nsDocShell> parent = GetInProcessParentDocshell()) {
2215 parent->DocLoaderIsEmpty(true);
2217 if (GetBrowsingContext()->IsContentSubframe() &&
2218 !GetBrowsingContext()->GetParent()->IsInProcess()) {
2219 if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
2220 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2221 EmbedderElementEventType::NoEvent);
2226 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2227 // These indices are used for fastback cache eviction, to determine
2228 // which session history entries are candidates for content viewer
2229 // eviction. We need to adjust by the number of entries that we
2230 // just purged from history, so that we look at the right session history
2231 // entries during eviction.
2232 if (aIndex == mPreviousEntryIndex) {
2233 mPreviousEntryIndex = -1;
2234 } else if (aIndex < mPreviousEntryIndex) {
2235 --mPreviousEntryIndex;
2237 if (mLoadedEntryIndex == aIndex) {
2238 mLoadedEntryIndex = 0;
2239 } else if (aIndex < mLoadedEntryIndex) {
2240 --mLoadedEntryIndex;
2243 for (auto* child : mChildList.ForwardRange()) {
2244 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2245 if (shell) {
2246 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2250 return NS_OK;
2253 NS_IMETHODIMP
2254 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) {
2255 bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
2256 if (currentValue == aValue) {
2257 return NS_OK;
2260 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2261 if (!timelines) {
2262 return NS_OK;
2265 if (aValue) {
2266 MOZ_ASSERT(!timelines->HasConsumer(this));
2267 timelines->AddConsumer(this);
2268 MOZ_ASSERT(timelines->HasConsumer(this));
2269 UseEntryScriptProfiling();
2270 } else {
2271 MOZ_ASSERT(timelines->HasConsumer(this));
2272 timelines->RemoveConsumer(this);
2273 MOZ_ASSERT(!timelines->HasConsumer(this));
2274 UnuseEntryScriptProfiling();
2277 return NS_OK;
2280 NS_IMETHODIMP
2281 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) {
2282 *aValue = !!mObserved;
2283 return NS_OK;
2286 nsresult nsDocShell::PopProfileTimelineMarkers(
2287 JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
2288 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2289 if (!timelines) {
2290 return NS_OK;
2293 nsTArray<dom::ProfileTimelineMarker> store;
2294 SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
2296 timelines->PopMarkers(this, aCx, store);
2298 if (!ToJSValue(aCx, store, aOut)) {
2299 JS_ClearPendingException(aCx);
2300 return NS_ERROR_UNEXPECTED;
2303 return NS_OK;
2306 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2307 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2308 return NS_OK;
2311 NS_IMETHODIMP
2312 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2313 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2314 if (!aValue && mItemType == typeChrome && !parent) {
2315 // Window dragging is always allowed for top level
2316 // chrome docshells.
2317 return NS_ERROR_FAILURE;
2319 mWindowDraggingAllowed = aValue;
2320 return NS_OK;
2323 NS_IMETHODIMP
2324 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2325 // window dragging regions in CSS (-moz-window-drag:drag)
2326 // can be slow. Default behavior is to only allow it for
2327 // chrome top level windows.
2328 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2329 if (mItemType == typeChrome && !parent) {
2330 // Top level chrome window
2331 *aValue = true;
2332 } else {
2333 *aValue = mWindowDraggingAllowed;
2335 return NS_OK;
2338 NS_IMETHODIMP
2339 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2340 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2341 return NS_OK;
2344 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2345 if (mContentViewer) {
2346 Document* doc = mContentViewer->GetDocument();
2347 if (doc) {
2348 return doc->GetChannel();
2351 return nullptr;
2354 NS_IMETHODIMP
2355 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2356 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2357 if (!weakObs) {
2358 return NS_ERROR_FAILURE;
2360 mScrollObservers.AppendElement(weakObs);
2361 return NS_OK;
2364 NS_IMETHODIMP
2365 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2366 nsWeakPtr obs = do_GetWeakReference(aObserver);
2367 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2370 void nsDocShell::NotifyAsyncPanZoomStarted() {
2371 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2372 while (iter.HasMore()) {
2373 nsWeakPtr ref = iter.GetNext();
2374 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2375 if (obs) {
2376 obs->AsyncPanZoomStarted();
2377 } else {
2378 iter.Remove();
2383 void nsDocShell::NotifyAsyncPanZoomStopped() {
2384 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2385 while (iter.HasMore()) {
2386 nsWeakPtr ref = iter.GetNext();
2387 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2388 if (obs) {
2389 obs->AsyncPanZoomStopped();
2390 } else {
2391 iter.Remove();
2396 NS_IMETHODIMP
2397 nsDocShell::NotifyScrollObservers() {
2398 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2399 while (iter.HasMore()) {
2400 nsWeakPtr ref = iter.GetNext();
2401 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2402 if (obs) {
2403 obs->ScrollPositionChanged();
2404 } else {
2405 iter.Remove();
2408 return NS_OK;
2411 //*****************************************************************************
2412 // nsDocShell::nsIDocShellTreeItem
2413 //*****************************************************************************
2415 NS_IMETHODIMP
2416 nsDocShell::GetName(nsAString& aName) {
2417 aName = mBrowsingContext->Name();
2418 return NS_OK;
2421 NS_IMETHODIMP
2422 nsDocShell::SetName(const nsAString& aName) {
2423 return mBrowsingContext->SetName(aName);
2426 NS_IMETHODIMP
2427 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2428 NS_ENSURE_ARG_POINTER(aResult);
2429 *aResult = mBrowsingContext->NameEquals(aName);
2430 return NS_OK;
2433 NS_IMETHODIMP
2434 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2435 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2436 return NS_OK;
2439 NS_IMETHODIMP
2440 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2441 if (mWillChangeProcess) {
2442 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2443 return NS_ERROR_FAILURE;
2446 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2449 NS_IMETHODIMP
2450 nsDocShell::ClearCachedPlatform() {
2451 RefPtr<nsGlobalWindowInner> win =
2452 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2453 if (win) {
2454 Navigator* navigator = win->Navigator();
2455 if (navigator) {
2456 navigator->ClearPlatformCache();
2460 return NS_OK;
2463 NS_IMETHODIMP
2464 nsDocShell::ClearCachedUserAgent() {
2465 RefPtr<nsGlobalWindowInner> win =
2466 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2467 if (win) {
2468 Navigator* navigator = win->Navigator();
2469 if (navigator) {
2470 navigator->ClearUserAgentCache();
2474 return NS_OK;
2477 NS_IMETHODIMP
2478 nsDocShell::GetMetaViewportOverride(
2479 MetaViewportOverride* aMetaViewportOverride) {
2480 NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
2482 *aMetaViewportOverride = mMetaViewportOverride;
2483 return NS_OK;
2486 NS_IMETHODIMP
2487 nsDocShell::SetMetaViewportOverride(
2488 MetaViewportOverride aMetaViewportOverride) {
2489 // We don't have a way to verify this coming from Javascript, so this check is
2490 // still needed.
2491 if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
2492 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
2493 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
2494 return NS_ERROR_INVALID_ARG;
2497 mMetaViewportOverride = aMetaViewportOverride;
2499 // Inform our presShell that it needs to re-check its need for a viewport
2500 // override.
2501 if (RefPtr<PresShell> presShell = GetPresShell()) {
2502 presShell->MaybeRecreateMobileViewportManager(true);
2505 return NS_OK;
2508 /* virtual */
2509 int32_t nsDocShell::ItemType() { return mItemType; }
2511 NS_IMETHODIMP
2512 nsDocShell::GetItemType(int32_t* aItemType) {
2513 NS_ENSURE_ARG_POINTER(aItemType);
2515 MOZ_DIAGNOSTIC_ASSERT(
2516 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2517 *aItemType = mItemType;
2518 return NS_OK;
2521 NS_IMETHODIMP
2522 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2523 if (!mParent) {
2524 *aParent = nullptr;
2525 } else {
2526 CallQueryInterface(mParent, aParent);
2528 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2529 // don't want to throw; we just want to return null.
2530 return NS_OK;
2533 // With Fission, related nsDocShell objects may exist in a different process. In
2534 // that case, this method will return `nullptr`, despite a parent nsDocShell
2535 // object existing.
2537 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2538 // parent entry is not in the current process, and handle the case where the
2539 // parent nsDocShell is inaccessible.
2540 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2541 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2542 return docshell.forget().downcast<nsDocShell>();
2545 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2546 MOZ_ASSERT(!mIsBeingDestroyed);
2548 // If there is an existing document then there is no need to create
2549 // a client for a future initial about:blank document.
2550 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
2551 mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
2552 MOZ_DIAGNOSTIC_ASSERT(mScriptGlobal->GetCurrentInnerWindowInternal()
2553 ->GetClientInfo()
2554 .isSome());
2555 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2556 return;
2559 // Don't recreate the initial client source. We call this multiple times
2560 // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
2561 if (mInitialClientSource) {
2562 return;
2565 // Don't pre-allocate the client when we are sandboxed. The inherited
2566 // principal does not take sandboxing into account.
2567 // TODO: Refactor sandboxing principal code out so we can use it here.
2568 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2569 return;
2572 // We cannot get inherited foreign partitioned principal here. Instead, we
2573 // directly check which principal we want to inherit for the service worker.
2574 nsIPrincipal* principal =
2575 aPrincipal
2576 ? aPrincipal
2577 : GetInheritedPrincipal(
2578 false, StoragePrincipalHelper::
2579 ShouldUsePartitionPrincipalForServiceWorker(this));
2581 // Sometimes there is no principal available when we are called from
2582 // CreateAboutBlankContentViewer. For example, sometimes the principal
2583 // is only extracted from the load context after the document is created
2584 // in Document::ResetToURI(). Ideally we would do something similar
2585 // here, but for now lets just avoid the issue by not preallocating the
2586 // client.
2587 if (!principal) {
2588 return;
2591 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2592 if (!win) {
2593 return;
2596 mInitialClientSource = ClientManager::CreateSource(
2597 ClientType::Window, win->EventTargetFor(TaskCategory::Other), principal);
2598 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2600 // Mark the initial client as execution ready, but owned by the docshell.
2601 // If the client is actually used this will cause ClientSource to force
2602 // the creation of the initial about:blank by calling
2603 // nsDocShell::GetDocument().
2604 mInitialClientSource->DocShellExecutionReady(this);
2606 // Next, check to see if the parent is controlled.
2607 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2608 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2609 nsPIDOMWindowInner* parentInner =
2610 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2611 if (!parentInner) {
2612 return;
2615 nsCOMPtr<nsIURI> uri;
2616 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
2618 // We're done if there is no parent controller or if this docshell
2619 // is not permitted to control for some reason.
2620 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2621 if (controller.isNothing() ||
2622 !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2623 return;
2626 mInitialClientSource->InheritController(controller.ref());
2629 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2630 if (mInitialClientSource) {
2631 Maybe<ClientInfo> result;
2632 result.emplace(mInitialClientSource->Info());
2633 return result;
2636 nsGlobalWindowInner* innerWindow =
2637 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2638 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2640 if (!doc || !doc->IsInitialDocument()) {
2641 return Maybe<ClientInfo>();
2644 return innerWindow->GetClientInfo();
2647 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2648 bool wasFrame = IsSubframe();
2650 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2651 NS_ENSURE_SUCCESS(rv, rv);
2653 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2654 if (wasFrame != IsSubframe() && priorityGroup) {
2655 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2658 // Curse ambiguous nsISupports inheritance!
2659 nsISupports* parent = GetAsSupports(aParent);
2661 // If parent is another docshell, we inherit all their flags for
2662 // allowing plugins, scripting etc.
2663 bool value;
2664 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2666 if (parentAsDocShell) {
2667 if (mAllowMetaRedirects &&
2668 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2669 SetAllowMetaRedirects(value);
2671 if (mAllowSubframes &&
2672 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2673 SetAllowSubframes(value);
2675 if (mAllowImages &&
2676 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2677 SetAllowImages(value);
2679 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2680 if (mAllowWindowControl &&
2681 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2682 SetAllowWindowControl(value);
2684 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2685 value = false;
2687 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2689 // We don't need to inherit metaViewportOverride, because the viewport
2690 // is only relevant for the outermost nsDocShell, not for any iframes
2691 // like this that might be embedded within it.
2694 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2695 if (parentURIListener) {
2696 mContentListener->SetParentContentListener(parentURIListener);
2699 // Inform windows when they're being removed from their parent.
2700 if (!aParent) {
2701 MaybeClearStorageAccessFlag();
2704 return NS_OK;
2707 void nsDocShell::MaybeClearStorageAccessFlag() {
2708 if (mScriptGlobal) {
2709 // Tell our window that the parent has now changed.
2710 mScriptGlobal->ParentWindowChanged();
2712 // Tell all of our children about the change recursively as well.
2713 for (auto* childDocLoader : mChildList.ForwardRange()) {
2714 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
2715 if (child) {
2716 static_cast<nsDocShell*>(child.get())->MaybeClearStorageAccessFlag();
2722 void nsDocShell::MaybeRestoreWindowName() {
2723 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2724 return;
2727 // We only restore window.name for the top-level content.
2728 if (!mBrowsingContext->IsTopContent()) {
2729 return;
2732 nsAutoString name;
2734 // Following implements https://html.spec.whatwg.org/#history-traversal:
2735 // Step 4.4. Check if the loading entry has a name.
2737 if (mLSHE) {
2738 mLSHE->GetName(name);
2741 if (mLoadingEntry) {
2742 name = mLoadingEntry->mInfo.GetName();
2745 if (name.IsEmpty()) {
2746 return;
2749 // Step 4.4.1. Set the name to the browsing context.
2750 Unused << mBrowsingContext->SetName(name);
2752 // Step 4.4.2. Clear the name of all entries that are contiguous and
2753 // same-origin with the loading entry.
2754 if (mLSHE) {
2755 nsSHistory::WalkContiguousEntries(
2756 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
2759 if (mLoadingEntry) {
2760 // Clear the name of the session entry in the child side. For parent side,
2761 // the clearing will be done when we commit the history to the parent.
2762 mLoadingEntry->mInfo.SetName(EmptyString());
2766 void nsDocShell::StoreWindowNameToSHEntries() {
2767 MOZ_ASSERT(mBrowsingContext->IsTopContent());
2769 nsAutoString name;
2770 mBrowsingContext->GetName(name);
2772 if (mOSHE) {
2773 nsSHistory::WalkContiguousEntries(
2774 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2777 if (mozilla::SessionHistoryInParent()) {
2778 if (XRE_IsParentProcess()) {
2779 SessionHistoryEntry* entry =
2780 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2781 if (entry) {
2782 nsSHistory::WalkContiguousEntries(
2783 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2785 } else {
2786 // Ask parent process to store the name in entries.
2787 mozilla::Unused
2788 << ContentChild::GetSingleton()
2789 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2790 mBrowsingContext, name);
2795 NS_IMETHODIMP
2796 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2797 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2798 *aParent = do_AddRef(parentBC->GetDocShell()).take();
2800 return NS_OK;
2803 NS_IMETHODIMP
2804 nsDocShell::GetSameTypeInProcessParentIgnoreBrowserBoundaries(
2805 nsIDocShell** aParent) {
2806 NS_ENSURE_ARG_POINTER(aParent);
2807 *aParent = nullptr;
2809 nsCOMPtr<nsIDocShellTreeItem> parent =
2810 do_QueryInterface(GetAsSupports(mParent));
2811 if (!parent) {
2812 return NS_OK;
2815 if (parent->ItemType() == mItemType) {
2816 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
2817 parentDS.forget(aParent);
2819 return NS_OK;
2822 NS_IMETHODIMP
2823 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2824 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2826 RefPtr<nsDocShell> root = this;
2827 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2828 while (parent) {
2829 root = parent;
2830 parent = root->GetInProcessParentDocshell();
2833 root.forget(aRootTreeItem);
2834 return NS_OK;
2837 NS_IMETHODIMP
2838 nsDocShell::GetInProcessSameTypeRootTreeItem(
2839 nsIDocShellTreeItem** aRootTreeItem) {
2840 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2841 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2843 nsCOMPtr<nsIDocShellTreeItem> parent;
2844 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2845 NS_ERROR_FAILURE);
2846 while (parent) {
2847 *aRootTreeItem = parent;
2848 NS_ENSURE_SUCCESS(
2849 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2850 NS_ERROR_FAILURE);
2852 NS_ADDREF(*aRootTreeItem);
2853 return NS_OK;
2856 NS_IMETHODIMP
2857 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2858 NS_ENSURE_ARG_POINTER(aTreeOwner);
2860 *aTreeOwner = mTreeOwner;
2861 NS_IF_ADDREF(*aTreeOwner);
2862 return NS_OK;
2865 NS_IMETHODIMP
2866 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2867 if (mIsBeingDestroyed && aTreeOwner) {
2868 return NS_ERROR_FAILURE;
2871 // Don't automatically set the progress based on the tree owner for frames
2872 if (!IsSubframe()) {
2873 nsCOMPtr<nsIWebProgress> webProgress =
2874 do_QueryInterface(GetAsSupports(this));
2876 if (webProgress) {
2877 nsCOMPtr<nsIWebProgressListener> oldListener =
2878 do_QueryInterface(mTreeOwner);
2879 nsCOMPtr<nsIWebProgressListener> newListener =
2880 do_QueryInterface(aTreeOwner);
2882 if (oldListener) {
2883 webProgress->RemoveProgressListener(oldListener);
2886 if (newListener) {
2887 webProgress->AddProgressListener(newListener,
2888 nsIWebProgress::NOTIFY_ALL);
2893 mTreeOwner = aTreeOwner; // Weak reference per API
2895 for (auto* childDocLoader : mChildList.ForwardRange()) {
2896 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
2897 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2899 if (child->ItemType() == mItemType) {
2900 child->SetTreeOwner(aTreeOwner);
2904 // If we're in the content process and have had a TreeOwner set on us, extract
2905 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2906 // that it hasn't changed.
2907 if (mTreeOwner && XRE_IsContentProcess()) {
2908 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2909 MOZ_ASSERT(newBrowserChild,
2910 "No BrowserChild actor for tree owner in Content!");
2912 if (mBrowserChild) {
2913 nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2914 do_QueryReferent(mBrowserChild);
2915 MOZ_RELEASE_ASSERT(
2916 oldBrowserChild == newBrowserChild,
2917 "Cannot change BrowserChild during nsDocShell lifetime!");
2918 } else {
2919 mBrowserChild = do_GetWeakReference(newBrowserChild);
2923 return NS_OK;
2926 NS_IMETHODIMP
2927 nsDocShell::GetHistoryID(nsID& aID) {
2928 aID = mBrowsingContext->GetHistoryID();
2929 return NS_OK;
2932 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
2934 NS_IMETHODIMP
2935 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2936 *aIsInUnload = mFiredUnloadEvent;
2937 return NS_OK;
2940 NS_IMETHODIMP
2941 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2942 NS_ENSURE_ARG_POINTER(aChildCount);
2943 *aChildCount = mChildList.Length();
2944 return NS_OK;
2947 NS_IMETHODIMP
2948 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2949 NS_ENSURE_ARG_POINTER(aChild);
2951 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2952 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2954 // Make sure we're not creating a loop in the docshell tree
2955 nsDocLoader* ancestor = this;
2956 do {
2957 if (childAsDocLoader == ancestor) {
2958 return NS_ERROR_ILLEGAL_VALUE;
2960 ancestor = ancestor->GetParent();
2961 } while (ancestor);
2963 // Make sure to remove the child from its current parent.
2964 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2965 if (childsParent) {
2966 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2967 NS_ENSURE_SUCCESS(rv, rv);
2970 // Make sure to clear the treeowner in case this child is a different type
2971 // from us.
2972 aChild->SetTreeOwner(nullptr);
2974 nsresult res = AddChildLoader(childAsDocLoader);
2975 NS_ENSURE_SUCCESS(res, res);
2976 NS_ASSERTION(!mChildList.IsEmpty(),
2977 "child list must not be empty after a successful add");
2979 /* Set the child's global history if the parent has one */
2980 if (mBrowsingContext->GetUseGlobalHistory()) {
2981 // childDocShell->SetUseGlobalHistory(true);
2982 // this should be set through BC inherit
2983 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
2986 if (aChild->ItemType() != mItemType) {
2987 return NS_OK;
2990 aChild->SetTreeOwner(mTreeOwner);
2992 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2993 if (!childAsDocShell) {
2994 return NS_OK;
2997 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2999 // Now take this document's charset and set the child's parentCharset field
3000 // to it. We'll later use that field, in the loading process, for the
3001 // charset choosing algorithm.
3002 // If we fail, at any point, we just return NS_OK.
3003 // This code has some performance impact. But this will be reduced when
3004 // the current charset will finally be stored as an Atom, avoiding the
3005 // alias resolution extra look-up.
3007 // we are NOT going to propagate the charset is this Chrome's docshell
3008 if (mItemType == nsIDocShellTreeItem::typeChrome) {
3009 return NS_OK;
3012 // get the parent's current charset
3013 if (!mContentViewer) {
3014 return NS_OK;
3016 Document* doc = mContentViewer->GetDocument();
3017 if (!doc) {
3018 return NS_OK;
3021 const Encoding* parentCS = doc->GetDocumentCharacterSet();
3022 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
3023 // set the child's parentCharset
3024 childAsDocShell->SetParentCharset(parentCS, charsetSource,
3025 doc->NodePrincipal());
3027 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
3028 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3030 return NS_OK;
3033 NS_IMETHODIMP
3034 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
3035 NS_ENSURE_ARG_POINTER(aChild);
3037 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3038 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3040 nsresult rv = RemoveChildLoader(childAsDocLoader);
3041 NS_ENSURE_SUCCESS(rv, rv);
3043 aChild->SetTreeOwner(nullptr);
3045 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3048 NS_IMETHODIMP
3049 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
3050 NS_ENSURE_ARG_POINTER(aChild);
3052 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
3053 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3055 child.forget(aChild);
3057 return NS_OK;
3060 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
3061 #ifdef DEBUG
3062 if (aIndex < 0) {
3063 NS_WARNING("Negative index passed to GetChildAt");
3064 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3065 NS_WARNING("Too large an index passed to GetChildAt");
3067 #endif
3069 nsIDocumentLoader* child = ChildAt(aIndex);
3071 // child may be nullptr here.
3072 return static_cast<nsDocShell*>(child);
3075 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
3076 nsISHEntry* aNewEntry,
3077 int32_t aChildOffset, uint32_t aLoadType,
3078 bool aCloneChildren) {
3079 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3080 nsresult rv = NS_OK;
3082 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
3083 /* You get here if you are currently building a
3084 * hierarchy ie.,you just visited a frameset page
3086 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
3087 rv = mLSHE->AddChild(aNewEntry, aChildOffset);
3089 } else if (!aCloneRef) {
3090 /* This is an initial load in some subframe. Just append it if we can */
3091 if (mOSHE) {
3092 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
3094 } else {
3095 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
3096 if (shistory) {
3097 rv = shistory->LegacySHistory()->AddChildSHEntryHelper(
3098 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
3101 return rv;
3104 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
3105 int32_t aChildOffset,
3106 bool aCloneChildren) {
3107 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3108 /* You will get here when you are in a subframe and
3109 * a new url has been loaded on you.
3110 * The mOSHE in this subframe will be the previous url's
3111 * mOSHE. This mOSHE will be used as the identification
3112 * for this subframe in the CloneAndReplace function.
3115 // In this case, we will end up calling AddEntry, which increases the
3116 // current index by 1
3117 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3118 if (rootSH) {
3119 mPreviousEntryIndex = rootSH->Index();
3122 nsresult rv;
3123 // XXX(farre): this is not Fission safe, expect errors. This never
3124 // get's executed once session history in the parent is enabled.
3125 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
3126 NS_WARNING_ASSERTION(
3127 parent || !UseRemoteSubframes(),
3128 "Failed to add child session history entry! This will be resolved once "
3129 "session history in the parent is enabled.");
3130 if (parent) {
3131 rv = nsDocShell::Cast(parent)->AddChildSHEntry(
3132 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
3135 if (rootSH) {
3136 mLoadedEntryIndex = rootSH->Index();
3138 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
3139 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
3140 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
3141 mLoadedEntryIndex));
3145 return rv;
3148 NS_IMETHODIMP
3149 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3150 *aOSHE = false;
3151 *aEntry = nullptr;
3152 if (mLSHE) {
3153 NS_ADDREF(*aEntry = mLSHE);
3154 } else if (mOSHE) {
3155 NS_ADDREF(*aEntry = mOSHE);
3156 *aOSHE = true;
3158 return NS_OK;
3161 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
3162 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
3163 mBrowsingContext) {
3164 if (XRE_IsContentProcess()) {
3165 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
3166 if (contentChild) {
3167 contentChild->SendSynchronizeLayoutHistoryState(
3168 mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
3170 } else {
3171 SessionHistoryEntry* entry =
3172 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
3173 if (entry) {
3174 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
3177 if (mLoadingEntry &&
3178 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
3179 mLoadingEntry->mInfo.SetLayoutHistoryState(
3180 mActiveEntry->GetLayoutHistoryState());
3184 return NS_OK;
3187 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
3188 if (mLoadGroup) {
3189 mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
3190 } else {
3191 NS_WARNING(
3192 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3193 "propagate the mode to");
3197 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3198 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3199 return mScriptGlobal;
3202 Document* nsDocShell::GetDocument() {
3203 NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
3204 return mContentViewer->GetDocument();
3207 Document* nsDocShell::GetExtantDocument() {
3208 return mContentViewer ? mContentViewer->GetDocument() : nullptr;
3211 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3212 if (NS_FAILED(EnsureScriptEnvironment())) {
3213 return nullptr;
3215 return mScriptGlobal;
3218 NS_IMETHODIMP
3219 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3220 NS_ENSURE_ARG_POINTER(aWindow);
3222 nsresult rv = EnsureScriptEnvironment();
3223 NS_ENSURE_SUCCESS(rv, rv);
3225 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3226 window.forget(aWindow);
3227 return NS_OK;
3230 NS_IMETHODIMP
3231 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3232 RefPtr<ContentFrameMessageManager> mm;
3233 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3234 mm = browserChild->GetMessageManager();
3235 } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3236 mm = win->GetMessageManager();
3238 mm.forget(aMessageManager);
3239 return NS_OK;
3242 NS_IMETHODIMP
3243 nsDocShell::GetIsNavigating(bool* aOut) {
3244 *aOut = mIsNavigating;
3245 return NS_OK;
3248 NS_IMETHODIMP
3249 nsDocShell::SetDeviceSizeIsPageSize(bool aValue) {
3250 if (mDeviceSizeIsPageSize != aValue) {
3251 mDeviceSizeIsPageSize = aValue;
3252 RefPtr<nsPresContext> presContext = GetPresContext();
3253 if (presContext) {
3254 presContext->MediaFeatureValuesChanged(
3255 {MediaFeatureChangeReason::DeviceSizeIsPageSizeChange},
3256 MediaFeatureChangePropagation::JustThisDocument);
3259 return NS_OK;
3262 NS_IMETHODIMP
3263 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) {
3264 *aValue = mDeviceSizeIsPageSize;
3265 return NS_OK;
3268 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3269 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3270 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3271 if (!rootSH || !aEntry) {
3272 return;
3275 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3278 //-------------------------------------
3279 //-- Helper Method for Print discovery
3280 //-------------------------------------
3281 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
3282 if (!mBrowsingContext->Top()->GetIsPrinting()) {
3283 return false;
3285 if (aDisplayErrorDialog) {
3286 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3288 return true;
3291 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3292 bool aCheckIfUnloadFired) {
3293 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
3294 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3295 if (!isAllowed) {
3296 return false;
3298 if (!mContentViewer) {
3299 return true;
3301 bool firingBeforeUnload;
3302 mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3303 return !firingBeforeUnload;
3306 //*****************************************************************************
3307 // nsDocShell::nsIWebNavigation
3308 //*****************************************************************************
3310 NS_IMETHODIMP
3311 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3312 *aCanGoBack = false;
3313 if (!IsNavigationAllowed(false)) {
3314 return NS_OK; // JS may not handle returning of an error code
3316 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3317 if (rootSH) {
3318 *aCanGoBack = rootSH->CanGo(-1);
3319 MOZ_LOG(gSHLog, LogLevel::Verbose,
3320 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
3322 return NS_OK;
3324 return NS_ERROR_FAILURE;
3327 NS_IMETHODIMP
3328 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3329 *aCanGoForward = false;
3330 if (!IsNavigationAllowed(false)) {
3331 return NS_OK; // JS may not handle returning of an error code
3333 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3334 if (rootSH) {
3335 *aCanGoForward = rootSH->CanGo(1);
3336 MOZ_LOG(gSHLog, LogLevel::Verbose,
3337 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
3338 return NS_OK;
3340 return NS_ERROR_FAILURE;
3343 NS_IMETHODIMP
3344 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
3345 if (!IsNavigationAllowed()) {
3346 return NS_OK; // JS may not handle returning of an error code
3349 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3350 mIsNavigating = true;
3352 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3353 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3354 ErrorResult rv;
3355 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
3356 return rv.StealNSResult();
3359 NS_IMETHODIMP
3360 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
3361 if (!IsNavigationAllowed()) {
3362 return NS_OK; // JS may not handle returning of an error code
3365 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3366 mIsNavigating = true;
3368 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3369 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3370 ErrorResult rv;
3371 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
3372 return rv.StealNSResult();
3375 // XXX(nika): We may want to stop exposing this API in the child process? Going
3376 // to a specific index from multiple different processes could definitely race.
3377 NS_IMETHODIMP
3378 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
3379 if (!IsNavigationAllowed()) {
3380 return NS_OK; // JS may not handle returning of an error code
3383 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3384 mIsNavigating = true;
3386 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3387 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3389 ErrorResult rv;
3390 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
3391 rv);
3392 return rv.StealNSResult();
3395 nsresult nsDocShell::LoadURI(const nsAString& aURI,
3396 const LoadURIOptions& aLoadURIOptions) {
3397 if (!IsNavigationAllowed()) {
3398 return NS_OK; // JS may not handle returning of an error code
3401 RefPtr<nsDocShellLoadState> loadState;
3402 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3403 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3405 uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3406 if (NS_ERROR_MALFORMED_URI == rv) {
3407 MOZ_LOG(gSHLog, LogLevel::Debug,
3408 ("Creating an active entry on nsDocShell %p to %s (because "
3409 "we're showing an error page)",
3410 this, NS_ConvertUTF16toUTF8(aURI).get()));
3412 // We need to store a session history entry. We don't have a valid URI, so
3413 // we use about:blank instead.
3414 nsCOMPtr<nsIURI> uri;
3415 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
3416 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
3417 if (aLoadURIOptions.mTriggeringPrincipal) {
3418 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
3419 } else {
3420 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
3422 if (mozilla::SessionHistoryInParent()) {
3423 mActiveEntry = MakeUnique<SessionHistoryInfo>(
3424 uri, triggeringPrincipal, nullptr, nullptr, nullptr,
3425 nsLiteralCString("text/html"));
3426 mBrowsingContext->SetActiveSessionHistoryEntry(
3427 Nothing(), mActiveEntry.get(), MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
3428 /* aUpdatedCacheKey = */ 0);
3430 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURI).get(), nullptr) &&
3431 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3432 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3436 if (NS_FAILED(rv) || !loadState) {
3437 return NS_ERROR_FAILURE;
3440 return LoadURI(loadState, true);
3443 NS_IMETHODIMP
3444 nsDocShell::LoadURIFromScript(const nsAString& aURI,
3445 JS::Handle<JS::Value> aLoadURIOptions,
3446 JSContext* aCx) {
3447 // generate dictionary for aLoadURIOptions and forward call
3448 LoadURIOptions loadURIOptions;
3449 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3450 return NS_ERROR_INVALID_ARG;
3452 return LoadURI(aURI, loadURIOptions);
3455 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3456 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3457 // such as the content-chrome boundary, don't fire the error event.
3458 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3459 return;
3462 // If embedder is same-process, then unblocking the load event is already
3463 // handled by nsDocLoader. Fire the error event on our embedder element if
3464 // requested.
3466 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3467 // more like the remote case when in-process.
3468 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3469 if (element) {
3470 if (aFireFrameErrorEvent) {
3471 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3472 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3473 fl->FireErrorEvent();
3477 return;
3480 // If we have a cross-process parent document, we must notify it that we no
3481 // longer block its load event. This is necessary for OOP sub-documents
3482 // because error documents do not result in a call to
3483 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3484 // (Obviously, we must do this before any of the returns below.)
3485 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3486 if (browserChild) {
3487 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3488 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3489 : EmbedderElementEventType::NoEvent);
3493 NS_IMETHODIMP
3494 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3495 const char16_t* aURL, nsIChannel* aFailedChannel,
3496 bool* aDisplayedErrorPage) {
3497 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3498 ("DOCSHELL %p DisplayLoadError %s\n", this,
3499 aURI ? aURI->GetSpecOrDefault().get() : ""));
3501 *aDisplayedErrorPage = false;
3502 // Get prompt and string bundle services
3503 nsCOMPtr<nsIPrompt> prompter;
3504 nsCOMPtr<nsIStringBundle> stringBundle;
3505 GetPromptAndStringBundle(getter_AddRefs(prompter),
3506 getter_AddRefs(stringBundle));
3508 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3509 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3511 const char* error = nullptr;
3512 // The key used to select the appropriate error message from the properties
3513 // file.
3514 const char* errorDescriptionID = nullptr;
3515 AutoTArray<nsString, 3> formatStrs;
3516 bool addHostPort = false;
3517 nsresult rv = NS_OK;
3518 nsAutoString messageStr;
3519 nsAutoCString cssClass;
3520 nsAutoCString errorPage;
3522 errorPage.AssignLiteral("neterror");
3524 // Turn the error code into a human readable error message.
3525 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3526 NS_ENSURE_ARG_POINTER(aURI);
3528 // Extract the schemes into a comma delimited list.
3529 nsAutoCString scheme;
3530 aURI->GetScheme(scheme);
3531 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3532 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3533 while (nestedURI) {
3534 nsCOMPtr<nsIURI> tempURI;
3535 nsresult rv2;
3536 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3537 if (NS_SUCCEEDED(rv2) && tempURI) {
3538 tempURI->GetScheme(scheme);
3539 formatStrs[0].AppendLiteral(", ");
3540 AppendASCIItoUTF16(scheme, formatStrs[0]);
3542 nestedURI = do_QueryInterface(tempURI);
3544 error = "unknownProtocolFound";
3545 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3546 NS_ENSURE_ARG_POINTER(aURI);
3547 error = "fileNotFound";
3548 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3549 NS_ENSURE_ARG_POINTER(aURI);
3550 error = "fileAccessDenied";
3551 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3552 NS_ENSURE_ARG_POINTER(aURI);
3553 // Get the host
3554 nsAutoCString host;
3555 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3556 innermostURI->GetHost(host);
3557 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3558 errorDescriptionID = "dnsNotFound2";
3559 error = "dnsNotFound";
3560 } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3561 NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3562 NS_ENSURE_ARG_POINTER(aURI);
3563 addHostPort = true;
3564 error = "connectionFailure";
3565 } else if (NS_ERROR_NET_INTERRUPT == aError) {
3566 NS_ENSURE_ARG_POINTER(aURI);
3567 addHostPort = true;
3568 error = "netInterrupt";
3569 } else if (NS_ERROR_NET_TIMEOUT == aError ||
3570 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
3571 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
3572 NS_ENSURE_ARG_POINTER(aURI);
3573 // Get the host
3574 nsAutoCString host;
3575 aURI->GetHost(host);
3576 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3577 error = "netTimeout";
3578 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3579 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError ||
3580 NS_ERROR_CSP_NAVIGATE_TO_VIOLATION == aError) {
3581 // CSP error
3582 cssClass.AssignLiteral("neterror");
3583 error = "cspBlocked";
3584 } else if (NS_ERROR_XFO_VIOLATION == aError) {
3585 // XFO error
3586 cssClass.AssignLiteral("neterror");
3587 error = "xfoBlocked";
3588 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3589 nsCOMPtr<nsINSSErrorsService> nsserr =
3590 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3592 uint32_t errorClass;
3593 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3594 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3597 nsCOMPtr<nsISupports> securityInfo;
3598 nsCOMPtr<nsITransportSecurityInfo> tsi;
3599 if (aFailedChannel) {
3600 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
3602 tsi = do_QueryInterface(securityInfo);
3603 if (tsi) {
3604 uint32_t securityState;
3605 tsi->GetSecurityState(&securityState);
3606 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3607 error = "sslv3Used";
3608 addHostPort = true;
3609 } else if (securityState &
3610 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3611 error = "weakCryptoUsed";
3612 addHostPort = true;
3614 } else {
3615 // No channel, let's obtain the generic error message
3616 if (nsserr) {
3617 nsserr->GetErrorMessage(aError, messageStr);
3620 // We don't have a message string here anymore but DisplayLoadError
3621 // requires a non-empty messageStr.
3622 messageStr.Truncate();
3623 messageStr.AssignLiteral(u" ");
3624 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3625 error = "nssBadCert";
3627 // If this is an HTTP Strict Transport Security host or a pinned host
3628 // and the certificate is bad, don't allow overrides (RFC 6797 section
3629 // 12.1).
3630 uint32_t flags =
3631 UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
3632 bool isStsHost = false;
3633 bool isPinnedHost = false;
3634 OriginAttributes attrsForHSTS;
3635 if (aFailedChannel) {
3636 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
3637 attrsForHSTS);
3638 } else {
3639 attrsForHSTS = GetOriginAttributes();
3642 if (XRE_IsParentProcess()) {
3643 nsCOMPtr<nsISiteSecurityService> sss =
3644 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3645 NS_ENSURE_SUCCESS(rv, rv);
3646 rv = sss->IsSecureURI(aURI, flags, attrsForHSTS, nullptr, nullptr,
3647 &isStsHost);
3648 NS_ENSURE_SUCCESS(rv, rv);
3649 } else {
3650 mozilla::dom::ContentChild* cc =
3651 mozilla::dom::ContentChild::GetSingleton();
3652 cc->SendIsSecureURI(aURI, flags, attrsForHSTS, &isStsHost);
3654 nsCOMPtr<nsIPublicKeyPinningService> pkps =
3655 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
3656 NS_ENSURE_SUCCESS(rv, rv);
3657 rv = pkps->HostHasPins(aURI, &isPinnedHost);
3659 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3660 false)) {
3661 cssClass.AssignLiteral("expertBadCert");
3664 // HSTS/pinning takes precedence over the expert bad cert pref. We
3665 // never want to show the "Add Exception" button for these sites.
3666 // In the future we should differentiate between an HSTS host and a
3667 // pinned host and display a more informative message to the user.
3668 if (isStsHost || isPinnedHost) {
3669 cssClass.AssignLiteral("badStsCert");
3672 // See if an alternate cert error page is registered
3673 nsAutoCString alternateErrorPage;
3674 nsresult rv = Preferences::GetCString(
3675 "security.alternate_certificate_error_page", alternateErrorPage);
3676 if (NS_SUCCEEDED(rv)) {
3677 errorPage.Assign(alternateErrorPage);
3679 } else {
3680 error = "nssFailure2";
3682 } else if (NS_ERROR_PHISHING_URI == aError ||
3683 NS_ERROR_MALWARE_URI == aError ||
3684 NS_ERROR_UNWANTED_URI == aError ||
3685 NS_ERROR_HARMFUL_URI == aError) {
3686 nsAutoCString host;
3687 aURI->GetHost(host);
3688 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3690 // Malware and phishing detectors may want to use an alternate error
3691 // page, but if the pref's not set, we'll fall back on the standard page
3692 nsAutoCString alternateErrorPage;
3693 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3694 alternateErrorPage);
3695 if (NS_SUCCEEDED(rv)) {
3696 errorPage.Assign(alternateErrorPage);
3699 if (NS_ERROR_PHISHING_URI == aError) {
3700 error = "deceptiveBlocked";
3701 } else if (NS_ERROR_MALWARE_URI == aError) {
3702 error = "malwareBlocked";
3703 } else if (NS_ERROR_UNWANTED_URI == aError) {
3704 error = "unwantedBlocked";
3705 } else if (NS_ERROR_HARMFUL_URI == aError) {
3706 error = "harmfulBlocked";
3709 cssClass.AssignLiteral("blacklist");
3710 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3711 errorPage.AssignLiteral("tabcrashed");
3712 error = "tabcrashed";
3714 RefPtr<EventTarget> handler = mChromeEventHandler;
3715 if (handler) {
3716 nsCOMPtr<Element> element = do_QueryInterface(handler);
3717 element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
3720 // DisplayLoadError requires a non-empty messageStr to proceed and call
3721 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3722 // space which will be trimmed and thus treated as empty by the front-end.
3723 if (messageStr.IsEmpty()) {
3724 messageStr.AssignLiteral(u" ");
3726 } else if (NS_ERROR_FRAME_CRASHED == aError) {
3727 errorPage.AssignLiteral("framecrashed");
3728 error = "framecrashed";
3729 messageStr.AssignLiteral(u" ");
3730 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3731 errorPage.AssignLiteral("restartrequired");
3732 error = "restartrequired";
3734 // DisplayLoadError requires a non-empty messageStr to proceed and call
3735 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3736 // space which will be trimmed and thus treated as empty by the front-end.
3737 if (messageStr.IsEmpty()) {
3738 messageStr.AssignLiteral(u" ");
3740 } else {
3741 // Errors requiring simple formatting
3742 switch (aError) {
3743 case NS_ERROR_MALFORMED_URI:
3744 // URI is malformed
3745 error = "malformedURI";
3746 errorDescriptionID = "malformedURI2";
3747 break;
3748 case NS_ERROR_REDIRECT_LOOP:
3749 // Doc failed to load because the server generated too many redirects
3750 error = "redirectLoop";
3751 break;
3752 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3753 // Doc failed to load because PSM is not installed
3754 error = "unknownSocketType";
3755 break;
3756 case NS_ERROR_NET_RESET:
3757 // Doc failed to load because the server kept reseting the connection
3758 // before we could read any data from it
3759 error = "netReset";
3760 break;
3761 case NS_ERROR_DOCUMENT_NOT_CACHED:
3762 // Doc failed to load because the cache does not contain a copy of
3763 // the document.
3764 error = "notCached";
3765 break;
3766 case NS_ERROR_OFFLINE:
3767 // Doc failed to load because we are offline.
3768 error = "netOffline";
3769 break;
3770 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3771 // Doc navigation attempted while Printing or Print Preview
3772 error = "isprinting";
3773 break;
3774 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3775 // Port blocked for security reasons
3776 addHostPort = true;
3777 error = "deniedPortAccess";
3778 break;
3779 case NS_ERROR_UNKNOWN_PROXY_HOST:
3780 // Proxy hostname could not be resolved.
3781 error = "proxyResolveFailure";
3782 break;
3783 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3784 case NS_ERROR_PROXY_FORBIDDEN:
3785 case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3786 case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3787 case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3788 // Proxy connection was refused.
3789 error = "proxyConnectFailure";
3790 break;
3791 case NS_ERROR_INVALID_CONTENT_ENCODING:
3792 // Bad Content Encoding.
3793 error = "contentEncodingError";
3794 break;
3795 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3796 // Channel refused to load from an unrecognized content type.
3797 error = "unsafeContentType";
3798 break;
3799 case NS_ERROR_CORRUPTED_CONTENT:
3800 // Broken Content Detected. e.g. Content-MD5 check failure.
3801 error = "corruptedContentErrorv2";
3802 break;
3803 case NS_ERROR_INTERCEPTION_FAILED:
3804 // ServiceWorker intercepted request, but something went wrong.
3805 error = "corruptedContentErrorv2";
3806 break;
3807 case NS_ERROR_NET_INADEQUATE_SECURITY:
3808 // Server negotiated bad TLS for HTTP/2.
3809 error = "inadequateSecurityError";
3810 addHostPort = true;
3811 break;
3812 case NS_ERROR_BLOCKED_BY_POLICY:
3813 case NS_ERROR_DOM_COOP_FAILED:
3814 case NS_ERROR_DOM_COEP_FAILED:
3815 // Page blocked by policy
3816 error = "blockedByPolicy";
3817 break;
3818 case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3819 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3820 // HTTP/2 or HTTP/3 stack detected a protocol error
3821 error = "networkProtocolError";
3822 break;
3824 default:
3825 break;
3829 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3830 // caused this error, we replace the error-page with about:httpsonlyerror
3831 bool isHttpsOnlyError =
3832 nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError);
3833 if (isHttpsOnlyError) {
3834 errorPage.AssignLiteral("httpsonlyerror");
3837 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3838 nsCOMPtr<nsIURI> errorPageURI;
3839 rv = loadURIDelegate->HandleLoadError(
3840 aURI, (isHttpsOnlyError ? NS_ERROR_HTTPS_ONLY : aError),
3841 NS_ERROR_GET_MODULE(aError), getter_AddRefs(errorPageURI));
3842 // If the docshell is going away there's no point in showing an error page.
3843 if (NS_FAILED(rv) || mIsBeingDestroyed) {
3844 *aDisplayedErrorPage = false;
3845 return NS_OK;
3848 if (errorPageURI) {
3849 *aDisplayedErrorPage =
3850 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3851 return NS_OK;
3855 // Test if the error should be displayed
3856 if (!error) {
3857 return NS_OK;
3860 if (!errorDescriptionID) {
3861 errorDescriptionID = error;
3864 Telemetry::AccumulateCategoricalKeyed(
3865 IsSubframe() ? "frame"_ns : "top"_ns,
3866 mozilla::dom::LoadErrorToTelemetryLabel(aError));
3868 // Test if the error needs to be formatted
3869 if (!messageStr.IsEmpty()) {
3870 // already obtained message
3871 } else {
3872 if (addHostPort) {
3873 // Build up the host:port string.
3874 nsAutoCString hostport;
3875 if (aURI) {
3876 aURI->GetHostPort(hostport);
3877 } else {
3878 hostport.Assign('?');
3880 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3883 nsAutoCString spec;
3884 rv = NS_ERROR_NOT_AVAILABLE;
3885 auto& nextFormatStr = *formatStrs.AppendElement();
3886 if (aURI) {
3887 // displaying "file://" is aesthetically unpleasing and could even be
3888 // confusing to the user
3889 if (SchemeIsFile(aURI)) {
3890 aURI->GetPathQueryRef(spec);
3891 } else {
3892 aURI->GetSpec(spec);
3895 nsCOMPtr<nsITextToSubURI> textToSubURI(
3896 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3897 if (NS_SUCCEEDED(rv)) {
3898 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3900 } else {
3901 spec.Assign('?');
3903 if (NS_FAILED(rv)) {
3904 CopyUTF8toUTF16(spec, nextFormatStr);
3906 rv = NS_OK;
3908 nsAutoString str;
3909 rv =
3910 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3911 NS_ENSURE_SUCCESS(rv, rv);
3912 messageStr.Assign(str);
3915 // Display the error as a page or an alert prompt
3916 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3918 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3919 SchemeIsHTTPS(aURI)) {
3920 // Maybe TLS intolerant. Treat this as an SSL error.
3921 error = "nssFailure2";
3924 if (mBrowsingContext->GetUseErrorPages()) {
3925 // Display an error page
3926 nsresult loadedPage =
3927 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3928 cssClass.get(), aFailedChannel);
3929 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3930 } else {
3931 // The prompter reqires that our private window has a document (or it
3932 // asserts). Satisfy that assertion now since GetDoc will force
3933 // creation of one if it hasn't already been created.
3934 if (mScriptGlobal) {
3935 Unused << mScriptGlobal->GetDoc();
3938 // Display a message box
3939 prompter->Alert(nullptr, messageStr.get());
3942 return NS_OK;
3945 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3947 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3948 const char* aErrorPage,
3949 const char* aErrorType,
3950 const char16_t* aDescription,
3951 const char* aCSSClass,
3952 nsIChannel* aFailedChannel) {
3953 MOZ_ASSERT(!mIsBeingDestroyed);
3955 #if defined(DEBUG)
3956 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3957 nsAutoCString chanName;
3958 if (aFailedChannel) {
3959 aFailedChannel->GetName(chanName);
3960 } else {
3961 chanName.AssignLiteral("<no channel>");
3964 MOZ_LOG(gDocShellLog, LogLevel::Debug,
3965 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3966 this, aURI ? aURI->GetSpecOrDefault().get() : "",
3967 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3969 #endif
3971 nsAutoCString url;
3972 if (aURI) {
3973 nsresult rv = aURI->GetSpec(url);
3974 NS_ENSURE_SUCCESS(rv, rv);
3975 } else if (aURL) {
3976 CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3977 } else {
3978 return NS_ERROR_INVALID_POINTER;
3981 // Create a URL to pass all the error information through to the page.
3983 #undef SAFE_ESCAPE
3984 #define SAFE_ESCAPE(output, input, params) \
3985 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3986 return NS_ERROR_OUT_OF_MEMORY; \
3989 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3990 SAFE_ESCAPE(escapedUrl, url, url_Path);
3991 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3992 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3993 url_Path);
3994 if (aCSSClass) {
3995 nsCString cssClass(aCSSClass);
3996 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3998 nsCString errorPageUrl("about:");
3999 errorPageUrl.AppendASCII(aErrorPage);
4000 errorPageUrl.AppendLiteral("?e=");
4002 errorPageUrl.AppendASCII(escapedError.get());
4003 errorPageUrl.AppendLiteral("&u=");
4004 errorPageUrl.AppendASCII(escapedUrl.get());
4005 if ((strcmp(aErrorPage, "blocked") == 0) &&
4006 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
4007 errorPageUrl.AppendLiteral("&o=1");
4009 if (!escapedCSSClass.IsEmpty()) {
4010 errorPageUrl.AppendLiteral("&s=");
4011 errorPageUrl.AppendASCII(escapedCSSClass.get());
4013 errorPageUrl.AppendLiteral("&c=UTF-8");
4015 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
4016 int32_t cpsState;
4017 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
4018 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
4019 errorPageUrl.AppendLiteral("&captive=true");
4022 // netError.xhtml's getDescription only handles the "d" parameter at the
4023 // end of the URL, so append it last.
4024 errorPageUrl.AppendLiteral("&d=");
4025 errorPageUrl.AppendASCII(escapedDescription.get());
4027 nsCOMPtr<nsIURI> errorPageURI;
4028 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
4029 NS_ENSURE_SUCCESS(rv, rv);
4031 return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
4034 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
4035 nsIChannel* aFailedChannel) {
4036 mFailedChannel = aFailedChannel;
4037 mFailedURI = aFailedURI;
4038 mFailedLoadType = mLoadType;
4040 if (mLSHE) {
4041 // Abandon mLSHE's BFCache entry and create a new one. This way, if
4042 // we go back or forward to another SHEntry with the same doc
4043 // identifier, the error page won't persist.
4044 mLSHE->AbandonBFCacheEntry();
4047 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
4048 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4049 if (mBrowsingContext) {
4050 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
4052 loadState->SetLoadType(LOAD_ERROR_PAGE);
4053 loadState->SetFirstParty(true);
4054 loadState->SetSourceBrowsingContext(mBrowsingContext);
4055 if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
4056 // We keep the loading entry for the load that failed here. If the user
4057 // reloads we want to try to reload the original load, not the error page.
4058 loadState->SetLoadingSessionHistoryInfo(
4059 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
4061 return InternalLoad(loadState);
4064 NS_IMETHODIMP
4065 nsDocShell::Reload(uint32_t aReloadFlags) {
4066 if (!IsNavigationAllowed()) {
4067 return NS_OK; // JS may not handle returning of an error code
4070 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
4071 "Reload command not updated to use load flags!");
4072 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4073 "Don't pass these flags to Reload");
4075 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4076 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4078 // Send notifications to the HistoryListener if any, about the impending
4079 // reload
4080 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4081 if (mozilla::SessionHistoryInParent()) {
4082 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
4083 bool forceReload = IsForceReloadType(loadType);
4084 if (!XRE_IsParentProcess()) {
4085 RefPtr<nsDocShell> docShell(this);
4086 nsCOMPtr<nsIContentViewer> cv(mContentViewer);
4088 bool okToUnload = true;
4089 MOZ_TRY(cv->PermitUnload(&okToUnload));
4090 if (!okToUnload) {
4091 return NS_OK;
4094 RefPtr<Document> doc(GetDocument());
4095 RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
4096 nsCOMPtr<nsIURI> currentURI(mCurrentURI);
4097 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
4099 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
4100 mBrowsingContext, forceReload,
4101 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo](
4102 Tuple<bool, Maybe<RefPtr<nsDocShellLoadState>>, Maybe<bool>>&&
4103 aResult) {
4104 bool canReload;
4105 Maybe<RefPtr<nsDocShellLoadState>> loadState;
4106 Maybe<bool> reloadingActiveEntry;
4108 Tie(canReload, loadState, reloadingActiveEntry) = aResult;
4110 if (!canReload) {
4111 return;
4114 if (loadState.isSome()) {
4115 MOZ_LOG(
4116 gSHLog, LogLevel::Debug,
4117 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
4118 loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
4119 docShell->LoadHistoryEntry(loadState.ref(), loadType,
4120 reloadingActiveEntry.ref());
4121 } else {
4122 MOZ_LOG(gSHLog, LogLevel::Debug,
4123 ("nsDocShell %p ReloadDocument", docShell.get()));
4124 ReloadDocument(docShell, doc, loadType, browsingContext,
4125 currentURI, referrerInfo,
4126 /* aNotifiedBeforeUnloadListeners */ true);
4129 [](mozilla::ipc::ResponseRejectReason) {});
4130 } else {
4131 // Parent process
4132 bool canReload = false;
4133 Maybe<RefPtr<nsDocShellLoadState>> loadState;
4134 Maybe<bool> reloadingActiveEntry;
4135 if (!mBrowsingContext->IsDiscarded()) {
4136 mBrowsingContext->Canonical()->NotifyOnHistoryReload(
4137 forceReload, canReload, loadState, reloadingActiveEntry);
4139 if (canReload) {
4140 if (loadState.isSome()) {
4141 MOZ_LOG(gSHLog, LogLevel::Debug,
4142 ("nsDocShell %p Reload - LoadHistoryEntry", this));
4143 LoadHistoryEntry(loadState.ref(), loadType,
4144 reloadingActiveEntry.ref());
4145 } else {
4146 MOZ_LOG(gSHLog, LogLevel::Debug,
4147 ("nsDocShell %p ReloadDocument", this));
4148 ReloadDocument(this, GetDocument(), loadType, mBrowsingContext,
4149 mCurrentURI, mReferrerInfo);
4153 return NS_OK;
4156 bool canReload = true;
4157 if (rootSH) {
4158 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
4161 if (!canReload) {
4162 return NS_OK;
4165 /* If you change this part of code, make sure bug 45297 does not re-occur */
4166 if (mOSHE) {
4167 return LoadHistoryEntry(
4168 mOSHE, loadType,
4169 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4172 if (mLSHE) { // In case a reload happened before the current load is done
4173 return LoadHistoryEntry(
4174 mLSHE, loadType,
4175 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4178 return ReloadDocument(this, GetDocument(), loadType, mBrowsingContext,
4179 mCurrentURI, mReferrerInfo);
4182 /* static */
4183 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
4184 uint32_t aLoadType,
4185 BrowsingContext* aBrowsingContext,
4186 nsIURI* aCurrentURI,
4187 nsIReferrerInfo* aReferrerInfo,
4188 bool aNotifiedBeforeUnloadListeners) {
4189 if (!aDocument) {
4190 return NS_OK;
4193 // Do not inherit owner from document
4194 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4195 nsAutoString srcdoc;
4196 nsIURI* baseURI = nullptr;
4197 nsCOMPtr<nsIURI> originalURI;
4198 nsCOMPtr<nsIURI> resultPrincipalURI;
4199 bool loadReplace = false;
4201 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
4202 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
4203 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
4205 nsAutoString contentTypeHint;
4206 aDocument->GetContentType(contentTypeHint);
4208 if (aDocument->IsSrcdocDocument()) {
4209 aDocument->GetSrcdocData(srcdoc);
4210 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4211 baseURI = aDocument->GetBaseURI();
4212 } else {
4213 srcdoc = VoidString();
4215 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
4216 if (chan) {
4217 uint32_t loadFlags;
4218 chan->GetLoadFlags(&loadFlags);
4219 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4220 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4221 if (httpChan) {
4222 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4225 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
4226 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4229 if (!triggeringPrincipal) {
4230 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4231 return NS_ERROR_FAILURE;
4234 // Stack variables to ensure changes to the member variables don't affect to
4235 // the call.
4236 nsCOMPtr<nsIURI> currentURI = aCurrentURI;
4238 // Reload always rewrites result principal URI.
4239 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4240 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4242 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
4243 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
4244 loadState->SetReferrerInfo(aReferrerInfo);
4245 loadState->SetOriginalURI(originalURI);
4246 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
4247 loadState->SetLoadReplace(loadReplace);
4248 loadState->SetTriggeringPrincipal(triggeringPrincipal);
4249 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
4250 loadState->SetPrincipalToInherit(triggeringPrincipal);
4251 loadState->SetCsp(csp);
4252 loadState->SetInternalLoadFlags(flags);
4253 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
4254 loadState->SetLoadType(aLoadType);
4255 loadState->SetFirstParty(true);
4256 loadState->SetSrcdocData(srcdoc);
4257 loadState->SetSourceBrowsingContext(aBrowsingContext);
4258 loadState->SetBaseURI(baseURI);
4259 loadState->SetHasValidUserGestureActivation(
4260 context && context->HasValidTransientUserGestureActivation());
4261 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
4262 return aDocShell->InternalLoad(loadState);
4265 NS_IMETHODIMP
4266 nsDocShell::Stop(uint32_t aStopFlags) {
4267 // Revoke any pending event related to content viewer restoration
4268 mRestorePresentationEvent.Revoke();
4270 if (mLoadType == LOAD_ERROR_PAGE) {
4271 if (mLSHE) {
4272 // Since error page loads never unset mLSHE, do so now
4273 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
4275 mActiveEntryIsLoadingFromSessionHistory = false;
4277 mFailedChannel = nullptr;
4278 mFailedURI = nullptr;
4281 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4282 // Stop the document loading and animations
4283 if (mContentViewer) {
4284 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4285 cv->Stop();
4287 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4288 // Stop the document loading only
4289 if (mContentViewer) {
4290 RefPtr<Document> doc = mContentViewer->GetDocument();
4291 if (doc) {
4292 doc->StopDocumentLoad();
4297 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4298 // Suspend any timers that were set for this loader. We'll clear
4299 // them out for good in CreateContentViewer.
4300 if (mRefreshURIList) {
4301 SuspendRefreshURIs();
4302 mSavedRefreshURIList.swap(mRefreshURIList);
4303 mRefreshURIList = nullptr;
4306 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4307 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4308 // redundant apis!
4309 Stop();
4311 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4312 // BFCache now, and the Stop above will have removed the DocumentChannel
4313 // from the loadgroup.
4314 mChannelToDisconnectOnPageHide = 0;
4317 for (auto* child : mChildList.ForwardRange()) {
4318 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
4319 if (shellAsNav) {
4320 shellAsNav->Stop(aStopFlags);
4324 return NS_OK;
4327 NS_IMETHODIMP
4328 nsDocShell::GetDocument(Document** aDocument) {
4329 NS_ENSURE_ARG_POINTER(aDocument);
4330 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4332 RefPtr<Document> doc = mContentViewer->GetDocument();
4333 if (!doc) {
4334 return NS_ERROR_NOT_AVAILABLE;
4337 doc.forget(aDocument);
4338 return NS_OK;
4341 NS_IMETHODIMP
4342 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4343 NS_ENSURE_ARG_POINTER(aURI);
4345 nsCOMPtr<nsIURI> uri = mCurrentURI;
4346 uri.forget(aURI);
4347 return NS_OK;
4350 NS_IMETHODIMP
4351 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4352 NS_ENSURE_ARG_POINTER(aSessionHistory);
4353 RefPtr<ChildSHistory> shistory = GetSessionHistory();
4354 shistory.forget(aSessionHistory);
4355 return NS_OK;
4358 //*****************************************************************************
4359 // nsDocShell::nsIWebPageDescriptor
4360 //*****************************************************************************
4362 NS_IMETHODIMP
4363 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
4364 const nsAString& aURI) {
4365 if (!aOtherDocShell) {
4366 return NS_ERROR_INVALID_POINTER;
4368 nsCOMPtr<nsIURI> newURI;
4369 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
4370 if (NS_FAILED(rv)) {
4371 return rv;
4374 RefPtr<nsDocShellLoadState> loadState;
4375 uint32_t cacheKey;
4376 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
4377 if (mozilla::SessionHistoryInParent()) {
4378 loadState = new nsDocShellLoadState(newURI);
4379 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
4380 return NS_ERROR_INVALID_POINTER;
4382 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
4383 } else {
4384 nsCOMPtr<nsISHEntry> entry;
4385 bool isOriginalSHE;
4386 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
4387 if (!entry) {
4388 return NS_ERROR_INVALID_POINTER;
4390 rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
4391 NS_ENSURE_SUCCESS(rv, rv);
4392 entry->GetCacheKey(&cacheKey);
4393 loadState->SetURI(newURI);
4394 loadState->SetSHEntry(nullptr);
4397 // We're doing a load of the page, via an API that
4398 // is only exposed to system code. The triggering principal for this load
4399 // should be the system principal.
4400 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4401 loadState->SetOriginalURI(nullptr);
4402 loadState->SetResultPrincipalURI(nullptr);
4404 return InternalLoad(loadState, Some(cacheKey));
4407 NS_IMETHODIMP
4408 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4409 MOZ_ASSERT(aPageDescriptor, "Null out param?");
4411 *aPageDescriptor = nullptr;
4413 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4414 if (src) {
4415 nsCOMPtr<nsISHEntry> dest;
4417 nsresult rv = src->Clone(getter_AddRefs(dest));
4418 if (NS_FAILED(rv)) {
4419 return rv;
4422 // null out inappropriate cloned attributes...
4423 dest->SetParent(nullptr);
4424 dest->SetIsSubFrame(false);
4426 return CallQueryInterface(dest, aPageDescriptor);
4429 return NS_ERROR_NOT_AVAILABLE;
4432 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
4433 const {
4434 nsCOMPtr<nsIInputStream> postData;
4435 if (mozilla::SessionHistoryInParent()) {
4436 if (mActiveEntry) {
4437 postData = mActiveEntry->GetPostData();
4438 } else if (mLoadingEntry) {
4439 postData = mLoadingEntry->mInfo.GetPostData();
4441 } else {
4442 if (mOSHE) {
4443 postData = mOSHE->GetPostData();
4444 } else if (mLSHE) {
4445 postData = mLSHE->GetPostData();
4449 return postData.forget();
4452 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4453 if (mozilla::SessionHistoryInParent()) {
4454 if (mActiveEntry) {
4455 return Some(mActiveEntry->GetCacheKey());
4458 if (mLoadingEntry) {
4459 return Some(mLoadingEntry->mInfo.GetCacheKey());
4461 } else {
4462 if (mOSHE) {
4463 return Some(mOSHE->GetCacheKey());
4466 if (mLSHE) {
4467 return Some(mLSHE->GetCacheKey());
4471 return Nothing();
4474 bool nsDocShell::FillLoadStateFromCurrentEntry(
4475 nsDocShellLoadState& aLoadState) {
4476 if (mLoadingEntry) {
4477 mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
4478 return true;
4480 if (mActiveEntry) {
4481 mActiveEntry->FillLoadInfo(aLoadState);
4482 return true;
4484 return false;
4487 //*****************************************************************************
4488 // nsDocShell::nsIBaseWindow
4489 //*****************************************************************************
4491 NS_IMETHODIMP
4492 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4493 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4494 int32_t aWidth, int32_t aHeight) {
4495 SetParentWidget(aParentWidget);
4496 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4497 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE);
4499 return NS_OK;
4502 NS_IMETHODIMP
4503 nsDocShell::Destroy() {
4504 // XXX: We allow this function to be called just once. If you are going to
4505 // reset new variables in this function, please make sure the variables will
4506 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4507 // in the setter functions for the variables would be enough.
4508 if (mIsBeingDestroyed) {
4509 return NS_ERROR_DOCSHELL_DYING;
4512 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4513 "Unexpected item type in docshell");
4515 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4516 if (serv) {
4517 const char* msg = mItemType == typeContent
4518 ? NS_WEBNAVIGATION_DESTROY
4519 : NS_CHROME_WEBNAVIGATION_DESTROY;
4520 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4523 mIsBeingDestroyed = true;
4525 // Brak the cycle with the initial client, if present.
4526 mInitialClientSource.reset();
4528 // Make sure we don't record profile timeline markers anymore
4529 SetRecordProfileTimelineMarkers(false);
4531 // Make sure to blow away our mLoadingURI just in case. No loads
4532 // from inside this pagehide.
4533 mLoadingURI = nullptr;
4535 // Fire unload event before we blow anything away.
4536 (void)FirePageHideNotification(true);
4538 // Clear pointers to any detached nsEditorData that's lying
4539 // around in shistory entries. Breaks cycle. See bug 430921.
4540 if (mOSHE) {
4541 mOSHE->SetEditorData(nullptr);
4543 if (mLSHE) {
4544 mLSHE->SetEditorData(nullptr);
4547 // Note: mContentListener can be null if Init() failed and we're being
4548 // called from the destructor.
4549 if (mContentListener) {
4550 mContentListener->DropDocShellReference();
4551 mContentListener->SetParentContentListener(nullptr);
4552 // Note that we do NOT set mContentListener to null here; that
4553 // way if someone tries to do a load in us after this point
4554 // the nsDSURIContentListener will block it. All of which
4555 // means that we should do this before calling Stop(), of
4556 // course.
4559 // Stop any URLs that are currently being loaded...
4560 Stop(nsIWebNavigation::STOP_ALL);
4562 mEditorData = nullptr;
4564 // Save the state of the current document, before destroying the window.
4565 // This is needed to capture the state of a frameset when the new document
4566 // causes the frameset to be destroyed...
4567 PersistLayoutHistoryState();
4569 // Remove this docshell from its parent's child list
4570 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4571 do_QueryInterface(GetAsSupports(mParent));
4572 if (docShellParentAsItem) {
4573 docShellParentAsItem->RemoveChild(this);
4576 if (mContentViewer) {
4577 mContentViewer->Close(nullptr);
4578 mContentViewer->Destroy();
4579 mContentViewer = nullptr;
4582 nsDocLoader::Destroy();
4584 mParentWidget = nullptr;
4585 mCurrentURI = nullptr;
4587 if (mScriptGlobal) {
4588 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4589 mScriptGlobal = nullptr;
4592 if (GetSessionHistory()) {
4593 // We want to destroy these content viewers now rather than
4594 // letting their destruction wait for the session history
4595 // entries to get garbage collected. (Bug 488394)
4596 GetSessionHistory()->EvictLocalContentViewers();
4599 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
4600 mBrowsingContext->PrepareForProcessChange();
4603 SetTreeOwner(nullptr);
4605 mBrowserChild = nullptr;
4607 mChromeEventHandler = nullptr;
4609 // Cancel any timers that were set for this docshell; this is needed
4610 // to break the cycle between us and the timers.
4611 CancelRefreshURITimers();
4613 return NS_OK;
4616 NS_IMETHODIMP
4617 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
4618 if (mParentWidget) {
4619 *aScale = mParentWidget->GetDefaultScale().scale;
4620 return NS_OK;
4623 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4624 if (ownerWindow) {
4625 return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
4628 *aScale = 1.0;
4629 return NS_OK;
4632 NS_IMETHODIMP
4633 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4634 if (mParentWidget) {
4635 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4636 return NS_OK;
4639 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4640 if (ownerWindow) {
4641 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4644 *aScale = 1.0;
4645 return NS_OK;
4648 NS_IMETHODIMP
4649 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4650 mBounds.MoveTo(aX, aY);
4652 if (mContentViewer) {
4653 NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4656 return NS_OK;
4659 NS_IMETHODIMP
4660 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4661 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4662 if (ownerWindow) {
4663 return ownerWindow->SetPositionDesktopPix(aX, aY);
4666 double scale = 1.0;
4667 GetDevicePixelsPerDesktopPixel(&scale);
4668 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4671 NS_IMETHODIMP
4672 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4673 return GetPositionAndSize(aX, aY, nullptr, nullptr);
4676 NS_IMETHODIMP
4677 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4678 int32_t x = 0, y = 0;
4679 GetPosition(&x, &y);
4680 return SetPositionAndSize(x, y, aWidth, aHeight,
4681 aRepaint ? nsIBaseWindow::eRepaint : 0);
4684 NS_IMETHODIMP
4685 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4686 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4689 NS_IMETHODIMP
4690 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4691 int32_t aHeight, uint32_t aFlags) {
4692 mBounds.SetRect(aX, aY, aWidth, aHeight);
4694 // Hold strong ref, since SetBounds can make us null out mContentViewer
4695 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
4696 if (viewer) {
4697 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4698 ? nsIContentViewer::eDelayResize
4699 : 0;
4700 // XXX Border figured in here or is that handled elsewhere?
4701 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4702 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4705 return NS_OK;
4708 NS_IMETHODIMP
4709 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4710 int32_t* aHeight) {
4711 if (mParentWidget) {
4712 // ensure size is up-to-date if window has changed resolution
4713 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4714 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4717 // We should really consider just getting this information from
4718 // our window instead of duplicating the storage and code...
4719 if (aWidth || aHeight) {
4720 // Caller wants to know our size; make sure to give them up to
4721 // date information.
4722 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4723 if (doc) {
4724 doc->FlushPendingNotifications(FlushType::Layout);
4728 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4729 return NS_OK;
4732 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4733 int32_t* aHeight) {
4734 if (aX) {
4735 *aX = mBounds.X();
4737 if (aY) {
4738 *aY = mBounds.Y();
4740 if (aWidth) {
4741 *aWidth = mBounds.Width();
4743 if (aHeight) {
4744 *aHeight = mBounds.Height();
4748 NS_IMETHODIMP
4749 nsDocShell::Repaint(bool aForce) {
4750 PresShell* presShell = GetPresShell();
4751 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4753 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4754 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4756 viewManager->InvalidateAllViews();
4757 return NS_OK;
4760 NS_IMETHODIMP
4761 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4762 NS_ENSURE_ARG_POINTER(aParentWidget);
4764 *aParentWidget = mParentWidget;
4765 NS_IF_ADDREF(*aParentWidget);
4767 return NS_OK;
4770 NS_IMETHODIMP
4771 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4772 MOZ_ASSERT(!mIsBeingDestroyed);
4773 mParentWidget = aParentWidget;
4775 return NS_OK;
4778 NS_IMETHODIMP
4779 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4780 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4782 if (mParentWidget) {
4783 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4784 } else {
4785 *aParentNativeWindow = nullptr;
4788 return NS_OK;
4791 NS_IMETHODIMP
4792 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4793 return NS_ERROR_NOT_IMPLEMENTED;
4796 NS_IMETHODIMP
4797 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4798 // the nativeHandle should be accessed from nsIAppWindow
4799 return NS_ERROR_NOT_IMPLEMENTED;
4802 NS_IMETHODIMP
4803 nsDocShell::GetVisibility(bool* aVisibility) {
4804 NS_ENSURE_ARG_POINTER(aVisibility);
4806 *aVisibility = false;
4808 if (!mContentViewer) {
4809 return NS_OK;
4812 PresShell* presShell = GetPresShell();
4813 if (!presShell) {
4814 return NS_OK;
4817 // get the view manager
4818 nsViewManager* vm = presShell->GetViewManager();
4819 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4821 // get the root view
4822 nsView* view = vm->GetRootView(); // views are not ref counted
4823 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4825 // if our root view is hidden, we are not visible
4826 if (view->GetVisibility() == nsViewVisibility_kHide) {
4827 return NS_OK;
4830 // otherwise, we must walk up the document and view trees checking
4831 // for a hidden view, unless we're an off screen browser, which
4832 // would make this test meaningless.
4834 RefPtr<nsDocShell> docShell = this;
4835 RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4836 while (parentItem) {
4837 // Null-check for crash in bug 267804
4838 if (!parentItem->GetPresShell()) {
4839 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4840 return NS_OK;
4843 vm = docShell->GetPresShell()->GetViewManager();
4844 if (vm) {
4845 view = vm->GetRootView();
4848 if (view) {
4849 view = view->GetParent(); // anonymous inner view
4850 if (view) {
4851 view = view->GetParent(); // subdocumentframe's view
4855 nsIFrame* frame = view ? view->GetFrame() : nullptr;
4856 if (frame && !frame->IsVisibleConsideringAncestors(
4857 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
4858 return NS_OK;
4861 docShell = parentItem;
4862 parentItem = docShell->GetInProcessParentDocshell();
4865 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4866 if (!treeOwnerAsWin) {
4867 *aVisibility = true;
4868 return NS_OK;
4871 // Check with the tree owner as well to give embedders a chance to
4872 // expose visibility as well.
4873 return treeOwnerAsWin->GetVisibility(aVisibility);
4876 void nsDocShell::ActivenessMaybeChanged() {
4877 const bool isActive = mBrowsingContext->IsActive();
4878 if (RefPtr<PresShell> presShell = GetPresShell()) {
4879 presShell->ActivenessMaybeChanged();
4882 // Tell the window about it
4883 if (mScriptGlobal) {
4884 mScriptGlobal->SetIsBackground(!isActive);
4885 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4886 // Update orientation when the top-level browsing context becomes active.
4887 if (isActive && mBrowsingContext->IsTop()) {
4888 // We only care about the top-level browsing context.
4889 uint16_t orientation = mBrowsingContext->GetOrientationLock();
4890 ScreenOrientation::UpdateActiveOrientationLock(orientation);
4893 doc->PostVisibilityUpdateEvent();
4897 // Tell the nsDOMNavigationTiming about it
4898 RefPtr<nsDOMNavigationTiming> timing = mTiming;
4899 if (!timing && mContentViewer) {
4900 if (Document* doc = mContentViewer->GetDocument()) {
4901 timing = doc->GetNavigationTiming();
4904 if (timing) {
4905 timing->NotifyDocShellStateChanged(
4906 isActive ? nsDOMNavigationTiming::DocShellState::eActive
4907 : nsDOMNavigationTiming::DocShellState::eInactive);
4910 // Restart or stop meta refresh timers if necessary
4911 if (mDisableMetaRefreshWhenInactive) {
4912 if (isActive) {
4913 ResumeRefreshURIs();
4914 } else {
4915 SuspendRefreshURIs();
4919 if (InputTaskManager::CanSuspendInputEvent()) {
4920 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
4924 NS_IMETHODIMP
4925 nsDocShell::SetIsAppTab(bool aIsAppTab) {
4926 mIsAppTab = aIsAppTab;
4927 return NS_OK;
4930 NS_IMETHODIMP
4931 nsDocShell::GetIsAppTab(bool* aIsAppTab) {
4932 *aIsAppTab = mIsAppTab;
4933 return NS_OK;
4936 NS_IMETHODIMP
4937 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4938 if (!mWillChangeProcess) {
4939 // Intentionally ignoring handling discarded browsing contexts.
4940 Unused << mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4941 } else {
4942 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4943 // shutdown. Sorry DevTools, your DocShell is in another process.
4944 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4946 return NS_OK;
4949 NS_IMETHODIMP
4950 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4951 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4952 return NS_OK;
4955 NS_IMETHODIMP
4956 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4957 NS_ENSURE_ARG_POINTER(aFailedChannel);
4958 Document* doc = GetDocument();
4959 if (!doc) {
4960 *aFailedChannel = nullptr;
4961 return NS_OK;
4963 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4964 return NS_OK;
4967 NS_IMETHODIMP
4968 nsDocShell::SetVisibility(bool aVisibility) {
4969 // Show()/Hide() may change mContentViewer.
4970 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4971 if (!cv) {
4972 return NS_OK;
4974 if (aVisibility) {
4975 cv->Show();
4976 } else {
4977 cv->Hide();
4980 return NS_OK;
4983 NS_IMETHODIMP
4984 nsDocShell::GetEnabled(bool* aEnabled) {
4985 NS_ENSURE_ARG_POINTER(aEnabled);
4986 *aEnabled = true;
4987 return NS_ERROR_NOT_IMPLEMENTED;
4990 NS_IMETHODIMP
4991 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4993 NS_IMETHODIMP
4994 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4995 // We don't create our own widget, so simply return the parent one.
4996 return GetParentWidget(aMainWidget);
4999 NS_IMETHODIMP
5000 nsDocShell::GetTitle(nsAString& aTitle) {
5001 aTitle = mTitle;
5002 return NS_OK;
5005 NS_IMETHODIMP
5006 nsDocShell::SetTitle(const nsAString& aTitle) {
5007 // Avoid unnecessary updates of the title if the URI and the title haven't
5008 // changed.
5009 if (mTitleValidForCurrentURI && mTitle == aTitle) {
5010 return NS_OK;
5013 // Store local title
5014 mTitle = aTitle;
5015 mTitleValidForCurrentURI = true;
5017 // When title is set on the top object it should then be passed to the
5018 // tree owner.
5019 if (mBrowsingContext->IsTop()) {
5020 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5021 if (treeOwnerAsWin) {
5022 treeOwnerAsWin->SetTitle(aTitle);
5026 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
5027 UpdateGlobalHistoryTitle(mCurrentURI);
5030 // Update SessionHistory with the document's title.
5031 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
5032 SetTitleOnHistoryEntry(true);
5035 return NS_OK;
5038 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
5039 if (mOSHE) {
5040 mOSHE->SetTitle(mTitle);
5043 if (mActiveEntry && mBrowsingContext) {
5044 mActiveEntry->SetTitle(mTitle);
5045 if (aUpdateEntryInSessionHistory) {
5046 if (XRE_IsParentProcess()) {
5047 SessionHistoryEntry* entry =
5048 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
5049 if (entry) {
5050 entry->SetTitle(mTitle);
5052 } else {
5053 mozilla::Unused
5054 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
5055 mBrowsingContext, mTitle);
5061 nsPoint nsDocShell::GetCurScrollPos() {
5062 nsPoint scrollPos;
5063 if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
5064 scrollPos = sf->GetVisualViewportOffset();
5066 return scrollPos;
5069 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
5070 int32_t aCurVerticalPos) {
5071 nsIScrollableFrame* sf = GetRootScrollFrame();
5072 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5074 ScrollMode scrollMode =
5075 sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
5077 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
5078 sf->ScrollTo(targetPos, scrollMode);
5080 // Set the visual viewport offset as well.
5082 RefPtr<PresShell> presShell = GetPresShell();
5083 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5085 nsPresContext* presContext = presShell->GetPresContext();
5086 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
5088 // Only the root content document can have a distinct visual viewport offset.
5089 if (!presContext->IsRootContentDocumentCrossProcess()) {
5090 return NS_OK;
5093 // Not on a platform with a distinct visual viewport - don't bother setting
5094 // the visual viewport offset.
5095 if (!presShell->IsVisualViewportSizeSet()) {
5096 return NS_OK;
5099 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
5100 scrollMode);
5102 return NS_OK;
5105 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
5106 if (mScrollbarPref == aPref) {
5107 return;
5109 mScrollbarPref = aPref;
5110 auto* ps = GetPresShell();
5111 if (!ps) {
5112 return;
5114 nsIFrame* scrollFrame = ps->GetRootScrollFrame();
5115 if (!scrollFrame) {
5116 return;
5118 ps->FrameNeedsReflow(scrollFrame, IntrinsicDirty::StyleChange,
5119 NS_FRAME_IS_DIRTY);
5122 //*****************************************************************************
5123 // nsDocShell::nsIRefreshURI
5124 //*****************************************************************************
5126 NS_IMETHODIMP
5127 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5128 uint32_t aDelay) {
5129 MOZ_ASSERT(!mIsBeingDestroyed);
5131 NS_ENSURE_ARG(aURI);
5133 /* Check if Meta refresh/redirects are permitted. Some
5134 * embedded applications may not want to do this.
5135 * Must do this before sending out NOTIFY_REFRESH events
5136 * because listeners may have side effects (e.g. displaying a
5137 * button to manually trigger the refresh later).
5139 bool allowRedirects = true;
5140 GetAllowMetaRedirects(&allowRedirects);
5141 if (!allowRedirects) {
5142 return NS_OK;
5145 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5146 // give them a chance to block this refresh.
5147 bool sameURI;
5148 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5149 if (NS_FAILED(rv)) {
5150 sameURI = false;
5152 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
5153 return NS_OK;
5156 nsCOMPtr<nsITimerCallback> refreshTimer =
5157 new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
5159 BusyFlags busyFlags = GetBusyFlags();
5161 if (!mRefreshURIList) {
5162 mRefreshURIList = nsArray::Create();
5165 if (busyFlags & BUSY_FLAGS_BUSY ||
5166 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
5167 // We don't want to create the timer right now. Instead queue up the
5168 // request and trigger the timer in EndPageLoad() or whenever we become
5169 // active.
5170 mRefreshURIList->AppendElement(refreshTimer);
5171 } else {
5172 // There is no page loading going on right now. Create the
5173 // timer and fire it right away.
5174 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5175 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
5177 nsCOMPtr<nsITimer> timer;
5178 MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
5179 nsITimer::TYPE_ONE_SHOT));
5181 mRefreshURIList->AppendElement(timer); // owning timer ref
5183 return NS_OK;
5186 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
5187 nsIPrincipal* aPrincipal,
5188 uint32_t aDelay,
5189 nsITimer* aTimer) {
5190 MOZ_ASSERT(aTimer, "Must have a timer here");
5192 // Remove aTimer from mRefreshURIList if needed
5193 if (mRefreshURIList) {
5194 uint32_t n = 0;
5195 mRefreshURIList->GetLength(&n);
5197 for (uint32_t i = 0; i < n; ++i) {
5198 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5199 if (timer == aTimer) {
5200 mRefreshURIList->RemoveElementAt(i);
5201 break;
5206 return ForceRefreshURI(aURI, aPrincipal, aDelay);
5209 NS_IMETHODIMP
5210 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5211 uint32_t aDelay) {
5212 NS_ENSURE_ARG(aURI);
5214 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
5215 loadState->SetOriginalURI(mCurrentURI);
5216 loadState->SetResultPrincipalURI(aURI);
5217 loadState->SetResultPrincipalURIIsSome(true);
5218 loadState->SetKeepResultPrincipalURIIfSet(true);
5219 loadState->SetIsMetaRefresh(true);
5221 // Set the triggering pricipal to aPrincipal if available, or current
5222 // document's principal otherwise.
5223 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
5224 RefPtr<Document> doc = GetDocument();
5225 if (!principal) {
5226 if (!doc) {
5227 return NS_ERROR_FAILURE;
5229 principal = doc->NodePrincipal();
5231 loadState->SetTriggeringPrincipal(principal);
5232 if (doc) {
5233 loadState->SetCsp(doc->GetCsp());
5234 loadState->SetHasValidUserGestureActivation(
5235 doc->HasValidTransientUserGestureActivation());
5236 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
5239 loadState->SetPrincipalIsExplicit(true);
5241 /* Check if this META refresh causes a redirection
5242 * to another site.
5244 bool equalUri = false;
5245 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5247 nsCOMPtr<nsIReferrerInfo> referrerInfo;
5248 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
5249 /* It is a META refresh based redirection within the threshold time
5250 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5251 * Pass a REPLACE flag to LoadURI().
5253 loadState->SetLoadType(LOAD_REFRESH_REPLACE);
5255 /* For redirects we mimic HTTP, which passes the
5256 * original referrer.
5257 * We will pass in referrer but will not send to server
5259 if (mReferrerInfo) {
5260 referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5261 ->CloneWithNewSendReferrer(false);
5263 } else {
5264 loadState->SetLoadType(LOAD_REFRESH);
5265 /* We do need to pass in a referrer, but we don't want it to
5266 * be sent to the server.
5267 * For most refreshes the current URI is an appropriate
5268 * internal referrer.
5270 referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5273 loadState->SetReferrerInfo(referrerInfo);
5274 loadState->SetLoadFlags(
5275 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5276 loadState->SetFirstParty(true);
5279 * LoadURI(...) will cancel all refresh timers... This causes the
5280 * Timer and its refreshData instance to be released...
5282 LoadURI(loadState, false);
5284 return NS_OK;
5287 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
5288 const char16_t* aEnd) {
5289 const char16_t* iter = aStart;
5290 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
5291 ++iter;
5293 return iter;
5296 static Tuple<const char16_t*, const char16_t*> ExtractURLString(
5297 const char16_t* aPosition, const char16_t* aEnd) {
5298 MOZ_ASSERT(aPosition != aEnd);
5300 // 1. Let urlString be the substring of input from the code point at
5301 // position to the end of the string.
5302 const char16_t* urlStart = aPosition;
5303 const char16_t* urlEnd = aEnd;
5305 // 2. If the code point in input pointed to by position is U+0055 (U) or
5306 // U+0075 (u), then advance position to the next code point.
5307 // Otherwise, jump to the step labeled skip quotes.
5308 if (*aPosition == 'U' || *aPosition == 'u') {
5309 ++aPosition;
5311 // 3. If the code point in input pointed to by position is U+0052 (R) or
5312 // U+0072 (r), then advance position to the next code point.
5313 // Otherwise, jump to the step labeled parse.
5314 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
5315 return MakeTuple(urlStart, urlEnd);
5318 ++aPosition;
5320 // 4. If the code point in input pointed to by position is U+004C (L) or
5321 // U+006C (l), then advance position to the next code point.
5322 // Otherwise, jump to the step labeled parse.
5323 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
5324 return MakeTuple(urlStart, urlEnd);
5327 ++aPosition;
5329 // 5. Skip ASCII whitespace within input given position.
5330 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5332 // 6. If the code point in input pointed to by position is U+003D (=),
5333 // then advance position to the next code point. Otherwise, jump to
5334 // the step labeled parse.
5335 if (aPosition == aEnd || *aPosition != '=') {
5336 return MakeTuple(urlStart, urlEnd);
5339 ++aPosition;
5341 // 7. Skip ASCII whitespace within input given position.
5342 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5345 // 8. Skip quotes: If the code point in input pointed to by position is
5346 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5347 // advance position to the next code point. Otherwise, let quote be
5348 // the empty string.
5349 Maybe<char> quote;
5350 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
5351 quote.emplace(*aPosition);
5352 ++aPosition;
5355 // 9. Set urlString to the substring of input from the code point at
5356 // position to the end of the string.
5357 urlStart = aPosition;
5358 urlEnd = aEnd;
5360 // 10. If quote is not the empty string, and there is a code point in
5361 // urlString equal to quote, then truncate urlString at that code
5362 // point, so that it and all subsequent code points are removed.
5363 const char16_t* quotePos;
5364 if (quote.isSome() &&
5365 (quotePos = nsCharTraits<char16_t>::find(
5366 urlStart, std::distance(urlStart, aEnd), quote.value()))) {
5367 urlEnd = quotePos;
5370 return MakeTuple(urlStart, urlEnd);
5373 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
5374 const nsAString& aHeader) {
5375 if (mIsBeingDestroyed) {
5376 return;
5379 const char16_t* position = aHeader.BeginReading();
5380 const char16_t* end = aHeader.EndReading();
5382 // See
5383 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5385 // 3. Skip ASCII whitespace
5386 position = SkipASCIIWhitespace(position, end);
5388 // 4. Let time be 0.
5389 CheckedInt<uint32_t> milliSeconds;
5391 // 5. Collect a sequence of code points that are ASCII digits
5392 const char16_t* digitsStart = position;
5393 while (position != end && mozilla::IsAsciiDigit(*position)) {
5394 ++position;
5397 if (position == digitsStart) {
5398 // 6. If timeString is the empty string, then:
5399 // 1. If the code point in input pointed to by position is not U+002E
5400 // (.), then return.
5401 if (position == end || *position != '.') {
5402 return;
5404 } else {
5405 // 7. Otherwise, set time to the result of parsing timeString using the
5406 // rules for parsing non-negative integers.
5407 nsContentUtils::ParseHTMLIntegerResultFlags result;
5408 uint32_t seconds =
5409 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
5410 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
5411 if (result & nsContentUtils::eParseHTMLInteger_Error) {
5412 // The spec assumes no errors here (since we only pass ASCII digits in),
5413 // but we can still overflow, so this block should deal with that (and
5414 // only that).
5415 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
5416 return;
5418 MOZ_ASSERT(
5419 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
5421 milliSeconds = seconds;
5422 milliSeconds *= 1000;
5423 if (!milliSeconds.isValid()) {
5424 return;
5428 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5429 // STOP characters (.) from input given position. Ignore any collected
5430 // characters.
5431 while (position != end &&
5432 (mozilla::IsAsciiDigit(*position) || *position == '.')) {
5433 ++position;
5436 // 9. Let urlRecord be document's URL.
5437 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
5439 // 10. If position is not past the end of input
5440 if (position != end) {
5441 // 1. If the code point in input pointed to by position is not U+003B (;),
5442 // U+002C (,), or ASCII whitespace, then return.
5443 if (*position != ';' && *position != ',' &&
5444 !mozilla::IsAsciiWhitespace(*position)) {
5445 return;
5448 // 2. Skip ASCII whitespace within input given position.
5449 position = SkipASCIIWhitespace(position, end);
5451 // 3. If the code point in input pointed to by position is U+003B (;) or
5452 // U+002C (,), then advance position to the next code point.
5453 if (position != end && (*position == ';' || *position == ',')) {
5454 ++position;
5456 // 4. Skip ASCII whitespace within input given position.
5457 position = SkipASCIIWhitespace(position, end);
5460 // 11. If position is not past the end of input, then:
5461 if (position != end) {
5462 const char16_t* urlStart;
5463 const char16_t* urlEnd;
5465 // 1-10. See ExtractURLString.
5466 Tie(urlStart, urlEnd) = ExtractURLString(position, end);
5468 // 11. Parse: Parse urlString relative to document. If that fails, return.
5469 // Otherwise, set urlRecord to the resulting URL record.
5470 nsresult rv =
5471 NS_NewURI(getter_AddRefs(urlRecord),
5472 Substring(urlStart, std::distance(urlStart, urlEnd)),
5473 /* charset = */ nullptr, aDocument->GetDocBaseURI());
5474 NS_ENSURE_SUCCESS_VOID(rv);
5478 nsIPrincipal* principal = aDocument->NodePrincipal();
5479 nsCOMPtr<nsIScriptSecurityManager> securityManager =
5480 nsContentUtils::GetSecurityManager();
5481 nsresult rv = securityManager->CheckLoadURIWithPrincipal(
5482 principal, urlRecord,
5483 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5484 aDocument->InnerWindowID());
5485 NS_ENSURE_SUCCESS_VOID(rv);
5487 bool isjs = true;
5488 rv = NS_URIChainHasFlags(
5489 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5490 NS_ENSURE_SUCCESS_VOID(rv);
5492 if (isjs) {
5493 return;
5496 RefreshURI(urlRecord, principal, milliSeconds.value());
5499 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5500 if (!aTimerList) {
5501 return;
5504 uint32_t n = 0;
5505 aTimerList->GetLength(&n);
5507 while (n) {
5508 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5510 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5512 if (timer) {
5513 timer->Cancel();
5518 NS_IMETHODIMP
5519 nsDocShell::CancelRefreshURITimers() {
5520 DoCancelRefreshURITimers(mRefreshURIList);
5521 DoCancelRefreshURITimers(mSavedRefreshURIList);
5522 DoCancelRefreshURITimers(mBFCachedRefreshURIList);
5523 mRefreshURIList = nullptr;
5524 mSavedRefreshURIList = nullptr;
5525 mBFCachedRefreshURIList = nullptr;
5527 return NS_OK;
5530 NS_IMETHODIMP
5531 nsDocShell::GetRefreshPending(bool* aResult) {
5532 if (!mRefreshURIList) {
5533 *aResult = false;
5534 return NS_OK;
5537 uint32_t count;
5538 nsresult rv = mRefreshURIList->GetLength(&count);
5539 if (NS_SUCCEEDED(rv)) {
5540 *aResult = (count != 0);
5542 return rv;
5545 void nsDocShell::RefreshURIToQueue() {
5546 if (mRefreshURIList) {
5547 uint32_t n = 0;
5548 mRefreshURIList->GetLength(&n);
5550 for (uint32_t i = 0; i < n; ++i) {
5551 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5552 if (!timer) {
5553 continue; // this must be a nsRefreshURI already
5556 // Replace this timer object with a nsRefreshTimer object.
5557 nsCOMPtr<nsITimerCallback> callback;
5558 timer->GetCallback(getter_AddRefs(callback));
5560 timer->Cancel();
5562 mRefreshURIList->ReplaceElementAt(callback, i);
5567 NS_IMETHODIMP
5568 nsDocShell::SuspendRefreshURIs() {
5569 RefreshURIToQueue();
5571 // Suspend refresh URIs for our child shells as well.
5572 for (auto* child : mChildList.ForwardRange()) {
5573 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5574 if (shell) {
5575 shell->SuspendRefreshURIs();
5579 return NS_OK;
5582 NS_IMETHODIMP
5583 nsDocShell::ResumeRefreshURIs() {
5584 RefreshURIFromQueue();
5586 // Resume refresh URIs for our child shells as well.
5587 for (auto* child : mChildList.ForwardRange()) {
5588 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5589 if (shell) {
5590 shell->ResumeRefreshURIs();
5594 return NS_OK;
5597 nsresult nsDocShell::RefreshURIFromQueue() {
5598 if (!mRefreshURIList) {
5599 return NS_OK;
5601 uint32_t n = 0;
5602 mRefreshURIList->GetLength(&n);
5604 while (n) {
5605 nsCOMPtr<nsITimerCallback> refreshInfo =
5606 do_QueryElementAt(mRefreshURIList, --n);
5608 if (refreshInfo) {
5609 // This is the nsRefreshTimer object, waiting to be
5610 // setup in a timer object and fired.
5611 // Create the timer and trigger it.
5612 uint32_t delay = static_cast<nsRefreshTimer*>(
5613 static_cast<nsITimerCallback*>(refreshInfo))
5614 ->GetDelay();
5615 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5616 if (win) {
5617 nsCOMPtr<nsITimer> timer;
5618 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5619 nsITimer::TYPE_ONE_SHOT);
5621 if (timer) {
5622 // Replace the nsRefreshTimer element in the queue with
5623 // its corresponding timer object, so that in case another
5624 // load comes through before the timer can go off, the timer will
5625 // get cancelled in CancelRefreshURITimer()
5626 mRefreshURIList->ReplaceElementAt(timer, n);
5632 return NS_OK;
5635 nsresult nsDocShell::Embed(nsIContentViewer* aContentViewer,
5636 WindowGlobalChild* aWindowActor,
5637 bool aIsTransientAboutBlank, bool aPersist,
5638 nsIRequest* aRequest) {
5639 // Save the LayoutHistoryState of the previous document, before
5640 // setting up new document
5641 PersistLayoutHistoryState();
5643 nsresult rv = SetupNewViewer(aContentViewer, aWindowActor);
5644 NS_ENSURE_SUCCESS(rv, rv);
5646 // XXX What if SetupNewViewer fails?
5647 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
5648 // Set history.state
5649 SetDocCurrentStateObj(mLSHE,
5650 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
5653 if (mLSHE) {
5654 // Restore the editing state, if it's stored in session history.
5655 if (mLSHE->HasDetachedEditor()) {
5656 ReattachEditorToWindow(mLSHE);
5659 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5662 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent()) {
5663 bool expired = false;
5664 uint32_t cacheKey = 0;
5665 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
5666 if (cacheChannel) {
5667 // Check if the page has expired from cache
5668 uint32_t expTime = 0;
5669 cacheChannel->GetCacheTokenExpirationTime(&expTime);
5670 uint32_t now = PRTimeToSeconds(PR_Now());
5671 if (expTime <= now) {
5672 expired = true;
5675 // The checks for updating cache key are similar to the old session
5676 // history in OnNewURI. Try to update the cache key if
5677 // - we should update session history and aren't doing a session
5678 // history load.
5679 // - we're doing a forced reload.
5680 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
5681 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
5682 IsForceReloadType(mLoadType)) {
5683 cacheChannel->GetCacheKey(&cacheKey);
5687 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
5688 MoveLoadingToActiveEntry(aPersist, expired, cacheKey);
5691 bool updateHistory = true;
5693 // Determine if this type of load should update history
5694 switch (mLoadType) {
5695 case LOAD_NORMAL_REPLACE:
5696 case LOAD_REFRESH_REPLACE:
5697 case LOAD_STOP_CONTENT_AND_REPLACE:
5698 case LOAD_RELOAD_BYPASS_CACHE:
5699 case LOAD_RELOAD_BYPASS_PROXY:
5700 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5701 case LOAD_REPLACE_BYPASS_CACHE:
5702 updateHistory = false;
5703 break;
5704 default:
5705 break;
5708 if (!updateHistory) {
5709 SetLayoutHistoryState(nullptr);
5712 return NS_OK;
5715 //*****************************************************************************
5716 // nsDocShell::nsIWebProgressListener
5717 //*****************************************************************************
5719 NS_IMETHODIMP
5720 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5721 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5722 int32_t aCurTotalProgress,
5723 int32_t aMaxTotalProgress) {
5724 return NS_OK;
5727 NS_IMETHODIMP
5728 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5729 uint32_t aStateFlags, nsresult aStatus) {
5730 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5731 // Save timing statistics.
5732 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5733 nsCOMPtr<nsIURI> uri;
5734 channel->GetURI(getter_AddRefs(uri));
5735 nsAutoCString aURI;
5736 uri->GetAsciiSpec(aURI);
5738 if (this == aProgress) {
5739 mozilla::Unused << MaybeInitTiming();
5740 mTiming->NotifyFetchStart(uri,
5741 ConvertLoadTypeToNavigationType(mLoadType));
5742 // If we are starting a DocumentChannel, we need to pass the timing
5743 // statistics so that should a process switch occur, the starting type can
5744 // be passed to the new DocShell running in the other content process.
5745 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5746 docChannel->SetNavigationTiming(mTiming);
5750 // Page has begun to load
5751 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5753 if ((aStateFlags & STATE_RESTORING) == 0) {
5754 // Show the progress cursor if the pref is set
5755 if (StaticPrefs::ui_use_activity_cursor()) {
5756 nsCOMPtr<nsIWidget> mainWidget;
5757 GetMainWidget(getter_AddRefs(mainWidget));
5758 if (mainWidget) {
5759 mainWidget->SetCursor(nsIWidget::Cursor{eCursor_spinning});
5763 if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
5764 if (IsForceReloadType(mLoadType)) {
5765 SessionStoreUtils::ResetSessionStore(mBrowsingContext);
5769 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5770 // Page is loading
5771 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5772 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5773 // Page has finished loading
5774 mBusyFlags = BUSY_FLAGS_NONE;
5776 // Hide the progress cursor if the pref is set
5777 if (StaticPrefs::ui_use_activity_cursor()) {
5778 nsCOMPtr<nsIWidget> mainWidget;
5779 GetMainWidget(getter_AddRefs(mainWidget));
5780 if (mainWidget) {
5781 mainWidget->SetCursor(nsIWidget::Cursor{eCursor_standard});
5786 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5787 nsCOMPtr<nsIWebProgress> webProgress =
5788 do_QueryInterface(GetAsSupports(this));
5789 // Is the document stop notification for this document?
5790 if (aProgress == webProgress.get()) {
5791 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5792 EndPageLoad(aProgress, channel, aStatus);
5795 // note that redirect state changes will go through here as well, but it
5796 // is better to handle those in OnRedirectStateChange where more
5797 // information is available.
5798 return NS_OK;
5801 NS_IMETHODIMP
5802 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5803 nsIURI* aURI, uint32_t aFlags) {
5804 // Since we've now changed Documents, notify the BrowsingContext that we've
5805 // changed. Ideally we'd just let the BrowsingContext do this when it
5806 // changes the current window global, but that happens before this and we
5807 // have a lot of tests that depend on the specific ordering of messages.
5808 bool isTopLevel = false;
5809 if (XRE_IsParentProcess() &&
5810 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
5811 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
5812 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5814 return NS_OK;
5817 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5818 nsIChannel* aNewChannel,
5819 uint32_t aRedirectFlags,
5820 uint32_t aStateFlags) {
5821 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5822 "Calling OnRedirectStateChange when there is no redirect");
5824 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5825 return; // not a toplevel document
5828 nsCOMPtr<nsIURI> oldURI, newURI;
5829 aOldChannel->GetURI(getter_AddRefs(oldURI));
5830 aNewChannel->GetURI(getter_AddRefs(newURI));
5831 if (!oldURI || !newURI) {
5832 return;
5835 // DocumentChannel adds redirect chain to global history in the parent
5836 // process. The redirect chain can't be queried from the content process, so
5837 // there's no need to update global history here.
5838 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5839 if (!docChannel) {
5840 // Below a URI visit is saved (see AddURIVisit method doc).
5841 // The visit chain looks something like:
5842 // ...
5843 // Site N - 1
5844 // => Site N
5845 // (redirect to =>) Site N + 1 (we are here!)
5847 // Get N - 1 and transition type
5848 nsCOMPtr<nsIURI> previousURI;
5849 uint32_t previousFlags = 0;
5850 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5852 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5853 net::ChannelIsPost(aOldChannel)) {
5854 // 1. Internal redirects are ignored because they are specific to the
5855 // channel implementation.
5856 // 2. POSTs are not saved by global history.
5858 // Regardless, we need to propagate the previous visit to the new
5859 // channel.
5860 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5861 } else {
5862 // Get the HTTP response code, if available.
5863 uint32_t responseStatus = 0;
5864 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5865 if (httpChannel) {
5866 Unused << httpChannel->GetResponseStatus(&responseStatus);
5869 // Add visit N -1 => N
5870 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5872 // Since N + 1 could be the final destination, we will not save N => N + 1
5873 // here. OnNewURI will do that, so we will cache it.
5874 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5878 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5879 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5880 mLoadType = LOAD_NORMAL_REPLACE;
5881 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5885 NS_IMETHODIMP
5886 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5887 nsresult aStatus, const char16_t* aMessage) {
5888 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5889 return NS_OK;
5892 NS_IMETHODIMP
5893 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5894 uint32_t aState) {
5895 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5896 return NS_OK;
5899 NS_IMETHODIMP
5900 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5901 nsIRequest* aRequest, uint32_t aEvent) {
5902 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5903 return NS_OK;
5906 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5907 const nsACString& aKeyword, bool aIsPrivateContext) {
5908 nsCOMPtr<nsIURIFixupInfo> info;
5909 if (!XRE_IsContentProcess()) {
5910 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5911 if (uriFixup) {
5912 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
5915 return info.forget();
5918 /* static */
5919 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
5920 nsIChannel* aChannel, nsIURI* aUrl) {
5921 if (!aChannel) {
5922 return nullptr;
5925 nsresult rv = NS_OK;
5926 nsAutoCString host;
5927 rv = aUrl->GetAsciiHost(host);
5928 if (NS_WARN_IF(NS_FAILED(rv))) {
5929 return nullptr;
5932 // No point in going further if "www." is included in the hostname
5933 // already. That is the only hueristic we're applying in this function.
5934 if (StringBeginsWith(host, "www."_ns)) {
5935 return nullptr;
5938 // Return if fixup enable pref is turned off.
5939 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5940 return nullptr;
5943 // Return if scheme is not HTTPS.
5944 if (!SchemeIsHTTPS(aUrl)) {
5945 return nullptr;
5948 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
5949 if (!info) {
5950 return nullptr;
5953 // Skip doing the fixup if our channel was redirected, because we
5954 // shouldn't be guessing things about the post-redirect URI.
5955 if (!info->RedirectChain().IsEmpty()) {
5956 return nullptr;
5959 int32_t port = 0;
5960 rv = aUrl->GetPort(&port);
5961 if (NS_WARN_IF(NS_FAILED(rv))) {
5962 return nullptr;
5965 // Don't fix up hosts with ports.
5966 if (port != -1) {
5967 return nullptr;
5970 // Don't fix up localhost url.
5971 if (host == "localhost") {
5972 return nullptr;
5975 // Don't fix up hostnames with IP address.
5976 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
5977 return nullptr;
5980 nsAutoCString userPass;
5981 rv = aUrl->GetUserPass(userPass);
5982 if (NS_WARN_IF(NS_FAILED(rv))) {
5983 return nullptr;
5986 // Security - URLs with user / password info should NOT be modified.
5987 if (!userPass.IsEmpty()) {
5988 return nullptr;
5991 nsCOMPtr<nsISupports> securityInfo;
5992 rv = aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
5993 if (NS_WARN_IF(NS_FAILED(rv))) {
5994 return nullptr;
5997 nsCOMPtr<nsITransportSecurityInfo> tsi = do_QueryInterface(securityInfo);
5998 if (NS_WARN_IF(!tsi)) {
5999 return nullptr;
6002 nsCOMPtr<nsIX509Cert> cert;
6003 rv = tsi->GetServerCert(getter_AddRefs(cert));
6004 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
6005 return nullptr;
6008 nsTArray<uint8_t> certBytes;
6009 rv = cert->GetRawDER(certBytes);
6010 if (NS_FAILED(rv)) {
6011 return nullptr;
6014 mozilla::pkix::Input serverCertInput;
6015 mozilla::pkix::Result rv1 =
6016 serverCertInput.Init(certBytes.Elements(), certBytes.Length());
6017 if (rv1 != mozilla::pkix::Success) {
6018 return nullptr;
6021 nsAutoCString newHost("www."_ns);
6022 newHost.Append(host);
6024 mozilla::pkix::Input newHostInput;
6025 rv1 = newHostInput.Init(
6026 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
6027 newHost.Length());
6028 if (rv1 != mozilla::pkix::Success) {
6029 return nullptr;
6032 // Check if adding a "www." prefix to the request's hostname will
6033 // cause the response's certificate to match.
6034 mozilla::psm::BRNameMatchingPolicy nameMatchingPolicy(
6035 mozilla::psm::BRNameMatchingPolicy::Mode::Enforce);
6036 rv1 = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput,
6037 nameMatchingPolicy);
6038 if (rv1 != mozilla::pkix::Success) {
6039 return nullptr;
6042 nsCOMPtr<nsIURI> newURI;
6043 Unused << NS_MutateURI(aUrl).SetHost(newHost).Finalize(
6044 getter_AddRefs(newURI));
6046 return newURI.forget();
6049 /* static */
6050 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
6051 nsIChannel* aChannel, nsresult aStatus,
6052 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
6053 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
6054 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData) {
6055 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
6056 aStatus != NS_ERROR_CONNECTION_REFUSED &&
6057 aStatus !=
6058 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6059 return nullptr;
6062 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
6063 return nullptr;
6066 nsCOMPtr<nsIURI> url;
6067 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6068 if (NS_FAILED(rv)) {
6069 return nullptr;
6073 // Try and make an alternative URI from the old one
6075 nsCOMPtr<nsIURI> newURI;
6076 nsCOMPtr<nsIInputStream> newPostData;
6078 nsAutoCString oldSpec;
6079 url->GetSpec(oldSpec);
6082 // First try keyword fixup
6084 nsAutoString keywordProviderName, keywordAsSent;
6085 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
6086 // we should only perform a keyword search under the following
6087 // conditions:
6088 // (0) Pref keyword.enabled is true
6089 // (1) the url scheme is http (or https)
6090 // (2) the url does not have a protocol scheme
6091 // If we don't enforce such a policy, then we end up doing
6092 // keyword searchs on urls we don't intend like imap, file,
6093 // mailbox, etc. This could lead to a security problem where we
6094 // send data to the keyword server that we shouldn't be.
6095 // Someone needs to clean up keywords in general so we can
6096 // determine on a per url basis if we want keywords
6097 // enabled...this is just a bandaid...
6098 nsAutoCString scheme;
6099 Unused << url->GetScheme(scheme);
6100 if (Preferences::GetBool("keyword.enabled", false) &&
6101 StringBeginsWith(scheme, "http"_ns)) {
6102 bool attemptFixup = false;
6103 nsAutoCString host;
6104 Unused << url->GetHost(host);
6105 if (host.FindChar('.') == kNotFound) {
6106 attemptFixup = true;
6107 } else {
6108 // For domains with dots, we check the public suffix validity.
6109 nsCOMPtr<nsIEffectiveTLDService> tldService =
6110 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
6111 if (tldService) {
6112 nsAutoCString suffix;
6113 attemptFixup =
6114 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
6115 suffix.IsEmpty();
6118 if (attemptFixup) {
6119 nsCOMPtr<nsIURIFixupInfo> info;
6120 // only send non-qualified hosts to the keyword server
6121 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
6122 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
6123 } else {
6125 // If this string was passed through nsStandardURL by
6126 // chance, then it may have been converted from UTF-8 to
6127 // ACE, which would result in a completely bogus keyword
6128 // query. Here we try to recover the original Unicode
6129 // value, but this is not 100% correct since the value may
6130 // have been normalized per the IDN normalization rules.
6132 // Since we don't have access to the exact original string
6133 // that was entered by the user, this will just have to do.
6134 bool isACE;
6135 nsAutoCString utf8Host;
6136 nsCOMPtr<nsIIDNService> idnSrv =
6137 do_GetService(NS_IDNSERVICE_CONTRACTID);
6138 if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6139 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6140 info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
6142 } else {
6143 info = KeywordToURI(host, aUsePrivateBrowsing);
6146 if (info) {
6147 info->GetPreferredURI(getter_AddRefs(newURI));
6148 if (newURI) {
6149 info->GetKeywordAsSent(keywordAsSent);
6150 info->GetKeywordProviderName(keywordProviderName);
6151 info->GetPostData(getter_AddRefs(newPostData));
6159 // Now try change the address, e.g. turn http://foo into
6160 // http://www.foo.com, and if that doesn't work try https with
6161 // https://foo and https://www.foo.com.
6163 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6164 // Skip fixup for anything except a normal document load
6165 // operation on the topframe.
6166 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
6168 if (doCreateAlternate) {
6169 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6170 nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
6171 // Only do this if our channel was loaded directly by the user from the
6172 // URL bar or similar (system principal) and not redirected, because we
6173 // shouldn't be guessing things about links from other sites, or a
6174 // post-redirect URI.
6175 doCreateAlternate = principal && principal->IsSystemPrincipal() &&
6176 loadInfo->RedirectChain().IsEmpty();
6178 // Test if keyword lookup produced a new URI or not
6179 if (doCreateAlternate && newURI) {
6180 bool sameURI = false;
6181 url->Equals(newURI, &sameURI);
6182 if (!sameURI) {
6183 // Keyword lookup made a new URI so no need to try
6184 // an alternate one.
6185 doCreateAlternate = false;
6188 if (doCreateAlternate) {
6189 newURI = nullptr;
6190 newPostData = nullptr;
6191 keywordProviderName.Truncate();
6192 keywordAsSent.Truncate();
6193 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6194 if (uriFixup) {
6195 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
6196 uriFixup->GetFixupURIInfo(oldSpec,
6197 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
6198 getter_AddRefs(fixupInfo));
6199 if (fixupInfo) {
6200 fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
6204 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6205 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6206 // Try HTTPS, since http didn't work
6207 if (SchemeIsHTTP(url)) {
6208 int32_t port = 0;
6209 url->GetPort(&port);
6211 // Fall back to HTTPS only if port is default
6212 if (port == -1) {
6213 newURI = nullptr;
6214 newPostData = nullptr;
6215 Unused << NS_MutateURI(url)
6216 .SetScheme("https"_ns)
6217 .Finalize(getter_AddRefs(newURI));
6222 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try prefixing the domain name
6223 // with www. to see if we can avoid showing the cert error page. For example,
6224 // https://example.com -> https://www.example.com.
6225 if (aStatus ==
6226 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6227 newPostData = nullptr;
6228 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
6231 // Did we make a new URI that is different to the old one? If so
6232 // load it.
6234 if (newURI) {
6235 // Make sure the new URI is different from the old one,
6236 // otherwise there's little point trying to load it again.
6237 bool sameURI = false;
6238 url->Equals(newURI, &sameURI);
6239 if (!sameURI) {
6240 if (aNewPostData) {
6241 newPostData.forget(aNewPostData);
6243 if (aNotifyKeywordSearchLoading) {
6244 // This notification is meant for Firefox Health Report so it
6245 // can increment counts from the search engine
6246 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6248 return newURI.forget();
6252 return nullptr;
6255 nsresult nsDocShell::FilterStatusForErrorPage(
6256 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
6257 bool aIsTopFrame, bool aUseErrorPages, bool aIsInitialDocument,
6258 bool* aSkippedUnknownProtocolNavigation) {
6259 // Errors to be shown only on top-level frames
6260 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6261 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6262 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6263 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6264 aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6265 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6266 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6267 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6268 aStatus == NS_ERROR_MALFORMED_URI ||
6269 aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
6270 aStatus == NS_ERROR_DOM_COOP_FAILED ||
6271 aStatus == NS_ERROR_DOM_COEP_FAILED) &&
6272 (aIsTopFrame || aUseErrorPages)) {
6273 return aStatus;
6276 if (aStatus == NS_ERROR_NET_TIMEOUT ||
6277 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
6278 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6279 aStatus == NS_ERROR_REDIRECT_LOOP ||
6280 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6281 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
6282 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
6283 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
6284 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
6285 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6286 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6287 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6288 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6289 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6290 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
6291 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
6292 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
6293 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
6294 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6295 // Errors to be shown for any frame
6296 return aStatus;
6299 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6300 // For unknown protocols we only display an error if the load is triggered
6301 // by the browser itself, or we're replacing the initial document (and
6302 // nothing else). Showing the error for page-triggered navigations causes
6303 // annoying behavior for users, see bug 1528305.
6305 // We could, maybe, try to detect if this is in response to some user
6306 // interaction (like clicking a link, or something else) and maybe show
6307 // the error page in that case. But this allows for ctrl+clicking and such
6308 // to see the error page.
6309 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6310 if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6311 StaticPrefs::dom_no_unknown_protocol_error_enabled() &&
6312 !aIsInitialDocument) {
6313 if (aSkippedUnknownProtocolNavigation) {
6314 *aSkippedUnknownProtocolNavigation = true;
6316 return NS_OK;
6318 return aStatus;
6321 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6322 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6323 // Caching channels would have to look at their flags to work
6324 // out which error to return. Or we can fix up the error here.
6325 if (!(aLoadType & LOAD_CMD_HISTORY)) {
6326 return NS_ERROR_OFFLINE;
6328 return aStatus;
6331 return NS_OK;
6334 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6335 nsIChannel* aChannel, nsresult aStatus) {
6336 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
6337 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
6338 static_cast<uint32_t>(aStatus)));
6339 if (!aChannel) {
6340 return NS_ERROR_NULL_POINTER;
6343 // Make sure to discard the initial client if we never created the initial
6344 // about:blank document. Do this before possibly returning from the method
6345 // due to an error.
6346 mInitialClientSource.reset();
6348 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6349 if (reporter) {
6350 nsCOMPtr<nsILoadGroup> loadGroup;
6351 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6352 if (loadGroup) {
6353 reporter->FlushConsoleReports(loadGroup);
6354 } else {
6355 reporter->FlushConsoleReports(GetDocument());
6359 nsCOMPtr<nsIURI> url;
6360 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6361 if (NS_FAILED(rv)) {
6362 return rv;
6365 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6366 if (timingChannel) {
6367 TimeStamp channelCreationTime;
6368 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6369 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6370 Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
6371 channelCreationTime);
6375 // Timing is picked up by the window, we don't need it anymore
6376 mTiming = nullptr;
6378 // clean up reload state for meta charset
6379 if (eCharsetReloadRequested == mCharsetReloadState) {
6380 mCharsetReloadState = eCharsetReloadStopOrigional;
6381 } else {
6382 mCharsetReloadState = eCharsetReloadInit;
6385 // Save a pointer to the currently-loading history entry.
6386 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6387 // entry further down in this method.
6388 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6389 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
6392 // one of many safeguards that prevent death and destruction if
6393 // someone is so very very rude as to bring this window down
6394 // during this load handler.
6396 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6398 // Notify the ContentViewer that the Document has finished loading. This
6399 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6400 if (!mEODForCurrentDocument && mContentViewer) {
6401 mIsExecutingOnLoadHandler = true;
6402 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
6403 contentViewer->LoadComplete(aStatus);
6404 mIsExecutingOnLoadHandler = false;
6406 mEODForCurrentDocument = true;
6408 // If all documents have completed their loading
6409 // favor native event dispatch priorities
6410 // over performance
6411 if (--gNumberOfDocumentsLoading == 0) {
6412 // Hint to use normal native event dispatch priorities
6413 FavorPerformanceHint(false);
6416 /* Check if the httpChannel has any cache-control related response headers,
6417 * like no-store, no-cache. If so, update SHEntry so that
6418 * when a user goes back/forward to this page, we appropriately do
6419 * form value restoration or load from server.
6421 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6422 if (!httpChannel) {
6423 // HttpChannel could be hiding underneath a Multipart channel.
6424 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6427 if (httpChannel) {
6428 // figure out if SH should be saving layout state.
6429 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6430 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6431 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
6432 mLSHE->SetSaveLayoutStateFlag(false);
6436 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6437 // onLoadHandler tries to load something different in
6438 // itself or one of its children, we can deal with it appropriately.
6439 if (mLSHE) {
6440 mLSHE->SetLoadType(LOAD_HISTORY);
6442 // Clear the mLSHE reference to indicate document loading is done one
6443 // way or another.
6444 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6446 mActiveEntryIsLoadingFromSessionHistory = false;
6448 // if there's a refresh header in the channel, this method
6449 // will set it up for us.
6450 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
6451 RefreshURIFromQueue();
6453 // Test whether this is the top frame or a subframe
6454 bool isTopFrame = mBrowsingContext->IsTop();
6456 bool hadErrorStatus = false;
6457 // If status code indicates an error it means that DocumentChannel already
6458 // tried to fixup the uri and failed. Throw an error dialog box here.
6459 if (NS_FAILED(aStatus)) {
6460 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6461 // the error event to our embedder, since tests are relying on this.
6462 // The error event is usually fired by the caller of InternalLoad, but
6463 // this particular error can happen asynchronously.
6464 // Bug 1629201 is filed for having much clearer decision making around
6465 // which cases need error events.
6466 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6467 aStatus == NS_ERROR_CONTENT_BLOCKED);
6468 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6470 bool isInitialDocument =
6471 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6472 bool skippedUnknownProtocolNavigation = false;
6473 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
6474 mBrowsingContext->GetUseErrorPages(),
6475 isInitialDocument,
6476 &skippedUnknownProtocolNavigation);
6477 hadErrorStatus = true;
6478 if (NS_FAILED(aStatus)) {
6479 if (!mIsBeingDestroyed) {
6480 DisplayLoadError(aStatus, url, nullptr, aChannel);
6482 } else if (skippedUnknownProtocolNavigation) {
6483 nsTArray<nsString> params;
6484 if (NS_FAILED(
6485 NS_GetSanitizedURIStringFromURI(url, *params.AppendElement()))) {
6486 params.LastElement().AssignLiteral(u"(unknown uri)");
6488 nsContentUtils::ReportToConsole(
6489 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
6490 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
6491 params);
6493 } else {
6494 // If we have a host
6495 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6496 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6499 if (hadErrorStatus) {
6500 // Don't send session store updates if the reason EndPageLoad was called is
6501 // because we are process switching. Sometimes the update takes too long and
6502 // incorrectly overrides session store data from the following load.
6503 return NS_OK;
6505 if constexpr (SessionStoreUtils::NATIVE_LISTENER) {
6506 if (Document* document = GetDocument()) {
6507 if (WindowGlobalChild* windowChild = document->GetWindowGlobalChild()) {
6508 RefPtr<SessionStoreDataCollector> collector =
6509 SessionStoreDataCollector::CollectSessionStoreData(windowChild);
6510 collector->RecordInputChange();
6511 collector->RecordScrollChange();
6516 return NS_OK;
6519 //*****************************************************************************
6520 // nsDocShell: Content Viewer Management
6521 //*****************************************************************************
6523 nsresult nsDocShell::EnsureContentViewer() {
6524 if (mContentViewer) {
6525 return NS_OK;
6527 if (mIsBeingDestroyed) {
6528 return NS_ERROR_FAILURE;
6531 nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6532 nsCOMPtr<nsIURI> baseURI;
6533 nsIPrincipal* principal = GetInheritedPrincipal(false);
6534 nsIPrincipal* partitionedPrincipal = GetInheritedPrincipal(false, true);
6536 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6537 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6538 if (parentItem) {
6539 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6540 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6541 if (parentElement) {
6542 baseURI = parentElement->GetBaseURI();
6543 cspToInheritForAboutBlank = parentElement->GetCsp();
6548 nsresult rv = CreateAboutBlankContentViewer(
6549 principal, partitionedPrincipal, cspToInheritForAboutBlank, baseURI,
6550 /* aIsInitialDocument */ true);
6552 NS_ENSURE_STATE(mContentViewer);
6554 if (NS_SUCCEEDED(rv)) {
6555 RefPtr<Document> doc(GetDocument());
6556 MOZ_ASSERT(doc,
6557 "Should have doc if CreateAboutBlankContentViewer "
6558 "succeeded!");
6559 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
6561 // Documents created using EnsureContentViewer may be transient
6562 // placeholders created by framescripts before content has a
6563 // chance to load. In some cases, window.open(..., "noopener")
6564 // will create such a document and then synchronously tear it
6565 // down, firing a "pagehide" event. Doing so violates our
6566 // assertions about DocGroups. It's easier to silence the
6567 // assertion here than to avoid creating the extra document.
6568 doc->IgnoreDocGroupMismatches();
6571 return rv;
6574 nsresult nsDocShell::CreateAboutBlankContentViewer(
6575 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
6576 nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI, bool aIsInitialDocument,
6577 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
6578 bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6579 WindowGlobalChild* aActor) {
6580 RefPtr<Document> blankDoc;
6581 nsCOMPtr<nsIContentViewer> viewer;
6582 nsresult rv = NS_ERROR_FAILURE;
6584 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6586 /* mCreatingDocument should never be true at this point. However, it's
6587 a theoretical possibility. We want to know about it and make it stop,
6588 and this sounds like a job for an assertion. */
6589 NS_ASSERTION(!mCreatingDocument,
6590 "infinite(?) loop creating document averted");
6591 if (mCreatingDocument) {
6592 return NS_ERROR_FAILURE;
6595 if (!mBrowsingContext->AncestorsAreCurrent() ||
6596 mBrowsingContext->IsInBFCache()) {
6597 mBrowsingContext->RemoveRootFromBFCacheSync();
6598 return NS_ERROR_NOT_AVAILABLE;
6601 // mContentViewer->PermitUnload may release |this| docshell.
6602 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6604 AutoRestore<bool> creatingDocument(mCreatingDocument);
6605 mCreatingDocument = true;
6607 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6608 mItemType != typeChrome) {
6609 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6610 mBrowsingContext->OriginAttributesRef());
6613 // Make sure timing is created. But first record whether we had it
6614 // already, so we don't clobber the timing for an in-progress load.
6615 bool hadTiming = mTiming;
6616 bool toBeReset = MaybeInitTiming();
6617 if (mContentViewer) {
6618 if (aCheckPermitUnload) {
6619 // We've got a content viewer already. Make sure the user
6620 // permits us to discard the current document and replace it
6621 // with about:blank. And also ensure we fire the unload events
6622 // in the current document.
6624 // Unload gets fired first for
6625 // document loaded from the session history.
6626 mTiming->NotifyBeforeUnload();
6628 bool okToUnload;
6629 rv = mContentViewer->PermitUnload(&okToUnload);
6631 if (NS_SUCCEEDED(rv) && !okToUnload) {
6632 // The user chose not to unload the page, interrupt the load.
6633 MaybeResetInitTiming(toBeReset);
6634 return NS_ERROR_FAILURE;
6636 if (mTiming) {
6637 mTiming->NotifyUnloadAccepted(mCurrentURI);
6641 mSavingOldViewer = aTryToSaveOldPresentation &&
6642 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
6644 // Make sure to blow away our mLoadingURI just in case. No loads
6645 // from inside this pagehide.
6646 mLoadingURI = nullptr;
6648 // Stop any in-progress loading, so that we don't accidentally trigger any
6649 // PageShow notifications from Embed() interrupting our loading below.
6650 Stop();
6652 // Notify the current document that it is about to be unloaded!!
6654 // It is important to fire the unload() notification *before* any state
6655 // is changed within the DocShell - otherwise, javascript will get the
6656 // wrong information :-(
6658 (void)FirePageHideNotification(!mSavingOldViewer);
6659 // pagehide notification might destroy this docshell.
6660 if (mIsBeingDestroyed) {
6661 return NS_ERROR_DOCSHELL_DYING;
6665 // Now make sure we don't think we're in the middle of firing unload after
6666 // this point. This will make us fire unload when the about:blank document
6667 // unloads... but that's ok, more or less. Would be nice if it fired load
6668 // too, of course.
6669 mFiredUnloadEvent = false;
6671 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6672 nsContentUtils::FindInternalContentViewer("text/html"_ns);
6674 if (docFactory) {
6675 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6676 const uint32_t sandboxFlags =
6677 mBrowsingContext->GetHasLoadedNonInitialDocument()
6678 ? mBrowsingContext->GetSandboxFlags()
6679 : mBrowsingContext->GetInitialSandboxFlags();
6680 // If we're sandboxed, then create a new null principal. We skip
6681 // this if we're being created from WindowGlobalChild, since in
6682 // that case we already have a null principal if required.
6683 // We can't compare againt the BrowsingContext sandbox flag, since
6684 // the value was taken when the load initiated and may have since
6685 // changed.
6686 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6687 if (aPrincipal) {
6688 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6689 } else {
6690 principal = NullPrincipal::CreateWithInheritedAttributes(this);
6692 partitionedPrincipal = principal;
6693 } else {
6694 principal = aPrincipal;
6695 partitionedPrincipal = aPartitionedPrincipal;
6698 // We cannot get the foreign partitioned prinicpal for the initial
6699 // about:blank page. So, we change to check if we need to use the
6700 // partitioned principal for the service worker here.
6701 MaybeCreateInitialClientSource(
6702 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6703 this)
6704 ? partitionedPrincipal
6705 : principal);
6707 // generate (about:blank) document to load
6708 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6709 partitionedPrincipal, this);
6710 if (blankDoc) {
6711 // Hack: manually set the CSP for the new document
6712 // Please create an actual copy of the CSP (do not share the same
6713 // reference) otherwise appending a new policy within the new
6714 // document will be incorrectly propagated to the opening doc.
6715 if (aCSP) {
6716 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6717 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6718 blankDoc->SetCsp(cspToInherit);
6721 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6723 blankDoc->SetEmbedderPolicy(aCOEP);
6725 // Hack: set the base URI manually, since this document never
6726 // got Reset() with a channel.
6727 blankDoc->SetBaseURI(aBaseURI);
6729 // Copy our sandbox flags to the document. These are immutable
6730 // after being set here.
6731 blankDoc->SetSandboxFlags(sandboxFlags);
6733 // create a content viewer for us and the new document
6734 docFactory->CreateInstanceForDocument(
6735 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6736 getter_AddRefs(viewer));
6738 // hook 'em up
6739 if (viewer) {
6740 viewer->SetContainer(this);
6741 rv = Embed(viewer, aActor, true, false, nullptr);
6742 NS_ENSURE_SUCCESS(rv, rv);
6744 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6745 /* aFireLocationChange */ true,
6746 /* aIsInitialAboutBlank */ true, /* aLocationFlags */ 0);
6747 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6752 // The transient about:blank viewer doesn't have a session history entry.
6753 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6755 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6756 // have one before entering this function.
6757 if (!hadTiming) {
6758 mTiming = nullptr;
6759 mBlankTiming = true;
6762 return rv;
6765 NS_IMETHODIMP
6766 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
6767 nsIPrincipal* aPartitionedPrincipal,
6768 nsIContentSecurityPolicy* aCSP) {
6769 return CreateAboutBlankContentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6770 nullptr, /* aIsInitialDocument */ false);
6773 nsresult nsDocShell::CreateContentViewerForActor(
6774 WindowGlobalChild* aWindowActor) {
6775 MOZ_ASSERT(aWindowActor);
6777 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6778 // FIXME: We may want to support non-initial documents here.
6779 nsresult rv = CreateAboutBlankContentViewer(
6780 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6781 /* aCsp */ nullptr,
6782 /* aBaseURI */ nullptr,
6783 /* aIsInitialDocument */ true,
6784 /* aCOEP */ Nothing(),
6785 /* aTryToSaveOldPresentation */ true,
6786 /* aCheckPermitUnload */ true, aWindowActor);
6787 #ifdef DEBUG
6788 if (NS_SUCCEEDED(rv)) {
6789 RefPtr<Document> doc(GetDocument());
6790 MOZ_ASSERT(
6791 doc,
6792 "Should have a document if CreateAboutBlankContentViewer succeeded");
6793 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6794 "New document should be in the same global as our actor");
6795 MOZ_ASSERT(doc->IsInitialDocument(),
6796 "New document should be an initial document");
6798 #endif
6800 return rv;
6803 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6804 nsIRequest* aNewRequest,
6805 Document* aNewDocument) {
6806 if (!mOSHE) {
6807 return false; // no entry to save into
6810 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6811 "mOSHE cannot be non-null with SHIP");
6812 nsCOMPtr<nsIContentViewer> viewer = mOSHE->GetContentViewer();
6813 if (viewer) {
6814 NS_WARNING("mOSHE already has a content viewer!");
6815 return false;
6818 // Only save presentation for "normal" loads and link loads. Anything else
6819 // probably wants to refetch the page, so caching the old presentation
6820 // would be incorrect.
6821 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6822 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6823 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6824 aLoadType != LOAD_ERROR_PAGE) {
6825 return false;
6828 // If the session history entry has the saveLayoutState flag set to false,
6829 // then we should not cache the presentation.
6830 if (!mOSHE->GetSaveLayoutStateFlag()) {
6831 return false;
6834 // If the document is not done loading, don't cache it.
6835 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6836 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6837 ("Blocked due to document still loading"));
6838 return false;
6841 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6842 return false;
6845 // Avoid doing the work of saving the presentation state in the case where
6846 // the content viewer cache is disabled.
6847 if (nsSHistory::GetMaxTotalViewers() == 0) {
6848 return false;
6851 // Don't cache the content viewer if we're in a subframe.
6852 if (mBrowsingContext->GetParent()) {
6853 return false; // this is a subframe load
6856 // If the document does not want its presentation cached, then don't.
6857 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6859 uint32_t bfCacheCombo = 0;
6860 bool canSavePresentation =
6861 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6862 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6863 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6864 auto* browsingContextGroup = mBrowsingContext->Group();
6865 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6866 browsingContextGroup->Toplevels();
6868 for (const auto& browsingContext : topLevelContext) {
6869 if (browsingContext != mBrowsingContext) {
6870 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6871 canSavePresentation = false;
6873 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6874 break;
6878 ReportBFCacheComboTelemetry(bfCacheCombo);
6880 return doc && canSavePresentation;
6883 /* static */
6884 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6885 // There are 11 possible reasons to make a request fails to use BFCache
6886 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6887 // the common combinations for reasons which make requests fail to use
6888 // BFCache. These combinations are generated based on some local browsings,
6889 // we need to adjust them when necessary.
6890 enum BFCacheStatusCombo : uint32_t {
6891 BFCACHE_SUCCESS,
6892 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6893 // If both unload and beforeunload listeners are presented, it'll be
6894 // recorded as unload
6895 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6896 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6897 mozilla::dom::BFCacheStatus::REQUEST,
6898 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6899 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6900 mozilla::dom::BFCacheStatus::REQUEST |
6901 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6902 UNLOAD_REQUEST_PEER_MSE =
6903 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6904 mozilla::dom::BFCacheStatus::REQUEST |
6905 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6906 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6907 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6908 mozilla::dom::BFCacheStatus::REQUEST |
6909 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6910 SUSPENDED_UNLOAD_REQUEST_PEER =
6911 mozilla::dom::BFCacheStatus::SUSPENDED |
6912 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6913 mozilla::dom::BFCacheStatus::REQUEST |
6914 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6915 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6916 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6919 // Beforeunload is recorded as a blocker only if it is the only one to block
6920 // bfcache.
6921 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6922 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6924 switch (aCombo) {
6925 case BFCACHE_SUCCESS:
6926 Telemetry::AccumulateCategorical(
6927 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6928 break;
6929 case NOT_ONLY_TOPLEVEL:
6930 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6931 Telemetry::AccumulateCategorical(
6932 Telemetry::LABELS_BFCACHE_COMBO::Other);
6933 break;
6935 Telemetry::AccumulateCategorical(
6936 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6937 Telemetry::AccumulateCategorical(
6938 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6939 break;
6940 case UNLOAD:
6941 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6942 break;
6943 case BEFOREUNLOAD:
6944 Telemetry::AccumulateCategorical(
6945 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6946 break;
6947 case UNLOAD_REQUEST:
6948 Telemetry::AccumulateCategorical(
6949 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6950 break;
6951 case REQUEST:
6952 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6953 break;
6954 case UNLOAD_REQUEST_PEER:
6955 Telemetry::AccumulateCategorical(
6956 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6957 break;
6958 case UNLOAD_REQUEST_PEER_MSE:
6959 Telemetry::AccumulateCategorical(
6960 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6961 break;
6962 case UNLOAD_REQUEST_MSE:
6963 Telemetry::AccumulateCategorical(
6964 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6965 break;
6966 case SUSPENDED_UNLOAD_REQUEST_PEER:
6967 Telemetry::AccumulateCategorical(
6968 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6969 break;
6970 case REMOTE_SUBFRAMES:
6971 Telemetry::AccumulateCategorical(
6972 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6973 break;
6974 default:
6975 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6976 break;
6980 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6981 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6982 MOZ_ASSERT(!mIsBeingDestroyed);
6984 NS_ASSERTION(!mEditorData,
6985 "Why reattach an editor when we already have one?");
6986 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6987 "Reattaching when there's not a detached editor.");
6989 if (mEditorData || !aSHEntry) {
6990 return;
6993 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6994 if (mEditorData) {
6995 #ifdef DEBUG
6996 nsresult rv =
6997 #endif
6998 mEditorData->ReattachToWindow(this);
6999 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7003 void nsDocShell::DetachEditorFromWindow() {
7004 if (!mEditorData || mEditorData->WaitingForLoad()) {
7005 // If there's nothing to detach, or if the editor data is actually set
7006 // up for the _new_ page that's coming in, don't detach.
7007 return;
7010 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7011 "Detaching editor when it's already detached.");
7013 nsresult res = mEditorData->DetachFromWindow();
7014 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7016 if (NS_SUCCEEDED(res)) {
7017 // Make mOSHE hold the owning ref to the editor data.
7018 if (mOSHE) {
7019 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
7020 "We should not set the editor data again once after we "
7021 "detached the editor data during destroying this docshell");
7022 mOSHE->SetEditorData(mEditorData.release());
7023 } else {
7024 mEditorData = nullptr;
7028 #ifdef DEBUG
7030 bool isEditable;
7031 GetEditable(&isEditable);
7032 NS_ASSERTION(!isEditable,
7033 "Window is still editable after detaching editor.");
7035 #endif // DEBUG
7038 nsresult nsDocShell::CaptureState() {
7039 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7041 if (!mOSHE || mOSHE == mLSHE) {
7042 // No entry to save into, or we're replacing the existing entry.
7043 return NS_ERROR_FAILURE;
7046 if (!mScriptGlobal) {
7047 return NS_ERROR_FAILURE;
7050 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7051 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7053 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7054 nsAutoCString spec;
7055 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
7056 if (uri) {
7057 uri->GetSpec(spec);
7059 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7060 ("Saving presentation into session history, URI: %s", spec.get()));
7063 mOSHE->SetWindowState(windowState);
7065 // Suspend refresh URIs and save off the timer queue
7066 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7068 // Capture the current content viewer bounds.
7069 if (mContentViewer) {
7070 nsIntRect bounds;
7071 mContentViewer->GetBounds(bounds);
7072 mOSHE->SetViewerBounds(bounds);
7075 // Capture the docshell hierarchy.
7076 mOSHE->ClearChildShells();
7078 uint32_t childCount = mChildList.Length();
7079 for (uint32_t i = 0; i < childCount; ++i) {
7080 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7081 NS_ASSERTION(childShell, "null child shell");
7083 mOSHE->AddChildShell(childShell);
7086 return NS_OK;
7089 NS_IMETHODIMP
7090 nsDocShell::RestorePresentationEvent::Run() {
7091 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7093 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7094 NS_WARNING("RestoreFromHistory failed");
7096 return NS_OK;
7099 NS_IMETHODIMP
7100 nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop) {
7101 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7103 nsresult rv;
7104 if (!aContentViewer) {
7105 rv = EnsureContentViewer();
7106 NS_ENSURE_SUCCESS(rv, rv);
7108 aContentViewer = mContentViewer;
7111 // Dispatch events for restoring the presentation. We try to simulate
7112 // the progress notifications loading the document would cause, so we add
7113 // the document's channel to the loadgroup to initiate stateChange
7114 // notifications.
7116 RefPtr<Document> doc = aContentViewer->GetDocument();
7117 if (doc) {
7118 nsIChannel* channel = doc->GetChannel();
7119 if (channel) {
7120 mEODForCurrentDocument = false;
7121 mIsRestoringDocument = true;
7122 mLoadGroup->AddRequest(channel, nullptr);
7123 mIsRestoringDocument = false;
7127 if (!aTop) {
7128 // This point corresponds to us having gotten OnStartRequest or
7129 // STATE_START, so do the same thing that CreateContentViewer does at
7130 // this point to ensure that unload/pagehide events for this document
7131 // will fire when it's unloaded again.
7132 mFiredUnloadEvent = false;
7134 // For non-top frames, there is no notion of making sure that the
7135 // previous document is in the domwindow when STATE_START notifications
7136 // happen. We can just call BeginRestore for all of the child shells
7137 // now.
7138 rv = BeginRestoreChildren();
7139 NS_ENSURE_SUCCESS(rv, rv);
7142 return NS_OK;
7145 nsresult nsDocShell::BeginRestoreChildren() {
7146 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7148 for (auto* childDocLoader : mChildList.ForwardRange()) {
7149 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7150 if (child) {
7151 nsresult rv = child->BeginRestore(nullptr, false);
7152 NS_ENSURE_SUCCESS(rv, rv);
7155 return NS_OK;
7158 NS_IMETHODIMP
7159 nsDocShell::FinishRestore() {
7160 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7162 // First we call finishRestore() on our children. In the simulated load,
7163 // all of the child frames finish loading before the main document.
7165 for (auto* childDocLoader : mChildList.ForwardRange()) {
7166 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7167 if (child) {
7168 child->FinishRestore();
7172 if (mOSHE && mOSHE->HasDetachedEditor()) {
7173 ReattachEditorToWindow(mOSHE);
7176 RefPtr<Document> doc = GetDocument();
7177 if (doc) {
7178 // Finally, we remove the request from the loadgroup. This will
7179 // cause onStateChange(STATE_STOP) to fire, which will fire the
7180 // pageshow event to the chrome.
7182 nsIChannel* channel = doc->GetChannel();
7183 if (channel) {
7184 mIsRestoringDocument = true;
7185 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7186 mIsRestoringDocument = false;
7190 return NS_OK;
7193 NS_IMETHODIMP
7194 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7195 *aRestoring = mIsRestoringDocument;
7196 return NS_OK;
7199 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7200 bool* aRestoring) {
7201 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7202 MOZ_ASSERT(!mIsBeingDestroyed);
7204 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7205 "RestorePresentation should only be called for history loads");
7207 nsCOMPtr<nsIContentViewer> viewer = aSHEntry->GetContentViewer();
7209 nsAutoCString spec;
7210 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7211 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7212 if (uri) {
7213 uri->GetSpec(spec);
7217 *aRestoring = false;
7219 if (!viewer) {
7220 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7221 ("no saved presentation for uri: %s", spec.get()));
7222 return NS_OK;
7225 // We need to make sure the content viewer's container is this docshell.
7226 // In subframe navigation, it's possible for the docshell that the
7227 // content viewer was originally loaded into to be replaced with a
7228 // different one. We don't currently support restoring the presentation
7229 // in that case.
7231 nsCOMPtr<nsIDocShell> container;
7232 viewer->GetContainer(getter_AddRefs(container));
7233 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7234 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7235 ("No valid container, clearing presentation"));
7236 aSHEntry->SetContentViewer(nullptr);
7237 return NS_ERROR_FAILURE;
7240 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7242 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7243 ("restoring presentation from session history: %s", spec.get()));
7245 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7247 // Post an event that will remove the request after we've returned
7248 // to the event loop. This mimics the way it is called by nsIChannel
7249 // implementations.
7251 // Revoke any pending restore (just in case).
7252 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7253 "should only have one RestorePresentationEvent");
7254 mRestorePresentationEvent.Revoke();
7256 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7257 nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(evt));
7258 if (NS_SUCCEEDED(rv)) {
7259 mRestorePresentationEvent = evt.get();
7260 // The rest of the restore processing will happen on our event
7261 // callback.
7262 *aRestoring = true;
7265 return rv;
7268 namespace {
7269 class MOZ_STACK_CLASS PresentationEventForgetter {
7270 public:
7271 explicit PresentationEventForgetter(
7272 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7273 aRestorePresentationEvent)
7274 : mRestorePresentationEvent(aRestorePresentationEvent),
7275 mEvent(aRestorePresentationEvent.get()) {}
7277 ~PresentationEventForgetter() { Forget(); }
7279 void Forget() {
7280 if (mRestorePresentationEvent.get() == mEvent) {
7281 mRestorePresentationEvent.Forget();
7282 mEvent = nullptr;
7286 private:
7287 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7288 mRestorePresentationEvent;
7289 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7292 } // namespace
7294 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7295 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7298 nsresult nsDocShell::RestoreFromHistory() {
7299 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7300 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7301 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7303 // This section of code follows the same ordering as CreateContentViewer.
7304 if (!mLSHE) {
7305 return NS_ERROR_FAILURE;
7308 nsCOMPtr<nsIContentViewer> viewer = mLSHE->GetContentViewer();
7309 if (!viewer) {
7310 return NS_ERROR_FAILURE;
7313 if (mSavingOldViewer) {
7314 // We determined that it was safe to cache the document presentation
7315 // at the time we initiated the new load. We need to check whether
7316 // it's still safe to do so, since there may have been DOM mutations
7317 // or new requests initiated.
7318 RefPtr<Document> doc = viewer->GetDocument();
7319 nsIRequest* request = nullptr;
7320 if (doc) {
7321 request = doc->GetChannel();
7323 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
7326 // Protect against mLSHE going away via a load triggered from
7327 // pagehide or unload.
7328 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7330 // Make sure to blow away our mLoadingURI just in case. No loads
7331 // from inside this pagehide.
7332 mLoadingURI = nullptr;
7334 // Notify the old content viewer that it's being hidden.
7335 FirePageHideNotification(!mSavingOldViewer);
7336 // pagehide notification might destroy this docshell.
7337 if (mIsBeingDestroyed) {
7338 return NS_ERROR_DOCSHELL_DYING;
7341 // If mLSHE was changed as a result of the pagehide event, then
7342 // something else was loaded. Don't finish restoring.
7343 if (mLSHE != origLSHE) {
7344 return NS_OK;
7347 // Add the request to our load group. We do this before swapping out
7348 // the content viewers so that consumers of STATE_START can access
7349 // the old document. We only deal with the toplevel load at this time --
7350 // to be consistent with normal document loading, subframes cannot start
7351 // loading until after data arrives, which is after STATE_START completes.
7353 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7354 mRestorePresentationEvent.get();
7355 Stop();
7356 // Make sure we're still restoring the same presentation.
7357 // If we aren't, docshell is in process doing another load already.
7358 NS_ENSURE_STATE(currentPresentationRestoration ==
7359 mRestorePresentationEvent.get());
7360 BeginRestore(viewer, true);
7361 NS_ENSURE_STATE(currentPresentationRestoration ==
7362 mRestorePresentationEvent.get());
7363 forgetter.Forget();
7365 // Set mFiredUnloadEvent = false so that the unload handler for the
7366 // *new* document will fire.
7367 mFiredUnloadEvent = false;
7369 mURIResultedInDocument = true;
7370 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7371 if (rootSH) {
7372 mPreviousEntryIndex = rootSH->Index();
7373 rootSH->LegacySHistory()->UpdateIndex();
7374 mLoadedEntryIndex = rootSH->Index();
7375 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7376 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7377 mLoadedEntryIndex));
7380 // Rather than call Embed(), we will retrieve the viewer from the session
7381 // history entry and swap it in.
7382 // XXX can we refactor this so that we can just call Embed()?
7383 PersistLayoutHistoryState();
7384 nsresult rv;
7385 if (mContentViewer) {
7386 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7387 if (mOSHE) {
7388 mOSHE->SyncPresentationState();
7390 mSavingOldViewer = false;
7394 mSavedRefreshURIList = nullptr;
7396 // In cases where we use a transient about:blank viewer between loads,
7397 // we never show the transient viewer, so _its_ previous viewer is never
7398 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7399 // before we grab the root view sibling, so that we don't grab a view
7400 // that's about to go away.
7402 if (mContentViewer) {
7403 // Make sure to hold a strong ref to previousViewer here while we
7404 // drop the reference to it from mContentViewer.
7405 nsCOMPtr<nsIContentViewer> previousViewer =
7406 mContentViewer->GetPreviousViewer();
7407 if (previousViewer) {
7408 mContentViewer->SetPreviousViewer(nullptr);
7409 previousViewer->Destroy();
7413 // Save off the root view's parent and sibling so that we can insert the
7414 // new content viewer's root view at the same position. Also save the
7415 // bounds of the root view's widget.
7417 nsView* rootViewSibling = nullptr;
7418 nsView* rootViewParent = nullptr;
7419 nsIntRect newBounds(0, 0, 0, 0);
7421 PresShell* oldPresShell = GetPresShell();
7422 if (oldPresShell) {
7423 nsViewManager* vm = oldPresShell->GetViewManager();
7424 if (vm) {
7425 nsView* oldRootView = vm->GetRootView();
7427 if (oldRootView) {
7428 rootViewSibling = oldRootView->GetNextSibling();
7429 rootViewParent = oldRootView->GetParent();
7431 mContentViewer->GetBounds(newBounds);
7436 nsCOMPtr<nsIContent> container;
7437 RefPtr<Document> sibling;
7438 if (rootViewParent && rootViewParent->GetParent()) {
7439 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7440 container = frame ? frame->GetContent() : nullptr;
7442 if (rootViewSibling) {
7443 nsIFrame* frame = rootViewSibling->GetFrame();
7444 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7447 // Transfer ownership to mContentViewer. By ensuring that either the
7448 // docshell or the session history, but not both, have references to the
7449 // content viewer, we prevent the viewer from being torn down after
7450 // Destroy() is called.
7452 if (mContentViewer) {
7453 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7454 viewer->SetPreviousViewer(mContentViewer);
7456 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
7457 // We don't plan to save a viewer in mOSHE; tell it to drop
7458 // any other state it's holding.
7459 mOSHE->SyncPresentationState();
7462 // Order the mContentViewer setup just like Embed does.
7463 mContentViewer = nullptr;
7465 // Now that we're about to switch documents, forget all of our children.
7466 // Note that we cached them as needed up in CaptureState above.
7467 DestroyChildren();
7469 mContentViewer.swap(viewer);
7471 // Grab all of the related presentation from the SHEntry now.
7472 // Clearing the viewer from the SHEntry will clear all of this state.
7473 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7474 mLSHE->SetWindowState(nullptr);
7476 bool sticky = mLSHE->GetSticky();
7478 RefPtr<Document> document = mContentViewer->GetDocument();
7480 nsCOMArray<nsIDocShellTreeItem> childShells;
7481 int32_t i = 0;
7482 nsCOMPtr<nsIDocShellTreeItem> child;
7483 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7484 child) {
7485 childShells.AppendObject(child);
7488 // get the previous content viewer size
7489 nsIntRect oldBounds(0, 0, 0, 0);
7490 mLSHE->GetViewerBounds(oldBounds);
7492 // Restore the refresh URI list. The refresh timers will be restarted
7493 // when EndPageLoad() is called.
7494 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7496 // Reattach to the window object.
7497 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7498 rv = mContentViewer->Open(windowState, mLSHE);
7499 mIsRestoringDocument = false;
7501 // Hack to keep nsDocShellEditorData alive across the
7502 // SetContentViewer(nullptr) call below.
7503 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7505 // Now remove it from the cached presentation.
7506 mLSHE->SetContentViewer(nullptr);
7507 mEODForCurrentDocument = false;
7509 mLSHE->SetEditorData(data.release());
7511 #ifdef DEBUG
7513 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7514 nsCOMPtr<nsIDocShellTreeItem> childShell;
7515 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7516 NS_ASSERTION(!refreshURIs && !childShell,
7517 "SHEntry should have cleared presentation state");
7519 #endif
7521 // Restore the sticky state of the viewer. The viewer has set this state
7522 // on the history entry in Destroy() just before marking itself non-sticky,
7523 // to avoid teardown of the presentation.
7524 mContentViewer->SetSticky(sticky);
7526 NS_ENSURE_SUCCESS(rv, rv);
7528 // mLSHE is now our currently-loaded document.
7529 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7531 // We aren't going to restore any items from the LayoutHistoryState,
7532 // but we don't want them to stay around in case the page is reloaded.
7533 SetLayoutHistoryState(nullptr);
7535 // This is the end of our Embed() replacement
7537 mSavingOldViewer = false;
7538 mEODForCurrentDocument = false;
7540 // Tell the event loop to favor plevents over user events, see comments
7541 // in CreateContentViewer.
7542 if (++gNumberOfDocumentsLoading == 1) {
7543 FavorPerformanceHint(true);
7546 if (document) {
7547 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7548 if (parent) {
7549 RefPtr<Document> d = parent->GetDocument();
7550 if (d) {
7551 if (d->EventHandlingSuppressed()) {
7552 document->SuppressEventHandling(d->EventHandlingSuppressed());
7557 // Use the uri from the mLSHE we had when we entered this function
7558 // (which need not match the document's URI if anchors are involved),
7559 // since that's the history entry we're loading. Note that if we use
7560 // origLSHE we don't have to worry about whether the entry in question
7561 // is still mLSHE or whether it's now mOSHE.
7562 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7563 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7564 /* aIsInitialAboutBlank */ false, /* aLocationFlags */ 0);
7567 // This is the end of our CreateContentViewer() replacement.
7568 // Now we simulate a load. First, we restore the state of the javascript
7569 // window object.
7570 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7571 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7573 // Now, dispatch a title change event which would happen as the
7574 // <head> is parsed.
7575 document->NotifyPossibleTitleChange(false);
7577 // Now we simulate appending child docshells for subframes.
7578 for (i = 0; i < childShells.Count(); ++i) {
7579 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7580 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7582 // Make sure to not clobber the state of the child. Since AddChild
7583 // always clobbers it, save it off first.
7584 bool allowRedirects;
7585 childShell->GetAllowMetaRedirects(&allowRedirects);
7587 bool allowSubframes;
7588 childShell->GetAllowSubframes(&allowSubframes);
7590 bool allowImages;
7591 childShell->GetAllowImages(&allowImages);
7593 bool allowMedia = childShell->GetAllowMedia();
7595 bool allowDNSPrefetch;
7596 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7598 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7599 bool allowContentRetargetingOnChildren =
7600 childShell->GetAllowContentRetargetingOnChildren();
7602 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7603 // the child inherits our state. Among other things, this means that the
7604 // child inherits our mPrivateBrowsingId, which is what we want.
7605 AddChild(childItem);
7607 childShell->SetAllowMetaRedirects(allowRedirects);
7608 childShell->SetAllowSubframes(allowSubframes);
7609 childShell->SetAllowImages(allowImages);
7610 childShell->SetAllowMedia(allowMedia);
7611 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7612 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7613 childShell->SetAllowContentRetargetingOnChildren(
7614 allowContentRetargetingOnChildren);
7616 rv = childShell->BeginRestore(nullptr, false);
7617 NS_ENSURE_SUCCESS(rv, rv);
7620 // Make sure to restore the window state after adding the child shells back
7621 // to the tree. This is necessary for Thaw() and Resume() to propagate
7622 // properly.
7623 rv = privWin->RestoreWindowState(windowState);
7624 NS_ENSURE_SUCCESS(rv, rv);
7626 RefPtr<PresShell> presShell = GetPresShell();
7628 // We may be displayed on a different monitor (or in a different
7629 // HiDPI mode) than when we got into the history list. So we need
7630 // to check if this has happened. See bug 838239.
7632 // Because the prescontext normally handles resolution changes via
7633 // a runnable (see nsPresContext::UIResolutionChanged), its device
7634 // context won't be -immediately- updated as a result of calling
7635 // presShell->BackingScaleFactorChanged().
7637 // But we depend on that device context when adjusting the view size
7638 // via mContentViewer->SetBounds(newBounds) below. So we need to
7639 // explicitly tell it to check for changed resolution here.
7640 if (presShell) {
7641 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7642 if (pc->DeviceContext()->CheckDPIChange()) {
7643 presShell->BackingScaleFactorChanged();
7645 // Recompute zoom and text-zoom and such.
7646 pc->RecomputeBrowsingContextDependentData();
7649 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7650 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7652 // Insert the new root view at the correct location in the view tree.
7653 if (container) {
7654 nsSubDocumentFrame* subDocFrame =
7655 do_QueryFrame(container->GetPrimaryFrame());
7656 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7657 } else {
7658 rootViewParent = nullptr;
7660 if (sibling && sibling->GetPresShell() &&
7661 sibling->GetPresShell()->GetViewManager()) {
7662 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7663 } else {
7664 rootViewSibling = nullptr;
7666 if (rootViewParent && newRootView &&
7667 newRootView->GetParent() != rootViewParent) {
7668 nsViewManager* parentVM = rootViewParent->GetViewManager();
7669 if (parentVM) {
7670 // InsertChild(parent, child, sib, true) inserts the child after
7671 // sib in content order, which is before sib in view order. BUT
7672 // when sib is null it inserts at the end of the the document
7673 // order, i.e., first in view order. But when oldRootSibling is
7674 // null, the old root as at the end of the view list --- last in
7675 // content order --- and we want to call InsertChild(parent, child,
7676 // nullptr, false) in that case.
7677 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7678 rootViewSibling ? true : false);
7680 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7681 "error in InsertChild");
7685 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7687 // If parent is suspended, increase suspension count.
7688 // This can't be done as early as event suppression since this
7689 // depends on docshell tree.
7690 privWinInner->SyncStateFromParentWindow();
7692 // Now that all of the child docshells have been put into place, we can
7693 // restart the timers for the window and all of the child frames.
7694 privWinInner->Resume();
7696 // Now that we have found the inner window of the page restored
7697 // from the history, we have to make sure that
7698 // performance.navigation.type is 2.
7699 privWinInner->GetPerformance()->GetDOMTiming()->NotifyRestoreStart();
7701 // Restore the refresh URI list. The refresh timers will be restarted
7702 // when EndPageLoad() is called.
7703 mRefreshURIList = refreshURIList;
7705 // Meta-refresh timers have been restarted for this shell, but not
7706 // for our children. Walk the child shells and restart their timers.
7707 for (auto* childDocLoader : mChildList.ForwardRange()) {
7708 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7709 if (child) {
7710 child->ResumeRefreshURIs();
7714 // Make sure this presentation is the same size as the previous
7715 // presentation. If this is not the same size we showed it at last time,
7716 // then we need to resize the widget.
7718 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7719 // presentation had the infobar visible, then we will resize the new
7720 // presentation to that smaller size. However, firing the locationchanged
7721 // event will hide the infobar, which will immediately resize the window
7722 // back to the larger size. A future optimization might be to restore
7723 // the presentation at the "wrong" size, then fire the locationchanged
7724 // event and check whether the docshell's new size is the same as the
7725 // cached viewer size (skipping the resize if they are equal).
7727 if (newRootView) {
7728 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7729 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7730 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7731 newBounds.width, newBounds.height));
7732 mContentViewer->SetBounds(newBounds);
7733 } else {
7734 nsIScrollableFrame* rootScrollFrame =
7735 presShell->GetRootScrollFrameAsScrollable();
7736 if (rootScrollFrame) {
7737 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7742 // The FinishRestore call below can kill these, null them out so we don't
7743 // have invalid pointer lying around.
7744 newRootView = rootViewSibling = rootViewParent = nullptr;
7745 newVM = nullptr;
7747 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7748 // update it.
7749 if (oldPresShell && presShell &&
7750 presShell->IsUnderHiddenEmbedderElement() !=
7751 oldPresShell->IsUnderHiddenEmbedderElement()) {
7752 presShell->SetIsUnderHiddenEmbedderElement(
7753 oldPresShell->IsUnderHiddenEmbedderElement());
7756 // Simulate the completion of the load.
7757 nsDocShell::FinishRestore();
7759 // Restart plugins, and paint the content.
7760 if (presShell) {
7761 presShell->Thaw();
7764 return privWin->FireDelayedDOMEvents(true);
7767 nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
7768 nsIRequest* aRequest,
7769 nsIStreamListener** aContentHandler) {
7770 if (DocGroup::TryToLoadIframesInBackground()) {
7771 ResetToFirstLoad();
7774 *aContentHandler = nullptr;
7776 if (!mTreeOwner || mIsBeingDestroyed) {
7777 // If we don't have a tree owner, then we're in the process of being
7778 // destroyed. Rather than continue trying to load something, just give up.
7779 return NS_ERROR_DOCSHELL_DYING;
7782 if (!mBrowsingContext->AncestorsAreCurrent() ||
7783 mBrowsingContext->IsInBFCache()) {
7784 mBrowsingContext->RemoveRootFromBFCacheSync();
7785 return NS_ERROR_NOT_AVAILABLE;
7788 // Can we check the content type of the current content viewer
7789 // and reuse it without destroying it and re-creating it?
7791 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7793 // Instantiate the content viewer object
7794 nsCOMPtr<nsIContentViewer> viewer;
7795 nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
7796 aContentHandler, getter_AddRefs(viewer));
7798 if (NS_FAILED(rv)) {
7799 return rv;
7802 // Notify the current document that it is about to be unloaded!!
7804 // It is important to fire the unload() notification *before* any state
7805 // is changed within the DocShell - otherwise, javascript will get the
7806 // wrong information :-(
7809 if (mSavingOldViewer) {
7810 // We determined that it was safe to cache the document presentation
7811 // at the time we initiated the new load. We need to check whether
7812 // it's still safe to do so, since there may have been DOM mutations
7813 // or new requests initiated.
7814 RefPtr<Document> doc = viewer->GetDocument();
7815 mSavingOldViewer = CanSavePresentation(mLoadType, aRequest, doc);
7818 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7820 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7821 if (aOpenedChannel) {
7822 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7824 FirePageHideNotification(!mSavingOldViewer);
7825 if (mIsBeingDestroyed) {
7826 // Force to stop the newly created orphaned viewer.
7827 viewer->Stop();
7828 return NS_ERROR_DOCSHELL_DYING;
7830 mLoadingURI = nullptr;
7832 // Set mFiredUnloadEvent = false so that the unload handler for the
7833 // *new* document will fire.
7834 mFiredUnloadEvent = false;
7836 // we've created a new document so go ahead and call
7837 // OnNewURI(), but don't fire OnLocationChange()
7838 // notifications before we've called Embed(). See bug 284993.
7839 mURIResultedInDocument = true;
7840 bool errorOnLocationChangeNeeded = false;
7841 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7842 nsCOMPtr<nsIURI> failedURI;
7844 if (mLoadType == LOAD_ERROR_PAGE) {
7845 // We need to set the SH entry and our current URI here and not
7846 // at the moment we load the page. We want the same behavior
7847 // of Stop() as for a normal page load. See bug 514232 for details.
7849 // Revert mLoadType to load type to state the page load failed,
7850 // following function calls need it.
7851 mLoadType = mFailedLoadType;
7853 Document* doc = viewer->GetDocument();
7854 if (doc) {
7855 doc->SetFailedChannel(failedChannel);
7858 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7859 if (failedChannel) {
7860 // Make sure we have a URI to set currentURI.
7861 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7862 } else {
7863 // if there is no failed channel we have to explicitly provide
7864 // a triggeringPrincipal for the history entry.
7865 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7868 if (!failedURI) {
7869 failedURI = mFailedURI;
7871 if (!failedURI) {
7872 // We need a URI object to store a session history entry, so make up a URI
7873 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7876 // When we don't have failedURI, something wrong will happen. See
7877 // bug 291876.
7878 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7880 mFailedChannel = nullptr;
7881 mFailedURI = nullptr;
7883 // Create an shistory entry for the old load.
7884 if (failedURI) {
7885 errorOnLocationChangeNeeded =
7886 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7887 nullptr, nullptr, false, false, false);
7890 // Be sure to have a correct mLSHE, it may have been cleared by
7891 // EndPageLoad. See bug 302115.
7892 ChildSHistory* shistory = GetSessionHistory();
7893 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7894 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7895 if (idx == -1) {
7896 idx = shistory->Index();
7898 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7901 mLoadType = LOAD_ERROR_PAGE;
7904 nsCOMPtr<nsIURI> finalURI;
7905 // If this a redirect, use the final url (uri)
7906 // else use the original url
7908 // Note that this should match what documents do (see Document::Reset).
7909 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7911 bool onLocationChangeNeeded = false;
7912 if (finalURI) {
7913 // Pass false for aCloneSHChildren, since we're loading a new page here.
7914 onLocationChangeNeeded =
7915 OnNewURI(finalURI, aOpenedChannel, nullptr, nullptr, nullptr, nullptr,
7916 false, true, false);
7919 // let's try resetting the load group if we need to...
7920 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7921 NS_ENSURE_SUCCESS(
7922 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7923 NS_ERROR_FAILURE);
7925 if (currentLoadGroup != mLoadGroup) {
7926 nsLoadFlags loadFlags = 0;
7928 // Cancel any URIs that are currently loading...
7929 // XXX: Need to do this eventually Stop();
7931 // Retarget the document to this loadgroup...
7933 /* First attach the channel to the right loadgroup
7934 * and then remove from the old loadgroup. This
7935 * puts the notifications in the right order and
7936 * we don't null-out mLSHE in OnStateChange() for
7937 * all redirected urls
7939 aOpenedChannel->SetLoadGroup(mLoadGroup);
7941 // Mark the channel as being a document URI...
7942 aOpenedChannel->GetLoadFlags(&loadFlags);
7943 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7944 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7945 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7946 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7949 aOpenedChannel->SetLoadFlags(loadFlags);
7951 mLoadGroup->AddRequest(aRequest, nullptr);
7952 if (currentLoadGroup) {
7953 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7956 // Update the notification callbacks, so that progress and
7957 // status information are sent to the right docshell...
7958 aOpenedChannel->SetNotificationCallbacks(this);
7961 if (DocGroup::TryToLoadIframesInBackground()) {
7962 if ((!mContentViewer || GetDocument()->IsInitialDocument()) &&
7963 IsSubframe()) {
7964 // At this point, we know we just created a new iframe document based on
7965 // the response from the server, and we check if it's a cross-domain
7966 // iframe
7968 RefPtr<Document> newDoc = viewer->GetDocument();
7970 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7971 nsCOMPtr<nsIPrincipal> parentPrincipal =
7972 parent->GetDocument()->NodePrincipal();
7973 nsCOMPtr<nsIPrincipal> thisPrincipal = newDoc->NodePrincipal();
7975 SiteIdentifier parentSite;
7976 SiteIdentifier thisSite;
7978 nsresult rv =
7979 BasePrincipal::Cast(parentPrincipal)->GetSiteIdentifier(parentSite);
7980 NS_ENSURE_SUCCESS(rv, rv);
7982 rv = BasePrincipal::Cast(thisPrincipal)->GetSiteIdentifier(thisSite);
7983 NS_ENSURE_SUCCESS(rv, rv);
7985 if (!parentSite.Equals(thisSite)) {
7986 if (profiler_thread_is_being_profiled_for_markers()) {
7987 nsCOMPtr<nsIURI> prinURI;
7988 BasePrincipal::Cast(thisPrincipal)->GetURI(getter_AddRefs(prinURI));
7989 nsPrintfCString marker("Iframe loaded in background: %s",
7990 prinURI->GetSpecOrDefault().get());
7991 PROFILER_MARKER_TEXT("Background Iframe", DOM, {}, marker);
7993 SetBackgroundLoadIframe();
7998 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
7999 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
8000 aOpenedChannel),
8001 NS_ERROR_FAILURE);
8003 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
8004 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
8007 if (TreatAsBackgroundLoad()) {
8008 nsCOMPtr<nsIRunnable> triggerParentCheckDocShell =
8009 NewRunnableMethod("nsDocShell::TriggerParentCheckDocShellIsEmpty", this,
8010 &nsDocShell::TriggerParentCheckDocShellIsEmpty);
8011 nsresult rv = NS_DispatchToCurrentThread(triggerParentCheckDocShell);
8012 NS_ENSURE_SUCCESS(rv, rv);
8015 mSavedRefreshURIList = nullptr;
8016 mSavingOldViewer = false;
8017 mEODForCurrentDocument = false;
8019 // if this document is part of a multipart document,
8020 // the ID can be used to distinguish it from the other parts.
8021 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
8022 if (multiPartChannel) {
8023 if (PresShell* presShell = GetPresShell()) {
8024 if (Document* doc = presShell->GetDocument()) {
8025 uint32_t partID;
8026 multiPartChannel->GetPartID(&partID);
8027 doc->SetPartID(partID);
8032 // Give hint to native plevent dispatch mechanism. If a document
8033 // is loading the native plevent dispatch mechanism should favor
8034 // performance over normal native event dispatch priorities.
8035 if (++gNumberOfDocumentsLoading == 1) {
8036 // Hint to favor performance for the plevent notification mechanism.
8037 // We want the pages to load as fast as possible even if its means
8038 // native messages might be starved.
8039 FavorPerformanceHint(true);
8042 if (errorOnLocationChangeNeeded) {
8043 FireOnLocationChange(this, failedChannel, failedURI,
8044 LOCATION_CHANGE_ERROR_PAGE);
8045 } else if (onLocationChangeNeeded) {
8046 uint32_t locationFlags =
8047 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
8048 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
8051 return NS_OK;
8054 nsresult nsDocShell::NewContentViewerObj(const nsACString& aContentType,
8055 nsIRequest* aRequest,
8056 nsILoadGroup* aLoadGroup,
8057 nsIStreamListener** aContentHandler,
8058 nsIContentViewer** aViewer) {
8059 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8061 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8062 nsContentUtils::FindInternalContentViewer(aContentType);
8063 if (!docLoaderFactory) {
8064 return NS_ERROR_FAILURE;
8067 // Now create an instance of the content viewer nsLayoutDLF makes the
8068 // determination if it should be a "view-source" instead of "view"
8069 nsresult rv = docLoaderFactory->CreateInstance(
8070 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
8071 aContentHandler, aViewer);
8072 NS_ENSURE_SUCCESS(rv, rv);
8074 (*aViewer)->SetContainer(this);
8075 return NS_OK;
8078 nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
8079 WindowGlobalChild* aWindowActor) {
8080 MOZ_ASSERT(!mIsBeingDestroyed);
8083 // Copy content viewer state from previous or parent content viewer.
8085 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8087 // Do NOT to maintain a reference to the old content viewer outside
8088 // of this "copying" block, or it will not be destroyed until the end of
8089 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8091 // In this block of code, if we get an error result, we return it
8092 // but if we get a null pointer, that's perfectly legal for parent
8093 // and parentContentViewer.
8096 int32_t x = 0;
8097 int32_t y = 0;
8098 int32_t cx = 0;
8099 int32_t cy = 0;
8101 // This will get the size from the current content viewer or from the
8102 // Init settings
8103 DoGetPositionAndSize(&x, &y, &cx, &cy);
8105 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8106 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
8107 NS_ERROR_FAILURE);
8108 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8110 const Encoding* reloadEncoding = nullptr;
8111 int32_t reloadEncodingSource = kCharsetUninitialized;
8112 // |newMUDV| also serves as a flag to set the data from the above vars
8113 nsCOMPtr<nsIContentViewer> newCv;
8115 if (mContentViewer || parent) {
8116 nsCOMPtr<nsIContentViewer> oldCv;
8117 if (mContentViewer) {
8118 // Get any interesting state from old content viewer
8119 // XXX: it would be far better to just reuse the document viewer ,
8120 // since we know we're just displaying the same document as before
8121 oldCv = mContentViewer;
8123 // Tell the old content viewer to hibernate in session history when
8124 // it is destroyed.
8126 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8127 if (mOSHE) {
8128 mOSHE->SyncPresentationState();
8130 mSavingOldViewer = false;
8132 } else {
8133 // No old content viewer, so get state from parent's content viewer
8134 parent->GetContentViewer(getter_AddRefs(oldCv));
8137 if (oldCv) {
8138 newCv = aNewViewer;
8139 if (newCv) {
8140 reloadEncoding =
8141 oldCv->GetReloadEncodingAndSource(&reloadEncodingSource);
8146 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8147 bool isUnderHiddenEmbedderElement = false;
8148 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8149 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
8150 if (contentViewer) {
8151 // Stop any activity that may be happening in the old document before
8152 // releasing it...
8153 contentViewer->Stop();
8155 // Try to extract the canvas background color from the old
8156 // presentation shell, so we can use it for the next document.
8157 if (PresShell* presShell = contentViewer->GetPresShell()) {
8158 bgcolor = presShell->GetCanvasBackground();
8159 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
8162 contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8163 aNewViewer->SetPreviousViewer(contentViewer);
8165 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8166 // We don't plan to save a viewer in mOSHE; tell it to drop
8167 // any other state it's holding.
8168 mOSHE->SyncPresentationState();
8171 mContentViewer = nullptr;
8173 // Now that we're about to switch documents, forget all of our children.
8174 // Note that we cached them as needed up in CaptureState above.
8175 DestroyChildren();
8177 mContentViewer = aNewViewer;
8179 nsCOMPtr<nsIWidget> widget;
8180 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8182 nsIntRect bounds(x, y, cx, cy);
8184 mContentViewer->SetNavigationTiming(mTiming);
8186 if (NS_FAILED(mContentViewer->Init(widget, bounds, aWindowActor))) {
8187 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
8188 viewer->Close(nullptr);
8189 viewer->Destroy();
8190 mContentViewer = nullptr;
8191 mCurrentURI = nullptr;
8192 NS_WARNING("ContentViewer Initialization failed");
8193 return NS_ERROR_FAILURE;
8196 // If we have old state to copy, set the old state onto the new content
8197 // viewer
8198 if (newCv) {
8199 newCv->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8202 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8204 // Stuff the bgcolor from the old pres shell into the new
8205 // pres shell. This improves page load continuity.
8206 if (RefPtr<PresShell> presShell = mContentViewer->GetPresShell()) {
8207 presShell->SetCanvasBackground(bgcolor);
8208 presShell->ActivenessMaybeChanged();
8209 if (isUnderHiddenEmbedderElement) {
8210 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8214 // XXX: It looks like the LayoutState gets restored again in Embed()
8215 // right after the call to SetupNewViewer(...)
8217 // We don't show the mContentViewer yet, since we want to draw the old page
8218 // until we have enough of the new page to show. Just return with the new
8219 // viewer still set to hidden.
8221 return NS_OK;
8224 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8225 SessionHistoryInfo* aInfo) {
8226 NS_ENSURE_TRUE_VOID(mContentViewer);
8228 RefPtr<Document> document = GetDocument();
8229 NS_ENSURE_TRUE_VOID(document);
8231 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8232 if (mozilla::SessionHistoryInParent()) {
8233 // If aInfo is null, just set the document's state object to null.
8234 if (aInfo) {
8235 scContainer = aInfo->GetStateData();
8237 MOZ_LOG(gSHLog, LogLevel::Debug,
8238 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8239 } else {
8240 if (aShEntry) {
8241 scContainer = aShEntry->GetStateData();
8243 // If aShEntry is null, just set the document's state object to null.
8247 // It's OK for scContainer too be null here; that just means there's no
8248 // state data associated with this history entry.
8249 document->SetStateObject(scContainer);
8252 nsresult nsDocShell::CheckLoadingPermissions() {
8253 // This method checks whether the caller may load content into
8254 // this docshell. Even though we've done our best to hide windows
8255 // from code that doesn't have the right to access them, it's
8256 // still possible for an evil site to open a window and access
8257 // frames in the new window through window.frames[] (which is
8258 // allAccess for historic reasons), so we still need to do this
8259 // check on load.
8260 nsresult rv = NS_OK;
8262 if (!IsSubframe()) {
8263 // We're not a frame. Permit all loads.
8264 return rv;
8267 // Note - The check for a current JSContext here isn't necessarily sensical.
8268 // It's just designed to preserve the old semantics during a mass-conversion
8269 // patch.
8270 if (!nsContentUtils::GetCurrentJSContext()) {
8271 return NS_OK;
8274 // Check if the caller is from the same origin as this docshell,
8275 // or any of its ancestors.
8276 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8277 bc = bc->GetParent()) {
8278 // If the BrowsingContext is not in process, then it
8279 // is true by construction that its principal will not
8280 // subsume the current docshell principal.
8281 if (!bc->IsInProcess()) {
8282 continue;
8285 nsCOMPtr<nsIScriptGlobalObject> sgo =
8286 bc->GetDocShell()->GetScriptGlobalObject();
8287 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8289 nsIPrincipal* p;
8290 if (!sop || !(p = sop->GetPrincipal())) {
8291 return NS_ERROR_UNEXPECTED;
8294 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8295 // Same origin, permit load
8296 return NS_OK;
8300 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8303 //*****************************************************************************
8304 // nsDocShell: Site Loading
8305 //*****************************************************************************
8307 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8308 bool aInPrivateBrowsing) {
8309 if (XRE_IsContentProcess()) {
8310 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8311 if (contentChild) {
8312 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8314 return;
8317 #ifdef MOZ_PLACES
8318 nsCOMPtr<nsIFaviconService> favSvc =
8319 do_GetService("@mozilla.org/browser/favicon-service;1");
8320 if (favSvc) {
8321 favSvc->CopyFavicons(aOldURI, aNewURI,
8322 aInPrivateBrowsing
8323 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8324 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8325 nullptr);
8327 #endif
8330 class InternalLoadEvent : public Runnable {
8331 public:
8332 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8333 : mozilla::Runnable("InternalLoadEvent"),
8334 mDocShell(aDocShell),
8335 mLoadState(aLoadState) {
8336 // For events, both target and filename should be the version of "null" they
8337 // expect. By the time the event is fired, both window targeting and file
8338 // downloading have been handled, so we should never have an internal load
8339 // event that retargets or had a download.
8340 mLoadState->SetTarget(u""_ns);
8341 mLoadState->SetFileName(VoidString());
8344 NS_IMETHOD
8345 Run() override {
8346 #ifndef ANDROID
8347 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8348 "InternalLoadEvent: Should always have a principal here");
8349 #endif
8350 return mDocShell->InternalLoad(mLoadState);
8353 private:
8354 RefPtr<nsDocShell> mDocShell;
8355 RefPtr<nsDocShellLoadState> mLoadState;
8359 * Returns true if we started an asynchronous load (i.e., from the network), but
8360 * the document we're loading there hasn't yet become this docshell's active
8361 * document.
8363 * When JustStartedNetworkLoad is true, you should be careful about modifying
8364 * mLoadType and mLSHE. These are both set when the asynchronous load first
8365 * starts, and the load expects that, when it eventually runs InternalLoad,
8366 * mLoadType and mLSHE will have their original values.
8368 bool nsDocShell::JustStartedNetworkLoad() {
8369 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8372 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8373 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8374 // <embed>, etc)
8376 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8377 // later when we call DoURILoad.
8378 nsContentPolicyType nsDocShell::DetermineContentType() {
8379 if (!IsSubframe()) {
8380 return nsIContentPolicy::TYPE_DOCUMENT;
8383 const auto& maybeEmbedderElementType =
8384 GetBrowsingContext()->GetEmbedderElementType();
8385 if (!maybeEmbedderElementType) {
8386 // If the EmbedderElementType hasn't been set yet, just assume we're
8387 // an iframe since that's more common.
8388 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8391 return maybeEmbedderElementType->EqualsLiteral("iframe")
8392 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8393 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8396 bool nsDocShell::NoopenerForceEnabled() {
8397 // If current's top-level browsing context's active document's
8398 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8399 // if currentDoc's origin is not same origin with currentDoc's top-level
8400 // origin, noopener is force enabled, and name is cleared to "_blank".
8401 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8402 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8403 topPolicy ==
8404 nsILoadInfo::
8405 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8406 !mBrowsingContext->SameOriginWithTop();
8409 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8410 MOZ_ASSERT(aLoadState, "need a load state!");
8411 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8412 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8413 "should not have picked target yet");
8415 nsresult rv = NS_OK;
8416 RefPtr<BrowsingContext> targetContext;
8418 // Only _self, _parent, and _top are supported in noopener case. But we
8419 // have to be careful to not apply that to the noreferrer case. See bug
8420 // 1358469.
8421 bool allowNamedTarget =
8422 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8423 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8424 if (allowNamedTarget ||
8425 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8426 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8427 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8428 targetContext = mBrowsingContext->FindWithName(
8429 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8432 if (!targetContext) {
8433 // If the targetContext doesn't exist, then this is a new docShell and we
8434 // should consider this a TYPE_DOCUMENT load
8436 // For example, when target="_blank"
8438 // If there's no targetContext, that means we are about to create a new
8439 // window. Perform a content policy check before creating the window. Please
8440 // note for all other docshell loads content policy checks are performed
8441 // within the contentSecurityManager when the channel is about to be
8442 // openend.
8443 nsISupports* requestingContext = nullptr;
8444 if (XRE_IsContentProcess()) {
8445 // In e10s the child process doesn't have access to the element that
8446 // contains the browsing context (because that element is in the chrome
8447 // process). So we just pass mScriptGlobal.
8448 requestingContext = ToSupports(mScriptGlobal);
8449 } else {
8450 // This is for loading non-e10s tabs and toplevel windows of various
8451 // sorts.
8452 // For the toplevel window cases, requestingElement will be null.
8453 nsCOMPtr<Element> requestingElement =
8454 mScriptGlobal->GetFrameElementInternal();
8455 requestingContext = requestingElement;
8458 // Ideally we should use the same loadinfo as within DoURILoad which
8459 // should match this one when both are applicable.
8460 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
8461 mScriptGlobal, aLoadState->TriggeringPrincipal(), requestingContext,
8462 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8464 // Since Content Policy checks are performed within docShell as well as
8465 // the ContentSecurityManager we need a reliable way to let certain
8466 // nsIContentPolicy consumers ignore duplicate calls.
8467 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8469 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8470 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8471 ""_ns, // mime guess
8472 &shouldLoad);
8474 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8475 if (NS_SUCCEEDED(rv)) {
8476 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8477 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8479 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8480 return NS_ERROR_BLOCKED_BY_POLICY;
8484 return NS_ERROR_CONTENT_BLOCKED;
8489 // Resolve the window target before going any further...
8490 // If the load has been targeted to another DocShell, then transfer the
8491 // load to it...
8494 // We've already done our owner-inheriting. Mask out that bit, so we
8495 // don't try inheriting an owner from the target window if we came up
8496 // with a null owner above.
8497 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8499 if (!targetContext) {
8500 // If the docshell's document is sandboxed, only open a new window
8501 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8502 // (i.e. if allow-popups is specified)
8503 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8504 Document* doc = mContentViewer->GetDocument();
8506 const bool isDocumentAuxSandboxed =
8507 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8509 if (isDocumentAuxSandboxed) {
8510 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8513 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8514 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8516 RefPtr<BrowsingContext> newBC;
8517 nsAutoCString spec;
8518 aLoadState->URI()->GetSpec(spec);
8520 // If we are a noopener load, we just hand the whole thing over to our
8521 // window.
8522 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8523 NoopenerForceEnabled()) {
8524 // Various asserts that we know to hold because NO_OPENER loads can only
8525 // happen for links.
8526 MOZ_ASSERT(!aLoadState->LoadReplace());
8527 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8528 aLoadState->TriggeringPrincipal());
8529 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8530 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8531 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8532 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8533 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8534 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8535 aLoadState->IsFormSubmission());
8536 MOZ_ASSERT(!aLoadState->HeadersStream());
8537 // If OnLinkClickSync was invoked inside the onload handler, the load
8538 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8539 // LOAD_LINK.
8540 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8541 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8542 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8543 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8545 RefPtr<nsDocShellLoadState> loadState =
8546 new nsDocShellLoadState(aLoadState->URI());
8548 // Set up our loadinfo so it will do the load as much like we would have
8549 // as possible.
8550 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8551 loadState->SetOriginalURI(aLoadState->OriginalURI());
8553 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8554 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8556 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8557 loadState->SetKeepResultPrincipalURIIfSet(
8558 aLoadState->KeepResultPrincipalURIIfSet());
8559 // LoadReplace will always be false due to asserts above, skip setting
8560 // it.
8561 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8562 loadState->SetTriggeringSandboxFlags(
8563 aLoadState->TriggeringSandboxFlags());
8564 loadState->SetCsp(aLoadState->Csp());
8565 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8566 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8567 // Explicit principal because we do not want any guesses as to what the
8568 // principal to inherit is: it should be aTriggeringPrincipal.
8569 loadState->SetPrincipalIsExplicit(true);
8570 loadState->SetLoadType(LOAD_LINK);
8571 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8572 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8574 loadState->SetHasValidUserGestureActivation(
8575 aLoadState->HasValidUserGestureActivation());
8577 // Propagate POST data to the new load.
8578 loadState->SetPostDataStream(aLoadState->PostDataStream());
8579 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8581 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8582 aLoadState->Target(), // window name
8583 u""_ns, // Features
8584 loadState,
8585 true, // aForceNoOpener
8586 getter_AddRefs(newBC));
8587 MOZ_ASSERT(!newBC);
8588 return rv;
8591 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8592 aLoadState->Target(), // window name
8593 u""_ns, // Features
8594 getter_AddRefs(newBC));
8596 // In some cases the Open call doesn't actually result in a new
8597 // window being opened. We can detect these cases by examining the
8598 // document in |newBC|, if any.
8599 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8600 newBC ? newBC->GetDOMWindow() : nullptr;
8601 if (piNewWin) {
8602 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8603 if (!newDoc || newDoc->IsInitialDocument()) {
8604 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8608 if (newBC) {
8609 targetContext = newBC;
8612 NS_ENSURE_SUCCESS(rv, rv);
8613 NS_ENSURE_TRUE(targetContext, rv);
8615 // If our target BrowsingContext is still pending initialization, ignore the
8616 // navigation request targeting it.
8617 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8618 return NS_OK;
8621 aLoadState->SetTargetBrowsingContext(targetContext);
8623 // Transfer the load to the target BrowsingContext... Clear the window target
8624 // name to the empty string to prevent recursive retargeting!
8626 // No window target
8627 aLoadState->SetTarget(u""_ns);
8628 // No forced download
8629 aLoadState->SetFileName(VoidString());
8630 return targetContext->InternalLoad(aLoadState);
8633 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8634 nsAutoCString result;
8635 if (NS_FAILED(aURI->GetRef(result))) {
8636 result.SetIsVoid(true);
8638 return result;
8641 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8642 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8644 bool equal = false;
8645 if (mCurrentURI &&
8646 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8647 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8648 flags |= LOCATION_CHANGE_HASHCHANGE;
8651 return flags;
8654 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8655 SameDocumentNavigationState& aState) {
8656 MOZ_ASSERT(aLoadState);
8657 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8658 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8659 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8660 LOAD_FLAGS_REPLACE_HISTORY) ||
8661 aLoadState->LoadType() == LOAD_HISTORY ||
8662 aLoadState->LoadType() == LOAD_LINK)) {
8663 return false;
8666 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8668 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8669 if (NS_SUCCEEDED(rvURINew)) {
8670 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8673 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8674 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8675 if (NS_SUCCEEDED(rvURIOld)) {
8676 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8678 if (NS_SUCCEEDED(rvURIOld)) {
8679 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8680 &aState.mSameExceptHashes))) {
8681 aState.mSameExceptHashes = false;
8686 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8687 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8688 nsCOMPtr<nsIURI> currentExposableURI =
8689 nsIOService::CreateExposableURI(currentURI);
8690 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8691 if (NS_SUCCEEDED(rvURIOld)) {
8692 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8694 if (NS_SUCCEEDED(rvURIOld)) {
8695 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8696 aLoadState->URI(), &aState.mSameExceptHashes))) {
8697 aState.mSameExceptHashes = false;
8699 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8700 // have to perform a special check here to avoid an actual navigation. If
8701 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8702 // fact that the new URI is currently http), then set mSameExceptHashes to
8703 // true and only perform a fragment navigation.
8704 if (!aState.mSameExceptHashes) {
8705 nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel();
8706 if (docChannel) {
8707 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8708 if (!docLoadInfo->GetLoadErrorPage()) {
8709 if (nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8710 currentExposableURI, aLoadState->URI(), docLoadInfo)) {
8711 aState.mSameExceptHashes = true;
8719 if (mozilla::SessionHistoryInParent()) {
8720 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8721 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8722 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8724 MOZ_LOG(gSHLog, LogLevel::Debug,
8725 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8726 this, aState.mHistoryNavBetweenSameDoc));
8727 } else {
8728 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8729 // We're doing a history load.
8731 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8732 &aState.mHistoryNavBetweenSameDoc);
8736 #ifdef DEBUG
8737 if (aState.mHistoryNavBetweenSameDoc) {
8738 nsCOMPtr<nsIInputStream> currentPostData;
8739 if (mozilla::SessionHistoryInParent()) {
8740 currentPostData = mActiveEntry->GetPostData();
8741 } else {
8742 currentPostData = mOSHE->GetPostData();
8744 NS_ASSERTION(currentPostData == aLoadState->PostDataStream(),
8745 "Different POST data for entries for the same page?");
8747 #endif
8749 // A same document navigation happens when we navigate between two SHEntries
8750 // for the same document. We do a same document navigation under two
8751 // circumstances. Either
8753 // a) we're navigating between two different SHEntries which share a
8754 // document, or
8756 // b) we're navigating to a new shentry whose URI differs from the
8757 // current URI only in its hash, the new hash is non-empty, and
8758 // we're not doing a POST.
8760 // The restriction that the SHEntries in (a) must be different ensures
8761 // that history.go(0) and the like trigger full refreshes, rather than
8762 // same document navigations.
8763 if (!mozilla::SessionHistoryInParent()) {
8764 bool doSameDocumentNavigation =
8765 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8766 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8767 aState.mSameExceptHashes && aState.mNewURIHasRef);
8768 MOZ_LOG(gSHLog, LogLevel::Debug,
8769 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8770 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8771 return doSameDocumentNavigation;
8774 if (aState.mHistoryNavBetweenSameDoc &&
8775 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8776 return true;
8779 MOZ_LOG(
8780 gSHLog, LogLevel::Debug,
8781 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8782 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8783 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8784 !aLoadState->PostDataStream() ? "true" : "false",
8785 aState.mSameExceptHashes ? "true" : "false",
8786 aState.mNewURIHasRef ? "true" : "false"));
8787 return !aLoadState->LoadIsFromSessionHistory() &&
8788 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8789 aState.mNewURIHasRef;
8792 nsresult nsDocShell::HandleSameDocumentNavigation(
8793 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState) {
8794 #ifdef DEBUG
8795 SameDocumentNavigationState state;
8796 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8797 #endif
8799 MOZ_LOG(gSHLog, LogLevel::Debug,
8800 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8801 mCurrentURI->GetSpecOrDefault().get(),
8802 aLoadState->URI()->GetSpecOrDefault().get()));
8804 RefPtr<Document> doc = GetDocument();
8805 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8806 doc->DoNotifyPossibleTitleChange();
8808 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8810 // Save the position of the scrollers.
8811 nsPoint scrollPos = GetCurScrollPos();
8813 // Reset mLoadType to its original value once we exit this block, because this
8814 // same document navigation might have started after a normal, network load,
8815 // and we don't want to clobber its load type. See bug 737307.
8816 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8818 // If a non-same-document-navigation (i.e., a network load) is pending, make
8819 // this a replacement load, so that we don't add a SHEntry here and the
8820 // network load goes into the SHEntry it expects to.
8821 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8822 mLoadType = LOAD_NORMAL_REPLACE;
8823 } else {
8824 mLoadType = aLoadState->LoadType();
8827 mURIResultedInDocument = true;
8829 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8831 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8832 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8833 // onLocationChange() notifications to the browser to update back/forward
8834 // buttons.
8835 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8836 Nothing());
8837 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8838 mLoadingEntry.swap(oldLoadingEntry);
8839 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8840 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8841 *aLoadState->GetLoadingSessionHistoryInfo());
8844 // Set the doc's URI according to the new history entry's URI.
8845 doc->SetDocumentURI(aLoadState->URI());
8847 /* This is a anchor traversal within the same page.
8848 * call OnNewURI() so that, this traversal will be
8849 * recorded in session and global history.
8851 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8852 newURIPartitionedPrincipalToInherit;
8853 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8854 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8855 if (mozilla::SessionHistoryInParent()) {
8856 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8857 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8858 newURIPartitionedPrincipalToInherit =
8859 mActiveEntry->GetPartitionedPrincipalToInherit();
8860 newCsp = mActiveEntry->GetCsp();
8861 } else {
8862 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8863 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8864 newURIPartitionedPrincipalToInherit =
8865 mOSHE->GetPartitionedPrincipalToInherit();
8866 newCsp = mOSHE->GetCsp();
8868 } else {
8869 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8870 newURIPrincipalToInherit = doc->NodePrincipal();
8871 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8872 newCsp = doc->GetCsp();
8875 uint32_t locationChangeFlags =
8876 GetSameDocumentNavigationFlags(aLoadState->URI());
8878 // Pass true for aCloneSHChildren, since we're not
8879 // changing documents here, so all of our subframes are
8880 // still relevant to the new session history entry.
8882 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8883 // flag on firing onLocationChange(...).
8884 // Anyway, aCloneSHChildren param is simply reflecting
8885 // doSameDocumentNavigation in this scope.
8887 // Note: we'll actually fire onLocationChange later, in order to preserve
8888 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8889 // 1668126)
8890 bool locationChangeNeeded =
8891 OnNewURI(aLoadState->URI(), nullptr, newURITriggeringPrincipal,
8892 newURIPrincipalToInherit, newURIPartitionedPrincipalToInherit,
8893 newCsp, false, true, true);
8895 nsCOMPtr<nsIInputStream> postData;
8896 uint32_t cacheKey = 0;
8898 bool scrollRestorationIsManual = false;
8899 if (!mozilla::SessionHistoryInParent()) {
8900 if (mOSHE) {
8901 /* save current position of scroller(s) (bug 59774) */
8902 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8903 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8904 // Get the postdata and page ident from the current page, if
8905 // the new load is being done via normal means. Note that
8906 // "normal means" can be checked for just by checking for
8907 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8908 // above -- it filters out some LOAD_CMD_NORMAL cases that we
8909 // wouldn't want here.
8910 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8911 postData = mOSHE->GetPostData();
8912 cacheKey = mOSHE->GetCacheKey();
8915 // Link our new SHEntry to the old SHEntry's back/forward
8916 // cache data, since the two SHEntries correspond to the
8917 // same document.
8918 if (mLSHE) {
8919 if (!aLoadState->LoadIsFromSessionHistory()) {
8920 // If we're not doing a history load, scroll restoration
8921 // should be inherited from the previous session history entry.
8922 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8923 scrollRestorationIsManual);
8925 mLSHE->AdoptBFCacheEntry(mOSHE);
8928 } else {
8929 if (mActiveEntry) {
8930 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8931 if (mBrowsingContext) {
8932 CollectWireframe();
8933 if (XRE_IsParentProcess()) {
8934 SessionHistoryEntry* entry =
8935 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8936 if (entry) {
8937 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8939 } else {
8940 mozilla::Unused << ContentChild::GetSingleton()
8941 ->SendSessionHistoryEntryScrollPosition(
8942 mBrowsingContext, scrollPos.x,
8943 scrollPos.y);
8947 if (mLoadingEntry) {
8948 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8949 // If we're not doing a history load, scroll restoration
8950 // should be inherited from the previous session history entry.
8951 // XXX This needs most probably tweaks once fragment navigation is
8952 // fixed to work with session-history-in-parent.
8953 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8954 scrollRestorationIsManual);
8959 // If we're doing a history load, use its scroll restoration state.
8960 if (aLoadState->LoadIsFromSessionHistory()) {
8961 if (mozilla::SessionHistoryInParent()) {
8962 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8963 ->mInfo.GetScrollRestorationIsManual();
8964 } else {
8965 scrollRestorationIsManual =
8966 aLoadState->SHEntry()->GetScrollRestorationIsManual();
8970 /* Assign mLSHE to mOSHE. This will either be a new entry created
8971 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8972 * loads.
8974 if (!mozilla::SessionHistoryInParent()) {
8975 if (mLSHE) {
8976 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8977 // Save the postData obtained from the previous page
8978 // in to the session history entry created for the
8979 // anchor page, so that any history load of the anchor
8980 // page will restore the appropriate postData.
8981 if (postData) {
8982 mOSHE->SetPostData(postData);
8985 // Make sure we won't just repost without hitting the
8986 // cache first
8987 if (cacheKey != 0) {
8988 mOSHE->SetCacheKey(cacheKey);
8992 /* Set the title for the SH entry for this target url so that
8993 * SH menus in go/back/forward buttons won't be empty for this.
8994 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
8995 * the code above.
8996 * Note, when session history lives in the parent process, this does not
8997 * update the title there.
8999 SetTitleOnHistoryEntry(false);
9000 } else {
9001 if (aLoadState->LoadIsFromSessionHistory()) {
9002 MOZ_LOG(
9003 gSHLog, LogLevel::Debug,
9004 ("Moving the loading entry to the active entry on nsDocShell %p to "
9005 "%s",
9006 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
9007 bool hadActiveEntry = !!mActiveEntry;
9009 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
9010 if (mActiveEntry) {
9011 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
9013 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
9014 if (currentLayoutHistoryState) {
9015 // Restore the existing nsILayoutHistoryState object, since it is
9016 // possibly being used by the layout. When doing a new load, the
9017 // shared state is copied from the existing active entry, so this
9018 // special case is needed only with the history loads.
9019 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
9022 if (cacheKey != 0) {
9023 mActiveEntry->SetCacheKey(cacheKey);
9025 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
9026 // does require a non-null uri if this is for a refresh load of the same
9027 // URI, but in that case mCurrentURI won't be null here.
9028 mBrowsingContext->SessionHistoryCommit(
9029 *mLoadingEntry, mLoadType, mCurrentURI, hadActiveEntry, true, true,
9030 /* No expiration update on the same document loads*/
9031 false, cacheKey);
9032 // FIXME Need to set postdata.
9034 // Set the title for the SH entry for this target url so that
9035 // SH menus in go/back/forward buttons won't be empty for this.
9036 // Note, when session history lives in the parent process, this does not
9037 // update the title there.
9038 SetTitleOnHistoryEntry(false);
9039 } else {
9040 Maybe<bool> scrollRestorationIsManual;
9041 if (mActiveEntry) {
9042 scrollRestorationIsManual.emplace(
9043 mActiveEntry->GetScrollRestorationIsManual());
9045 // Get the postdata and page ident from the current page, if the new
9046 // load is being done via normal means. Note that "normal means" can be
9047 // checked for just by checking for LOAD_CMD_NORMAL, given the loadType
9048 // and allowScroll check above -- it filters out some LOAD_CMD_NORMAL
9049 // cases that we wouldn't want here.
9050 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
9051 postData = mActiveEntry->GetPostData();
9052 cacheKey = mActiveEntry->GetCacheKey();
9056 MOZ_LOG(gSHLog, LogLevel::Debug,
9057 ("Creating an active entry on nsDocShell %p to %s", this,
9058 aLoadState->URI()->GetSpecOrDefault().get()));
9059 if (mActiveEntry) {
9060 mActiveEntry =
9061 MakeUnique<SessionHistoryInfo>(*mActiveEntry, aLoadState->URI());
9062 } else {
9063 mActiveEntry = MakeUnique<SessionHistoryInfo>(
9064 aLoadState->URI(), newURITriggeringPrincipal,
9065 newURIPrincipalToInherit, newURIPartitionedPrincipalToInherit,
9066 newCsp, mContentTypeHint);
9069 // Save the postData obtained from the previous page in to the session
9070 // history entry created for the anchor page, so that any history load of
9071 // the anchor page will restore the appropriate postData.
9072 if (postData) {
9073 mActiveEntry->SetPostData(postData);
9076 // Make sure we won't just repost without hitting the
9077 // cache first
9078 if (cacheKey != 0) {
9079 mActiveEntry->SetCacheKey(cacheKey);
9082 // Set the title for the SH entry for this target url so that
9083 // SH menus in go/back/forward buttons won't be empty for this.
9084 mActiveEntry->SetTitle(mTitle);
9086 if (scrollRestorationIsManual.isSome()) {
9087 mActiveEntry->SetScrollRestorationIsManual(
9088 scrollRestorationIsManual.value());
9091 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9092 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
9093 } else {
9094 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
9095 // FIXME We should probably just compute mChildOffset in the parent
9096 // instead of passing it over IPC here.
9097 mBrowsingContext->SetActiveSessionHistoryEntry(
9098 Some(scrollPos), mActiveEntry.get(), mLoadType, cacheKey);
9099 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9104 if (locationChangeNeeded) {
9105 FireOnLocationChange(this, nullptr, aLoadState->URI(), locationChangeFlags);
9108 /* Restore the original LSHE if we were loading something
9109 * while same document navigation was initiated.
9111 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
9112 mLoadingEntry.swap(oldLoadingEntry);
9114 /* Set the title for the Global History entry for this anchor url.
9116 UpdateGlobalHistoryTitle(aLoadState->URI());
9118 SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
9120 // Inform the favicon service that the favicon for oldURI also
9121 // applies to aLoadState->URI().
9122 CopyFavicon(currentURI, aLoadState->URI(), UsePrivateBrowsing());
9124 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9125 RefPtr<nsGlobalWindowInner> win =
9126 scriptGlobal ? scriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
9128 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9129 // the function decides whether a scroll is appropriate based on the
9130 // arguments it receives. But even if we don't end up scrolling,
9131 // ScrollToAnchor performs other important tasks, such as informing
9132 // the presShell that we have a new hash. See bug 680257.
9133 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
9134 aState.mNewHash, aLoadState->LoadType());
9135 NS_ENSURE_SUCCESS(rv, rv);
9137 /* restore previous position of scroller(s), if we're moving
9138 * back in history (bug 59774)
9140 nscoord bx = 0;
9141 nscoord by = 0;
9142 bool needsScrollPosUpdate = false;
9143 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
9144 (aLoadState->LoadType() == LOAD_HISTORY ||
9145 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
9146 !scrollRestorationIsManual) {
9147 needsScrollPosUpdate = true;
9148 if (mozilla::SessionHistoryInParent()) {
9149 mActiveEntry->GetScrollPosition(&bx, &by);
9150 } else {
9151 mOSHE->GetScrollPosition(&bx, &by);
9155 // Dispatch the popstate and hashchange events, as appropriate.
9157 // The event dispatch below can cause us to re-enter script and
9158 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9159 // reference to avoid null derefs. See bug 914521.
9160 if (win) {
9161 // Fire a hashchange event URIs differ, and only in their hashes.
9162 bool doHashchange = aState.mSameExceptHashes &&
9163 (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
9164 !aState.mCurrentHash.Equals(aState.mNewHash));
9166 if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
9167 win->DispatchSyncPopState();
9170 if (needsScrollPosUpdate && win->HasActiveDocument()) {
9171 SetCurScrollPosEx(bx, by);
9174 if (doHashchange) {
9175 // Note that currentURI hasn't changed because it's on the
9176 // stack, so we can just use it directly as the old URI.
9177 win->DispatchAsyncHashchange(currentURI, aLoadState->URI());
9181 return NS_OK;
9184 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
9185 nsDocShellLoadState* aLoadState) {
9186 if (!aLoadState->AllowFocusMove()) {
9187 return false;
9190 const auto& sourceBC = aLoadState->SourceBrowsingContext();
9191 if (!sourceBC || !sourceBC->IsActive()) {
9192 // If the navigation didn't come from a foreground tab, then we don't steal
9193 // focus.
9194 return false;
9196 auto* bc = aDocShell->GetBrowsingContext();
9197 if (sourceBC.get() == bc) {
9198 // If it comes from the same tab / frame, don't steal focus either.
9199 return false;
9201 auto* fm = nsFocusManager::GetFocusManager();
9202 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
9203 // If we're already on the foreground tab of the foreground window, then we
9204 // don't need to do this. This helps to e.g. not steal focus from the
9205 // browser chrome unnecessarily.
9206 return false;
9208 if (auto* doc = aDocShell->GetExtantDocument()) {
9209 if (doc->IsInitialDocument()) {
9210 // If we're the initial load for the browsing context, the browser
9211 // chrome determines what to focus. This is important because the
9212 // browser chrome may want to e.g focus the url-bar
9213 return false;
9216 // Take loadDivertedInBackground into account so the behavior would be the
9217 // same as how the tab first opened.
9218 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9221 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
9222 Maybe<uint32_t> aCacheKey) {
9223 MOZ_ASSERT(aLoadState, "need a load state!");
9224 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
9225 "need a valid TriggeringPrincipal");
9227 if (!aLoadState->TriggeringPrincipal()) {
9228 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9229 return NS_ERROR_FAILURE;
9231 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
9232 return NS_ERROR_NOT_AVAILABLE;
9235 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
9237 mOriginalUriString.Truncate();
9239 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9240 ("DOCSHELL %p InternalLoad %s\n", this,
9241 aLoadState->URI()->GetSpecOrDefault().get()));
9243 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
9245 // Cancel loads coming from Docshells that are being destroyed.
9246 if (mIsBeingDestroyed) {
9247 return NS_ERROR_NOT_AVAILABLE;
9250 nsresult rv = EnsureScriptEnvironment();
9251 if (NS_FAILED(rv)) {
9252 return rv;
9255 // If we have a target to move to, do that now.
9256 if (!aLoadState->Target().IsEmpty()) {
9257 return PerformRetargeting(aLoadState);
9260 if (aLoadState->TargetBrowsingContext().IsNull()) {
9261 aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
9264 MOZ_DIAGNOSTIC_ASSERT(
9265 aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
9266 "Load must be targeting this BrowsingContext");
9268 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
9270 // If we don't have a target, we're loading into ourselves, and our load
9271 // delegate may want to intercept that load.
9272 SameDocumentNavigationState sameDocumentNavigationState;
9273 bool sameDocument =
9274 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
9275 !aLoadState->GetPendingRedirectedChannel();
9277 // Note: We do this check both here and in BrowsingContext::
9278 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9279 // available in the process triggering the load, and we don't want the target
9280 // process to have to trust the triggering process to do the appropriate
9281 // checks for the BrowsingContext's sandbox flags.
9282 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
9284 NS_ENSURE_STATE(!HasUnloadedParent());
9286 rv = CheckLoadingPermissions();
9287 if (NS_FAILED(rv)) {
9288 return rv;
9291 if (mFiredUnloadEvent) {
9292 if (IsOKToLoadURI(aLoadState->URI())) {
9293 MOZ_ASSERT(aLoadState->Target().IsEmpty(),
9294 "Shouldn't have a window target here!");
9296 // If this is a replace load, make whatever load triggered
9297 // the unload event also a replace load, so we don't
9298 // create extra history entries.
9299 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
9300 LOAD_FLAGS_REPLACE_HISTORY)) {
9301 mLoadType = LOAD_NORMAL_REPLACE;
9304 // Do this asynchronously
9305 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
9306 return Dispatch(TaskCategory::Other, ev.forget());
9309 // Just ignore this load attempt
9310 return NS_OK;
9313 // If we are loading a URI that should inherit a security context (basically
9314 // javascript: at this point), and the caller has said that principal
9315 // inheritance is allowed, there are a few possible cases:
9317 // 1) We are provided with the principal to inherit. In that case, we just use
9318 // it.
9320 // 2) The load is coming from some other application. In this case we don't
9321 // want to inherit from whatever document we have loaded now, since the
9322 // load is unrelated to it.
9324 // 3) It's a load from our application, but does not provide an explicit
9325 // principal to inherit. In that case, we want to inherit the principal of
9326 // our current document, or of our parent document (if any) if we don't
9327 // have a current document.
9329 bool inherits;
9331 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
9332 !aLoadState->PrincipalToInherit() &&
9333 (aLoadState->HasInternalLoadFlags(
9334 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
9335 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9336 aLoadState->URI(), &inherits)) &&
9337 inherits) {
9338 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
9340 // If principalToInherit is still null (e.g. if some of the conditions of
9341 // were not satisfied), then no inheritance of any sort will happen: the
9342 // load will just get a principal based on the URI being loaded.
9345 // If this docshell is owned by a frameloader, make sure to cancel
9346 // possible frameloader initialization before loading a new page.
9347 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
9348 if (parent) {
9349 RefPtr<Document> doc = parent->GetDocument();
9350 if (doc) {
9351 doc->TryCancelFrameLoaderInitialization(this);
9355 // Before going any further vet loads initiated by external programs.
9356 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9357 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
9359 // Disallow external chrome: loads targetted at content windows
9360 if (SchemeIsChrome(aLoadState->URI())) {
9361 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9362 return NS_ERROR_FAILURE;
9365 // clear the decks to prevent context bleed-through (bug 298255)
9366 rv = CreateAboutBlankContentViewer(nullptr, nullptr, nullptr, nullptr,
9367 /* aIsInitialDocument */ false);
9368 if (NS_FAILED(rv)) {
9369 return NS_ERROR_FAILURE;
9373 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
9374 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
9375 mURIResultedInDocument = false; // reset the clock...
9377 // See if this is actually a load between two history entries for the same
9378 // document. If the process fails, or if we successfully navigate within the
9379 // same document, return.
9380 if (sameDocument) {
9381 nsresult rv =
9382 HandleSameDocumentNavigation(aLoadState, sameDocumentNavigationState);
9383 NS_ENSURE_SUCCESS(rv, rv);
9384 if (shouldTakeFocus) {
9385 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9387 return rv;
9390 if (mBrowsingContext->IsTopContent() &&
9391 !aLoadState->GetPendingRedirectedChannel()) {
9392 nsCOMPtr<nsITopLevelNavigationDelegate> delegate =
9393 do_GetInterface(mTreeOwner);
9394 if (delegate) {
9395 bool shouldNavigate = false;
9396 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9397 rv = delegate->ShouldNavigate(
9398 this, aLoadState->URI(), aLoadState->LoadType(), referrerInfo,
9399 !!aLoadState->PostDataStream(), aLoadState->TriggeringPrincipal(),
9400 aLoadState->Csp(), &shouldNavigate);
9401 if (NS_SUCCEEDED(rv) && !shouldNavigate) {
9402 return NS_OK;
9407 // mContentViewer->PermitUnload can destroy |this| docShell, which
9408 // causes the next call of CanSavePresentation to crash.
9409 // Hold onto |this| until we return, to prevent a crash from happening.
9410 // (bug#331040)
9411 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9413 // Don't init timing for javascript:, since it generally doesn't
9414 // actually start a load or anything. If it does, we'll init
9415 // timing then, from OnStateChange.
9417 // XXXbz mTiming should know what channel it's for, so we don't
9418 // need this hackery.
9419 bool toBeReset = false;
9420 bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
9422 if (!isJavaScript) {
9423 toBeReset = MaybeInitTiming();
9425 bool isNotDownload = aLoadState->FileName().IsVoid();
9426 if (mTiming && isNotDownload) {
9427 mTiming->NotifyBeforeUnload();
9429 // Check if the page doesn't want to be unloaded. The javascript:
9430 // protocol handler deals with this for javascript: URLs.
9431 if (!isJavaScript && isNotDownload &&
9432 !aLoadState->NotifiedBeforeUnloadListeners() && mContentViewer) {
9433 bool okToUnload;
9434 rv = mContentViewer->PermitUnload(&okToUnload);
9436 if (NS_SUCCEEDED(rv) && !okToUnload) {
9437 // The user chose not to unload the page, interrupt the
9438 // load.
9439 MaybeResetInitTiming(toBeReset);
9440 return NS_OK;
9444 if (mTiming && isNotDownload) {
9445 mTiming->NotifyUnloadAccepted(mCurrentURI);
9448 // In e10s, in the parent process, we refuse to load anything other than
9449 // "safe" resources that we ship or trust enough to give "special" URLs.
9450 // Similar check will be performed by the ParentProcessDocumentChannel if in
9451 // use.
9452 if (XRE_IsE10sParentProcess() &&
9453 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
9454 !CanLoadInParentProcess(aLoadState->URI())) {
9455 return NS_ERROR_FAILURE;
9458 // Whenever a top-level browsing context is navigated, the user agent MUST
9459 // lock the orientation of the document to the document's default
9460 // orientation. We don't explicitly check for a top-level browsing context
9461 // here because orientation is only set on top-level browsing contexts.
9462 if (mBrowsingContext->GetOrientationLock() != hal::eScreenOrientation_None) {
9463 MOZ_ASSERT(mBrowsingContext->IsTop());
9464 MOZ_ALWAYS_SUCCEEDS(
9465 mBrowsingContext->SetOrientationLock(hal::eScreenOrientation_None));
9466 if (mBrowsingContext->IsActive()) {
9467 ScreenOrientation::UpdateActiveOrientationLock(
9468 hal::eScreenOrientation_None);
9472 // Check for saving the presentation here, before calling Stop().
9473 // This is necessary so that we can catch any pending requests.
9474 // Since the new request has not been created yet, we pass null for the
9475 // new request parameter.
9476 // Also pass nullptr for the document, since it doesn't affect the return
9477 // value for our purposes here.
9478 bool savePresentation =
9479 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr);
9481 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9482 // separate check for SHIP so that we know if there are ongoing requests
9483 // before calling Stop() below.
9484 if (mozilla::SessionHistoryInParent()) {
9485 Document* document = GetDocument();
9486 uint32_t flags = 0;
9487 if (document && !document->CanSavePresentation(nullptr, flags, true)) {
9488 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9489 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9490 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9491 // Also, we want to report all the flags to the parent process here (and
9492 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9493 // telemetry data correctly.
9494 document->DisallowBFCaching(flags);
9498 // Don't stop current network activity for javascript: URL's since
9499 // they might not result in any data, and thus nothing should be
9500 // stopped in those cases. In the case where they do result in
9501 // data, the javascript: URL channel takes care of stopping
9502 // current network activity.
9503 if (!isJavaScript && isNotDownload) {
9504 // Stop any current network activity.
9505 // Also stop content if this is a zombie doc. otherwise
9506 // the onload will be delayed by other loads initiated in the
9507 // background by the first document that
9508 // didn't fully load before the next load was initiated.
9509 // If not a zombie, don't stop content until data
9510 // starts arriving from the new URI...
9512 if ((mContentViewer && mContentViewer->GetPreviousViewer()) ||
9513 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
9514 rv = Stop(nsIWebNavigation::STOP_ALL);
9515 } else {
9516 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9519 if (NS_FAILED(rv)) {
9520 return rv;
9524 mLoadType = aLoadState->LoadType();
9526 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9527 // been called. But when loading an error page, do not clear the
9528 // mLSHE for the real page.
9529 if (mLoadType != LOAD_ERROR_PAGE) {
9530 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
9531 Nothing());
9532 if (aLoadState->LoadIsFromSessionHistory() &&
9533 !mozilla::SessionHistoryInParent()) {
9534 // We're making history navigation or a reload. Make sure our history ID
9535 // points to the same ID as SHEntry's docshell ID.
9536 nsID historyID = {};
9537 aLoadState->SHEntry()->GetDocshellID(historyID);
9539 Unused << mBrowsingContext->SetHistoryID(historyID);
9543 mSavingOldViewer = savePresentation;
9545 // If we have a saved content viewer in history, restore and show it now.
9546 if (aLoadState->LoadIsFromSessionHistory() &&
9547 (mLoadType & LOAD_CMD_HISTORY)) {
9548 // https://html.spec.whatwg.org/#history-traversal:
9549 // To traverse the history
9550 // "If entry has a different Document object than the current entry, then
9551 // run the following substeps: Remove any tasks queued by the history
9552 // traversal task source..."
9553 // Same document object case was handled already above with
9554 // HandleSameDocumentNavigation call.
9555 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
9556 if (shistory) {
9557 shistory->RemovePendingHistoryNavigations();
9559 if (!mozilla::SessionHistoryInParent()) {
9560 // It's possible that the previous viewer of mContentViewer is the
9561 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9562 // If that's the case, we need to go ahead and force it into its shentry
9563 // so we can restore it.
9564 if (mContentViewer) {
9565 nsCOMPtr<nsIContentViewer> prevViewer =
9566 mContentViewer->GetPreviousViewer();
9567 if (prevViewer) {
9568 #ifdef DEBUG
9569 nsCOMPtr<nsIContentViewer> prevPrevViewer =
9570 prevViewer->GetPreviousViewer();
9571 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9572 #endif
9573 nsCOMPtr<nsISHEntry> viewerEntry;
9574 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9575 if (viewerEntry == aLoadState->SHEntry()) {
9576 // Make sure this viewer ends up in the right place
9577 mContentViewer->SetPreviousViewer(nullptr);
9578 prevViewer->Destroy();
9582 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9583 bool restoring;
9584 rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
9585 if (restoring) {
9586 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
9587 return rv;
9589 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
9591 // We failed to restore the presentation, so clean up.
9592 // Both the old and new history entries could potentially be in
9593 // an inconsistent state.
9594 if (NS_FAILED(rv)) {
9595 if (oldEntry) {
9596 oldEntry->SyncPresentationState();
9599 aLoadState->SHEntry()->SyncPresentationState();
9604 bool isTopLevelDoc = mBrowsingContext->IsTopContent();
9606 OriginAttributes attrs = GetOriginAttributes();
9607 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
9609 PredictorLearn(aLoadState->URI(), nullptr,
9610 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
9611 PredictorPredict(aLoadState->URI(), nullptr,
9612 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
9614 nsCOMPtr<nsIRequest> req;
9615 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
9617 if (NS_SUCCEEDED(rv)) {
9618 if (shouldTakeFocus) {
9619 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9623 if (NS_FAILED(rv)) {
9624 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9625 UnblockEmbedderLoadEventForFailure();
9626 if (DisplayLoadError(rv, aLoadState->URI(), nullptr, chan) &&
9627 // FIXME: At this point code was using internal load flags, but checking
9628 // non-internal load flags?
9629 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
9630 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9633 // We won't report any error if this is an unknown protocol error. The
9634 // reason behind this is that it will allow enumeration of external
9635 // protocols if we report an error for each unknown protocol.
9636 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
9637 return NS_OK;
9641 return rv;
9644 /* static */
9645 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
9646 nsCOMPtr<nsIURI> uri = aURI;
9647 // In e10s, in the parent process, we refuse to load anything other than
9648 // "safe" resources that we ship or trust enough to give "special" URLs.
9649 bool canLoadInParent = false;
9650 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9651 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
9652 canLoadInParent) {
9653 // We allow UI resources.
9654 return true;
9656 // For about: and extension-based URIs, which don't get
9657 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9658 while (uri && uri->SchemeIs("view-source")) {
9659 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
9660 if (nested) {
9661 nested->GetInnerURI(getter_AddRefs(uri));
9662 } else {
9663 break;
9666 // Allow about: URIs, and allow moz-extension ones if we're running
9667 // extension content in the parent process.
9668 if (!uri || uri->SchemeIs("about") ||
9669 (!StaticPrefs::extensions_webextensions_remote() &&
9670 uri->SchemeIs("moz-extension"))) {
9671 return true;
9673 #ifdef MOZ_THUNDERBIRD
9674 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
9675 uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
9676 uri->SchemeIs("snews")) {
9677 return true;
9679 #endif
9680 nsAutoCString scheme;
9681 uri->GetScheme(scheme);
9682 // Allow ext+foo URIs (extension-registered custom protocols). See
9683 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9684 if (StringBeginsWith(scheme, "ext+"_ns) &&
9685 !StaticPrefs::extensions_webextensions_remote()) {
9686 return true;
9688 // Final exception for some legacy automated tests:
9689 if (xpc::IsInAutomation() &&
9690 StaticPrefs::security_allow_unsafe_parent_loads()) {
9691 return true;
9693 return false;
9696 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
9697 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
9698 RefPtr<Document> document;
9699 bool inheritedFromCurrent = false;
9701 if (aConsiderCurrentDocument && mContentViewer) {
9702 document = mContentViewer->GetDocument();
9703 inheritedFromCurrent = true;
9706 if (!document) {
9707 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9708 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9709 if (parentItem) {
9710 document = parentItem->GetDocument();
9714 if (!document) {
9715 if (!aConsiderCurrentDocument) {
9716 return nullptr;
9719 // Make sure we end up with _something_ as the principal no matter
9720 // what.If this fails, we'll just get a null docViewer and bail.
9721 EnsureContentViewer();
9722 if (!mContentViewer) {
9723 return nullptr;
9725 document = mContentViewer->GetDocument();
9728 //-- Get the document's principal
9729 if (document) {
9730 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
9731 ? document->PartitionedPrincipal()
9732 : document->NodePrincipal();
9734 // Don't allow loads in typeContent docShells to inherit the system
9735 // principal from existing documents.
9736 if (inheritedFromCurrent && mItemType == typeContent &&
9737 docPrincipal->IsSystemPrincipal()) {
9738 return nullptr;
9741 return docPrincipal;
9744 return nullptr;
9747 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9748 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9749 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9750 const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9751 nsCOMPtr<nsIChannel> channel;
9752 if (aSrcdoc.IsVoid()) {
9753 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9754 nullptr, // PerformanceStorage
9755 nullptr, // loadGroup
9756 aCallbacks, aLoadFlags));
9758 if (aBaseURI) {
9759 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9760 if (vsc) {
9761 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9764 } else if (SchemeIsViewSource(aURI)) {
9765 // Instantiate view source handler protocol, if it doesn't exist already.
9766 nsCOMPtr<nsIIOService> io(do_GetIOService());
9767 MOZ_ASSERT(io);
9768 nsCOMPtr<nsIProtocolHandler> handler;
9769 nsresult rv =
9770 io->GetProtocolHandler("view-source", getter_AddRefs(handler));
9771 if (NS_FAILED(rv)) {
9772 return rv;
9775 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9776 if (!vsh) {
9777 return NS_ERROR_FAILURE;
9780 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9781 getter_AddRefs(channel)));
9782 } else {
9783 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
9784 aSrcdoc, "text/html"_ns, aLoadInfo,
9785 true));
9786 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9787 MOZ_ASSERT(isc);
9788 isc->SetBaseURI(aBaseURI);
9791 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9792 nsresult rv = channel->SetLoadFlags(aLoadFlags);
9793 NS_ENSURE_SUCCESS(rv, rv);
9796 channel.forget(aChannel);
9797 return NS_OK;
9800 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9801 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9802 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9803 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9804 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9805 nsIChannel** aChannel) {
9806 MOZ_ASSERT(aLoadInfo);
9808 nsString srcdoc = VoidString();
9809 bool isSrcdoc =
9810 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9811 if (isSrcdoc) {
9812 srcdoc = aLoadState->SrcdocData();
9815 if (aLoadState->PrincipalToInherit()) {
9816 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9818 aLoadInfo->SetLoadTriggeredFromExternal(
9819 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
9820 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
9821 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9822 aLoadInfo->SetOriginalFrameSrcLoad(
9823 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9825 bool inheritAttrs = false;
9826 if (aLoadState->PrincipalToInherit()) {
9827 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9828 aLoadState->PrincipalToInherit(), aLoadState->URI(),
9829 true, // aInheritForAboutBlank
9830 isSrcdoc);
9833 OriginAttributes attrs;
9835 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9836 // true. Otherwise we just use the origin attributes from docshell.
9837 if (inheritAttrs) {
9838 MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9839 "We should have PrincipalToInherit here.");
9840 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9841 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9842 // have the same origin attributes with docshell.
9843 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9844 attrs == aOriginAttributes);
9845 } else {
9846 attrs = aOriginAttributes;
9847 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9848 aLoadState->URI());
9851 aRv = aLoadInfo->SetOriginAttributes(attrs);
9852 if (NS_WARN_IF(NS_FAILED(aRv))) {
9853 return false;
9856 if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9857 aLoadInfo->SetIsFromProcessingFrameAttributes();
9860 // Propagate the IsFormSubmission flag to the loadInfo.
9861 if (aLoadState->IsFormSubmission()) {
9862 aLoadInfo->SetIsFormSubmission(true);
9865 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
9867 nsCOMPtr<nsIChannel> channel;
9868 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9869 aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9870 aLoadState->BaseURI());
9871 NS_ENSURE_SUCCESS(aRv, false);
9873 if (!channel) {
9874 return false;
9877 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9878 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9879 // HTTPS_ONLY_EXEMPT.
9880 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
9882 // hack
9883 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9884 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9885 do_QueryInterface(channel));
9886 nsCOMPtr<nsIURI> referrer;
9887 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9888 if (referrerInfo) {
9889 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9891 if (httpChannelInternal) {
9892 if (aLoadState->HasInternalLoadFlags(
9893 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9894 aRv = httpChannelInternal->SetThirdPartyFlags(
9895 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9896 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9898 if (aLoadState->FirstParty()) {
9899 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9900 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9901 } else {
9902 aRv = httpChannelInternal->SetDocumentURI(referrer);
9903 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9905 aRv = httpChannelInternal->SetRedirectMode(
9906 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9907 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9910 if (httpChannel) {
9911 if (aLoadState->HeadersStream()) {
9912 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9914 // Set the referrer explicitly
9915 // Referrer is currenly only set for link clicks here.
9916 if (referrerInfo) {
9917 aRv = httpChannel->SetReferrerInfo(referrerInfo);
9918 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9921 // Mark the http channel as UrgentStart for top level document loading in
9922 // active tab.
9923 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9924 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9925 if (cos) {
9926 cos->AddClassFlags(nsIClassOfService::UrgentStart);
9931 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
9932 : aLoadState->URI());
9934 const nsACString& typeHint = aLoadState->TypeHint();
9935 if (!typeHint.IsVoid()) {
9936 channel->SetContentType(typeHint);
9939 const nsAString& fileName = aLoadState->FileName();
9940 if (!fileName.IsVoid()) {
9941 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9942 NS_ENSURE_SUCCESS(aRv, false);
9943 if (!fileName.IsEmpty()) {
9944 aRv = channel->SetContentDispositionFilename(fileName);
9945 NS_ENSURE_SUCCESS(aRv, false);
9949 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
9950 nsCOMPtr<nsIURI> referrer;
9951 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9952 if (referrerInfo) {
9953 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9955 // save true referrer for those who need it (e.g. xpinstall whitelisting)
9956 // Currently only http and ftp channels support this.
9957 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
9959 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
9960 props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true);
9964 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
9965 auto loadType = aLoadState->LoadType();
9967 if (loadType == LOAD_RELOAD_NORMAL &&
9968 StaticPrefs::
9969 browser_soft_reload_only_force_validate_top_level_document()) {
9970 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
9971 if (cachingChannel) {
9972 cachingChannel->SetForceValidateCacheContent(true);
9976 // figure out if we need to set the post data stream on the channel...
9977 if (aLoadState->PostDataStream()) {
9978 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
9979 do_QueryInterface(channel)) {
9980 // XXX it's a bit of a hack to rewind the postdata stream here but
9981 // it has to be done in case the post data is being reused multiple
9982 // times.
9983 nsCOMPtr<nsISeekableStream> postDataSeekable =
9984 do_QueryInterface(aLoadState->PostDataStream());
9985 if (postDataSeekable) {
9986 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
9987 NS_ENSURE_SUCCESS(aRv, false);
9990 // we really need to have a content type associated with this stream!!
9991 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
9994 /* If there is a valid postdata *and* it is a History Load,
9995 * set up the cache key on the channel, to retrieve the
9996 * data *only* from the cache. If it is a normal reload, the
9997 * cache is free to go to the server for updated postdata.
9999 if (cacheChannel && aCacheKey != 0) {
10000 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
10001 cacheChannel->SetCacheKey(aCacheKey);
10002 uint32_t loadFlags;
10003 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
10004 channel->SetLoadFlags(loadFlags |
10005 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
10007 } else if (loadType == LOAD_RELOAD_NORMAL) {
10008 cacheChannel->SetCacheKey(aCacheKey);
10011 } else {
10012 /* If there is no postdata, set the cache key on the channel, and
10013 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10014 * will be free to get it from net if it is not found in cache.
10015 * New cache may use it creatively on CGI pages with GET
10016 * method and even on those that say "no-cache"
10018 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
10019 loadType == LOAD_RELOAD_CHARSET_CHANGE ||
10020 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
10021 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
10022 if (cacheChannel && aCacheKey != 0) {
10023 cacheChannel->SetCacheKey(aCacheKey);
10028 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
10029 // Allow execution against our context if the principals match
10030 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10033 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
10034 timedChannel->SetTimingEnabled(true);
10036 nsString initiatorType;
10037 switch (aLoadInfo->InternalContentPolicyType()) {
10038 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
10039 initiatorType = u"embed"_ns;
10040 break;
10041 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
10042 initiatorType = u"object"_ns;
10043 break;
10044 default: {
10045 const auto& embedderElementType =
10046 aBrowsingContext->GetEmbedderElementType();
10047 if (embedderElementType) {
10048 initiatorType = *embedderElementType;
10050 break;
10054 if (!initiatorType.IsEmpty()) {
10055 timedChannel->SetInitiatorType(initiatorType);
10059 nsCOMPtr<nsIURI> rpURI;
10060 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10061 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
10062 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
10063 if (originalResultPrincipalURI &&
10064 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
10065 // Unconditionally override, we want the replay to be equal to what has
10066 // been captured.
10067 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
10070 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
10071 // The LOAD_REPLACE flag and its handling here will be removed as part
10072 // of bug 1319110. For now preserve its restoration here to not break
10073 // any code expecting it being set specially on redirected channels.
10074 // If the flag has originally been set to change result of
10075 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10076 // any harm.
10077 uint32_t loadFlags;
10078 aRv = channel->GetLoadFlags(&loadFlags);
10079 NS_ENSURE_SUCCESS(aRv, false);
10080 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10083 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10084 if (csp) {
10085 // Navigational requests that are same origin need to be upgraded in case
10086 // upgrade-insecure-requests is present. Please note that for document
10087 // navigations that bit is re-computed in case we encounter a server
10088 // side redirect so the navigation is not same-origin anymore.
10089 bool upgradeInsecureRequests = false;
10090 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10091 if (upgradeInsecureRequests) {
10092 // only upgrade if the navigation is same origin
10093 nsCOMPtr<nsIPrincipal> resultPrincipal;
10094 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10095 channel, getter_AddRefs(resultPrincipal));
10096 NS_ENSURE_SUCCESS(aRv, false);
10097 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10098 aLoadState->TriggeringPrincipal(), resultPrincipal)) {
10099 aLoadInfo->SetUpgradeInsecureRequests(true);
10103 // For document loads we store the CSP that potentially needs to
10104 // be inherited by the new document, e.g. in case we are loading
10105 // an opaque origin like a data: URI. The actual inheritance
10106 // check happens within Document::InitCSP().
10107 // Please create an actual copy of the CSP (do not share the same
10108 // reference) otherwise a Meta CSP of an opaque origin will
10109 // incorrectly be propagated to the embedding document.
10110 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
10111 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
10112 aLoadInfo->SetCSPToInherit(cspToInherit);
10115 channel.forget(aChannel);
10116 return true;
10119 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
10120 Maybe<uint32_t> aCacheKey,
10121 nsIRequest** aRequest) {
10122 // Double-check that we're still around to load this URI.
10123 if (mIsBeingDestroyed) {
10124 // Return NS_OK despite not doing anything to avoid throwing exceptions
10125 // from nsLocation::SetHref if the unload handler of the existing page
10126 // tears us down.
10127 return NS_OK;
10130 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
10131 if (NS_WARN_IF(!uriLoader)) {
10132 return NS_ERROR_UNEXPECTED;
10135 // Persist and sync layout history state before we load a new uri, as this
10136 // might be our last chance to do so, in the content process.
10137 PersistLayoutHistoryState();
10138 SynchronizeLayoutHistoryState();
10140 nsresult rv;
10141 nsContentPolicyType contentPolicyType = DetermineContentType();
10143 if (IsSubframe()) {
10144 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10145 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10146 "DoURILoad thinks this is a frame and InternalLoad does not");
10148 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10149 // Only allow URLs able to return data in iframes.
10150 bool doesNotReturnData = false;
10151 NS_URIChainHasFlags(aLoadState->URI(),
10152 nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
10153 &doesNotReturnData);
10154 if (doesNotReturnData) {
10155 // The context to check user-interaction with for the purposes of
10156 // popup-blocking.
10158 // We generally want to check the context that initiated the navigation.
10159 WindowContext* sourceWindowContext = [&] {
10160 const MaybeDiscardedBrowsingContext& sourceBC =
10161 aLoadState->SourceBrowsingContext();
10162 if (!sourceBC.IsNullOrDiscarded()) {
10163 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
10164 return wc;
10167 return mBrowsingContext->GetParentWindowContext();
10168 }();
10170 MOZ_ASSERT(sourceWindowContext);
10171 // FIXME: We can't check user-interaction against an OOP window. This is
10172 // the next best thing we can really do. The load state keeps whether
10173 // the navigation had a user interaction in process
10174 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10175 // consume it, which we want to prevent popup-spamming from the same
10176 // click event.
10177 WindowContext* context =
10178 sourceWindowContext->IsInProcess()
10179 ? sourceWindowContext
10180 : mBrowsingContext->GetCurrentWindowContext();
10181 const bool popupBlocked = [&] {
10182 const bool active = mBrowsingContext->IsActive();
10184 // For same-origin-with-top windows, we grant a single free popup
10185 // without user activation, see bug 1680721.
10187 // We consume the flag now even if there's no user activation.
10188 const bool hasFreePass = [&] {
10189 if (!active ||
10190 !(context->IsInProcess() && context->SameOriginWithTop())) {
10191 return false;
10193 nsGlobalWindowInner* win =
10194 context->TopWindowContext()->GetInnerWindow();
10195 return win && win->TryOpenExternalProtocolIframe();
10196 }();
10198 if (context->IsInProcess() &&
10199 context->ConsumeTransientUserGestureActivation()) {
10200 // If the user has interacted with the page, consume it.
10201 return false;
10204 // TODO(emilio): Can we remove this check? It seems like what prompted
10205 // this code (bug 1514547) should be covered by transient user
10206 // activation, see bug 1514547.
10207 if (active &&
10208 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10209 return false;
10212 if (sourceWindowContext->CanShowPopup()) {
10213 return false;
10216 if (hasFreePass) {
10217 return false;
10220 return true;
10221 }();
10223 // No error must be returned when iframes are blocked.
10224 if (popupBlocked) {
10225 nsAutoString message;
10226 nsresult rv = nsContentUtils::GetLocalizedString(
10227 nsContentUtils::eDOM_PROPERTIES,
10228 "ExternalProtocolFrameBlockedNoUserActivation", message);
10229 if (NS_SUCCEEDED(rv)) {
10230 nsContentUtils::ReportToConsoleByWindowID(
10231 message, nsIScriptError::warningFlag, "DOM"_ns,
10232 context->InnerWindowId());
10234 return NS_OK;
10239 // Only allow view-source scheme in top-level docshells. view-source is
10240 // the only scheme to which this applies at the moment due to potential
10241 // timing attacks to read data from cross-origin iframes. If this widens
10242 // we should add a protocol flag for whether the scheme is allowed in
10243 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10244 nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
10245 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10246 while (nestedURI) {
10247 // view-source should always be an nsINestedURI, loop and check the
10248 // scheme on this and all inner URIs that are also nested URIs.
10249 if (SchemeIsViewSource(tempURI)) {
10250 return NS_ERROR_UNKNOWN_PROTOCOL;
10252 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10253 nestedURI = do_QueryInterface(tempURI);
10255 } else {
10256 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10257 "DoURILoad thinks this is a document and InternalLoad does not");
10260 // FIXME We still have a ton of codepaths that don't pass through
10261 // DocumentLoadListener, so probably need to create session history info
10262 // in more places.
10263 if (aLoadState->GetLoadingSessionHistoryInfo()) {
10264 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
10267 // open a channel for the url
10269 // If we have a pending channel, use the channel we've already created here.
10270 // We don't need to set up load flags for our channel, as it has already been
10271 // created.
10273 if (nsCOMPtr<nsIChannel> channel =
10274 aLoadState->GetPendingRedirectedChannel()) {
10275 // If we have a request outparameter, shove our channel into it.
10276 if (aRequest) {
10277 nsCOMPtr<nsIRequest> outRequest = channel;
10278 outRequest.forget(aRequest);
10281 return OpenRedirectedChannel(aLoadState);
10284 // There are two cases we care about:
10285 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10286 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10287 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10288 // element for the load. loadingPrincipal is the NodePrincipal of the
10289 // frame element.
10290 nsCOMPtr<nsINode> loadingNode;
10291 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10292 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10293 nsCOMPtr<nsISupports> topLevelLoadingContext;
10295 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10296 loadingNode = nullptr;
10297 loadingPrincipal = nullptr;
10298 loadingWindow = mScriptGlobal;
10299 if (XRE_IsContentProcess()) {
10300 // In e10s the child process doesn't have access to the element that
10301 // contains the browsing context (because that element is in the chrome
10302 // process).
10303 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
10304 topLevelLoadingContext = ToSupports(browserChild);
10305 } else {
10306 // This is for loading non-e10s tabs and toplevel windows of various
10307 // sorts.
10308 // For the toplevel window cases, requestingElement will be null.
10309 nsCOMPtr<Element> requestingElement =
10310 loadingWindow->GetFrameElementInternal();
10311 topLevelLoadingContext = requestingElement;
10313 } else {
10314 loadingWindow = nullptr;
10315 loadingNode = mScriptGlobal->GetFrameElementInternal();
10316 if (loadingNode) {
10317 // If we have a loading node, then use that as our loadingPrincipal.
10318 loadingPrincipal = loadingNode->NodePrincipal();
10319 #ifdef DEBUG
10320 // Get the docshell type for requestingElement.
10321 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
10322 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10323 // requestingElement docshell type = current docshell type.
10324 MOZ_ASSERT(
10325 mItemType == elementDocShell->ItemType(),
10326 "subframes should have the same docshell type as their parent");
10327 #endif
10328 } else {
10329 if (mIsBeingDestroyed) {
10330 // If this isn't a top-level load and mScriptGlobal's frame element is
10331 // null, then the element got removed from the DOM while we were trying
10332 // to load this resource. This docshell is scheduled for destruction
10333 // already, so bail out here.
10334 return NS_OK;
10336 // If we are not being destroyed and we do not have access to the loading
10337 // node, then we are a remote subframe. Set the loading principal
10338 // to be a null principal and then set it correctly in the parent.
10339 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
10343 if (!aLoadState->TriggeringPrincipal()) {
10344 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10345 return NS_ERROR_FAILURE;
10348 // We want to inherit aLoadState->PrincipalToInherit() when:
10349 // 1. ChannelShouldInheritPrincipal returns true.
10350 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10351 // configured as unique opaque origin.
10352 bool inheritPrincipal = false;
10354 if (aLoadState->PrincipalToInherit()) {
10355 bool isSrcdoc =
10356 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
10357 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10358 aLoadState->PrincipalToInherit(), aLoadState->URI(),
10359 true, // aInheritForAboutBlank
10360 isSrcdoc);
10362 inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
10365 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
10366 nsSecurityFlags securityFlags =
10367 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
10369 if (mLoadType == LOAD_ERROR_PAGE) {
10370 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10373 if (inheritPrincipal) {
10374 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10377 // Must never have a parent for TYPE_DOCUMENT loads
10378 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10379 !mBrowsingContext->GetParent());
10380 // Subdocuments must have a parent
10381 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
10382 mBrowsingContext->GetParent());
10383 mBrowsingContext->SetTriggeringAndInheritPrincipals(
10384 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
10385 aLoadState->GetLoadIdentifier());
10386 RefPtr<LoadInfo> loadInfo =
10387 (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10388 ? new LoadInfo(loadingWindow, aLoadState->TriggeringPrincipal(),
10389 topLevelLoadingContext, securityFlags, sandboxFlags)
10390 : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
10391 loadingNode, securityFlags, contentPolicyType,
10392 Maybe<mozilla::dom::ClientInfo>(),
10393 Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
10394 sandboxFlags);
10395 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
10397 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess() &&
10398 context->HasValidTransientUserGestureActivation()) {
10399 aLoadState->SetHasValidUserGestureActivation(true);
10402 // in case this docshell load was triggered by a valid transient user gesture,
10403 // or also the load originates from external, then we pass that information on
10404 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10405 if (aLoadState->HasValidUserGestureActivation() ||
10406 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
10407 loadInfo->SetHasValidUserGestureActivation(true);
10409 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
10410 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
10412 uint32_t cacheKey = 0;
10413 if (aCacheKey) {
10414 cacheKey = *aCacheKey;
10415 } else if (mozilla::SessionHistoryInParent()) {
10416 if (mLoadingEntry) {
10417 cacheKey = mLoadingEntry->mInfo.GetCacheKey();
10418 } else if (mActiveEntry) { // for reload cases
10419 cacheKey = mActiveEntry->GetCacheKey();
10421 } else {
10422 if (mLSHE) {
10423 cacheKey = mLSHE->GetCacheKey();
10424 } else if (mOSHE) { // for reload cases
10425 cacheKey = mOSHE->GetCacheKey();
10429 bool uriModified;
10430 if (mLSHE || mLoadingEntry) {
10431 if (mLoadingEntry) {
10432 uriModified = mLoadingEntry->mInfo.GetURIWasModified();
10433 } else {
10434 uriModified = mLSHE->GetURIWasModified();
10436 } else {
10437 uriModified = false;
10440 bool isXFOError = false;
10441 if (mFailedChannel) {
10442 nsresult status;
10443 mFailedChannel->GetStatus(&status);
10444 isXFOError = status == NS_ERROR_XFO_VIOLATION;
10447 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
10448 mBrowsingContext, Some(uriModified), Some(isXFOError));
10450 // Get the unstripped URI from the current document channel. The unstripped
10451 // URI will be preserved if it's a reload.
10452 nsCOMPtr<nsIURI> currentUnstrippedURI;
10453 nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel();
10454 if (docChannel) {
10455 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
10456 docLoadInfo->GetUnstrippedURI(getter_AddRefs(currentUnstrippedURI));
10459 // Strip the target query parameters before creating the channel.
10460 aLoadState->MaybeStripTrackerQueryStrings(mBrowsingContext,
10461 currentUnstrippedURI);
10463 nsCOMPtr<nsIChannel> channel;
10464 if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI())) {
10465 channel = DocumentChannel::CreateForDocument(aLoadState, loadInfo,
10466 loadFlags, this, cacheKey,
10467 uriModified, isXFOError);
10468 MOZ_ASSERT(channel);
10470 // Disable keyword fixup when using DocumentChannel, since
10471 // DocumentLoadListener will handle this for us (in the parent process).
10472 mAllowKeywordFixup = false;
10473 } else if (!CreateAndConfigureRealChannelForLoadState(
10474 mBrowsingContext, aLoadState, loadInfo, this, this,
10475 GetOriginAttributes(), loadFlags, cacheKey, rv,
10476 getter_AddRefs(channel))) {
10477 return rv;
10480 // Make sure to give the caller a channel if we managed to create one
10481 // This is important for correct error page/session history interaction
10482 if (aRequest) {
10483 NS_ADDREF(*aRequest = channel);
10486 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10487 if (csp) {
10488 // Check CSP navigate-to
10489 bool allowsNavigateTo = false;
10490 rv = csp->GetAllowsNavigateTo(aLoadState->URI(),
10491 aLoadState->IsFormSubmission(),
10492 false, /* aWasRedirected */
10493 false, /* aEnforceWhitelist */
10494 &allowsNavigateTo);
10495 NS_ENSURE_SUCCESS(rv, rv);
10497 if (!allowsNavigateTo) {
10498 return NS_ERROR_CSP_NAVIGATE_TO_VIOLATION;
10502 const nsACString& typeHint = aLoadState->TypeHint();
10503 if (!typeHint.IsVoid()) {
10504 mContentTypeHint = typeHint;
10505 } else {
10506 mContentTypeHint.Truncate();
10509 // Load attributes depend on load type...
10510 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10511 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10512 // only want to force cache load for this channel, not the whole
10513 // loadGroup.
10514 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10515 if (cachingChannel) {
10516 cachingChannel->SetAllowStaleCacheContent(true);
10520 uint32_t openFlags =
10521 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
10522 return OpenInitializedChannel(channel, uriLoader, openFlags);
10525 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10526 const char* aFromRawSegment,
10527 uint32_t aToOffset, uint32_t aCount,
10528 uint32_t* aWriteCount) {
10529 // aFromSegment now contains aCount bytes of data.
10531 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10532 buf->Append(aFromRawSegment, aCount);
10534 // Indicate that we have consumed all of aFromSegment
10535 *aWriteCount = aCount;
10536 return NS_OK;
10539 /* static */ nsresult nsDocShell::AddHeadersToChannel(
10540 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
10541 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10542 NS_ENSURE_STATE(httpChannel);
10544 uint32_t numRead;
10545 nsAutoCString headersString;
10546 nsresult rv = aHeadersData->ReadSegments(
10547 AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10548 NS_ENSURE_SUCCESS(rv, rv);
10550 // used during the manipulation of the String from the InputStream
10551 nsAutoCString headerName;
10552 nsAutoCString headerValue;
10553 int32_t crlf;
10554 int32_t colon;
10557 // Iterate over the headersString: for each "\r\n" delimited chunk,
10558 // add the value as a header to the nsIHttpChannel
10561 static const char kWhitespace[] = "\b\t\r\n ";
10562 while (true) {
10563 crlf = headersString.Find("\r\n");
10564 if (crlf == kNotFound) {
10565 return NS_OK;
10568 const nsACString& oneHeader = StringHead(headersString, crlf);
10570 colon = oneHeader.FindChar(':');
10571 if (colon == kNotFound) {
10572 return NS_ERROR_UNEXPECTED;
10575 headerName = StringHead(oneHeader, colon);
10576 headerValue = Substring(oneHeader, colon + 1);
10578 headerName.Trim(kWhitespace);
10579 headerValue.Trim(kWhitespace);
10581 headersString.Cut(0, crlf + 2);
10584 // FINALLY: we can set the header!
10587 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10588 NS_ENSURE_SUCCESS(rv, rv);
10591 MOZ_ASSERT_UNREACHABLE("oops");
10592 return NS_ERROR_UNEXPECTED;
10595 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10596 BrowsingContext* aBrowsingContext, uint32_t aLoadType) {
10597 MOZ_ASSERT(aBrowsingContext);
10599 uint32_t openFlags = 0;
10600 if (aLoadType == LOAD_LINK) {
10601 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10603 if (!aBrowsingContext->GetAllowContentRetargeting()) {
10604 openFlags |= nsIURILoader::DONT_RETARGET;
10607 return openFlags;
10610 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
10611 nsIURILoader* aURILoader,
10612 uint32_t aOpenFlags) {
10613 nsresult rv = NS_OK;
10615 // If anything fails here, make sure to clear our initial ClientSource.
10616 auto cleanupInitialClient =
10617 MakeScopeExit([&] { mInitialClientSource.reset(); });
10619 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10620 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10622 MaybeCreateInitialClientSource();
10624 // Let the client channel helper know if we are using DocumentChannel,
10625 // since redirects get handled in the parent process in that case.
10626 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
10627 if (docChannel && XRE_IsContentProcess()) {
10628 // Tell the content process nsDocumentOpenInfo to not try to do
10629 // any sort of targeting.
10630 aOpenFlags |= nsIURILoader::DONT_RETARGET;
10633 // Since we are loading a document we need to make sure the proper reserved
10634 // and initial client data is stored on the nsILoadInfo. The
10635 // ClientChannelHelper does this and ensures that it is propagated properly
10636 // on redirects. We pass no reserved client here so that the helper will
10637 // create the reserved ClientSource if necessary.
10638 Maybe<ClientInfo> noReservedClient;
10639 if (docChannel) {
10640 // When using DocumentChannel, all redirect handling is done in the parent,
10641 // so we just need the child variant to watch for the internal redirect
10642 // to the final channel.
10643 rv = AddClientChannelHelperInChild(
10644 aChannel, win->EventTargetFor(TaskCategory::Other));
10645 docChannel->SetInitialClientInfo(GetInitialClientInfo());
10646 } else {
10647 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
10648 GetInitialClientInfo(),
10649 win->EventTargetFor(TaskCategory::Other));
10651 NS_ENSURE_SUCCESS(rv, rv);
10653 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
10654 NS_ENSURE_SUCCESS(rv, rv);
10656 // We're about to load a new page and it may take time before necko
10657 // gives back any data, so main thread might have a chance to process a
10658 // collector slice
10659 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
10661 // Success. Keep the initial ClientSource if it exists.
10662 cleanupInitialClient.release();
10664 return NS_OK;
10667 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
10668 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
10669 MOZ_ASSERT(channel);
10671 // If anything fails here, make sure to clear our initial ClientSource.
10672 auto cleanupInitialClient =
10673 MakeScopeExit([&] { mInitialClientSource.reset(); });
10675 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10676 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10678 MaybeCreateInitialClientSource();
10680 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
10682 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
10683 if (loadInfo->GetExternalContentPolicyType() ==
10684 ExtContentPolicy::TYPE_DOCUMENT) {
10685 li->UpdateBrowsingContextID(mBrowsingContext->Id());
10686 } else if (loadInfo->GetExternalContentPolicyType() ==
10687 ExtContentPolicy::TYPE_SUBDOCUMENT) {
10688 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
10690 // TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
10692 // If we did a process switch, then we should have an existing allocated
10693 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10694 CreateReservedSourceIfNeeded(channel,
10695 win->EventTargetFor(TaskCategory::Other));
10697 RefPtr<nsDocumentOpenInfo> loader =
10698 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10699 channel->SetLoadGroup(mLoadGroup);
10701 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10703 nsresult rv = NS_OK;
10704 if (XRE_IsParentProcess()) {
10705 // If we're in the parent, the we don't have an nsIChildChannel, just
10706 // the original channel, which is already open in this process.
10708 // DocumentLoadListener expects to get an nsIParentChannel, so
10709 // we create a wrapper around the channel and nsIStreamListener
10710 // that forwards functionality as needed, and then we register
10711 // it under the provided identifier.
10712 RefPtr<ParentChannelWrapper> wrapper =
10713 new ParentChannelWrapper(channel, loader);
10714 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10716 mLoadGroup->AddRequest(channel, nullptr);
10717 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10718 do_QueryInterface(channel)) {
10719 // Our channel was redirected from another process, so doesn't need to
10720 // be opened again. However, it does need its listener hooked up
10721 // correctly.
10722 rv = childChannel->CompleteRedirectSetup(loader);
10723 } else {
10724 // It's possible for the redirected channel to not implement
10725 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10726 // can just open the local instance and it will work.
10727 rv = channel->AsyncOpen(loader);
10729 if (rv == NS_ERROR_NO_CONTENT) {
10730 return NS_OK;
10732 NS_ENSURE_SUCCESS(rv, rv);
10734 // Success. Keep the initial ClientSource if it exists.
10735 cleanupInitialClient.release();
10736 return NS_OK;
10739 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10740 nsACString& aNewHash, uint32_t aLoadType) {
10741 if (!mCurrentURI) {
10742 return NS_OK;
10745 RefPtr<PresShell> presShell = GetPresShell();
10746 if (!presShell) {
10747 // If we failed to get the shell, or if there is no shell,
10748 // nothing left to do here.
10749 return NS_OK;
10752 nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
10753 if (rootScroll) {
10754 rootScroll->ClearDidHistoryRestore();
10757 // If we have no new anchor, we do not want to scroll, unless there is a
10758 // current anchor and we are doing a history load. So return if we have no
10759 // new anchor, and there is no current anchor or the load is not a history
10760 // load.
10761 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10762 return NS_OK;
10765 // Both the new and current URIs refer to the same page. We can now
10766 // browse to the hash stored in the new URI.
10768 if (!aNewHash.IsEmpty()) {
10769 // anchor is there, but if it's a load from history,
10770 // we don't have any anchor jumping to do
10771 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10773 // We assume that the bytes are in UTF-8, as it says in the
10774 // spec:
10775 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
10777 // We try the UTF-8 string first, and then try the document's
10778 // charset (see below). If the string is not UTF-8,
10779 // conversion will fail and give us an empty Unicode string.
10780 // In that case, we should just fall through to using the
10781 // page's charset.
10782 nsresult rv = NS_ERROR_FAILURE;
10783 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10784 if (!uStr.IsEmpty()) {
10785 rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
10788 if (NS_FAILED(rv)) {
10789 char* str = ToNewCString(aNewHash, mozilla::fallible);
10790 if (!str) {
10791 return NS_ERROR_OUT_OF_MEMORY;
10793 nsUnescape(str);
10794 NS_ConvertUTF8toUTF16 utf16Str(str);
10795 if (!utf16Str.IsEmpty()) {
10796 rv = presShell->GoToAnchor(utf16Str, scroll,
10797 ScrollFlags::ScrollSmoothAuto);
10799 free(str);
10802 // Above will fail if the anchor name is not UTF-8. Need to
10803 // convert from document charset to unicode.
10804 if (NS_FAILED(rv)) {
10805 // Get a document charset
10806 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
10807 Document* doc = mContentViewer->GetDocument();
10808 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
10809 nsAutoCString charset;
10810 doc->GetDocumentCharacterSet()->Name(charset);
10812 nsCOMPtr<nsITextToSubURI> textToSubURI =
10813 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10814 NS_ENSURE_SUCCESS(rv, rv);
10816 // Unescape and convert to unicode
10817 nsAutoString uStr;
10819 rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
10820 NS_ENSURE_SUCCESS(rv, rv);
10822 // Ignore return value of GoToAnchor, since it will return an error
10823 // if there is no such anchor in the document, which is actually a
10824 // success condition for us (we want to update the session history
10825 // with the new URI no matter whether we actually scrolled
10826 // somewhere).
10828 // When aNewHash contains "%00", unescaped string may be empty.
10829 // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10830 presShell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
10831 ScrollFlags::ScrollSmoothAuto);
10833 } else {
10834 // Tell the shell it's at an anchor, without scrolling.
10835 presShell->GoToAnchor(u""_ns, false);
10837 // An empty anchor was found, but if it's a load from history,
10838 // we don't have to jump to the top of the page. Scrollbar
10839 // position will be restored by the caller, based on positions
10840 // stored in session history.
10841 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
10842 return NS_OK;
10844 // An empty anchor. Scroll to the top of the page. Ignore the
10845 // return value; failure to scroll here (e.g. if there is no
10846 // root scrollframe) is not grounds for canceling the load!
10847 SetCurScrollPosEx(0, 0);
10850 return NS_OK;
10853 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10854 nsIPrincipal* aTriggeringPrincipal,
10855 nsIPrincipal* aPrincipalToInherit,
10856 nsIPrincipal* aPartitionedPrincipalToInherit,
10857 nsIContentSecurityPolicy* aCsp,
10858 bool aFireOnLocationChange, bool aAddToGlobalHistory,
10859 bool aCloneSHChildren) {
10860 MOZ_ASSERT(aURI, "uri is null");
10861 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10863 MOZ_ASSERT(!aPrincipalToInherit ||
10864 (aPrincipalToInherit && aTriggeringPrincipal));
10866 #if defined(DEBUG)
10867 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10868 nsAutoCString chanName;
10869 if (aChannel) {
10870 aChannel->GetName(chanName);
10871 } else {
10872 chanName.AssignLiteral("<no channel>");
10875 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10876 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10877 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10879 #endif
10881 bool equalUri = false;
10883 // Get the post data and the HTTP response code from the channel.
10884 uint32_t responseStatus = 0;
10885 nsCOMPtr<nsIInputStream> inputStream;
10886 if (aChannel) {
10887 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10889 // Check if the HTTPChannel is hiding under a multiPartChannel
10890 if (!httpChannel) {
10891 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10894 if (httpChannel) {
10895 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10896 if (uploadChannel) {
10897 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10900 // If the response status indicates an error, unlink this session
10901 // history entry from any entries sharing its document.
10902 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10903 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10904 mLSHE->AbandonBFCacheEntry();
10905 // FIXME Do the same for mLoadingEntry
10910 // Determine if this type of load should update history.
10911 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
10913 // We don't update session history on reload unless we're loading
10914 // an iframe in shift-reload case.
10915 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
10917 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10918 // root browsing context.
10919 // FIXME If session history in the parent is enabled then we only do this if
10920 // the session history object is in process, otherwise we can't really
10921 // use the mLSHE anyway. Once session history is only stored in the
10922 // parent then this code will probably be removed anyway.
10923 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
10924 if (!rootSH) {
10925 updateSHistory = false;
10926 updateGHistory = false; // XXX Why global history too?
10929 // Check if the url to be loaded is the same as the one already loaded.
10930 if (mCurrentURI) {
10931 aURI->Equals(mCurrentURI, &equalUri);
10934 #ifdef DEBUG
10935 bool shAvailable = (rootSH != nullptr);
10937 // XXX This log message is almost useless because |updateSHistory|
10938 // and |updateGHistory| are not correct at this point.
10940 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10941 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10942 " equalURI=%i\n",
10943 shAvailable, updateSHistory, updateGHistory, equalUri));
10944 #endif
10946 /* If the url to be loaded is the same as the one already there,
10947 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10948 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10949 * AddToSessionHistory() won't mess with the current SHEntry and
10950 * if this page has any frame children, it also will be handled
10951 * properly. see bug 83684
10953 * NB: If mOSHE is null but we have a current URI, then it probably
10954 * means that we must be at the transient about:blank content viewer;
10955 * we should let the normal load continue, since there's nothing to
10956 * replace. Sometimes this happens after a session restore (eg process
10957 * switch) and mCurrentURI is not about:blank; we assume we can let the load
10958 * continue (Bug 1301399).
10960 * XXX Hopefully changing the loadType at this time will not hurt
10961 * anywhere. The other way to take care of sequentially repeating
10962 * frameset pages is to add new methods to nsIDocShellTreeItem.
10963 * Hopefully I don't have to do that.
10965 if (equalUri &&
10966 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
10967 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
10968 mLoadType == LOAD_STOP_CONTENT) &&
10969 !inputStream) {
10970 mLoadType = LOAD_NORMAL_REPLACE;
10973 // If this is a refresh to the currently loaded url, we don't
10974 // have to update session or global history.
10975 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10976 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
10979 /* If the user pressed shift-reload, cache will create a new cache key
10980 * for the page. Save the new cacheKey in Session History.
10981 * see bug 90098
10983 if (aChannel && IsForceReloadType(mLoadType)) {
10984 MOZ_ASSERT(!updateSHistory || IsSubframe(),
10985 "We shouldn't be updating session history for forced"
10986 " reloads unless we're in a newly created iframe!");
10988 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
10989 uint32_t cacheKey = 0;
10990 // Get the Cache Key and store it in SH.
10991 if (cacheChannel) {
10992 cacheChannel->GetCacheKey(&cacheKey);
10994 // If we already have a loading history entry, store the new cache key
10995 // in it. Otherwise, since we're doing a reload and won't be updating
10996 // our history entry, store the cache key in our current history entry.
10997 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
10999 if (!mozilla::SessionHistoryInParent()) {
11000 // Since we're force-reloading, clear all the sub frame history.
11001 ClearFrameHistory(mLSHE);
11002 ClearFrameHistory(mOSHE);
11006 if (!mozilla::SessionHistoryInParent()) {
11007 // Clear subframe history on refresh.
11008 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
11009 // this case. One should re-validate after bug 1331865 fixed.
11010 if (mLoadType == LOAD_REFRESH) {
11011 ClearFrameHistory(mLSHE);
11012 ClearFrameHistory(mOSHE);
11015 if (updateSHistory) {
11016 // Update session history if necessary...
11017 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11018 /* This is a fresh page getting loaded for the first time
11019 *.Create a Entry for it and add it to SH, if this is the
11020 * rootDocShell
11022 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11023 aPrincipalToInherit,
11024 aPartitionedPrincipalToInherit, aCsp,
11025 aCloneSHChildren, getter_AddRefs(mLSHE));
11027 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11028 // Even if we don't add anything to SHistory, ensure the current index
11029 // points to the same SHEntry as our mLSHE.
11031 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11032 mLSHE);
11036 // If this is a POST request, we do not want to include this in global
11037 // history.
11038 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11039 !net::ChannelIsPost(aChannel)) {
11040 nsCOMPtr<nsIURI> previousURI;
11041 uint32_t previousFlags = 0;
11043 if (mLoadType & LOAD_CMD_RELOAD) {
11044 // On a reload request, we don't set redirecting flags.
11045 previousURI = aURI;
11046 } else {
11047 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11050 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11053 // If this was a history load or a refresh, or it was a history load but
11054 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11055 // in session history.
11056 if (!mozilla::SessionHistoryInParent() && rootSH &&
11057 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11058 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11059 mPreviousEntryIndex = rootSH->Index();
11060 if (!mozilla::SessionHistoryInParent()) {
11061 rootSH->LegacySHistory()->UpdateIndex();
11063 mLoadedEntryIndex = rootSH->Index();
11064 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11065 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11066 mLoadedEntryIndex));
11069 // aCloneSHChildren exactly means "we are not loading a new document".
11070 uint32_t locationFlags =
11071 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11073 bool onLocationChangeNeeded =
11074 SetCurrentURI(aURI, aChannel, aFireOnLocationChange,
11075 /* aIsInitialAboutBlank */ false, locationFlags);
11076 // Make sure to store the referrer from the channel, if any
11077 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11078 if (httpChannel) {
11079 mReferrerInfo = httpChannel->GetReferrerInfo();
11081 return onLocationChangeNeeded;
11084 void nsDocShell::CollectWireframe() {
11085 if (mozilla::SessionHistoryInParent() &&
11086 StaticPrefs::browser_history_collectWireframes() &&
11087 mBrowsingContext->IsTopContent() && mActiveEntry) {
11088 RefPtr<Document> doc = mContentViewer->GetDocument();
11089 Nullable<Wireframe> wireframe;
11090 doc->GetWireframeWithoutFlushing(false, wireframe);
11091 if (!wireframe.IsNull()) {
11092 if (XRE_IsParentProcess()) {
11093 SessionHistoryEntry* entry =
11094 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11095 if (entry) {
11096 entry->SetWireframe(Some(wireframe.Value()));
11098 } else {
11099 mozilla::Unused
11100 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11101 mBrowsingContext, wireframe.Value());
11107 //*****************************************************************************
11108 // nsDocShell: Session History
11109 //*****************************************************************************
11111 NS_IMETHODIMP
11112 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11113 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11114 MOZ_LOG(gSHLog, LogLevel::Debug,
11115 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11116 NS_ConvertUTF16toUTF8(aTitle).get(),
11117 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11118 // Implements History.pushState and History.replaceState
11120 // Here's what we do, roughly in the order specified by HTML5. The specific
11121 // steps we are executing are at
11122 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11123 // and
11124 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11125 // This function basically implements #dom-history-pushstate and
11126 // UpdateURLAndHistory implements #url-and-history-update-steps.
11128 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11129 // step 5.
11130 // B. If the third argument is present, #dom-history-pushstate step 7.
11131 // 7.1. Resolve the url, relative to our document.
11132 // 7.2. If (a) fails, raise a SECURITY_ERR
11133 // 7.4. Compare the resulting absolute URL to the document's address. If
11134 // any part of the URLs difer other than the <path>, <query>, and
11135 // <fragment> components, raise a SECURITY_ERR and abort.
11136 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11137 // Remove from the session history all entries after the current entry,
11138 // as we would after a regular navigation, and save the current
11139 // entry's scroll position (bug 590573).
11140 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11141 // either add a state object entry to the session history after the
11142 // current entry with the following properties, or modify the current
11143 // session history entry to set
11144 // a. cloned data as the state object,
11145 // b. if the third argument was present, the absolute URL found in
11146 // step 2
11147 // Also clear the new history entry's POST data (see bug 580069).
11148 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11149 // replaceState), notify bfcache that we've navigated to a new page.
11150 // F. If the third argument is present, set the document's current address
11151 // to the absolute URL found in step B. This is
11152 // #url-and-history-update-steps step 4.
11154 // It's important that this function not run arbitrary scripts after step A
11155 // and before completing step E. For example, if a script called
11156 // history.back() before we completed step E, bfcache might destroy an
11157 // active content viewer. Since EvictOutOfRangeContentViewers at the end of
11158 // step E might run script, we can't just put a script blocker around the
11159 // critical section.
11161 // Note that we completely ignore the aTitle parameter.
11163 nsresult rv;
11165 // Don't clobber the load type of an existing network load.
11166 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11168 // pushState effectively becomes replaceState when we've started a network
11169 // load but haven't adopted its document yet. This mirrors what we do with
11170 // changes to the hash at this stage of the game.
11171 if (JustStartedNetworkLoad()) {
11172 aReplace = true;
11175 RefPtr<Document> document = GetDocument();
11176 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11178 // Step A: Serialize aData using structured clone.
11179 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11180 // step 5.
11181 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11183 // scContainer->Init might cause arbitrary JS to run, and this code might
11184 // navigate the page we're on, potentially to a different origin! (bug
11185 // 634834) To protect against this, we abort if our principal changes due
11186 // to the InitFromJSVal() call.
11188 RefPtr<Document> origDocument = GetDocument();
11189 if (!origDocument) {
11190 return NS_ERROR_DOM_SECURITY_ERR;
11192 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11194 scContainer = new nsStructuredCloneContainer();
11195 rv = scContainer->InitFromJSVal(aData, aCx);
11196 NS_ENSURE_SUCCESS(rv, rv);
11198 RefPtr<Document> newDocument = GetDocument();
11199 if (!newDocument) {
11200 return NS_ERROR_DOM_SECURITY_ERR;
11202 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11204 bool principalsEqual = false;
11205 origPrincipal->Equals(newPrincipal, &principalsEqual);
11206 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11209 // Check that the state object isn't too long.
11210 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11211 if (maxStateObjSize < 0) {
11212 maxStateObjSize = 0;
11215 uint64_t scSize;
11216 rv = scContainer->GetSerializedNBytes(&scSize);
11217 NS_ENSURE_SUCCESS(rv, rv);
11219 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11221 // Step B: Resolve aURL.
11222 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11223 // step 7.
11224 bool equalURIs = true;
11225 nsCOMPtr<nsIURI> currentURI;
11226 if (mCurrentURI) {
11227 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11228 } else {
11229 currentURI = mCurrentURI;
11231 nsCOMPtr<nsIURI> newURI;
11232 if (aURL.Length() == 0) {
11233 newURI = currentURI;
11234 } else {
11235 // 7.1: Resolve aURL relative to mURI
11237 nsIURI* docBaseURI = document->GetDocBaseURI();
11238 if (!docBaseURI) {
11239 return NS_ERROR_FAILURE;
11242 nsAutoCString spec;
11243 docBaseURI->GetSpec(spec);
11245 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11246 document->GetDocumentCharacterSet(), docBaseURI);
11248 // 7.2: If 2a fails, raise a SECURITY_ERR
11249 if (NS_FAILED(rv)) {
11250 return NS_ERROR_DOM_SECURITY_ERR;
11253 // 7.4 and 7.5: Same-origin check.
11254 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11255 // In addition to checking that the security manager says that
11256 // the new URI has the same origin as our current URI, we also
11257 // check that the two URIs have the same userpass. (The
11258 // security manager says that |http://foo.com| and
11259 // |http://me@foo.com| have the same origin.) currentURI
11260 // won't contain the password part of the userpass, so this
11261 // means that it's never valid to specify a password in a
11262 // pushState or replaceState URI.
11264 nsCOMPtr<nsIScriptSecurityManager> secMan =
11265 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11266 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11268 // It's very important that we check that newURI is of the same
11269 // origin as currentURI, not docBaseURI, because a page can
11270 // set docBaseURI arbitrarily to any domain.
11271 nsAutoCString currentUserPass, newUserPass;
11272 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11273 NS_ERROR_FAILURE);
11274 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11275 bool isPrivateWin =
11276 document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
11278 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11279 isPrivateWin)) ||
11280 !currentUserPass.Equals(newUserPass)) {
11281 return NS_ERROR_DOM_SECURITY_ERR;
11283 } else {
11284 // It's a file:// URI
11285 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11287 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11288 newURI, false, document->InnerWindowID()))) {
11289 return NS_ERROR_DOM_SECURITY_ERR;
11293 if (currentURI) {
11294 currentURI->Equals(newURI, &equalURIs);
11295 } else {
11296 equalURIs = false;
11299 } // end of same-origin check
11301 // Step 8: call "URL and history update steps"
11302 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11303 currentURI, equalURIs);
11304 NS_ENSURE_SUCCESS(rv, rv);
11306 return NS_OK;
11309 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11310 nsIStructuredCloneContainer* aData,
11311 const nsAString& aTitle, bool aReplace,
11312 nsIURI* aCurrentURI, bool aEqualURIs) {
11313 // Implements
11314 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11316 // If we have a pending title change, handle it before creating a new entry.
11317 aDocument->DoNotifyPossibleTitleChange();
11319 // Step 2, if aReplace is false: Create a new entry in the session
11320 // history. This will erase all SHEntries after the new entry and make this
11321 // entry the current one. This operation may modify mOSHE, which we need
11322 // later, so we keep a reference here.
11323 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11324 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11326 // If this push/replaceState changed the document's current URI and the new
11327 // URI differs from the old URI in more than the hash, or if the old
11328 // SHEntry's URI was modified in this way by a push/replaceState call
11329 // set URIWasModified to true for the current SHEntry (bug 669671).
11330 bool sameExceptHashes = true;
11331 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11332 bool uriWasModified;
11333 if (sameExceptHashes) {
11334 if (mozilla::SessionHistoryInParent()) {
11335 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11336 } else {
11337 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11339 } else {
11340 uriWasModified = true;
11343 mLoadType = LOAD_PUSHSTATE;
11345 nsCOMPtr<nsISHEntry> newSHEntry;
11346 if (!aReplace) {
11347 // Step 2.
11349 // Step 2.2, "Remove any tasks queued by the history traversal task
11350 // source that are associated with any Document objects in the
11351 // top-level browsing context's document family." This is very hard in
11352 // SessionHistoryInParent since we can't synchronously access the
11353 // pending navigations that are already sent to the parent. We can
11354 // abort any AsyncGo navigations that are waiting to be sent. If we
11355 // send a message to the parent, it would be processed after any
11356 // navigations previously sent. So long as we consider the "history
11357 // traversal task source" to be the list in this process we match the
11358 // spec. If we move the entire list to the parent, we can handle the
11359 // aborting of loads there, but we don't have a way to synchronously
11360 // remove entries as we do here for non-SHIP.
11361 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11362 if (shistory) {
11363 shistory->RemovePendingHistoryNavigations();
11366 nsPoint scrollPos = GetCurScrollPos();
11368 bool scrollRestorationIsManual;
11369 if (mozilla::SessionHistoryInParent()) {
11370 // FIXME Need to save the current scroll position on mActiveEntry.
11371 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11372 } else {
11373 // Save the current scroll position (bug 590573). Step 2.3.
11374 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11376 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11379 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11381 if (mozilla::SessionHistoryInParent()) {
11382 MOZ_LOG(gSHLog, LogLevel::Debug,
11383 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11384 nsString title(mActiveEntry->GetTitle());
11385 UpdateActiveEntry(false,
11386 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11387 /* aOriginalURI = */ nullptr,
11388 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11389 csp, title, scrollRestorationIsManual, aData,
11390 uriWasModified);
11391 } else {
11392 // Since we're not changing which page we have loaded, pass
11393 // true for aCloneChildren.
11394 nsresult rv = AddToSessionHistory(
11395 aNewURI, nullptr,
11396 aDocument->NodePrincipal(), // triggeringPrincipal
11397 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11398 NS_ENSURE_SUCCESS(rv, rv);
11400 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11402 // Session history entries created by pushState inherit scroll restoration
11403 // mode from the current entry.
11404 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11406 nsString title;
11407 mOSHE->GetTitle(title);
11409 // Set the new SHEntry's title (bug 655273).
11410 newSHEntry->SetTitle(title);
11412 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11413 // two entries correspond to the same document.
11414 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11415 NS_ERROR_FAILURE);
11417 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11418 // we'll just set mOSHE here.
11419 mOSHE = newSHEntry;
11421 } else if (mozilla::SessionHistoryInParent()) {
11422 MOZ_LOG(gSHLog, LogLevel::Debug,
11423 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11424 this, mActiveEntry.get()));
11425 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11426 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11427 // in our case. We could also set it to aNewURI, with the same result.
11428 // We don't use aTitle here, see bug 544535.
11429 nsString title;
11430 if (mActiveEntry) {
11431 title = mActiveEntry->GetTitle();
11433 UpdateActiveEntry(
11434 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11435 aDocument->NodePrincipal(), aDocument->GetCsp(), title,
11436 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11437 uriWasModified);
11438 } else {
11439 // Step 3.
11440 newSHEntry = mOSHE;
11442 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11443 // Since we're not changing which page we have loaded, pass
11444 // true for aCloneChildren.
11445 if (!newSHEntry) {
11446 nsresult rv = AddToSessionHistory(
11447 aNewURI, nullptr,
11448 aDocument->NodePrincipal(), // triggeringPrincipal
11449 nullptr, nullptr, aDocument->GetCsp(), true,
11450 getter_AddRefs(newSHEntry));
11451 NS_ENSURE_SUCCESS(rv, rv);
11452 mOSHE = newSHEntry;
11455 newSHEntry->SetURI(aNewURI);
11456 newSHEntry->SetOriginalURI(aNewURI);
11457 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11458 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11459 // in our case. We could also set it to aNewURI, with the same result.
11460 newSHEntry->SetResultPrincipalURI(nullptr);
11461 newSHEntry->SetLoadReplace(false);
11464 if (!mozilla::SessionHistoryInParent()) {
11465 // Step 2.4 and 3: Modify new/original session history entry and clear its
11466 // POST data, if there is any.
11467 newSHEntry->SetStateData(aData);
11468 newSHEntry->SetPostData(nullptr);
11470 newSHEntry->SetURIWasModified(uriWasModified);
11472 // Step E as described at the top of AddState: If aReplace is false,
11473 // indicating that we're doing a pushState rather than a replaceState,
11474 // notify bfcache that we've added a page to the history so it can evict
11475 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11476 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11477 // root session history if this call is coming from a document.open() in a
11478 // docshell subtree that disables session history.
11479 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11480 if (rootSH) {
11481 rootSH->LegacySHistory()->EvictContentViewersOrReplaceEntry(newSHEntry,
11482 aReplace);
11486 // Step 4: If the document's URI changed, update document's URI and update
11487 // global history.
11489 // We need to call FireOnLocationChange so that the browser's address bar
11490 // gets updated and the back button is enabled, but we only need to
11491 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11492 // since SetCurrentURI will call FireOnLocationChange for us.
11494 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11495 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11496 // notification is allowed only when we know docshell is not loading a new
11497 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11498 // FireOnLocationChange(...) breaks security UI.
11500 // If the docshell is shutting down, don't update the document URI, as we
11501 // can't load into a docshell that is being destroyed.
11502 if (!aEqualURIs && !mIsBeingDestroyed) {
11503 aDocument->SetDocumentURI(aNewURI);
11504 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11505 /* aIsInitialAboutBlank */ false,
11506 GetSameDocumentNavigationFlags(aNewURI));
11508 AddURIVisit(aNewURI, aCurrentURI, 0);
11510 // AddURIVisit doesn't set the title for the new URI in global history,
11511 // so do that here.
11512 UpdateGlobalHistoryTitle(aNewURI);
11514 // Inform the favicon service that our old favicon applies to this new
11515 // URI.
11516 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11517 } else {
11518 FireDummyOnLocationChange();
11520 aDocument->SetStateObject(aData);
11522 return NS_OK;
11525 NS_IMETHODIMP
11526 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11527 if (mozilla::SessionHistoryInParent()) {
11528 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11529 return NS_OK;
11532 *aIsManual = false;
11533 if (mOSHE) {
11534 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11537 return NS_OK;
11540 NS_IMETHODIMP
11541 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11542 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11544 return NS_OK;
11547 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11548 nsISHEntry* aSHEntry, bool aIsManual) {
11549 if (aSHEntry) {
11550 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11553 if (mActiveEntry && mBrowsingContext) {
11554 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11555 if (XRE_IsParentProcess()) {
11556 SessionHistoryEntry* entry =
11557 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11558 if (entry) {
11559 entry->SetScrollRestorationIsManual(aIsManual);
11561 } else {
11562 mozilla::Unused << ContentChild::GetSingleton()
11563 ->SendSessionHistoryEntryScrollRestorationIsManual(
11564 mBrowsingContext, aIsManual);
11569 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11570 uint32_t aCacheKey) {
11571 if (aSHEntry) {
11572 aSHEntry->SetCacheKey(aCacheKey);
11575 if (mActiveEntry && mBrowsingContext) {
11576 mActiveEntry->SetCacheKey(aCacheKey);
11577 if (XRE_IsParentProcess()) {
11578 SessionHistoryEntry* entry =
11579 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11580 if (entry) {
11581 entry->SetCacheKey(aCacheKey);
11583 } else {
11584 mozilla::Unused
11585 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11586 mBrowsingContext, aCacheKey);
11591 /* static */
11592 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11593 // I believe none of the about: urls should go in the history. But then
11594 // that could just be me... If the intent is only deny about:blank then we
11595 // should just do a spec compare, rather than two gets of the scheme and
11596 // then the path. -Gagan
11597 nsresult rv;
11598 nsAutoCString buf;
11600 rv = aURI->GetScheme(buf);
11601 if (NS_FAILED(rv)) {
11602 return false;
11605 if (buf.EqualsLiteral("about")) {
11606 rv = aURI->GetPathQueryRef(buf);
11607 if (NS_FAILED(rv)) {
11608 return false;
11611 if (buf.EqualsLiteral("blank")) {
11612 return false;
11614 // We only want to add about:newtab if it's not privileged:
11615 if (buf.EqualsLiteral("newtab")) {
11616 NS_ENSURE_TRUE(aChannel, false);
11617 nsCOMPtr<nsIPrincipal> resultPrincipal;
11618 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11619 aChannel, getter_AddRefs(resultPrincipal));
11620 NS_ENSURE_SUCCESS(rv, false);
11621 return !resultPrincipal->IsSystemPrincipal();
11625 return true;
11628 nsresult nsDocShell::AddToSessionHistory(
11629 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11630 nsIPrincipal* aPrincipalToInherit,
11631 nsIPrincipal* aPartitionedPrincipalToInherit,
11632 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11633 nsISHEntry** aNewEntry) {
11634 MOZ_ASSERT(aURI, "uri is null");
11635 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11636 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11638 #if defined(DEBUG)
11639 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11640 nsAutoCString chanName;
11641 if (aChannel) {
11642 aChannel->GetName(chanName);
11643 } else {
11644 chanName.AssignLiteral("<no channel>");
11647 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11648 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11649 aURI->GetSpecOrDefault().get(), chanName.get()));
11651 #endif
11653 nsresult rv = NS_OK;
11654 nsCOMPtr<nsISHEntry> entry;
11657 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11658 * the existing SH entry in the page and replace the url and
11659 * other vitalities.
11661 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11662 !mBrowsingContext->IsTop()) {
11663 // This is a subframe
11664 entry = mOSHE;
11665 if (entry) {
11666 entry->ClearEntry();
11670 // Create a new entry if necessary.
11671 if (!entry) {
11672 entry = new nsSHEntry();
11675 // Get the post data & referrer
11676 nsCOMPtr<nsIInputStream> inputStream;
11677 nsCOMPtr<nsIURI> originalURI;
11678 nsCOMPtr<nsIURI> resultPrincipalURI;
11679 bool loadReplace = false;
11680 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11681 uint32_t cacheKey = 0;
11682 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11683 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11684 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11685 aPartitionedPrincipalToInherit;
11686 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11687 bool expired = false; // by default the page is not expired
11688 bool discardLayoutState = false;
11689 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11690 bool userActivation = false;
11692 if (aChannel) {
11693 cacheChannel = do_QueryInterface(aChannel);
11695 /* If there is a caching channel, get the Cache Key and store it
11696 * in SH.
11698 if (cacheChannel) {
11699 cacheChannel->GetCacheKey(&cacheKey);
11701 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11703 // Check if the httpChannel is hiding under a multipartChannel
11704 if (!httpChannel) {
11705 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11707 if (httpChannel) {
11708 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11709 if (uploadChannel) {
11710 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11712 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11713 uint32_t loadFlags;
11714 aChannel->GetLoadFlags(&loadFlags);
11715 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11716 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11717 MOZ_ASSERT(NS_SUCCEEDED(rv));
11719 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11722 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11723 if (!triggeringPrincipal) {
11724 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11726 if (!csp) {
11727 csp = loadInfo->GetCspToInherit();
11730 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11732 userActivation = loadInfo->GetHasValidUserGestureActivation();
11734 // For now keep storing just the principal in the SHEntry.
11735 if (!principalToInherit) {
11736 if (loadInfo->GetLoadingSandboxed()) {
11737 if (loadInfo->GetLoadingPrincipal()) {
11738 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11739 loadInfo->GetLoadingPrincipal());
11740 } else {
11741 // get the OriginAttributes
11742 OriginAttributes attrs;
11743 loadInfo->GetOriginAttributes(&attrs);
11744 principalToInherit = NullPrincipal::Create(attrs);
11746 } else {
11747 principalToInherit = loadInfo->PrincipalToInherit();
11751 if (!partitionedPrincipalToInherit) {
11752 // XXXehsan is it correct to fall back to the principal to inherit in all
11753 // cases? For example, what about the cases where we are using the load
11754 // info's principal to inherit? Do we need to add a similar concept to
11755 // load info for partitioned principal?
11756 partitionedPrincipalToInherit = principalToInherit;
11760 nsAutoString srcdoc;
11761 bool srcdocEntry = false;
11762 nsCOMPtr<nsIURI> baseURI;
11764 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11765 if (inStrmChan) {
11766 bool isSrcdocChannel;
11767 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11768 if (isSrcdocChannel) {
11769 inStrmChan->GetSrcdocData(srcdoc);
11770 srcdocEntry = true;
11771 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11772 } else {
11773 srcdoc.SetIsVoid(true);
11776 /* If cache got a 'no-store', ask SH not to store
11777 * HistoryLayoutState. By default, SH will set this
11778 * flag to true and save HistoryLayoutState.
11780 bool saveLayoutState = !discardLayoutState;
11782 if (cacheChannel) {
11783 // Check if the page has expired from cache
11784 uint32_t expTime = 0;
11785 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11786 uint32_t now = PRTimeToSeconds(PR_Now());
11787 if (expTime <= now) {
11788 expired = true;
11792 // Title is set in nsDocShell::SetTitle()
11793 entry->Create(aURI, // uri
11794 u""_ns, // Title
11795 inputStream, // Post data stream
11796 cacheKey, // CacheKey
11797 mContentTypeHint, // Content-type
11798 triggeringPrincipal, // Channel or provided principal
11799 principalToInherit, partitionedPrincipalToInherit, csp,
11800 HistoryID(), GetCreatedDynamically(), originalURI,
11801 resultPrincipalURI, loadReplace, referrerInfo, srcdoc,
11802 srcdocEntry, baseURI, saveLayoutState, expired, userActivation);
11804 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11805 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11806 Maybe<int32_t> previousEntryIndex;
11807 Maybe<int32_t> loadedEntryIndex;
11808 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11809 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11810 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11812 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11813 if (previousEntryIndex.isSome()) {
11814 mPreviousEntryIndex = previousEntryIndex.value();
11816 if (loadedEntryIndex.isSome()) {
11817 mLoadedEntryIndex = loadedEntryIndex.value();
11820 // aCloneChildren implies that we are retaining the same document, thus we
11821 // need to signal to the top WC that the new SHEntry may receive a fresh
11822 // user interaction flag.
11823 if (aCloneChildren) {
11824 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11825 if (topWc && !topWc->IsDiscarded()) {
11826 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11829 } else {
11830 // This is a subframe, make sure that this new SHEntry will be
11831 // marked with user interaction.
11832 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11833 if (topWc && !topWc->IsDiscarded()) {
11834 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11836 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11837 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11838 aCloneChildren);
11842 // Return the new SH entry...
11843 if (aNewEntry) {
11844 *aNewEntry = nullptr;
11845 if (NS_SUCCEEDED(rv)) {
11846 entry.forget(aNewEntry);
11850 return rv;
11853 void nsDocShell::UpdateActiveEntry(
11854 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11855 nsIURI* aOriginalURI, nsIPrincipal* aTriggeringPrincipal,
11856 nsIContentSecurityPolicy* aCsp, const nsAString& aTitle,
11857 bool aScrollRestorationIsManual, nsIStructuredCloneContainer* aData,
11858 bool aURIWasModified) {
11859 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11860 MOZ_ASSERT(aURI, "uri is null");
11861 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
11862 "This code only deals with pushState");
11863 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
11865 MOZ_LOG(gSHLog, LogLevel::Debug,
11866 ("Creating an active entry on nsDocShell %p to %s", this,
11867 aURI->GetSpecOrDefault().get()));
11869 // Even if we're replacing an existing entry we create new a
11870 // SessionHistoryInfo. In the parent process we'll keep the existing
11871 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11872 // entry keeps identity but its data is replaced.
11873 bool replace = aReplace && mActiveEntry;
11875 if (!replace) {
11876 CollectWireframe();
11879 if (mActiveEntry) {
11880 // Link this entry to the previous active entry.
11881 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
11882 } else {
11883 mActiveEntry = MakeUnique<SessionHistoryInfo>(
11884 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
11886 mActiveEntry->SetOriginalURI(aOriginalURI);
11887 mActiveEntry->SetTitle(aTitle);
11888 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
11889 mActiveEntry->SetURIWasModified(aURIWasModified);
11890 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
11892 if (replace) {
11893 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
11894 } else {
11895 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
11896 // FIXME We should probably just compute mChildOffset in the parent
11897 // instead of passing it over IPC here.
11898 mBrowsingContext->SetActiveSessionHistoryEntry(
11899 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
11900 /* aCacheKey = */ 0);
11901 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
11905 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
11906 bool aUserActivation) {
11907 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11909 nsresult rv;
11910 RefPtr<nsDocShellLoadState> loadState;
11911 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
11912 NS_ENSURE_SUCCESS(rv, rv);
11914 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
11915 // that's the only thing holding a ref to aEntry that will cause aEntry to
11916 // die while we're loading it. So hold a strong ref to aEntry here, just
11917 // in case.
11918 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11920 loadState->SetHasValidUserGestureActivation(
11921 loadState->HasValidUserGestureActivation() || aUserActivation);
11923 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
11926 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
11927 uint32_t aLoadType,
11928 bool aUserActivation) {
11929 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
11930 loadState->SetHasValidUserGestureActivation(
11931 loadState->HasValidUserGestureActivation() || aUserActivation);
11933 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
11936 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
11937 uint32_t aLoadType,
11938 bool aLoadingCurrentEntry) {
11939 if (!IsNavigationAllowed()) {
11940 return NS_OK;
11943 // We are setting load type afterwards so we don't have to
11944 // send it in an IPC message
11945 aLoadState->SetLoadType(aLoadType);
11947 nsresult rv;
11948 if (SchemeIsJavascript(aLoadState->URI())) {
11949 // We're loading a URL that will execute script from inside asyncOpen.
11950 // Replace the current document with about:blank now to prevent
11951 // anything from the current document from leaking into any JavaScript
11952 // code in the URL.
11953 // Don't cache the presentation if we're going to just reload the
11954 // current entry. Caching would lead to trying to save the different
11955 // content viewers in the same nsISHEntry object.
11956 rv = CreateAboutBlankContentViewer(
11957 aLoadState->PrincipalToInherit(),
11958 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
11959 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
11961 if (NS_FAILED(rv)) {
11962 // The creation of the intermittent about:blank content
11963 // viewer failed for some reason (potentially because the
11964 // user prevented it). Interrupt the history load.
11965 return NS_OK;
11968 if (!aLoadState->TriggeringPrincipal()) {
11969 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
11970 // URIs will pick it up from the about:blank page we just loaded,
11971 // and we don't really want even that in this case.
11972 nsCOMPtr<nsIPrincipal> principal =
11973 NullPrincipal::CreateWithInheritedAttributes(this);
11974 aLoadState->SetTriggeringPrincipal(principal);
11978 /* If there is a valid postdata *and* the user pressed
11979 * reload or shift-reload, take user's permission before we
11980 * repost the data to the server.
11982 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
11983 bool repost;
11984 rv = ConfirmRepost(&repost);
11985 if (NS_FAILED(rv)) {
11986 return rv;
11989 // If the user pressed cancel in the dialog, return. We're done here.
11990 if (!repost) {
11991 return NS_BINDING_ABORTED;
11995 // If there is no valid triggeringPrincipal, we deny the load
11996 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
11997 "need a valid triggeringPrincipal to load from history");
11998 if (!aLoadState->TriggeringPrincipal()) {
11999 return NS_ERROR_FAILURE;
12002 return InternalLoad(aLoadState); // No nsIRequest
12005 NS_IMETHODIMP
12006 nsDocShell::PersistLayoutHistoryState() {
12007 nsresult rv = NS_OK;
12009 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12010 bool scrollRestorationIsManual;
12011 if (mozilla::SessionHistoryInParent()) {
12012 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12013 } else {
12014 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12016 nsCOMPtr<nsILayoutHistoryState> layoutState;
12017 if (RefPtr<PresShell> presShell = GetPresShell()) {
12018 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12019 } else if (scrollRestorationIsManual) {
12020 // Even if we don't have layout anymore, we may want to reset the
12021 // current scroll state in layout history.
12022 GetLayoutHistoryState(getter_AddRefs(layoutState));
12025 if (scrollRestorationIsManual && layoutState) {
12026 layoutState->ResetScrollState();
12029 CollectWireframe();
12032 return rv;
12035 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12036 nsISHEntry* aNewEntry) {
12037 if (aOldEntry == mOSHE) {
12038 mOSHE = aNewEntry;
12041 if (aOldEntry == mLSHE) {
12042 mLSHE = aNewEntry;
12046 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12047 const Maybe<nsISHEntry*>& aOSHE) {
12048 // We want to hold on to the reference in mLSHE before we update it.
12049 // Otherwise, SetHistoryEntry could release the last reference to
12050 // the entry while aOSHE is pointing to it.
12051 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12052 if (aLSHE.isSome()) {
12053 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12054 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12056 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12057 if (aOSHE.isSome()) {
12058 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12059 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12063 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12064 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12065 // We need to sync up the docshell and session history trees for
12066 // subframe navigation. If the load was in a subframe, we forward up to
12067 // the root docshell, which will then recursively sync up all docshells
12068 // to their corresponding entries in the new session history tree.
12069 // If we don't do this, then we can cache a content viewer on the wrong
12070 // cloned entry, and subsequently restore it at the wrong time.
12071 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12072 if (topBC->IsDiscarded()) {
12073 topBC = nullptr;
12075 RefPtr<BrowsingContext> currBC =
12076 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12077 if (topBC && *aPtr) {
12078 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12080 nsCOMPtr<nsISHEntry> entry(aEntry);
12081 entry.swap(*aPtr);
12082 return entry.forget();
12085 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12086 RefPtr<ChildSHistory> childSHistory =
12087 mBrowsingContext->Top()->GetChildSessionHistory();
12088 return childSHistory.forget();
12091 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12092 nsIHttpChannel** aReturn) {
12093 NS_ENSURE_ARG_POINTER(aReturn);
12094 if (!aChannel) {
12095 return NS_ERROR_FAILURE;
12098 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12099 if (multiPartChannel) {
12100 nsCOMPtr<nsIChannel> baseChannel;
12101 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12102 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12103 *aReturn = httpChannel;
12104 NS_IF_ADDREF(*aReturn);
12106 return NS_OK;
12109 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12110 // By default layout State will be saved.
12111 if (!aChannel) {
12112 return false;
12115 // figure out if SH should be saving layout state
12116 bool noStore = false;
12117 Unused << aChannel->IsNoStoreResponse(&noStore);
12118 return noStore;
12121 NS_IMETHODIMP
12122 nsDocShell::GetEditor(nsIEditor** aEditor) {
12123 NS_ENSURE_ARG_POINTER(aEditor);
12124 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12125 htmlEditor.forget(aEditor);
12126 return NS_OK;
12129 NS_IMETHODIMP
12130 nsDocShell::SetEditor(nsIEditor* aEditor) {
12131 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12132 // If TextEditor comes, throw an error.
12133 if (aEditor && !htmlEditor) {
12134 return NS_ERROR_INVALID_ARG;
12136 return SetHTMLEditorInternal(htmlEditor);
12139 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12140 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12143 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12144 if (!aHTMLEditor && !mEditorData) {
12145 return NS_OK;
12148 nsresult rv = EnsureEditorData();
12149 if (NS_FAILED(rv)) {
12150 return rv;
12153 return mEditorData->SetHTMLEditor(aHTMLEditor);
12156 NS_IMETHODIMP
12157 nsDocShell::GetEditable(bool* aEditable) {
12158 NS_ENSURE_ARG_POINTER(aEditable);
12159 *aEditable = mEditorData && mEditorData->GetEditable();
12160 return NS_OK;
12163 NS_IMETHODIMP
12164 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12165 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12167 if (mEditorData) {
12168 *aHasEditingSession = !!mEditorData->GetEditingSession();
12169 } else {
12170 *aHasEditingSession = false;
12173 return NS_OK;
12176 NS_IMETHODIMP
12177 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12178 nsresult rv = EnsureEditorData();
12179 if (NS_FAILED(rv)) {
12180 return rv;
12183 return mEditorData->MakeEditable(aInWaitForUriLoad);
12186 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12187 bool needToAddURIVisit = true;
12188 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12189 if (props) {
12190 mozilla::Unused << props->GetPropertyAsBool(
12191 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12194 return needToAddURIVisit;
12197 /* static */ void nsDocShell::ExtractLastVisit(
12198 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12199 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12200 if (!props) {
12201 return;
12204 nsresult rv;
12205 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12206 if (NS_SUCCEEDED(rv)) {
12207 uri.forget(aURI);
12209 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12210 aChannelRedirectFlags);
12212 NS_WARNING_ASSERTION(
12213 NS_SUCCEEDED(rv),
12214 "Could not fetch previous flags, URI will be treated like referrer");
12216 } else {
12217 // There is no last visit for this channel, so this must be the first
12218 // link. Link the visit to the referrer of this request, if any.
12219 // Treat referrer as null if there is an error getting it.
12220 NS_GetReferrerFromChannel(aChannel, aURI);
12224 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12225 uint32_t aChannelRedirectFlags) {
12226 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12227 if (!props || !aURI) {
12228 return;
12231 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12232 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12233 aChannelRedirectFlags);
12236 /* static */ void nsDocShell::InternalAddURIVisit(
12237 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12238 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12239 nsIWidget* aWidget, uint32_t aLoadType) {
12240 MOZ_ASSERT(aURI, "Visited URI is null!");
12241 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12242 "Do not add error or bypass pages to global history");
12244 bool usePrivateBrowsing = false;
12245 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12247 // Only content-type docshells save URI visits. Also don't do
12248 // anything here if we're not supposed to use global history.
12249 if (!aBrowsingContext->IsContent() ||
12250 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12251 return;
12254 nsCOMPtr<IHistory> history = components::History::Service();
12256 if (history) {
12257 uint32_t visitURIFlags = 0;
12259 if (aBrowsingContext->IsTop()) {
12260 visitURIFlags |= IHistory::TOP_LEVEL;
12263 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12264 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12265 } else if (aChannelRedirectFlags &
12266 nsIChannelEventSink::REDIRECT_PERMANENT) {
12267 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12268 } else {
12269 MOZ_ASSERT(!aChannelRedirectFlags,
12270 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12271 "if any flags in aChannelRedirectFlags is set.");
12274 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12275 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12276 if (aResponseStatus == 301 || aResponseStatus == 308) {
12277 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12280 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12281 // simple retry attempt by the user is unlikely to solve them.
12282 // 408 is special cased, since may actually indicate a temporary
12283 // connection problem.
12284 else if (aResponseStatus != 408 &&
12285 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12286 aResponseStatus == 505)) {
12287 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12290 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12291 visitURIFlags);
12295 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12296 uint32_t aChannelRedirectFlags,
12297 uint32_t aResponseStatus) {
12298 nsPIDOMWindowOuter* outer = GetWindow();
12299 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12301 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12302 aResponseStatus, mBrowsingContext, widget, mLoadType);
12305 //*****************************************************************************
12306 // nsDocShell: Helper Routines
12307 //*****************************************************************************
12309 NS_IMETHODIMP
12310 nsDocShell::SetLoadType(uint32_t aLoadType) {
12311 mLoadType = aLoadType;
12312 return NS_OK;
12315 NS_IMETHODIMP
12316 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12317 *aLoadType = mLoadType;
12318 return NS_OK;
12321 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12322 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12323 *aRepost = true;
12324 return NS_OK;
12327 nsCOMPtr<nsIPromptCollection> prompter =
12328 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12329 if (!prompter) {
12330 return NS_ERROR_NOT_AVAILABLE;
12333 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12336 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12337 nsIStringBundle** aStringBundle) {
12338 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12339 NS_ERROR_FAILURE);
12341 nsCOMPtr<nsIStringBundleService> stringBundleService =
12342 mozilla::components::StringBundle::Service();
12343 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12345 NS_ENSURE_SUCCESS(
12346 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12347 NS_ERROR_FAILURE);
12349 return NS_OK;
12352 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12353 PresShell* presShell = GetPresShell();
12354 NS_ENSURE_TRUE(presShell, nullptr);
12356 return presShell->GetRootScrollFrameAsScrollable();
12359 nsresult nsDocShell::EnsureScriptEnvironment() {
12360 if (mScriptGlobal) {
12361 return NS_OK;
12364 if (mIsBeingDestroyed) {
12365 return NS_ERROR_NOT_AVAILABLE;
12368 #ifdef DEBUG
12369 NS_ASSERTION(!mInEnsureScriptEnv,
12370 "Infinite loop! Calling EnsureScriptEnvironment() from "
12371 "within EnsureScriptEnvironment()!");
12373 // Yeah, this isn't re-entrant safe, but that's ok since if we
12374 // re-enter this method, we'll infinitely loop...
12375 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12376 mInEnsureScriptEnv = true;
12377 #endif
12379 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12380 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12382 uint32_t chromeFlags;
12383 browserChrome->GetChromeFlags(&chromeFlags);
12385 // If our window is modal and we're not opened as chrome, make
12386 // this window a modal content window.
12387 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12388 MOZ_ASSERT(mScriptGlobal);
12390 // Ensure the script object is set up to run script.
12391 return mScriptGlobal->EnsureScriptEnvironment();
12394 nsresult nsDocShell::EnsureEditorData() {
12395 MOZ_ASSERT(!mIsBeingDestroyed);
12397 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12398 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12399 // We shouldn't recreate the editor data if it already exists, or
12400 // we're shutting down, or we already have a detached editor data
12401 // stored in the session history. We should only have one editordata
12402 // per docshell.
12403 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12406 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12409 nsresult nsDocShell::EnsureFind() {
12410 if (!mFind) {
12411 mFind = new nsWebBrowserFind();
12414 // we promise that the nsIWebBrowserFind that we return has been set
12415 // up to point to the focused, or content window, so we have to
12416 // set that up each time.
12418 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12419 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12421 // default to our window
12422 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12423 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12424 nsFocusManager::GetFocusedDescendant(ourWindow,
12425 nsFocusManager::eIncludeAllDescendants,
12426 getter_AddRefs(windowToSearch));
12428 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12429 if (!findInFrames) {
12430 return NS_ERROR_NO_INTERFACE;
12433 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12434 if (NS_FAILED(rv)) {
12435 return rv;
12437 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12438 if (NS_FAILED(rv)) {
12439 return rv;
12442 return NS_OK;
12445 NS_IMETHODIMP
12446 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12447 NS_ENSURE_ARG(aDoomed);
12448 *aDoomed = mIsBeingDestroyed;
12449 return NS_OK;
12452 NS_IMETHODIMP
12453 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12454 NS_ENSURE_ARG(aResult);
12455 *aResult = mIsExecutingOnLoadHandler;
12456 return NS_OK;
12459 NS_IMETHODIMP
12460 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12461 nsCOMPtr<nsILayoutHistoryState> state;
12462 if (mozilla::SessionHistoryInParent()) {
12463 if (mActiveEntry) {
12464 state = mActiveEntry->GetLayoutHistoryState();
12466 } else {
12467 if (mOSHE) {
12468 state = mOSHE->GetLayoutHistoryState();
12471 state.forget(aLayoutHistoryState);
12472 return NS_OK;
12475 NS_IMETHODIMP
12476 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12477 if (mOSHE) {
12478 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12480 if (mActiveEntry) {
12481 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12483 return NS_OK;
12486 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12487 nsIInterfaceRequestor* aRequestor) {
12488 if (aRequestor) {
12489 mWeakPtr = do_GetWeakReference(aRequestor);
12493 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12494 mWeakPtr = nullptr;
12497 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12499 NS_IMETHODIMP
12500 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12501 void** aSink) {
12502 NS_ENSURE_ARG_POINTER(aSink);
12503 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12504 if (ifReq) {
12505 return ifReq->GetInterface(aIID, aSink);
12507 *aSink = nullptr;
12508 return NS_NOINTERFACE;
12511 //*****************************************************************************
12512 // nsDocShell::nsIAuthPromptProvider
12513 //*****************************************************************************
12515 NS_IMETHODIMP
12516 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12517 void** aResult) {
12518 // a priority prompt request will override a false mAllowAuth setting
12519 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12521 if (!mAllowAuth && !priorityPrompt) {
12522 return NS_ERROR_NOT_AVAILABLE;
12525 // we're either allowing auth, or it's a proxy request
12526 nsresult rv;
12527 nsCOMPtr<nsIPromptFactory> wwatch =
12528 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12529 NS_ENSURE_SUCCESS(rv, rv);
12531 rv = EnsureScriptEnvironment();
12532 NS_ENSURE_SUCCESS(rv, rv);
12534 // Get the an auth prompter for our window so that the parenting
12535 // of the dialogs works as it should when using tabs.
12537 return wwatch->GetPrompt(mScriptGlobal, aIID,
12538 reinterpret_cast<void**>(aResult));
12541 //*****************************************************************************
12542 // nsDocShell::nsILoadContext
12543 //*****************************************************************************
12545 NS_IMETHODIMP
12546 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12547 CallGetInterface(this, aWindow);
12548 return NS_OK;
12551 NS_IMETHODIMP
12552 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12553 return mBrowsingContext->GetTopWindow(aWindow);
12556 NS_IMETHODIMP
12557 nsDocShell::GetTopFrameElement(Element** aElement) {
12558 return mBrowsingContext->GetTopFrameElement(aElement);
12561 NS_IMETHODIMP
12562 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12563 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12566 NS_IMETHODIMP
12567 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12568 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12571 NS_IMETHODIMP
12572 nsDocShell::GetIsContent(bool* aIsContent) {
12573 *aIsContent = (mItemType == typeContent);
12574 return NS_OK;
12577 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12578 MOZ_ASSERT(aURI, "Must have a URI!");
12580 if (!mFiredUnloadEvent) {
12581 return true;
12584 if (!mLoadingURI) {
12585 return false;
12588 bool isPrivateWin = false;
12589 Document* doc = GetDocument();
12590 if (doc) {
12591 isPrivateWin =
12592 doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
12595 nsCOMPtr<nsIScriptSecurityManager> secMan =
12596 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12597 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12598 aURI, mLoadingURI, false, isPrivateWin));
12602 // Routines for selection and clipboard
12604 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12605 nsIController** aResult) {
12606 NS_ENSURE_ARG_POINTER(aResult);
12607 *aResult = nullptr;
12609 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12611 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12612 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12614 return root->GetControllerForCommand(aCommand, false /* for any window */,
12615 aResult);
12618 NS_IMETHODIMP
12619 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12620 NS_ENSURE_ARG_POINTER(aResult);
12621 *aResult = false;
12623 nsresult rv = NS_ERROR_FAILURE;
12625 nsCOMPtr<nsIController> controller;
12626 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12627 if (controller) {
12628 rv = controller->IsCommandEnabled(aCommand, aResult);
12631 return rv;
12634 NS_IMETHODIMP
12635 nsDocShell::DoCommand(const char* aCommand) {
12636 nsresult rv = NS_ERROR_FAILURE;
12638 nsCOMPtr<nsIController> controller;
12639 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12640 if (controller) {
12641 rv = controller->DoCommand(aCommand);
12644 return rv;
12647 NS_IMETHODIMP
12648 nsDocShell::DoCommandWithParams(const char* aCommand,
12649 nsICommandParams* aParams) {
12650 nsCOMPtr<nsIController> controller;
12651 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12652 if (NS_WARN_IF(NS_FAILED(rv))) {
12653 return rv;
12656 nsCOMPtr<nsICommandController> commandController =
12657 do_QueryInterface(controller, &rv);
12658 if (NS_WARN_IF(NS_FAILED(rv))) {
12659 return rv;
12662 return commandController->DoCommandWithParams(aCommand, aParams);
12665 nsresult nsDocShell::EnsureCommandHandler() {
12666 if (!mCommandManager) {
12667 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12668 mCommandManager = new nsCommandManager(domWindow);
12671 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12674 // link handling
12676 class OnLinkClickEvent : public Runnable {
12677 public:
12678 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12679 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12680 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12682 NS_IMETHOD Run() override {
12683 AutoPopupStatePusher popupStatePusher(mPopupState);
12685 // We need to set up an AutoJSAPI here for the following reason: When we
12686 // do OnLinkClickSync we'll eventually end up in
12687 // nsGlobalWindow::OpenInternal which only does popup blocking if
12688 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12689 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12690 // concerned.
12691 AutoJSAPI jsapi;
12692 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12693 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12694 mTriggeringPrincipal);
12696 return NS_OK;
12699 private:
12700 RefPtr<nsDocShell> mHandler;
12701 nsCOMPtr<nsIContent> mContent;
12702 RefPtr<nsDocShellLoadState> mLoadState;
12703 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12704 PopupBlocker::PopupControlState mPopupState;
12705 bool mNoOpenerImplied;
12706 bool mIsTrusted;
12709 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12710 nsDocShellLoadState* aLoadState,
12711 bool aNoOpenerImplied, bool aIsTrusted,
12712 nsIPrincipal* aTriggeringPrincipal)
12713 : mozilla::Runnable("OnLinkClickEvent"),
12714 mHandler(aHandler),
12715 mContent(aContent),
12716 mLoadState(aLoadState),
12717 mTriggeringPrincipal(aTriggeringPrincipal),
12718 mPopupState(PopupBlocker::GetPopupControlState()),
12719 mNoOpenerImplied(aNoOpenerImplied),
12720 mIsTrusted(aIsTrusted) {}
12722 nsresult nsDocShell::OnLinkClick(
12723 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12724 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12725 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12726 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12727 #ifndef ANDROID
12728 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12729 #endif
12730 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12732 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12733 return NS_OK;
12736 // On history navigation through Back/Forward buttons, don't execute
12737 // automatic JavaScript redirection such as |anchorElement.click()| or
12738 // |formElement.submit()|.
12740 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12741 // nsDocShell::OnLinkClickSync(...) instead.
12742 if (ShouldBlockLoadingForBackButton()) {
12743 return NS_OK;
12746 if (aContent->IsEditable()) {
12747 return NS_OK;
12750 bool noOpenerImplied = false;
12751 nsAutoString target(aTargetSpec);
12752 if (ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent)) {
12753 target = u"_blank";
12754 if (!aTargetSpec.Equals(target)) {
12755 noOpenerImplied = true;
12759 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12760 loadState->SetTarget(target);
12761 loadState->SetFileName(aFileName);
12762 loadState->SetPostDataStream(aPostDataStream);
12763 loadState->SetHeadersStream(aHeadersDataStream);
12764 loadState->SetFirstParty(true);
12765 loadState->SetTriggeringPrincipal(
12766 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12767 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12768 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12769 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12771 nsCOMPtr<nsIRunnable> ev =
12772 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12773 aIsTrusted, aTriggeringPrincipal);
12774 return Dispatch(TaskCategory::UI, ev.forget());
12777 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12778 nsIURI* aLinkURI,
12779 nsIContent* aContent) {
12780 // Don't modify non-default targets.
12781 if (!aOriginalTarget.IsEmpty()) {
12782 return false;
12785 // Only check targets that are in extension panels or app tabs.
12786 // (isAppTab will be false for app tab subframes).
12787 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12788 if (!mmGroup.EqualsLiteral("webext-browsers") && !mIsAppTab) {
12789 return false;
12792 // External links from within app tabs should always open in new tabs
12793 // instead of replacing the app tab's page (Bug 575561)
12794 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12795 // get either host, just return false to use the original target.
12796 nsAutoCString linkHost;
12797 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12798 return false;
12801 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12802 if (!docURI) {
12803 return false;
12806 nsAutoCString docHost;
12807 if (NS_FAILED(docURI->GetHost(docHost))) {
12808 return false;
12811 if (linkHost.Equals(docHost)) {
12812 return false;
12815 // Special case: ignore "www" prefix if it is part of host string
12816 return linkHost.Length() < docHost.Length()
12817 ? !docHost.Equals("www."_ns + linkHost)
12818 : !linkHost.Equals("www."_ns + docHost);
12821 static bool IsElementAnchorOrArea(nsIContent* aContent) {
12822 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
12823 // or XHTML namespace.
12824 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
12827 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12828 nsDocShellLoadState* aLoadState,
12829 bool aNoOpenerImplied,
12830 nsIPrincipal* aTriggeringPrincipal) {
12831 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12832 return NS_OK;
12835 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12836 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12837 // but |HTMLFormElement::SubmitSubmission(...)|.
12838 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
12839 ShouldBlockLoadingForBackButton()) {
12840 return NS_OK;
12843 if (aContent->IsEditable()) {
12844 return NS_OK;
12847 // if the triggeringPrincipal is not passed explicitly, then we
12848 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12849 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
12850 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
12853 // defer to an external protocol handler if necessary...
12854 nsCOMPtr<nsIExternalProtocolService> extProtService =
12855 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12856 if (extProtService) {
12857 nsAutoCString scheme;
12858 aLoadState->URI()->GetScheme(scheme);
12859 if (!scheme.IsEmpty()) {
12860 // if the URL scheme does not correspond to an exposed protocol, then
12861 // we need to hand this link click over to the external protocol
12862 // handler.
12863 bool isExposed;
12864 nsresult rv =
12865 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12866 if (NS_SUCCEEDED(rv) && !isExposed) {
12867 return extProtService->LoadURI(aLoadState->URI(), triggeringPrincipal,
12868 nullptr, mBrowsingContext,
12869 /* aTriggeredExternally */ false);
12874 uint32_t triggeringSandboxFlags = 0;
12875 if (mBrowsingContext) {
12876 triggeringSandboxFlags = mBrowsingContext->GetSandboxFlags();
12879 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12880 bool isElementAnchorOrArea = IsElementAnchorOrArea(aContent);
12881 bool triggeringPrincipalIsSystemPrincipal =
12882 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
12883 if (isElementAnchorOrArea) {
12884 MOZ_ASSERT(aContent->IsHTMLElement());
12885 nsAutoString relString;
12886 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel,
12887 relString);
12888 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
12889 relString);
12891 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
12892 bool explicitOpenerSet = false;
12894 // The opener behaviour follows a hierarchy, such that if a higher
12895 // priority behaviour is specified, it always takes priority. That
12896 // priority is currently: norefrerer > noopener > opener > default
12898 while (tok.hasMoreTokens()) {
12899 const nsAString& token = tok.nextToken();
12900 if (token.LowerCaseEqualsLiteral("noreferrer")) {
12901 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
12902 INTERNAL_LOAD_FLAGS_NO_OPENER;
12903 // noreferrer cannot be overwritten by a 'rel=opener'.
12904 explicitOpenerSet = true;
12905 break;
12908 if (token.LowerCaseEqualsLiteral("noopener")) {
12909 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12910 explicitOpenerSet = true;
12913 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12914 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
12915 explicitOpenerSet = true;
12919 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12920 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
12921 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12924 if (aNoOpenerImplied) {
12925 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12929 // Get the owner document of the link that was clicked, this will be
12930 // the document that the link is in, or the last document that the
12931 // link was in. From that document, we'll get the URI to use as the
12932 // referrer, since the current URI in this docshell may be a
12933 // new document that we're in the process of loading.
12934 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
12936 // Now check that the referrerDoc's inner window is the current inner
12937 // window for mScriptGlobal. If it's not, then we don't want to
12938 // follow this link.
12939 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
12940 NS_ENSURE_TRUE(referrerInner, NS_ERROR_UNEXPECTED);
12941 if (!mScriptGlobal ||
12942 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
12943 // We're no longer the current inner window
12944 return NS_OK;
12947 // referrer could be null here in some odd cases, but that's ok,
12948 // we'll just load the link w/o sending a referrer in those cases.
12950 // If this is an anchor element, grab its type property to use as a hint
12951 nsAutoString typeHint;
12952 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
12953 if (anchor) {
12954 anchor->GetType(typeHint);
12955 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
12956 nsAutoCString type, dummy;
12957 NS_ParseRequestContentType(utf8Hint, type, dummy);
12958 CopyUTF8toUTF16(type, typeHint);
12961 // Link click (or form submission) can be triggered inside an onload
12962 // handler, and we don't want to add history entry in this case.
12963 bool inOnLoadHandler = false;
12964 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
12965 uint32_t loadType = inOnLoadHandler ? LOAD_NORMAL_REPLACE : LOAD_LINK;
12967 nsCOMPtr<nsIReferrerInfo> referrerInfo =
12968 isElementAnchorOrArea ? new ReferrerInfo(*aContent->AsElement())
12969 : new ReferrerInfo(*referrerDoc);
12970 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
12972 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
12973 aLoadState->SetReferrerInfo(referrerInfo);
12974 aLoadState->SetInternalLoadFlags(flags);
12975 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
12976 aLoadState->SetLoadType(loadType);
12977 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
12978 aLoadState->SetHasValidUserGestureActivation(
12979 context && context->HasValidTransientUserGestureActivation());
12981 nsresult rv = InternalLoad(aLoadState);
12983 if (NS_SUCCEEDED(rv)) {
12984 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
12985 referrerInfo);
12988 return rv;
12991 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
12992 const nsAString& aTargetSpec) {
12993 if (aContent->IsEditable()) {
12994 return NS_OK;
12997 nsresult rv = NS_ERROR_FAILURE;
12999 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13000 if (!browserChrome) {
13001 return rv;
13004 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13005 nsAutoCString spec;
13006 rv = exposableURI->GetDisplaySpec(spec);
13007 NS_ENSURE_SUCCESS(rv, rv);
13009 NS_ConvertUTF8toUTF16 uStr(spec);
13011 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13012 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13014 rv = browserChrome->SetLinkStatus(uStr);
13015 return rv;
13018 nsresult nsDocShell::OnLeaveLink() {
13019 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13020 nsresult rv = NS_ERROR_FAILURE;
13022 if (browserChrome) {
13023 rv = browserChrome->SetLinkStatus(u""_ns);
13025 return rv;
13028 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13029 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13030 UserActivation::IsHandlingUserInput() ||
13031 !Preferences::GetBool("accessibility.blockjsredirection")) {
13032 return false;
13035 bool canGoForward = false;
13036 GetCanGoForward(&canGoForward);
13037 return canGoForward;
13040 bool nsDocShell::PluginsAllowedInCurrentDoc() {
13041 if (!mContentViewer) {
13042 return false;
13045 Document* doc = mContentViewer->GetDocument();
13046 if (!doc) {
13047 return false;
13050 return doc->GetAllowPlugins();
13053 //----------------------------------------------------------------------
13054 // Web Shell Services API
13056 // This functions is only called when a new charset is detected in loading a
13057 // document.
13058 nsresult nsDocShell::CharsetChangeReloadDocument(
13059 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13060 // XXX hack. keep the aCharset and aSource wait to pick it up
13061 nsCOMPtr<nsIContentViewer> cv;
13062 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
13063 if (cv) {
13064 int32_t source;
13065 Unused << cv->GetReloadEncodingAndSource(&source);
13066 if (aSource > source) {
13067 cv->SetReloadEncodingAndSource(aEncoding, aSource);
13068 if (eCharsetReloadRequested != mCharsetReloadState) {
13069 mCharsetReloadState = eCharsetReloadRequested;
13070 switch (mLoadType) {
13071 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13072 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13073 LOAD_FLAGS_BYPASS_PROXY);
13074 case LOAD_RELOAD_BYPASS_CACHE:
13075 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13076 default:
13077 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13082 // return failure if this request is not accepted due to mCharsetReloadState
13083 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13086 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13087 if (eCharsetReloadRequested != mCharsetReloadState) {
13088 Stop(nsIWebNavigation::STOP_ALL);
13089 return NS_OK;
13091 // return failer if this request is not accepted due to mCharsetReloadState
13092 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13095 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13096 #if NS_PRINT_PREVIEW
13097 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mContentViewer);
13098 return viewer->ExitPrintPreview();
13099 #else
13100 return NS_OK;
13101 #endif
13104 /* [infallible] */
13105 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13106 bool* aIsTopLevelContentDocShell) {
13107 *aIsTopLevelContentDocShell = false;
13109 if (mItemType == typeContent) {
13110 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13113 return NS_OK;
13116 // Implements nsILoadContext.originAttributes
13117 NS_IMETHODIMP
13118 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13119 JS::MutableHandle<JS::Value> aVal) {
13120 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13123 // Implements nsIDocShell.GetOriginAttributes()
13124 NS_IMETHODIMP
13125 nsDocShell::GetOriginAttributes(JSContext* aCx,
13126 JS::MutableHandle<JS::Value> aVal) {
13127 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13130 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13131 nsIURI* aURI) {
13132 MOZ_ASSERT(aPrincipal);
13133 MOZ_ASSERT(aURI);
13135 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13136 return false;
13139 nsCOMPtr<nsIDocShellTreeItem> parent;
13140 GetInProcessSameTypeParent(getter_AddRefs(parent));
13141 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13142 nsPIDOMWindowInner* parentInner =
13143 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13145 StorageAccess storage =
13146 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13148 // If the partitioned service worker is enabled, service worker is allowed to
13149 // control the window if partition is enabled.
13150 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13151 RefPtr<Document> doc = parentInner->GetExtantDoc();
13153 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13154 return true;
13158 return storage == StorageAccess::eAllow;
13161 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13162 MOZ_ASSERT(!mIsBeingDestroyed);
13163 return mBrowsingContext->SetOriginAttributes(aAttrs);
13166 NS_IMETHODIMP
13167 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13168 RefPtr<nsDocShell> self = this;
13169 RefPtr<ChildProcessChannelListener> cpcl =
13170 ChildProcessChannelListener::GetSingleton();
13172 // Call into InternalLoad with the pending channel when it is received.
13173 cpcl->RegisterCallback(
13174 aIdentifier, [self, aHistoryIndex](
13175 nsDocShellLoadState* aLoadState,
13176 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13177 aStreamFilterEndpoints,
13178 nsDOMNavigationTiming* aTiming) {
13179 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13180 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13181 aLoadState->GetPendingRedirectedChannel()->Cancel(NS_BINDING_ABORTED);
13182 return NS_BINDING_ABORTED;
13185 self->mLoadType = aLoadState->LoadType();
13186 nsCOMPtr<nsIURI> previousURI;
13187 uint32_t previousFlags = 0;
13188 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13189 getter_AddRefs(previousURI), &previousFlags);
13190 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13191 previousURI, previousFlags);
13193 if (aTiming) {
13194 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13195 self->mBlankTiming = false;
13198 // If we're performing a history load, locate the correct history entry,
13199 // and set the relevant bits on our loadState.
13200 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13201 !mozilla::SessionHistoryInParent()) {
13202 nsCOMPtr<nsISHistory> legacySHistory =
13203 self->GetSessionHistory()->LegacySHistory();
13205 nsCOMPtr<nsISHEntry> entry;
13206 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13207 getter_AddRefs(entry));
13208 if (NS_SUCCEEDED(rv)) {
13209 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13210 aLoadState->SetLoadType(LOAD_HISTORY);
13211 aLoadState->SetSHEntry(entry);
13215 self->InternalLoad(aLoadState);
13217 if (aLoadState->GetOriginalURIString().isSome()) {
13218 // Save URI string in case it's needed later when
13219 // sending to search engine service in EndPageLoad()
13220 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13223 for (auto& endpoint : aStreamFilterEndpoints) {
13224 extensions::StreamFilterParent::Attach(
13225 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13228 // If the channel isn't pending, then it means that InternalLoad
13229 // never connected it, and we shouldn't try to continue. This
13230 // can happen even if InternalLoad returned NS_OK.
13231 bool pending = false;
13232 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13233 NS_ASSERTION(pending, "We should have connected the pending channel!");
13234 if (!pending) {
13235 return NS_BINDING_ABORTED;
13237 return NS_OK;
13239 return NS_OK;
13242 NS_IMETHODIMP
13243 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13244 JSContext* aCx) {
13245 OriginAttributes attrs;
13246 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13247 return NS_ERROR_INVALID_ARG;
13250 return SetOriginAttributes(attrs);
13253 NS_IMETHODIMP
13254 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13255 if (PresShell* presShell = GetPresShell()) {
13256 *aOut = presShell->AsyncPanZoomEnabled();
13257 return NS_OK;
13260 // If we don't have a presShell, fall back to the default platform value of
13261 // whether or not APZ is enabled.
13262 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13263 return NS_OK;
13266 bool nsDocShell::HasUnloadedParent() {
13267 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13268 wc = wc->GetParentWindowContext()) {
13269 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13270 wc->GetBrowsingContext()->IsDiscarded()) {
13271 // If a parent is OOP and the parent WindowContext is no
13272 // longer current, we can assume the parent was unloaded.
13273 return true;
13276 if (wc->GetBrowsingContext()->IsInProcess() &&
13277 (!wc->GetBrowsingContext()->GetDocShell() ||
13278 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13279 return true;
13282 return false;
13285 /* static */
13286 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13287 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13288 aLoadType & LOAD_CMD_HISTORY);
13291 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13292 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13293 return;
13296 // Global history is interested into sub-frame visits only for link-coloring
13297 // purposes, thus title updates are skipped for those.
13299 // Moreover, some iframe documents (such as the ones created via
13300 // document.open()) inherit the document uri of the caller, which would cause
13301 // us to override a previously set page title with one from the subframe.
13302 if (IsSubframe()) {
13303 return;
13306 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13307 history->SetURITitle(aURI, mTitle);
13311 bool nsDocShell::IsInvisible() { return mInvisible; }
13313 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13315 // The caller owns |aAsyncCause| here.
13316 void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
13317 const nsAString& aFunctionName,
13318 const nsAString& aFilename,
13319 const uint32_t aLineNumber,
13320 JS::Handle<JS::Value> aAsyncStack,
13321 const char* aAsyncCause) {
13322 // If first start, mark interval start.
13323 if (mJSRunToCompletionDepth == 0) {
13324 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
13325 if (timelines && timelines->HasConsumer(this)) {
13326 timelines->AddMarkerForDocShell(
13327 this, mozilla::MakeUnique<JavascriptTimelineMarker>(
13328 aReason, aFunctionName, aFilename, aLineNumber,
13329 MarkerTracingType::START, aAsyncStack, aAsyncCause));
13333 mJSRunToCompletionDepth++;
13336 void nsDocShell::NotifyJSRunToCompletionStop() {
13337 mJSRunToCompletionDepth--;
13339 // If last stop, mark interval end.
13340 if (mJSRunToCompletionDepth == 0) {
13341 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
13342 if (timelines && timelines->HasConsumer(this)) {
13343 timelines->AddMarkerForDocShell(this, "Javascript",
13344 MarkerTracingType::END);
13349 /* static */
13350 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13351 const nsString& aKeyword) {
13352 if (aProvider.IsEmpty()) {
13353 return;
13355 nsresult rv;
13356 nsCOMPtr<nsISupportsString> isupportsString =
13357 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13358 NS_ENSURE_SUCCESS_VOID(rv);
13360 rv = isupportsString->SetData(aProvider);
13361 NS_ENSURE_SUCCESS_VOID(rv);
13363 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13364 if (obsSvc) {
13365 // Note that "keyword-search" refers to a search via the url
13366 // bar, not a bookmarks keyword search.
13367 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13371 NS_IMETHODIMP
13372 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13373 bool* aShouldIntercept) {
13374 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13375 aShouldIntercept);
13378 NS_IMETHODIMP
13379 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13380 return mInterceptController->ChannelIntercepted(aChannel);
13383 bool nsDocShell::InFrameSwap() {
13384 RefPtr<nsDocShell> shell = this;
13385 do {
13386 if (shell->mInFrameSwap) {
13387 return true;
13389 shell = shell->GetInProcessParentDocshell();
13390 } while (shell);
13391 return false;
13394 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13395 return std::move(mInitialClientSource);
13398 NS_IMETHODIMP
13399 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13400 if (!NS_SUCCEEDED(EnsureEditorData())) {
13401 return NS_ERROR_FAILURE;
13404 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13405 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13408 NS_IMETHODIMP
13409 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13410 *aBrowserChild = GetBrowserChild().take();
13411 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13414 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13415 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13416 return tc.forget();
13419 nsCommandManager* nsDocShell::GetCommandManager() {
13420 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13421 return mCommandManager;
13424 NS_IMETHODIMP_(void)
13425 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13426 mBrowsingContext->GetOriginAttributes(aAttrs);
13429 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13430 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13431 return docShell->GetHTMLEditorInternal();
13434 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13435 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13436 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13439 #define MATRIX_LENGTH 20
13441 NS_IMETHODIMP
13442 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13443 if (aMatrix.Length() == MATRIX_LENGTH) {
13444 mColorMatrix.reset(new gfx::Matrix5x4());
13445 static_assert(
13446 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13447 "Size mismatch for our memcpy");
13448 memcpy(mColorMatrix->components, aMatrix.Elements(),
13449 sizeof(mColorMatrix->components));
13450 } else if (aMatrix.Length() == 0) {
13451 mColorMatrix.reset();
13452 } else {
13453 return NS_ERROR_INVALID_ARG;
13456 PresShell* presShell = GetPresShell();
13457 if (!presShell) {
13458 return NS_ERROR_FAILURE;
13461 nsIFrame* frame = presShell->GetRootFrame();
13462 if (!frame) {
13463 return NS_ERROR_FAILURE;
13466 frame->SchedulePaint();
13468 return NS_OK;
13471 NS_IMETHODIMP
13472 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13473 if (mColorMatrix) {
13474 aMatrix.SetLength(MATRIX_LENGTH);
13475 static_assert(
13476 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13477 "Size mismatch for our memcpy");
13478 memcpy(aMatrix.Elements(), mColorMatrix->components,
13479 MATRIX_LENGTH * sizeof(float));
13482 return NS_OK;
13485 #undef MATRIX_LENGTH
13487 NS_IMETHODIMP
13488 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13489 *aForceReload = IsForceReloading();
13490 return NS_OK;
13493 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13495 NS_IMETHODIMP
13496 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13497 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13498 return NS_OK;
13501 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13503 bool nsDocShell::GetIsAttemptingToNavigate() {
13504 // XXXbz the document.open spec says to abort even if there's just a
13505 // queued navigation task, sort of. It's not clear whether browsers
13506 // actually do that, and we didn't use to do it, so for now let's
13507 // not do that.
13508 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13509 if (mDocumentRequest) {
13510 // There's definitely a navigation in progress.
13511 return true;
13514 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13515 // until the script runs, which means they're not sending loadgroup
13516 // notifications and hence not getting set as mDocumentRequest. Look through
13517 // our loadgroup for document-level javascript: loads.
13518 if (!mLoadGroup) {
13519 return false;
13522 nsCOMPtr<nsISimpleEnumerator> requests;
13523 mLoadGroup->GetRequests(getter_AddRefs(requests));
13524 bool hasMore = false;
13525 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13526 nsCOMPtr<nsISupports> elem;
13527 requests->GetNext(getter_AddRefs(elem));
13528 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13529 if (!scriptChannel) {
13530 continue;
13533 if (scriptChannel->GetIsDocumentLoad()) {
13534 // This is a javascript: load that might lead to a new document,
13535 // hence a navigation.
13536 return true;
13540 return mCheckingSessionHistory;
13543 void nsDocShell::SetLoadingSessionHistoryInfo(
13544 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo) {
13545 // FIXME Would like to assert this, but can't yet.
13546 // MOZ_ASSERT(!mLoadingEntry);
13547 MOZ_LOG(gSHLog, LogLevel::Debug,
13548 ("Setting the loading entry on nsDocShell %p to %s", this,
13549 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13550 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13553 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13554 uint32_t aCacheKey) {
13555 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13557 MOZ_LOG(gSHLog, LogLevel::Debug,
13558 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13560 bool hadActiveEntry = !!mActiveEntry;
13561 mActiveEntry = nullptr;
13562 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13563 mActiveEntryIsLoadingFromSessionHistory =
13564 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13565 if (mLoadingEntry) {
13566 MOZ_LOG(gSHLog, LogLevel::Debug,
13567 ("Moving the loading entry to the active entry on nsDocShell %p "
13568 "to %s",
13569 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13570 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13571 mLoadingEntry.swap(loadingEntry);
13572 if (!mActiveEntryIsLoadingFromSessionHistory) {
13573 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13577 if (mActiveEntry) {
13578 if (aCacheKey != 0) {
13579 mActiveEntry->SetCacheKey(aCacheKey);
13581 MOZ_ASSERT(loadingEntry);
13582 uint32_t loadType =
13583 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13585 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13586 // does require a non-null uri if this is for a refresh load of the same
13587 // URI, but in that case mCurrentURI won't be null here.
13588 mBrowsingContext->SessionHistoryCommit(*loadingEntry, loadType, mCurrentURI,
13589 hadActiveEntry, aPersist, false,
13590 aExpired, aCacheKey);
13594 static bool IsFaviconLoad(nsIRequest* aRequest) {
13595 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13596 if (!channel) {
13597 return false;
13600 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13601 return li && li->InternalContentPolicyType() ==
13602 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13605 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13606 nsIRequest* aRequest) {
13607 // Ignore favicon loads, they don't need to block caching.
13608 if (IsFaviconLoad(aRequest)) {
13609 return;
13612 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13614 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13616 if (mBrowsingContext->GetCurrentWindowContext()) {
13617 // We have three states: no request, one request with an id and
13618 // eiher one request without an id or multiple requests. Nothing() is no
13619 // request, Some(non-zero) is one request with an id and Some(0) is one
13620 // request without an id or multiple requests.
13621 Maybe<uint64_t> singleChannelId;
13622 if (mRequestForBlockingFromBFCacheCount > 1) {
13623 singleChannelId = Some(0);
13624 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13625 nsCOMPtr<nsIIdentChannel> identChannel;
13626 if (aStartRequest) {
13627 identChannel = do_QueryInterface(aRequest);
13628 } else {
13629 // aChannel is the channel that's being removed, but we need to check if
13630 // the remaining channel in the loadgroup has an id.
13631 nsCOMPtr<nsISimpleEnumerator> requests;
13632 mLoadGroup->GetRequests(getter_AddRefs(requests));
13633 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13634 if (!IsFaviconLoad(request) &&
13635 !!(identChannel = do_QueryInterface(request))) {
13636 break;
13641 if (identChannel) {
13642 singleChannelId = Some(identChannel->ChannelId());
13643 } else {
13644 singleChannelId = Some(0);
13646 } else {
13647 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13648 singleChannelId = Nothing();
13651 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13652 nsAutoCString uri("[no uri]");
13653 if (mCurrentURI) {
13654 uri = mCurrentURI->GetSpecOrDefault();
13656 if (singleChannelId.isNothing()) {
13657 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13658 ("Loadgroup for %s doesn't have any requests relevant for "
13659 "blocking BFCache",
13660 uri.get()));
13661 } else if (singleChannelId.value() == 0) {
13662 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13663 ("Loadgroup for %s has multiple requests relevant for blocking "
13664 "BFCache",
13665 uri.get()));
13666 } else {
13667 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13668 ("Loadgroup for %s has one request with id %" PRIu64
13669 " relevant for blocking BFCache",
13670 uri.get(), singleChannelId.value()));
13674 if (mSingleChannelId != singleChannelId) {
13675 mSingleChannelId = singleChannelId;
13676 WindowGlobalChild* wgc =
13677 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13678 if (wgc) {
13679 wgc->SendSetSingleChannelId(singleChannelId);
13685 NS_IMETHODIMP
13686 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13687 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13688 nsAutoCString uri("[no uri]");
13689 if (mCurrentURI) {
13690 uri = mCurrentURI->GetSpecOrDefault();
13692 nsAutoCString name;
13693 aRequest->GetName(name);
13694 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13695 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13697 RecordSingleChannelId(true, aRequest);
13698 return nsDocLoader::OnStartRequest(aRequest);
13701 NS_IMETHODIMP
13702 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13703 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13704 nsAutoCString uri("[no uri]");
13705 if (mCurrentURI) {
13706 uri = mCurrentURI->GetSpecOrDefault();
13708 nsAutoCString name;
13709 aRequest->GetName(name);
13710 MOZ_LOG(
13711 gSHIPBFCacheLog, LogLevel::Verbose,
13712 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13714 RecordSingleChannelId(false, aRequest);
13715 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13718 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13719 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13721 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13722 nsCOMPtr<nsISimpleEnumerator> requests;
13723 mLoadGroup->GetRequests(getter_AddRefs(requests));
13724 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13725 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13726 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13727 static_cast<DocumentChannelChild*>(channel.get())
13728 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13731 mChannelToDisconnectOnPageHide = 0;