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/. */
6 
7 #include "nsDocShell.h"
8 
9 #include <algorithm>
10 
11 #ifdef XP_WIN
12 #  include <process.h>
13 #  define getpid _getpid
14 #else
15 #  include <unistd.h>  // for getpid()
16 #endif
17 
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/Components.h"
24 #include "mozilla/DebugOnly.h"
25 #include "mozilla/Encoding.h"
26 #include "mozilla/EventStateManager.h"
27 #include "mozilla/HTMLEditor.h"
28 #include "mozilla/LoadInfo.h"
29 #include "mozilla/Logging.h"
30 #include "mozilla/MediaFeatureChange.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/PresShell.h"
33 #include "mozilla/ResultExtensions.h"
34 #include "mozilla/SchedulerGroup.h"
35 #include "mozilla/ScrollTypes.h"
36 #include "mozilla/Services.h"
37 #include "mozilla/StaticPrefs_browser.h"
38 #include "mozilla/StaticPrefs_dom.h"
39 #include "mozilla/StaticPrefs_extensions.h"
40 #include "mozilla/StaticPrefs_privacy.h"
41 #include "mozilla/StaticPrefs_security.h"
42 #include "mozilla/StaticPrefs_ui.h"
43 #include "mozilla/StaticPrefs_fission.h"
44 #include "mozilla/StartupTimeline.h"
45 #include "mozilla/StorageAccess.h"
46 #include "mozilla/Telemetry.h"
47 #include "mozilla/Unused.h"
48 #include "mozilla/WidgetUtils.h"
49 
50 #include "mozilla/dom/ChildProcessChannelListener.h"
51 #include "mozilla/dom/ClientChannelHelper.h"
52 #include "mozilla/dom/ClientHandle.h"
53 #include "mozilla/dom/ClientInfo.h"
54 #include "mozilla/dom/ClientManager.h"
55 #include "mozilla/dom/ClientSource.h"
56 #include "mozilla/dom/ContentChild.h"
57 #include "mozilla/dom/ContentFrameMessageManager.h"
58 #include "mozilla/dom/DocGroup.h"
59 #include "mozilla/dom/Element.h"
60 #include "mozilla/dom/HTMLAnchorElement.h"
61 #include "mozilla/dom/HTMLIFrameElement.h"
62 #include "mozilla/dom/PerformanceNavigation.h"
63 #include "mozilla/dom/PermissionMessageUtils.h"
64 #include "mozilla/dom/PopupBlocker.h"
65 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
66 #include "mozilla/dom/ScreenOrientation.h"
67 #include "mozilla/dom/ScriptSettings.h"
68 #include "mozilla/dom/ServiceWorkerInterceptController.h"
69 #include "mozilla/dom/ServiceWorkerUtils.h"
70 #include "mozilla/dom/SessionHistoryEntry.h"
71 #include "mozilla/dom/SessionStorageManager.h"
72 #include "mozilla/dom/BrowserChild.h"
73 #include "mozilla/dom/ToJSValue.h"
74 #include "mozilla/dom/UserActivation.h"
75 #include "mozilla/dom/ChildSHistory.h"
76 #include "mozilla/dom/nsCSPContext.h"
77 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
78 #include "mozilla/dom/LoadURIOptionsBinding.h"
79 #include "mozilla/dom/JSWindowActorChild.h"
80 #include "mozilla/ipc/ProtocolUtils.h"
81 #include "mozilla/net/DocumentChannel.h"
82 #include "mozilla/net/UrlClassifierFeatureFactory.h"
83 #include "ReferrerInfo.h"
84 
85 #include "nsIApplicationCacheChannel.h"
86 #include "nsIApplicationCacheContainer.h"
87 #include "nsIAppShell.h"
88 #include "nsIAuthPrompt.h"
89 #include "nsIAuthPrompt2.h"
90 #include "nsICachingChannel.h"
91 #include "nsICaptivePortalService.h"
92 #include "nsIChannel.h"
93 #include "nsIChannelEventSink.h"
94 #include "nsIClassOfService.h"
95 #include "nsIConsoleReportCollector.h"
96 #include "nsIContent.h"
97 #include "nsIContentInlines.h"
98 #include "nsIContentSecurityPolicy.h"
99 #include "nsIContentViewer.h"
100 #include "nsIController.h"
101 #include "nsIDocShellTreeItem.h"
102 #include "nsIDocShellTreeOwner.h"
103 #include "mozilla/dom/Document.h"
104 #include "nsIDocumentLoaderFactory.h"
105 #include "nsIDOMWindow.h"
106 #include "nsIEditingSession.h"
107 #include "nsIEffectiveTLDService.h"
108 #include "nsIExternalProtocolService.h"
109 #include "nsIFormPOSTActionChannel.h"
110 #include "nsIFrame.h"
111 #include "nsIGlobalObject.h"
112 #include "nsIHttpChannel.h"
113 #include "nsIHttpChannelInternal.h"
114 #include "nsIIDNService.h"
115 #include "nsIInputStreamChannel.h"
116 #include "nsIInterfaceRequestorUtils.h"
117 #include "nsILayoutHistoryState.h"
118 #include "nsILoadInfo.h"
119 #include "nsIMultiPartChannel.h"
120 #include "nsINestedURI.h"
121 #include "nsINetworkPredictor.h"
122 #include "nsINode.h"
123 #include "nsINSSErrorsService.h"
124 #include "nsIObserverService.h"
125 #include "nsIOService.h"
126 #include "nsIPrincipal.h"
127 #include "nsIPrivacyTransitionObserver.h"
128 #include "nsIPrompt.h"
129 #include "nsIPromptFactory.h"
130 #include "nsIReflowObserver.h"
131 #include "nsIScriptChannel.h"
132 #include "nsIScriptObjectPrincipal.h"
133 #include "nsIScriptSecurityManager.h"
134 #include "nsIScrollableFrame.h"
135 #include "nsIScrollObserver.h"
136 #include "nsISecureBrowserUI.h"
137 #include "nsISeekableStream.h"
138 #include "nsISelectionDisplay.h"
139 #include "nsISHEntry.h"
140 #include "nsISiteSecurityService.h"
141 #include "nsISocketProvider.h"
142 #include "nsIStringBundle.h"
143 #include "nsIStructuredCloneContainer.h"
144 #include "nsIBrowserChild.h"
145 #include "nsITextToSubURI.h"
146 #include "nsITimedChannel.h"
147 #include "nsITimer.h"
148 #include "nsITransportSecurityInfo.h"
149 #include "nsIUploadChannel.h"
150 #include "nsIURIFixup.h"
151 #include "nsIURIMutator.h"
152 #include "nsIURILoader.h"
153 #include "nsIViewSourceChannel.h"
154 #include "nsIWebBrowserChrome.h"
155 #include "nsIWebBrowserChrome3.h"
156 #include "nsIWebBrowserChromeFocus.h"
157 #include "nsIWebBrowserFind.h"
158 #include "nsIWebProgress.h"
159 #include "nsIWidget.h"
160 #include "nsIWindowWatcher.h"
161 #include "nsIWritablePropertyBag2.h"
162 
163 #include "nsCommandManager.h"
164 #include "nsPIDOMWindow.h"
165 #include "nsPIWindowRoot.h"
166 
167 #include "IHistory.h"
168 #include "IUrlClassifierUITelemetry.h"
169 
170 #include "nsArray.h"
171 #include "nsArrayUtils.h"
172 #include "nsCExternalHandlerService.h"
173 #include "nsContentDLF.h"
174 #include "nsContentPolicyUtils.h"  // NS_CheckContentLoadPolicy(...)
175 #include "nsContentSecurityManager.h"
176 #include "nsContentSecurityUtils.h"
177 #include "nsContentUtils.h"
178 #include "nsCURILoader.h"
179 #include "nsDocShellCID.h"
180 #include "nsDocShellEditorData.h"
181 #include "nsDocShellEnumerator.h"
182 #include "nsDocShellLoadState.h"
183 #include "nsDocShellLoadTypes.h"
184 #include "nsDOMCID.h"
185 #include "nsDOMNavigationTiming.h"
186 #include "nsDSURIContentListener.h"
187 #include "nsEditingSession.h"
188 #include "nsError.h"
189 #include "nsEscape.h"
190 #include "nsFocusManager.h"
191 #include "nsGlobalWindow.h"
192 #include "nsISearchService.h"
193 #include "nsJSEnvironment.h"
194 #include "nsNetCID.h"
195 #include "nsNetUtil.h"
196 #include "nsObjectLoadingContent.h"
197 #include "nsPingListener.h"
198 #include "nsPoint.h"
199 #include "nsQueryObject.h"
200 #include "nsQueryActor.h"
201 #include "nsRect.h"
202 #include "nsRefreshTimer.h"
203 #include "nsSandboxFlags.h"
204 #include "nsSHEntry.h"
205 #include "nsSHistory.h"
206 #include "nsSHEntry.h"
207 #include "nsStructuredCloneContainer.h"
208 #include "nsSubDocumentFrame.h"
209 #include "nsView.h"
210 #include "nsViewManager.h"
211 #include "nsViewSourceHandler.h"
212 #include "nsWebBrowserFind.h"
213 #include "nsWhitespaceTokenizer.h"
214 #include "nsWidgetsCID.h"
215 #include "nsXULAppAPI.h"
216 
217 #include "GeckoProfiler.h"
218 #include "mozilla/NullPrincipal.h"
219 #include "Navigator.h"
220 #include "prenv.h"
221 #include "URIUtils.h"
222 
223 #include "timeline/JavascriptTimelineMarker.h"
224 #include "nsDocShellTelemetryUtils.h"
225 
226 #ifdef MOZ_PLACES
227 #  include "nsIFaviconService.h"
228 #  include "mozIPlacesPendingOperation.h"
229 #endif
230 
231 #if NS_PRINT_PREVIEW
232 #  include "nsIDocumentViewerPrint.h"
233 #  include "nsIWebBrowserPrint.h"
234 #endif
235 
236 #ifdef MOZ_GECKO_PROFILER
237 #  include "ProfilerMarkerPayload.h"
238 #endif
239 
240 using namespace mozilla;
241 using namespace mozilla::dom;
242 using namespace mozilla::net;
243 
244 using mozilla::ipc::Endpoint;
245 
246 // Threshold value in ms for META refresh based redirects
247 #define REFRESH_REDIRECT_TIMER 15000
248 
249 // Hint for native dispatch of events on how long to delay after
250 // all documents have loaded in milliseconds before favoring normal
251 // native event dispatch priorites over performance
252 // Can be overridden with docshell.event_starvation_delay_hint pref.
253 #define NS_EVENT_STARVATION_DELAY_HINT 2000
254 
255 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
256 
257 // Number of documents currently loading
258 static int32_t gNumberOfDocumentsLoading = 0;
259 
260 // Global count of docshells with the private attribute set
261 static uint32_t gNumberOfPrivateDocShells = 0;
262 
263 #ifdef DEBUG
264 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
265 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
266     "DocShellAndDOMWindowLeak");
267 #endif
268 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
269 extern mozilla::LazyLogModule gPageCacheLog;
270 
271 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
272 const char kAppstringsBundleURL[] =
273     "chrome://global/locale/appstrings.properties";
274 
FavorPerformanceHint(bool aPerfOverStarvation)275 static void FavorPerformanceHint(bool aPerfOverStarvation) {
276   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
277   if (appShell) {
278     appShell->FavorPerformanceHint(
279         aPerfOverStarvation,
280         Preferences::GetUint("docshell.event_starvation_delay_hint",
281                              NS_EVENT_STARVATION_DELAY_HINT));
282   }
283 }
284 
IncreasePrivateDocShellCount()285 static void IncreasePrivateDocShellCount() {
286   gNumberOfPrivateDocShells++;
287   if (gNumberOfPrivateDocShells > 1 || !XRE_IsContentProcess()) {
288     return;
289   }
290 
291   mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
292   cc->SendPrivateDocShellsExist(true);
293 }
294 
DecreasePrivateDocShellCount()295 static void DecreasePrivateDocShellCount() {
296   MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
297   gNumberOfPrivateDocShells--;
298   if (!gNumberOfPrivateDocShells) {
299     if (XRE_IsContentProcess()) {
300       dom::ContentChild* cc = dom::ContentChild::GetSingleton();
301       cc->SendPrivateDocShellsExist(false);
302       return;
303     }
304 
305     nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
306     if (obsvc) {
307       obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
308     }
309   }
310 }
311 
IsTopLevelDoc(BrowsingContext * aBrowsingContext,nsILoadInfo * aLoadInfo)312 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext,
313                           nsILoadInfo* aLoadInfo) {
314   MOZ_ASSERT(aBrowsingContext);
315   MOZ_ASSERT(aLoadInfo);
316 
317   if (aLoadInfo->GetExternalContentPolicyType() !=
318       nsIContentPolicy::TYPE_DOCUMENT) {
319     return false;
320   }
321 
322   return aBrowsingContext->IsTopContent();
323 }
324 
325 // True if loading for top level document loading in active tab.
IsUrgentStart(BrowsingContext * aBrowsingContext,nsILoadInfo * aLoadInfo,uint32_t aLoadType)326 static bool IsUrgentStart(BrowsingContext* aBrowsingContext,
327                           nsILoadInfo* aLoadInfo, uint32_t aLoadType) {
328   MOZ_ASSERT(aBrowsingContext);
329   MOZ_ASSERT(aLoadInfo);
330 
331   if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) {
332     return false;
333   }
334 
335   if (aLoadType &
336       (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) {
337     return true;
338   }
339 
340   return aBrowsingContext->GetIsActive();
341 }
342 
nsDocShell(BrowsingContext * aBrowsingContext,uint64_t aContentWindowID)343 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
344                        uint64_t aContentWindowID)
345     : nsDocLoader(),
346       mHistoryID(aBrowsingContext->GetHistoryID()),
347       mContentWindowID(aContentWindowID),
348       mBrowsingContext(aBrowsingContext),
349       mForcedCharset(nullptr),
350       mParentCharset(nullptr),
351       mTreeOwner(nullptr),
352       mScrollbarPref(ScrollbarPreference::Auto),
353       mCharsetReloadState(eCharsetReloadInit),
354       mParentCharsetSource(0),
355       mFrameMargins(-1, -1),
356       mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome),
357       mPreviousEntryIndex(-1),
358       mLoadedEntryIndex(-1),
359       mChildOffset(0),
360       mBusyFlags(BUSY_FLAGS_NONE),
361       mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
362       mLoadType(0),
363       mFailedLoadType(0),
364       mDisplayMode(nsIDocShell::DISPLAY_MODE_BROWSER),
365       mJSRunToCompletionDepth(0),
366       mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE),
367       mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
368       mFullscreenAllowed(CHECK_ATTRIBUTES),
369       mCreatingDocument(false),
370 #ifdef DEBUG
371       mInEnsureScriptEnv(false),
372 #endif
373       mCreated(false),
374       mAllowSubframes(true),
375       mAllowJavascript(true),
376       mAllowMetaRedirects(true),
377       mAllowImages(true),
378       mAllowMedia(true),
379       mAllowDNSPrefetch(true),
380       mAllowWindowControl(true),
381       mUseErrorPages(true),
382       mCSSErrorReportingEnabled(false),
383       mAllowAuth(mItemType == typeContent),
384       mAllowKeywordFixup(false),
385       mIsOffScreenBrowser(false),
386       mDisableMetaRefreshWhenInactive(false),
387       mIsAppTab(false),
388       mDeviceSizeIsPageSize(false),
389       mWindowDraggingAllowed(false),
390       mInFrameSwap(false),
391       mCanExecuteScripts(false),
392       mFiredUnloadEvent(false),
393       mEODForCurrentDocument(false),
394       mURIResultedInDocument(false),
395       mIsBeingDestroyed(false),
396       mIsExecutingOnLoadHandler(false),
397       mIsPrintingOrPP(false),
398       mSavingOldViewer(false),
399       mDynamicallyCreated(false),
400       mAffectPrivateSessionLifetime(true),
401       mInvisible(false),
402       mHasLoadedNonBlankURI(false),
403       mBlankTiming(false),
404       mTitleValidForCurrentURI(false),
405       mWillChangeProcess(false),
406       mIsNavigating(false),
407       mSuspendMediaWhenInactive(false) {
408   // If no outer window ID was provided, generate a new one.
409   if (aContentWindowID == 0) {
410     mContentWindowID = nsContentUtils::GenerateWindowId();
411   }
412 
413   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
414 
415 #ifdef DEBUG
416   // We're counting the number of |nsDocShells| to help find leaks
417   ++gNumberOfDocShells;
418   MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
419           ("++DOCSHELL %p == %ld [pid = %d] [id = %s]\n", (void*)this,
420            gNumberOfDocShells, getpid(), nsIDToCString(mHistoryID).get()));
421 #endif
422 }
423 
~nsDocShell()424 nsDocShell::~nsDocShell() {
425   MOZ_ASSERT(!mObserved);
426 
427   // Avoid notifying observers while we're in the dtor.
428   mIsBeingDestroyed = true;
429 
430   Destroy();
431 
432   if (mContentViewer) {
433     mContentViewer->Close(nullptr);
434     mContentViewer->Destroy();
435     mContentViewer = nullptr;
436   }
437 
438   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
439 
440 #ifdef DEBUG
441   if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
442     nsAutoCString url;
443     if (mLastOpenedURI) {
444       url = mLastOpenedURI->GetSpecOrDefault();
445 
446       // Data URLs can be very long, so truncate to avoid flooding the log.
447       const uint32_t maxURLLength = 1000;
448       if (url.Length() > maxURLLength) {
449         url.Truncate(maxURLLength);
450       }
451     }
452 
453     // We're counting the number of |nsDocShells| to help find leaks
454     --gNumberOfDocShells;
455     MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
456             ("--DOCSHELL %p == %ld [pid = %d] [id = %s] [url = %s]\n",
457              (void*)this, gNumberOfDocShells, getpid(),
458              nsIDToCString(mHistoryID).get(), url.get()));
459   }
460 #endif
461 }
462 
463 /* static */
Create(BrowsingContext * aBrowsingContext,uint64_t aContentWindowID)464 already_AddRefed<nsDocShell> nsDocShell::Create(
465     BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
466   MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
467 
468   nsresult rv;
469   RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
470 
471   // Initialize the underlying nsDocLoader.
472   rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
473   if (NS_WARN_IF(NS_FAILED(rv))) {
474     return nullptr;
475   }
476 
477   // Create our ContentListener
478   ds->mContentListener = new nsDSURIContentListener(ds);
479 
480   // If parent intercept is not enabled then we must forward to
481   // the network controller from docshell.  We also enable if we're
482   // in the parent process in order to support non-e10s configurations.
483   // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
484   // constructor.
485   if (!ServiceWorkerParentInterceptEnabled() || XRE_IsParentProcess()) {
486     ds->mInterceptController = new ServiceWorkerInterceptController();
487   }
488 
489   // We want to hold a strong ref to the loadgroup, so it better hold a weak
490   // ref to us...  use an InterfaceRequestorProxy to do this.
491   nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
492   ds->mLoadGroup->SetNotificationCallbacks(proxy);
493 
494   // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
495   // It could be nice to directly set up our DocLoader tree?
496   rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
497   if (NS_WARN_IF(NS_FAILED(rv))) {
498     return nullptr;
499   }
500 
501   // Add |ds| as a progress listener to itself.  A little weird, but simpler
502   // than reproducing all the listener-notification logic in overrides of the
503   // various methods via which nsDocLoader can be notified.   Note that this
504   // holds an nsWeakPtr to |ds|, so it's ok.
505   rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
506                                        nsIWebProgress::NOTIFY_STATE_NETWORK |
507                                        nsIWebProgress::NOTIFY_LOCATION);
508   if (NS_WARN_IF(NS_FAILED(rv))) {
509     return nullptr;
510   }
511 
512   // If our BrowsingContext has private browsing enabled, update the number of
513   // private browsing docshells.
514   if (aBrowsingContext->UsePrivateBrowsing()) {
515     ds->NotifyPrivateBrowsingChanged();
516   }
517 
518   // If our parent is present in this process, set up our parent now.
519   RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
520   if (parent && parent->GetDocShell()) {
521     parent->GetDocShell()->AddChild(ds);
522   }
523 
524   // Make |ds| the primary DocShell for the given context.
525   aBrowsingContext->SetDocShell(ds);
526 
527   // Set |ds| default load flags on load group.
528   ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
529 
530   return ds.forget();
531 }
532 
DestroyChildren()533 void nsDocShell::DestroyChildren() {
534   nsCOMPtr<nsIDocShellTreeItem> shell;
535   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
536   while (iter.HasMore()) {
537     shell = do_QueryObject(iter.GetNext());
538     NS_ASSERTION(shell, "docshell has null child");
539 
540     if (shell) {
541       shell->SetTreeOwner(nullptr);
542     }
543   }
544 
545   nsDocLoader::DestroyChildren();
546 }
547 
NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell,nsDocLoader,mScriptGlobal,mInitialClientSource,mBrowsingContext,mChromeEventHandler)548 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
549                                             mScriptGlobal, mInitialClientSource,
550                                             mBrowsingContext,
551                                             mChromeEventHandler)
552 
553 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
554 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
555 
556 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
557   NS_INTERFACE_MAP_ENTRY(nsIDocShell)
558   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
559   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
560   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
561   NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
562   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
563   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
564   NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
565   NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
566   NS_INTERFACE_MAP_ENTRY(nsILoadContext)
567   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
568                                      mInterceptController)
569   NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
570 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
571 
572 NS_IMETHODIMP
573 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
574   MOZ_ASSERT(aSink, "null out param");
575 
576   *aSink = nullptr;
577 
578   if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
579     NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
580     *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
581   } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
582     *aSink = mContentListener;
583   } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
584               aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
585               aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
586               aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
587               aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
588              NS_SUCCEEDED(EnsureScriptEnvironment())) {
589     return mScriptGlobal->QueryInterface(aIID, aSink);
590   } else if (aIID.Equals(NS_GET_IID(Document)) &&
591              NS_SUCCEEDED(EnsureContentViewer())) {
592     RefPtr<Document> doc = mContentViewer->GetDocument();
593     doc.forget(aSink);
594     return *aSink ? NS_OK : NS_NOINTERFACE;
595   } else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
596     *aSink = nullptr;
597 
598     // Return application cache associated with this docshell, if any
599 
600     nsCOMPtr<nsIContentViewer> contentViewer;
601     GetContentViewer(getter_AddRefs(contentViewer));
602     if (!contentViewer) {
603       return NS_ERROR_NO_INTERFACE;
604     }
605 
606     RefPtr<Document> doc = contentViewer->GetDocument();
607     NS_ASSERTION(doc, "Should have a document.");
608     if (!doc) {
609       return NS_ERROR_NO_INTERFACE;
610     }
611 
612 #if defined(DEBUG)
613     MOZ_LOG(
614         gDocShellLog, LogLevel::Debug,
615         ("nsDocShell[%p]: returning app cache container %p", this, doc.get()));
616 #endif
617     return doc->QueryInterface(aIID, aSink);
618   } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
619              NS_SUCCEEDED(EnsureScriptEnvironment())) {
620     nsresult rv;
621     nsCOMPtr<nsIWindowWatcher> wwatch =
622         do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
623     NS_ENSURE_SUCCESS(rv, rv);
624 
625     // Get the an auth prompter for our window so that the parenting
626     // of the dialogs works as it should when using tabs.
627     nsIPrompt* prompt;
628     rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
629     NS_ENSURE_SUCCESS(rv, rv);
630 
631     *aSink = prompt;
632     return NS_OK;
633   } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
634              aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
635     return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
636                ? NS_OK
637                : NS_NOINTERFACE;
638   } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
639     RefPtr<ChildSHistory> shistory = GetSessionHistory();
640     if (shistory) {
641       // XXX(nika): Stop exposing nsISHistory through GetInterface.
642       nsCOMPtr<nsISHistory> legacy = shistory->LegacySHistory();
643       legacy.forget(aSink);
644       return NS_OK;
645     }
646     return NS_NOINTERFACE;
647   } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
648     nsresult rv = EnsureFind();
649     if (NS_FAILED(rv)) {
650       return rv;
651     }
652 
653     *aSink = mFind;
654     NS_ADDREF((nsISupports*)*aSink);
655     return NS_OK;
656   } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
657     if (PresShell* presShell = GetPresShell()) {
658       return presShell->QueryInterface(aIID, aSink);
659     }
660   } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
661     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
662     nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
663     if (NS_SUCCEEDED(rv) && treeOwner) {
664       return treeOwner->QueryInterface(aIID, aSink);
665     }
666   } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
667     *aSink = GetBrowserChild().take();
668     return *aSink ? NS_OK : NS_ERROR_FAILURE;
669   } else {
670     return nsDocLoader::GetInterface(aIID, aSink);
671   }
672 
673   NS_IF_ADDREF(((nsISupports*)*aSink));
674   return *aSink ? NS_OK : NS_NOINTERFACE;
675 }
676 
677 NS_IMETHODIMP
SetCancelContentJSEpoch(int32_t aEpoch)678 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
679   // Note: this gets called fairly early (before a pageload actually starts).
680   // We could probably defer this even longer.
681   nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
682   static_cast<BrowserChild*>(browserChild.get())
683       ->SetCancelContentJSEpoch(aEpoch);
684   return NS_OK;
685 }
686 
687 NS_IMETHODIMP
LoadURI(nsDocShellLoadState * aLoadState,bool aSetNavigating)688 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
689   MOZ_ASSERT(aLoadState, "Must have a valid load state!");
690   MOZ_ASSERT(
691       (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
692       "Should not have these flags set");
693 
694   if (!aLoadState->TriggeringPrincipal()) {
695     MOZ_ASSERT(false, "LoadURI must have a triggering principal");
696     return NS_ERROR_FAILURE;
697   }
698 
699   bool oldIsNavigating = mIsNavigating;
700   auto cleanupIsNavigating =
701       MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
702   if (aSetNavigating) {
703     mIsNavigating = true;
704   }
705 
706   PopupBlocker::PopupControlState popupState;
707   if (aLoadState->LoadFlags() & LOAD_FLAGS_ALLOW_POPUPS) {
708     popupState = PopupBlocker::openAllowed;
709   } else {
710     popupState = PopupBlocker::openOverridden;
711   }
712   AutoPopupStatePusher statePusher(popupState);
713 
714   if (aLoadState->GetOriginalURIString().isSome()) {
715     // Save URI string in case it's needed later when
716     // sending to search engine service in EndPageLoad()
717     mOriginalUriString = *aLoadState->GetOriginalURIString();
718   }
719 
720   if (aLoadState->GetCancelContentJSEpoch().isSome()) {
721     SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
722   }
723 
724   // Note: we allow loads to get through here even if mFiredUnloadEvent is
725   // true; that case will get handled in LoadInternal or LoadHistoryEntry,
726   // so we pass false as the second parameter to IsNavigationAllowed.
727   // However, we don't allow the page to change location *in the middle of*
728   // firing beforeunload, so we do need to check if *beforeunload* is currently
729   // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
730   if (!IsNavigationAllowed(true, false)) {
731     return NS_OK;  // JS may not handle returning of an error code
732   }
733 
734   nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
735   if (aLoadState->LoadFlags() & LOAD_FLAGS_FORCE_TRR) {
736     defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
737   } else if (aLoadState->LoadFlags() & LOAD_FLAGS_DISABLE_TRR) {
738     defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
739   }
740   mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags);
741 
742   if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
743       mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
744     StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
745   }
746 
747   // LoadType used to be set to a default value here, if no LoadInfo/LoadState
748   // object was passed in. That functionality has been removed as of bug
749   // 1492648. LoadType should now be set up by the caller at the time they
750   // create their nsDocShellLoadState object to pass into LoadURI.
751 
752   MOZ_LOG(
753       gDocShellLeakLog, LogLevel::Debug,
754       ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
755        aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
756 
757   if (!aLoadState->SHEntry() &&
758       !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
759                            LOAD_FLAGS_REPLACE_HISTORY)) {
760     // This is possibly a subframe, so handle it accordingly.
761     //
762     // If history exists, it will be loaded into the aLoadState object, and the
763     // LoadType will be changed.
764     MaybeHandleSubframeHistory(aLoadState);
765   }
766 
767   if (aLoadState->SHEntry()) {
768 #ifdef DEBUG
769     MOZ_LOG(gDocShellLog, LogLevel::Debug,
770             ("nsDocShell[%p]: loading from session history", this));
771 #endif
772 
773     return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType());
774   }
775 
776   // On history navigation via Back/Forward buttons, don't execute
777   // automatic JavaScript redirection such as |location.href = ...| or
778   // |window.open()|
779   //
780   // LOAD_NORMAL:        window.open(...) etc.
781   // LOAD_STOP_CONTENT:  location.href = ..., location.assign(...)
782   if ((aLoadState->LoadType() == LOAD_NORMAL ||
783        aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
784       ShouldBlockLoadingForBackButton()) {
785     return NS_OK;
786   }
787 
788   BrowsingContext::Type bcType = mBrowsingContext->GetType();
789 
790   // Set up the inheriting principal in LoadState.
791   nsresult rv = aLoadState->SetupInheritingPrincipal(
792       bcType, mBrowsingContext->OriginAttributesRef());
793   NS_ENSURE_SUCCESS(rv, rv);
794 
795   rv = aLoadState->SetupTriggeringPrincipal(
796       mBrowsingContext->OriginAttributesRef());
797   NS_ENSURE_SUCCESS(rv, rv);
798 
799   aLoadState->CalculateLoadURIFlags();
800 
801   MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
802              "Typehint should be null when calling InternalLoad from LoadURI");
803   MOZ_ASSERT(aLoadState->FileName().IsVoid(),
804              "FileName should be null when calling InternalLoad from LoadURI");
805   MOZ_ASSERT(aLoadState->SHEntry() == nullptr,
806              "SHEntry should be null when calling InternalLoad from LoadURI");
807 
808   return InternalLoad(aLoadState,
809                       nullptr,   // no nsIDocShell
810                       nullptr);  // no nsIRequest
811 }
812 
MaybeHandleSubframeHistory(nsDocShellLoadState * aLoadState)813 void nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState) {
814   // First, verify if this is a subframe.
815   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
816   GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
817   nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
818 
819   if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
820     // This is the root docshell. If we got here while
821     // executing an onLoad Handler,this load will not go
822     // into session history.
823     bool inOnLoadHandler = false;
824     GetIsExecutingOnLoadHandler(&inOnLoadHandler);
825     if (inOnLoadHandler) {
826       aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
827     }
828     return;
829   }
830 
831   /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
832    * loaded through a history mechanism, then get the SH entry for the child
833    * from the parent. This is done to restore frameset navigation while going
834    * back/forward. If the parent was loaded through any other loadType, set the
835    * child's loadType too accordingly, so that session history does not get
836    * confused.
837    */
838 
839   // Get the parent's load type
840   uint32_t parentLoadType;
841   parentDS->GetLoadType(&parentLoadType);
842 
843   // Get the ShEntry for the child from the parent
844   nsCOMPtr<nsISHEntry> currentSH;
845   bool oshe = false;
846   parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
847   bool dynamicallyAddedChild = mDynamicallyCreated;
848 
849   if (!dynamicallyAddedChild && !oshe && currentSH) {
850     // Only use the old SHEntry, if we're sure enough that
851     // it wasn't originally for some other frame.
852     nsCOMPtr<nsISHEntry> shEntry;
853     currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
854         mChildOffset, getter_AddRefs(shEntry));
855     if (shEntry) {
856       aLoadState->SetSHEntry(shEntry);
857     }
858   }
859 
860   // Make some decisions on the child frame's loadType based on the
861   // parent's loadType, if the subframe hasn't loaded anything into it.
862   //
863   // In some cases privileged scripts may try to get the DOMWindow
864   // reference of this docshell before the loading starts, causing the
865   // initial about:blank content viewer being created and mCurrentURI being
866   // set. To handle this case we check if mCurrentURI is about:blank and
867   // currentSHEntry is null.
868   nsCOMPtr<nsISHEntry> currentChildEntry;
869   GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
870 
871   if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry)) {
872     // This is a pre-existing subframe. If
873     // 1. The load of this frame was not originally initiated by session
874     //    history directly (i.e. (!shEntry) condition succeeded, but it can
875     //    still be a history load on parent which causes this frame being
876     //    loaded), which we checked with the above assert, and
877     // 2. mCurrentURI is not null, nor the initial about:blank,
878     // it is possible that a parent's onLoadHandler or even self's
879     // onLoadHandler is loading a new page in this child. Check parent's and
880     // self's busy flag and if it is set, we don't want this onLoadHandler
881     // load to get in to session history.
882     BusyFlags parentBusy = parentDS->GetBusyFlags();
883     BusyFlags selfBusy = GetBusyFlags();
884 
885     if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
886       aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
887       aLoadState->SetSHEntry(nullptr);
888     }
889     return;
890   }
891 
892   // This is a newly created frame. Check for exception cases first.
893   // By default the subframe will inherit the parent's loadType.
894   if (aLoadState->SHEntry() &&
895       (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK ||
896        parentLoadType == LOAD_NORMAL_EXTERNAL)) {
897     // The parent was loaded normally. In this case, this *brand new*
898     // child really shouldn't have a SHEntry. If it does, it could be
899     // because the parent is replacing an existing frame with a new frame,
900     // in the onLoadHandler. We don't want this url to get into session
901     // history. Clear off shEntry, and set load type to
902     // LOAD_BYPASS_HISTORY.
903     bool inOnLoadHandler = false;
904     parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
905     if (inOnLoadHandler) {
906       aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
907       aLoadState->SetSHEntry(nullptr);
908     }
909   } else if (parentLoadType == LOAD_REFRESH) {
910     // Clear shEntry. For refresh loads, we have to load
911     // what comes through the pipe, not what's in history.
912     aLoadState->SetSHEntry(nullptr);
913   } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
914              (aLoadState->SHEntry() &&
915               ((parentLoadType & LOAD_CMD_HISTORY) ||
916                (parentLoadType == LOAD_RELOAD_NORMAL) ||
917                (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
918                (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
919                (parentLoadType ==
920                 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
921     // If the parent url, bypassed history or was loaded from
922     // history, pass on the parent's loadType to the new child
923     // frame too, so that the child frame will also
924     // avoid getting into history.
925     aLoadState->SetLoadType(parentLoadType);
926   } else if (parentLoadType == LOAD_ERROR_PAGE) {
927     // If the parent document is an error page, we don't
928     // want to update global/session history. However,
929     // this child frame is not an error page.
930     aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
931   } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
932              (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
933              (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
934     // the new frame should inherit the parent's load type so that it also
935     // bypasses the cache and/or proxy
936     aLoadState->SetLoadType(parentLoadType);
937   }
938 }
939 
940 /*
941  * Reset state to a new content model within the current document and the
942  * document viewer. Called by the document before initiating an out of band
943  * document.write().
944  */
945 NS_IMETHODIMP
PrepareForNewContentModel()946 nsDocShell::PrepareForNewContentModel() {
947   // Clear out our form control state, because the state of controls
948   // in the pre-open() document should not affect the state of
949   // controls that are now going to be written.
950   SetLayoutHistoryState(nullptr);
951   mEODForCurrentDocument = false;
952   return NS_OK;
953 }
954 
955 NS_IMETHODIMP
FirePageHideNotification(bool aIsUnload)956 nsDocShell::FirePageHideNotification(bool aIsUnload) {
957   FirePageHideNotificationInternal(aIsUnload, false);
958   return NS_OK;
959 }
960 
FirePageHideNotificationInternal(bool aIsUnload,bool aSkipCheckingDynEntries)961 void nsDocShell::FirePageHideNotificationInternal(
962     bool aIsUnload, bool aSkipCheckingDynEntries) {
963   if (mContentViewer && !mFiredUnloadEvent) {
964     // Keep an explicit reference since calling PageHide could release
965     // mContentViewer
966     nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
967     mFiredUnloadEvent = true;
968 
969     if (mTiming) {
970       mTiming->NotifyUnloadEventStart();
971     }
972 
973     contentViewer->PageHide(aIsUnload);
974 
975     if (mTiming) {
976       mTiming->NotifyUnloadEventEnd();
977     }
978 
979     AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
980     uint32_t n = mChildList.Length();
981     kids.SetCapacity(n);
982     for (uint32_t i = 0; i < n; i++) {
983       kids.AppendElement(do_QueryInterface(ChildAt(i)));
984     }
985 
986     n = kids.Length();
987     for (uint32_t i = 0; i < n; ++i) {
988       RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
989       if (child) {
990         // Skip checking dynamic subframe entries in our children.
991         child->FirePageHideNotificationInternal(aIsUnload, true);
992       }
993     }
994 
995     // If the document is unloading, remove all dynamic subframe entries.
996     if (aIsUnload && !aSkipCheckingDynEntries) {
997       RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
998       if (rootSH && mOSHE) {
999         int32_t index = rootSH->Index();
1000         rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
1001       }
1002     }
1003 
1004     // Now make sure our editor, if any, is detached before we go
1005     // any farther.
1006     DetachEditorFromWindow();
1007   }
1008 }
1009 
Dispatch(TaskCategory aCategory,already_AddRefed<nsIRunnable> && aRunnable)1010 nsresult nsDocShell::Dispatch(TaskCategory aCategory,
1011                               already_AddRefed<nsIRunnable>&& aRunnable) {
1012   nsCOMPtr<nsIRunnable> runnable(aRunnable);
1013   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1014   if (NS_WARN_IF(!win)) {
1015     // Window should only be unavailable after destroyed.
1016     MOZ_ASSERT(mIsBeingDestroyed);
1017     return NS_ERROR_FAILURE;
1018   }
1019 
1020   if (win->GetDocGroup()) {
1021     return win->GetDocGroup()->Dispatch(aCategory, runnable.forget());
1022   }
1023 
1024   return SchedulerGroup::Dispatch(aCategory, runnable.forget());
1025 }
1026 
1027 NS_IMETHODIMP
DispatchLocationChangeEvent()1028 nsDocShell::DispatchLocationChangeEvent() {
1029   return Dispatch(
1030       TaskCategory::Other,
1031       NewRunnableMethod("nsDocShell::FireDummyOnLocationChange", this,
1032                         &nsDocShell::FireDummyOnLocationChange));
1033 }
1034 
1035 NS_IMETHODIMP
StartDelayedAutoplayMediaComponents()1036 nsDocShell::StartDelayedAutoplayMediaComponents() {
1037   RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow();
1038   if (outerWindow) {
1039     outerWindow->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
1040   }
1041   return NS_OK;
1042 }
1043 
MaybeInitTiming()1044 bool nsDocShell::MaybeInitTiming() {
1045   if (mTiming && !mBlankTiming) {
1046     return false;
1047   }
1048 
1049   bool canBeReset = false;
1050 
1051   if (mScriptGlobal && mBlankTiming) {
1052     nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
1053     if (innerWin && innerWin->GetPerformance()) {
1054       mTiming = innerWin->GetPerformance()->GetDOMTiming();
1055       mBlankTiming = false;
1056     }
1057   }
1058 
1059   if (!mTiming) {
1060     mTiming = new nsDOMNavigationTiming(this);
1061     canBeReset = true;
1062   }
1063 
1064   mTiming->NotifyNavigationStart(
1065       mBrowsingContext->GetIsActive()
1066           ? nsDOMNavigationTiming::DocShellState::eActive
1067           : nsDOMNavigationTiming::DocShellState::eInactive);
1068 
1069   return canBeReset;
1070 }
1071 
MaybeResetInitTiming(bool aReset)1072 void nsDocShell::MaybeResetInitTiming(bool aReset) {
1073   if (aReset) {
1074     mTiming = nullptr;
1075   }
1076 }
1077 
GetNavigationTiming() const1078 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
1079   return mTiming;
1080 }
1081 
1082 //
1083 // Bug 13871: Prevent frameset spoofing
1084 //
1085 // This routine answers: 'Is origin's document from same domain as
1086 // target's document?'
1087 //
1088 // file: uris are considered the same domain for the purpose of
1089 // frame navigation regardless of script accessibility (bug 420425)
1090 //
1091 /* static */
ValidateOrigin(BrowsingContext * aOrigin,BrowsingContext * aTarget)1092 bool nsDocShell::ValidateOrigin(BrowsingContext* aOrigin,
1093                                 BrowsingContext* aTarget) {
1094   nsIDocShell* originDocShell = aOrigin->GetDocShell();
1095   MOZ_ASSERT(originDocShell, "originDocShell must not be null");
1096   Document* originDocument = originDocShell->GetDocument();
1097   NS_ENSURE_TRUE(originDocument, false);
1098 
1099   nsIDocShell* targetDocShell = aTarget->GetDocShell();
1100   MOZ_ASSERT(targetDocShell, "targetDocShell must not be null");
1101   Document* targetDocument = targetDocShell->GetDocument();
1102   NS_ENSURE_TRUE(targetDocument, false);
1103 
1104   bool equal;
1105   nsresult rv = originDocument->NodePrincipal()->Equals(
1106       targetDocument->NodePrincipal(), &equal);
1107   if (NS_SUCCEEDED(rv) && equal) {
1108     return true;
1109   }
1110   // Not strictly equal, special case if both are file: uris
1111   nsCOMPtr<nsIURI> originURI;
1112   nsCOMPtr<nsIURI> targetURI;
1113   nsCOMPtr<nsIURI> innerOriginURI;
1114   nsCOMPtr<nsIURI> innerTargetURI;
1115 
1116   // Casting to BasePrincipal, as we can't get InnerMost URI otherwise
1117   auto* originDocumentBasePrincipal =
1118       BasePrincipal::Cast(originDocument->NodePrincipal());
1119 
1120   rv = originDocumentBasePrincipal->GetURI(getter_AddRefs(originURI));
1121   if (NS_SUCCEEDED(rv) && originURI) {
1122     innerOriginURI = NS_GetInnermostURI(originURI);
1123   }
1124 
1125   auto* targetDocumentBasePrincipal =
1126       BasePrincipal::Cast(targetDocument->NodePrincipal());
1127 
1128   rv = targetDocumentBasePrincipal->GetURI(getter_AddRefs(targetURI));
1129   if (NS_SUCCEEDED(rv) && targetURI) {
1130     innerTargetURI = NS_GetInnermostURI(targetURI);
1131   }
1132 
1133   return innerOriginURI && innerTargetURI && SchemeIsFile(innerOriginURI) &&
1134          SchemeIsFile(innerTargetURI);
1135 }
1136 
GetEldestPresContext()1137 nsPresContext* nsDocShell::GetEldestPresContext() {
1138   nsIContentViewer* viewer = mContentViewer;
1139   while (viewer) {
1140     nsIContentViewer* prevViewer = viewer->GetPreviousViewer();
1141     if (!prevViewer) {
1142       return viewer->GetPresContext();
1143     }
1144     viewer = prevViewer;
1145   }
1146 
1147   return nullptr;
1148 }
1149 
GetPresContext()1150 nsPresContext* nsDocShell::GetPresContext() {
1151   if (!mContentViewer) {
1152     return nullptr;
1153   }
1154 
1155   return mContentViewer->GetPresContext();
1156 }
1157 
GetPresShell()1158 PresShell* nsDocShell::GetPresShell() {
1159   nsPresContext* presContext = GetPresContext();
1160   return presContext ? presContext->GetPresShell() : nullptr;
1161 }
1162 
GetEldestPresShell()1163 PresShell* nsDocShell::GetEldestPresShell() {
1164   nsPresContext* presContext = GetEldestPresContext();
1165 
1166   if (presContext) {
1167     return presContext->GetPresShell();
1168   }
1169 
1170   return nullptr;
1171 }
1172 
1173 NS_IMETHODIMP
GetContentViewer(nsIContentViewer ** aContentViewer)1174 nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer) {
1175   NS_ENSURE_ARG_POINTER(aContentViewer);
1176 
1177   *aContentViewer = mContentViewer;
1178   NS_IF_ADDREF(*aContentViewer);
1179   return NS_OK;
1180 }
1181 
1182 NS_IMETHODIMP
GetOuterWindowID(uint64_t * aWindowID)1183 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) {
1184   *aWindowID = mContentWindowID;
1185   return NS_OK;
1186 }
1187 
1188 NS_IMETHODIMP
SetChromeEventHandler(EventTarget * aChromeEventHandler)1189 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) {
1190   mChromeEventHandler = aChromeEventHandler;
1191 
1192   if (mScriptGlobal) {
1193     mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1194   }
1195 
1196   return NS_OK;
1197 }
1198 
1199 NS_IMETHODIMP
GetChromeEventHandler(EventTarget ** aChromeEventHandler)1200 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) {
1201   NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1202   RefPtr<EventTarget> handler = mChromeEventHandler;
1203   handler.forget(aChromeEventHandler);
1204   return NS_OK;
1205 }
1206 
1207 NS_IMETHODIMP
SetCurrentURI(nsIURI * aURI)1208 nsDocShell::SetCurrentURI(nsIURI* aURI) {
1209   // Note that securityUI will set STATE_IS_INSECURE, even if
1210   // the scheme of |aURI| is "https".
1211   SetCurrentURI(aURI, nullptr, true, 0);
1212   return NS_OK;
1213 }
1214 
SetCurrentURI(nsIURI * aURI,nsIRequest * aRequest,bool aFireOnLocationChange,uint32_t aLocationFlags)1215 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1216                                bool aFireOnLocationChange,
1217                                uint32_t aLocationFlags) {
1218   MOZ_ASSERT(!mIsBeingDestroyed);
1219 
1220   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1221           ("DOCSHELL %p SetCurrentURI %s\n", this,
1222            aURI ? aURI->GetSpecOrDefault().get() : ""));
1223 
1224   // We don't want to send a location change when we're displaying an error
1225   // page, and we don't want to change our idea of "current URI" either
1226   if (mLoadType == LOAD_ERROR_PAGE) {
1227     return false;
1228   }
1229 
1230   bool uriIsEqual = false;
1231   if (!mCurrentURI || !aURI ||
1232       NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
1233     mTitleValidForCurrentURI = false;
1234   }
1235 
1236   mCurrentURI = aURI;
1237 
1238 #ifdef DEBUG
1239   mLastOpenedURI = aURI;
1240 #endif
1241 
1242   if (!NS_IsAboutBlank(mCurrentURI)) {
1243     mHasLoadedNonBlankURI = true;
1244   }
1245 
1246   bool isRoot = mBrowsingContext->IsTop();
1247   bool isSubFrame = false;  // Is this a subframe navigation?
1248 
1249   if (mLSHE) {
1250     isSubFrame = mLSHE->GetIsSubFrame();
1251   }
1252 
1253   if (!isSubFrame && !isRoot) {
1254     /*
1255      * We don't want to send OnLocationChange notifications when
1256      * a subframe is being loaded for the first time, while
1257      * visiting a frameset page
1258      */
1259     return false;
1260   }
1261 
1262   if (aFireOnLocationChange) {
1263     FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1264   }
1265   return !aFireOnLocationChange;
1266 }
1267 
1268 NS_IMETHODIMP
GetCharset(nsACString & aCharset)1269 nsDocShell::GetCharset(nsACString& aCharset) {
1270   aCharset.Truncate();
1271 
1272   PresShell* presShell = GetPresShell();
1273   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1274   Document* doc = presShell->GetDocument();
1275   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1276   doc->GetDocumentCharacterSet()->Name(aCharset);
1277   return NS_OK;
1278 }
1279 
1280 NS_IMETHODIMP
GatherCharsetMenuTelemetry()1281 nsDocShell::GatherCharsetMenuTelemetry() {
1282   nsCOMPtr<nsIContentViewer> viewer;
1283   GetContentViewer(getter_AddRefs(viewer));
1284   if (!viewer) {
1285     return NS_OK;
1286   }
1287 
1288   Document* doc = viewer->GetDocument();
1289   if (!doc || doc->WillIgnoreCharsetOverride()) {
1290     return NS_OK;
1291   }
1292 
1293   Telemetry::ScalarSet(Telemetry::ScalarID::ENCODING_OVERRIDE_USED, true);
1294 
1295   nsIURI* url = doc->GetOriginalURI();
1296   bool isFileURL = url && SchemeIsFile(url);
1297 
1298   int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1299   switch (charsetSource) {
1300     case kCharsetFromTopLevelDomain:
1301       // Unlabeled doc on a domain that we map to a fallback encoding
1302       Telemetry::AccumulateCategorical(
1303           Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::RemoteTld);
1304       break;
1305     case kCharsetFromFallback:
1306     case kCharsetFromDocTypeDefault:
1307     case kCharsetFromCache:
1308     case kCharsetFromParentFrame:
1309       // Changing charset on an unlabeled doc.
1310       if (isFileURL) {
1311         Telemetry::AccumulateCategorical(
1312             Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::Local);
1313       } else {
1314         Telemetry::AccumulateCategorical(
1315             Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::RemoteNonTld);
1316       }
1317       break;
1318     case kCharsetFromInitialAutoDetection:
1319     case kCharsetFromFinalAutoDetection:
1320       // Changing charset on unlabeled doc where chardet fired
1321       if (isFileURL) {
1322         Telemetry::AccumulateCategorical(
1323             Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::LocalChardet);
1324       } else {
1325         Telemetry::AccumulateCategorical(
1326             Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::RemoteChardet);
1327       }
1328       break;
1329     case kCharsetFromMetaPrescan:
1330     case kCharsetFromMetaTag:
1331     case kCharsetFromChannel:
1332       // Changing charset on a doc that had a charset label.
1333       Telemetry::AccumulateCategorical(
1334           Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::Labeled);
1335       break;
1336     case kCharsetFromParentForced:
1337     case kCharsetFromUserForced:
1338       // Changing charset on a document that already had an override.
1339       Telemetry::AccumulateCategorical(
1340           Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::AlreadyOverridden);
1341       break;
1342     case kCharsetFromIrreversibleAutoDetection:
1343     case kCharsetFromOtherComponent:
1344     case kCharsetFromByteOrderMark:
1345     case kCharsetUninitialized:
1346     default:
1347       // Bug. This isn't supposed to happen.
1348       Telemetry::AccumulateCategorical(
1349           Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::Bug);
1350       break;
1351   }
1352   return NS_OK;
1353 }
1354 
1355 NS_IMETHODIMP
SetCharset(const nsACString & aCharset)1356 nsDocShell::SetCharset(const nsACString& aCharset) {
1357   // set the charset override
1358   return SetForcedCharset(aCharset);
1359 }
1360 
1361 NS_IMETHODIMP
SetForcedCharset(const nsACString & aCharset)1362 nsDocShell::SetForcedCharset(const nsACString& aCharset) {
1363   if (aCharset.IsEmpty()) {
1364     mForcedCharset = nullptr;
1365     return NS_OK;
1366   }
1367   const Encoding* encoding = Encoding::ForLabel(aCharset);
1368   if (!encoding) {
1369     // Reject unknown labels
1370     return NS_ERROR_INVALID_ARG;
1371   }
1372   if (!encoding->IsAsciiCompatible() && encoding != ISO_2022_JP_ENCODING) {
1373     // Reject XSS hazards
1374     return NS_ERROR_INVALID_ARG;
1375   }
1376   mForcedCharset = encoding;
1377   return NS_OK;
1378 }
1379 
1380 NS_IMETHODIMP
GetForcedCharset(nsACString & aResult)1381 nsDocShell::GetForcedCharset(nsACString& aResult) {
1382   if (mForcedCharset) {
1383     mForcedCharset->Name(aResult);
1384   } else {
1385     aResult.Truncate();
1386   }
1387   return NS_OK;
1388 }
1389 
SetParentCharset(const Encoding * & aCharset,int32_t aCharsetSource,nsIPrincipal * aPrincipal)1390 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1391                                   int32_t aCharsetSource,
1392                                   nsIPrincipal* aPrincipal) {
1393   mParentCharset = aCharset;
1394   mParentCharsetSource = aCharsetSource;
1395   mParentCharsetPrincipal = aPrincipal;
1396 }
1397 
GetParentCharset(const Encoding * & aCharset,int32_t * aCharsetSource,nsIPrincipal ** aPrincipal)1398 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1399                                   int32_t* aCharsetSource,
1400                                   nsIPrincipal** aPrincipal) {
1401   aCharset = mParentCharset;
1402   *aCharsetSource = mParentCharsetSource;
1403   NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1404 }
1405 
1406 NS_IMETHODIMP
GetHasTrackingContentBlocked(Promise ** aPromise)1407 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
1408   MOZ_ASSERT(aPromise);
1409 
1410   ErrorResult rv;
1411   RefPtr<Document> doc(GetDocument());
1412   RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
1413   if (NS_WARN_IF(rv.Failed())) {
1414     return rv.StealNSResult();
1415   }
1416 
1417   // Retrieve the document's content blocking events from the parent process.
1418   RefPtr<Document::GetContentBlockingEventsPromise> promise =
1419       doc->GetContentBlockingEvents();
1420   if (promise) {
1421     promise->Then(
1422         GetCurrentThreadSerialEventTarget(), __func__,
1423         [retPromise](const Document::GetContentBlockingEventsPromise::
1424                          ResolveOrRejectValue& aValue) {
1425           if (aValue.IsResolve()) {
1426             bool has = aValue.ResolveValue() &
1427                        nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
1428             retPromise->MaybeResolve(has);
1429           } else {
1430             retPromise->MaybeResolve(false);
1431           }
1432         });
1433   } else {
1434     retPromise->MaybeResolve(false);
1435   }
1436 
1437   retPromise.forget(aPromise);
1438   return NS_OK;
1439 }
1440 
1441 NS_IMETHODIMP
GetAllowPlugins(bool * aAllowPlugins)1442 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
1443   NS_ENSURE_ARG_POINTER(aAllowPlugins);
1444 
1445   *aAllowPlugins = mBrowsingContext->GetAllowPlugins();
1446   return NS_OK;
1447 }
1448 
1449 NS_IMETHODIMP
SetAllowPlugins(bool aAllowPlugins)1450 nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
1451   mBrowsingContext->SetAllowPlugins(aAllowPlugins);
1452   // XXX should enable or disable a plugin host
1453   return NS_OK;
1454 }
1455 
1456 NS_IMETHODIMP
GetAllowJavascript(bool * aAllowJavascript)1457 nsDocShell::GetAllowJavascript(bool* aAllowJavascript) {
1458   NS_ENSURE_ARG_POINTER(aAllowJavascript);
1459 
1460   *aAllowJavascript = mAllowJavascript;
1461   return NS_OK;
1462 }
1463 
1464 NS_IMETHODIMP
GetCssErrorReportingEnabled(bool * aEnabled)1465 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
1466   MOZ_ASSERT(aEnabled);
1467   *aEnabled = mCSSErrorReportingEnabled;
1468   return NS_OK;
1469 }
1470 
1471 NS_IMETHODIMP
SetCssErrorReportingEnabled(bool aEnabled)1472 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1473   mCSSErrorReportingEnabled = aEnabled;
1474   return NS_OK;
1475 }
1476 
1477 NS_IMETHODIMP
SetAllowJavascript(bool aAllowJavascript)1478 nsDocShell::SetAllowJavascript(bool aAllowJavascript) {
1479   mAllowJavascript = aAllowJavascript;
1480   RecomputeCanExecuteScripts();
1481   return NS_OK;
1482 }
1483 
1484 NS_IMETHODIMP
GetUsePrivateBrowsing(bool * aUsePrivateBrowsing)1485 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1486   NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1487   return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1488 }
1489 
NotifyPrivateBrowsingChanged()1490 void nsDocShell::NotifyPrivateBrowsingChanged() {
1491   MOZ_ASSERT(!mIsBeingDestroyed);
1492 
1493   if (mAffectPrivateSessionLifetime) {
1494     if (UsePrivateBrowsing()) {
1495       IncreasePrivateDocShellCount();
1496     } else {
1497       DecreasePrivateDocShellCount();
1498     }
1499   }
1500 
1501   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1502   while (iter.HasMore()) {
1503     nsWeakPtr ref = iter.GetNext();
1504     nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1505     if (!obs) {
1506       mPrivacyObservers.RemoveElement(ref);
1507     } else {
1508       obs->PrivateModeChanged(UsePrivateBrowsing());
1509     }
1510   }
1511 }
1512 
1513 NS_IMETHODIMP
SetUsePrivateBrowsing(bool aUsePrivateBrowsing)1514 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1515   return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1516 }
1517 
1518 NS_IMETHODIMP
SetPrivateBrowsing(bool aUsePrivateBrowsing)1519 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1520   return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1521 }
1522 
1523 NS_IMETHODIMP
GetHasLoadedNonBlankURI(bool * aResult)1524 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1525   NS_ENSURE_ARG_POINTER(aResult);
1526 
1527   *aResult = mHasLoadedNonBlankURI;
1528   return NS_OK;
1529 }
1530 
1531 NS_IMETHODIMP
GetUseRemoteTabs(bool * aUseRemoteTabs)1532 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1533   NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1534   return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1535 }
1536 
1537 NS_IMETHODIMP
SetRemoteTabs(bool aUseRemoteTabs)1538 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1539   return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1540 }
1541 
1542 NS_IMETHODIMP
GetUseRemoteSubframes(bool * aUseRemoteSubframes)1543 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1544   NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1545   return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1546 }
1547 
1548 NS_IMETHODIMP
SetRemoteSubframes(bool aUseRemoteSubframes)1549 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1550   return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1551 }
1552 
1553 NS_IMETHODIMP
SetAffectPrivateSessionLifetime(bool aAffectLifetime)1554 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) {
1555   MOZ_ASSERT(!mIsBeingDestroyed);
1556 
1557   bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
1558   if (change && UsePrivateBrowsing()) {
1559     if (aAffectLifetime) {
1560       IncreasePrivateDocShellCount();
1561     } else {
1562       DecreasePrivateDocShellCount();
1563     }
1564   }
1565   mAffectPrivateSessionLifetime = aAffectLifetime;
1566 
1567   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
1568   while (iter.HasMore()) {
1569     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
1570     if (shell) {
1571       shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
1572     }
1573   }
1574   return NS_OK;
1575 }
1576 
1577 NS_IMETHODIMP
GetAffectPrivateSessionLifetime(bool * aAffectLifetime)1578 nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime) {
1579   *aAffectLifetime = mAffectPrivateSessionLifetime;
1580   return NS_OK;
1581 }
1582 
1583 NS_IMETHODIMP
AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver * aObserver)1584 nsDocShell::AddWeakPrivacyTransitionObserver(
1585     nsIPrivacyTransitionObserver* aObserver) {
1586   nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1587   if (!weakObs) {
1588     return NS_ERROR_NOT_AVAILABLE;
1589   }
1590   mPrivacyObservers.AppendElement(weakObs);
1591   return NS_OK;
1592 }
1593 
1594 NS_IMETHODIMP
AddWeakReflowObserver(nsIReflowObserver * aObserver)1595 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1596   nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1597   if (!weakObs) {
1598     return NS_ERROR_FAILURE;
1599   }
1600   mReflowObservers.AppendElement(weakObs);
1601   return NS_OK;
1602 }
1603 
1604 NS_IMETHODIMP
RemoveWeakReflowObserver(nsIReflowObserver * aObserver)1605 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1606   nsWeakPtr obs = do_GetWeakReference(aObserver);
1607   return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1608 }
1609 
1610 NS_IMETHODIMP
NotifyReflowObservers(bool aInterruptible,DOMHighResTimeStamp aStart,DOMHighResTimeStamp aEnd)1611 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1612                                   DOMHighResTimeStamp aStart,
1613                                   DOMHighResTimeStamp aEnd) {
1614   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1615   while (iter.HasMore()) {
1616     nsWeakPtr ref = iter.GetNext();
1617     nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1618     if (!obs) {
1619       mReflowObservers.RemoveElement(ref);
1620     } else if (aInterruptible) {
1621       obs->ReflowInterruptible(aStart, aEnd);
1622     } else {
1623       obs->Reflow(aStart, aEnd);
1624     }
1625   }
1626   return NS_OK;
1627 }
1628 
1629 NS_IMETHODIMP
GetAllowMetaRedirects(bool * aReturn)1630 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1631   NS_ENSURE_ARG_POINTER(aReturn);
1632 
1633   *aReturn = mAllowMetaRedirects;
1634   return NS_OK;
1635 }
1636 
1637 NS_IMETHODIMP
SetAllowMetaRedirects(bool aValue)1638 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1639   mAllowMetaRedirects = aValue;
1640   return NS_OK;
1641 }
1642 
1643 NS_IMETHODIMP
GetAllowSubframes(bool * aAllowSubframes)1644 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1645   NS_ENSURE_ARG_POINTER(aAllowSubframes);
1646 
1647   *aAllowSubframes = mAllowSubframes;
1648   return NS_OK;
1649 }
1650 
1651 NS_IMETHODIMP
SetAllowSubframes(bool aAllowSubframes)1652 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1653   mAllowSubframes = aAllowSubframes;
1654   return NS_OK;
1655 }
1656 
1657 NS_IMETHODIMP
GetAllowImages(bool * aAllowImages)1658 nsDocShell::GetAllowImages(bool* aAllowImages) {
1659   NS_ENSURE_ARG_POINTER(aAllowImages);
1660 
1661   *aAllowImages = mAllowImages;
1662   return NS_OK;
1663 }
1664 
1665 NS_IMETHODIMP
SetAllowImages(bool aAllowImages)1666 nsDocShell::SetAllowImages(bool aAllowImages) {
1667   mAllowImages = aAllowImages;
1668   return NS_OK;
1669 }
1670 
1671 NS_IMETHODIMP
GetAllowMedia(bool * aAllowMedia)1672 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1673   *aAllowMedia = mAllowMedia;
1674   return NS_OK;
1675 }
1676 
1677 NS_IMETHODIMP
SetAllowMedia(bool aAllowMedia)1678 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1679   mAllowMedia = aAllowMedia;
1680 
1681   // Mute or unmute audio contexts attached to the inner window.
1682   if (mScriptGlobal) {
1683     if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1684       if (aAllowMedia) {
1685         innerWin->UnmuteAudioContexts();
1686       } else {
1687         innerWin->MuteAudioContexts();
1688       }
1689     }
1690   }
1691 
1692   return NS_OK;
1693 }
1694 
1695 NS_IMETHODIMP
GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)1696 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1697   *aAllowDNSPrefetch = mAllowDNSPrefetch;
1698   return NS_OK;
1699 }
1700 
1701 NS_IMETHODIMP
SetAllowDNSPrefetch(bool aAllowDNSPrefetch)1702 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
1703   mAllowDNSPrefetch = aAllowDNSPrefetch;
1704   return NS_OK;
1705 }
1706 
1707 NS_IMETHODIMP
GetAllowWindowControl(bool * aAllowWindowControl)1708 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
1709   *aAllowWindowControl = mAllowWindowControl;
1710   return NS_OK;
1711 }
1712 
1713 NS_IMETHODIMP
SetAllowWindowControl(bool aAllowWindowControl)1714 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
1715   mAllowWindowControl = aAllowWindowControl;
1716   return NS_OK;
1717 }
1718 
1719 NS_IMETHODIMP
GetAllowContentRetargeting(bool * aAllowContentRetargeting)1720 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
1721   *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
1722   return NS_OK;
1723 }
1724 
1725 NS_IMETHODIMP
SetAllowContentRetargeting(bool aAllowContentRetargeting)1726 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
1727   mBrowsingContext->SetAllowContentRetargeting(aAllowContentRetargeting);
1728   return NS_OK;
1729 }
1730 
1731 NS_IMETHODIMP
GetAllowContentRetargetingOnChildren(bool * aAllowContentRetargetingOnChildren)1732 nsDocShell::GetAllowContentRetargetingOnChildren(
1733     bool* aAllowContentRetargetingOnChildren) {
1734   *aAllowContentRetargetingOnChildren =
1735       mBrowsingContext->GetAllowContentRetargetingOnChildren();
1736   return NS_OK;
1737 }
1738 
1739 NS_IMETHODIMP
SetAllowContentRetargetingOnChildren(bool aAllowContentRetargetingOnChildren)1740 nsDocShell::SetAllowContentRetargetingOnChildren(
1741     bool aAllowContentRetargetingOnChildren) {
1742   mBrowsingContext->SetAllowContentRetargetingOnChildren(
1743       aAllowContentRetargetingOnChildren);
1744   return NS_OK;
1745 }
1746 
1747 NS_IMETHODIMP
GetFullscreenAllowed(bool * aFullscreenAllowed)1748 nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed) {
1749   NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
1750 
1751   // Browsers and apps have their mFullscreenAllowed retrieved from their
1752   // corresponding iframe in their parent upon creation.
1753   if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
1754     *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
1755     return NS_OK;
1756   }
1757 
1758   // Assume false until we determine otherwise...
1759   *aFullscreenAllowed = false;
1760 
1761   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1762   if (!win) {
1763     return NS_OK;
1764   }
1765   if (nsCOMPtr<Element> frameElement = win->GetFrameElementInternal()) {
1766     if (frameElement->IsXULElement()) {
1767       if (frameElement->HasAttr(kNameSpaceID_None,
1768                                 nsGkAtoms::disablefullscreen)) {
1769         // Document inside this frame is explicitly disabled.
1770         return NS_OK;
1771       }
1772     } else {
1773       // We do not allow document inside any containing element other
1774       // than iframe to enter fullscreen.
1775       if (auto* iframe = HTMLIFrameElement::FromNode(*frameElement)) {
1776         // If any ancestor iframe does not have allowfullscreen attribute
1777         // set, then fullscreen is not allowed.
1778         if (!iframe->AllowFullscreen()) {
1779           return NS_OK;
1780         }
1781       } else if (frameElement->IsHTMLElement(nsGkAtoms::embed)) {
1782         // Respect allowfullscreen only if this is a rewritten YouTube embed.
1783         nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent =
1784             do_QueryInterface(frameElement);
1785         if (!objectLoadingContent) {
1786           return NS_OK;
1787         }
1788         nsObjectLoadingContent* olc =
1789             static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
1790         if (!olc->IsRewrittenYoutubeEmbed()) {
1791           return NS_OK;
1792         }
1793         // We don't have to check prefixed attributes because Flash does not
1794         // support them.
1795         if (!frameElement->HasAttr(kNameSpaceID_None,
1796                                    nsGkAtoms::allowfullscreen)) {
1797           return NS_OK;
1798         }
1799       } else {
1800         // neither iframe nor embed
1801         return NS_OK;
1802       }
1803     }
1804   }
1805 
1806   // If we have no parent then we're the root docshell; no ancestor of the
1807   // original docshell doesn't have a allowfullscreen attribute, so
1808   // report fullscreen as allowed.
1809   RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
1810   if (!parent) {
1811     *aFullscreenAllowed = true;
1812     return NS_OK;
1813   }
1814 
1815   // Otherwise, we have a parent, continue the checking for
1816   // mozFullscreenAllowed in the parent docshell's ancestors.
1817   return parent->GetFullscreenAllowed(aFullscreenAllowed);
1818 }
1819 
1820 NS_IMETHODIMP
GetMayEnableCharacterEncodingMenu(bool * aMayEnableCharacterEncodingMenu)1821 nsDocShell::GetMayEnableCharacterEncodingMenu(
1822     bool* aMayEnableCharacterEncodingMenu) {
1823   *aMayEnableCharacterEncodingMenu = false;
1824   if (!mContentViewer) {
1825     return NS_OK;
1826   }
1827   Document* doc = mContentViewer->GetDocument();
1828   if (!doc) {
1829     return NS_OK;
1830   }
1831   if (doc->WillIgnoreCharsetOverride()) {
1832     return NS_OK;
1833   }
1834 
1835   *aMayEnableCharacterEncodingMenu = true;
1836   return NS_OK;
1837 }
1838 
1839 NS_IMETHODIMP
GetCharsetAutodetected(bool * aCharsetAutodetected)1840 nsDocShell::GetCharsetAutodetected(bool* aCharsetAutodetected) {
1841   *aCharsetAutodetected = false;
1842   if (!mContentViewer) {
1843     return NS_OK;
1844   }
1845   Document* doc = mContentViewer->GetDocument();
1846   if (!doc) {
1847     return NS_OK;
1848   }
1849   int32_t source = doc->GetDocumentCharacterSetSource();
1850 
1851   if (source == kCharsetFromInitialAutoDetection ||
1852       source == kCharsetFromFinalAutoDetection ||
1853       source == kCharsetFromUserForcedAutoDetection) {
1854     *aCharsetAutodetected = true;
1855   }
1856 
1857   return NS_OK;
1858 }
1859 
1860 NS_IMETHODIMP
GetAllDocShellsInSubtree(int32_t aItemType,DocShellEnumeratorDirection aDirection,nsTArray<RefPtr<nsIDocShell>> & aResult)1861 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
1862                                      DocShellEnumeratorDirection aDirection,
1863                                      nsTArray<RefPtr<nsIDocShell>>& aResult) {
1864   aResult.Clear();
1865 
1866   nsDocShellEnumerator docShellEnum(
1867       (aDirection == ENUMERATE_FORWARDS)
1868           ? nsDocShellEnumerator::EnumerationDirection::Forwards
1869           : nsDocShellEnumerator::EnumerationDirection::Backwards,
1870       aItemType, *this);
1871 
1872   nsresult rv = docShellEnum.BuildDocShellArray(aResult);
1873   if (NS_FAILED(rv)) {
1874     return rv;
1875   }
1876 
1877   return NS_OK;
1878 }
1879 
1880 NS_IMETHODIMP
GetAppType(AppType * aAppType)1881 nsDocShell::GetAppType(AppType* aAppType) {
1882   *aAppType = mAppType;
1883   return NS_OK;
1884 }
1885 
1886 NS_IMETHODIMP
SetAppType(AppType aAppType)1887 nsDocShell::SetAppType(AppType aAppType) {
1888   mAppType = aAppType;
1889   return NS_OK;
1890 }
1891 
1892 NS_IMETHODIMP
GetAllowAuth(bool * aAllowAuth)1893 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
1894   *aAllowAuth = mAllowAuth;
1895   return NS_OK;
1896 }
1897 
1898 NS_IMETHODIMP
SetAllowAuth(bool aAllowAuth)1899 nsDocShell::SetAllowAuth(bool aAllowAuth) {
1900   mAllowAuth = aAllowAuth;
1901   return NS_OK;
1902 }
1903 
1904 NS_IMETHODIMP
GetZoom(float * aZoom)1905 nsDocShell::GetZoom(float* aZoom) {
1906   NS_ENSURE_ARG_POINTER(aZoom);
1907   *aZoom = 1.0f;
1908   return NS_OK;
1909 }
1910 
1911 NS_IMETHODIMP
SetZoom(float aZoom)1912 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
1913 
1914 NS_IMETHODIMP
GetBusyFlags(BusyFlags * aBusyFlags)1915 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
1916   NS_ENSURE_ARG_POINTER(aBusyFlags);
1917 
1918   *aBusyFlags = mBusyFlags;
1919   return NS_OK;
1920 }
1921 
1922 NS_IMETHODIMP
TabToTreeOwner(bool aForward,bool aForDocumentNavigation,bool * aTookFocus)1923 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
1924                            bool* aTookFocus) {
1925   NS_ENSURE_ARG_POINTER(aTookFocus);
1926 
1927   nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
1928   if (chromeFocus) {
1929     if (aForward) {
1930       *aTookFocus =
1931           NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
1932     } else {
1933       *aTookFocus =
1934           NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
1935     }
1936   } else {
1937     *aTookFocus = false;
1938   }
1939 
1940   return NS_OK;
1941 }
1942 
1943 NS_IMETHODIMP
GetLoadURIDelegate(nsILoadURIDelegate ** aLoadURIDelegate)1944 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
1945   nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
1946   delegate.forget(aLoadURIDelegate);
1947   return NS_OK;
1948 }
1949 
GetLoadURIDelegate()1950 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
1951   if (nsCOMPtr<nsILoadURIDelegate> result =
1952           do_QueryActor("LoadURIDelegate", GetWindow())) {
1953     return result.forget();
1954   }
1955 
1956   return nullptr;
1957 }
1958 
1959 NS_IMETHODIMP
GetUseErrorPages(bool * aUseErrorPages)1960 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
1961   *aUseErrorPages = mUseErrorPages;
1962   return NS_OK;
1963 }
1964 
1965 NS_IMETHODIMP
SetUseErrorPages(bool aUseErrorPages)1966 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
1967   mUseErrorPages = aUseErrorPages;
1968   return NS_OK;
1969 }
1970 
1971 NS_IMETHODIMP
GetPreviousEntryIndex(int32_t * aPreviousEntryIndex)1972 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
1973   *aPreviousEntryIndex = mPreviousEntryIndex;
1974   return NS_OK;
1975 }
1976 
1977 NS_IMETHODIMP
GetLoadedEntryIndex(int32_t * aLoadedEntryIndex)1978 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
1979   *aLoadedEntryIndex = mLoadedEntryIndex;
1980   return NS_OK;
1981 }
1982 
1983 NS_IMETHODIMP
HistoryPurged(int32_t aNumEntries)1984 nsDocShell::HistoryPurged(int32_t aNumEntries) {
1985   // These indices are used for fastback cache eviction, to determine
1986   // which session history entries are candidates for content viewer
1987   // eviction.  We need to adjust by the number of entries that we
1988   // just purged from history, so that we look at the right session history
1989   // entries during eviction.
1990   mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
1991   mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
1992 
1993   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
1994   while (iter.HasMore()) {
1995     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
1996     if (shell) {
1997       shell->HistoryPurged(aNumEntries);
1998     }
1999   }
2000 
2001   return NS_OK;
2002 }
2003 
TriggerParentCheckDocShellIsEmpty()2004 void nsDocShell::TriggerParentCheckDocShellIsEmpty() {
2005   if (RefPtr<nsDocShell> parent = GetInProcessParentDocshell()) {
2006     parent->DocLoaderIsEmpty(true);
2007   }
2008   if (GetBrowsingContext()->IsContentSubframe() &&
2009       !GetBrowsingContext()->GetParent()->IsInProcess()) {
2010     if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
2011       mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2012           EmbedderElementEventType::NoEvent);
2013     }
2014   }
2015 }
2016 
HistoryEntryRemoved(int32_t aIndex)2017 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2018   // These indices are used for fastback cache eviction, to determine
2019   // which session history entries are candidates for content viewer
2020   // eviction.  We need to adjust by the number of entries that we
2021   // just purged from history, so that we look at the right session history
2022   // entries during eviction.
2023   if (aIndex == mPreviousEntryIndex) {
2024     mPreviousEntryIndex = -1;
2025   } else if (aIndex < mPreviousEntryIndex) {
2026     --mPreviousEntryIndex;
2027   }
2028   if (mLoadedEntryIndex == aIndex) {
2029     mLoadedEntryIndex = 0;
2030   } else if (aIndex < mLoadedEntryIndex) {
2031     --mLoadedEntryIndex;
2032   }
2033 
2034   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2035   while (iter.HasMore()) {
2036     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2037     if (shell) {
2038       static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2039     }
2040   }
2041 
2042   return NS_OK;
2043 }
2044 
2045 NS_IMETHODIMP
SetRecordProfileTimelineMarkers(bool aValue)2046 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) {
2047   bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
2048   if (currentValue == aValue) {
2049     return NS_OK;
2050   }
2051 
2052   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2053   if (!timelines) {
2054     return NS_OK;
2055   }
2056 
2057   if (aValue) {
2058     MOZ_ASSERT(!timelines->HasConsumer(this));
2059     timelines->AddConsumer(this);
2060     MOZ_ASSERT(timelines->HasConsumer(this));
2061     UseEntryScriptProfiling();
2062   } else {
2063     MOZ_ASSERT(timelines->HasConsumer(this));
2064     timelines->RemoveConsumer(this);
2065     MOZ_ASSERT(!timelines->HasConsumer(this));
2066     UnuseEntryScriptProfiling();
2067   }
2068 
2069   return NS_OK;
2070 }
2071 
2072 NS_IMETHODIMP
GetRecordProfileTimelineMarkers(bool * aValue)2073 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) {
2074   *aValue = !!mObserved;
2075   return NS_OK;
2076 }
2077 
PopProfileTimelineMarkers(JSContext * aCx,JS::MutableHandle<JS::Value> aOut)2078 nsresult nsDocShell::PopProfileTimelineMarkers(
2079     JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
2080   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2081   if (!timelines) {
2082     return NS_OK;
2083   }
2084 
2085   nsTArray<dom::ProfileTimelineMarker> store;
2086   SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
2087 
2088   timelines->PopMarkers(this, aCx, store);
2089 
2090   if (!ToJSValue(aCx, store, aOut)) {
2091     JS_ClearPendingException(aCx);
2092     return NS_ERROR_UNEXPECTED;
2093   }
2094 
2095   return NS_OK;
2096 }
2097 
Now(DOMHighResTimeStamp * aWhen)2098 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2099   *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2100   return NS_OK;
2101 }
2102 
2103 NS_IMETHODIMP
SetWindowDraggingAllowed(bool aValue)2104 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2105   RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2106   if (!aValue && mItemType == typeChrome && !parent) {
2107     // Window dragging is always allowed for top level
2108     // chrome docshells.
2109     return NS_ERROR_FAILURE;
2110   }
2111   mWindowDraggingAllowed = aValue;
2112   return NS_OK;
2113 }
2114 
2115 NS_IMETHODIMP
GetWindowDraggingAllowed(bool * aValue)2116 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2117   // window dragging regions in CSS (-moz-window-drag:drag)
2118   // can be slow. Default behavior is to only allow it for
2119   // chrome top level windows.
2120   RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2121   if (mItemType == typeChrome && !parent) {
2122     // Top level chrome window
2123     *aValue = true;
2124   } else {
2125     *aValue = mWindowDraggingAllowed;
2126   }
2127   return NS_OK;
2128 }
2129 
2130 NS_IMETHODIMP
GetCurrentDocumentChannel(nsIChannel ** aResult)2131 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2132   NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2133   return NS_OK;
2134 }
2135 
GetCurrentDocChannel()2136 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2137   if (mContentViewer) {
2138     Document* doc = mContentViewer->GetDocument();
2139     if (doc) {
2140       return doc->GetChannel();
2141     }
2142   }
2143   return nullptr;
2144 }
2145 
2146 NS_IMETHODIMP
AddWeakScrollObserver(nsIScrollObserver * aObserver)2147 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2148   nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2149   if (!weakObs) {
2150     return NS_ERROR_FAILURE;
2151   }
2152   mScrollObservers.AppendElement(weakObs);
2153   return NS_OK;
2154 }
2155 
2156 NS_IMETHODIMP
RemoveWeakScrollObserver(nsIScrollObserver * aObserver)2157 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2158   nsWeakPtr obs = do_GetWeakReference(aObserver);
2159   return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2160 }
2161 
NotifyAsyncPanZoomStarted()2162 void nsDocShell::NotifyAsyncPanZoomStarted() {
2163   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2164   while (iter.HasMore()) {
2165     nsWeakPtr ref = iter.GetNext();
2166     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2167     if (obs) {
2168       obs->AsyncPanZoomStarted();
2169     } else {
2170       mScrollObservers.RemoveElement(ref);
2171     }
2172   }
2173 }
2174 
NotifyAsyncPanZoomStopped()2175 void nsDocShell::NotifyAsyncPanZoomStopped() {
2176   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2177   while (iter.HasMore()) {
2178     nsWeakPtr ref = iter.GetNext();
2179     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2180     if (obs) {
2181       obs->AsyncPanZoomStopped();
2182     } else {
2183       mScrollObservers.RemoveElement(ref);
2184     }
2185   }
2186 }
2187 
2188 NS_IMETHODIMP
NotifyScrollObservers()2189 nsDocShell::NotifyScrollObservers() {
2190   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2191   while (iter.HasMore()) {
2192     nsWeakPtr ref = iter.GetNext();
2193     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2194     if (obs) {
2195       obs->ScrollPositionChanged();
2196     } else {
2197       mScrollObservers.RemoveElement(ref);
2198     }
2199   }
2200   return NS_OK;
2201 }
2202 
2203 //*****************************************************************************
2204 // nsDocShell::nsIDocShellTreeItem
2205 //*****************************************************************************
2206 
2207 NS_IMETHODIMP
GetName(nsAString & aName)2208 nsDocShell::GetName(nsAString& aName) {
2209   aName = mBrowsingContext->Name();
2210   return NS_OK;
2211 }
2212 
2213 NS_IMETHODIMP
SetName(const nsAString & aName)2214 nsDocShell::SetName(const nsAString& aName) {
2215   mBrowsingContext->SetName(aName);
2216   return NS_OK;
2217 }
2218 
2219 NS_IMETHODIMP
NameEquals(const nsAString & aName,bool * aResult)2220 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2221   NS_ENSURE_ARG_POINTER(aResult);
2222   *aResult = mBrowsingContext->NameEquals(aName);
2223   return NS_OK;
2224 }
2225 
2226 NS_IMETHODIMP
GetCustomUserAgent(nsAString & aCustomUserAgent)2227 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2228   mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2229   return NS_OK;
2230 }
2231 
2232 NS_IMETHODIMP
SetCustomUserAgent(const nsAString & aCustomUserAgent)2233 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2234   if (mWillChangeProcess) {
2235     NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2236     return NS_ERROR_FAILURE;
2237   }
2238 
2239   mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2240   return NS_OK;
2241 }
2242 
2243 NS_IMETHODIMP
ClearCachedUserAgent()2244 nsDocShell::ClearCachedUserAgent() {
2245   RefPtr<nsGlobalWindowInner> win =
2246       mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2247   if (win) {
2248     Navigator* navigator = win->Navigator();
2249     if (navigator) {
2250       navigator->ClearUserAgentCache();
2251     }
2252   }
2253 
2254   return NS_OK;
2255 }
2256 
2257 NS_IMETHODIMP
GetTouchEventsOverride(TouchEventsOverride * aTouchEventsOverride)2258 nsDocShell::GetTouchEventsOverride(TouchEventsOverride* aTouchEventsOverride) {
2259   *aTouchEventsOverride = mTouchEventsOverride;
2260   return NS_OK;
2261 }
2262 
2263 NS_IMETHODIMP
SetTouchEventsOverride(TouchEventsOverride aTouchEventsOverride)2264 nsDocShell::SetTouchEventsOverride(TouchEventsOverride aTouchEventsOverride) {
2265   // We don't have a way to verify this coming from Javascript, so this check is
2266   // still needed.
2267   if (!(aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_NONE ||
2268         aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_ENABLED ||
2269         aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_DISABLED)) {
2270     return NS_ERROR_INVALID_ARG;
2271   }
2272 
2273   mTouchEventsOverride = aTouchEventsOverride;
2274 
2275   uint32_t childCount = mChildList.Length();
2276   for (uint32_t i = 0; i < childCount; ++i) {
2277     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
2278     if (childShell) {
2279       childShell->SetTouchEventsOverride(aTouchEventsOverride);
2280     }
2281   }
2282   return NS_OK;
2283 }
2284 
2285 NS_IMETHODIMP
GetMetaViewportOverride(MetaViewportOverride * aMetaViewportOverride)2286 nsDocShell::GetMetaViewportOverride(
2287     MetaViewportOverride* aMetaViewportOverride) {
2288   NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
2289 
2290   *aMetaViewportOverride = mMetaViewportOverride;
2291   return NS_OK;
2292 }
2293 
2294 NS_IMETHODIMP
SetMetaViewportOverride(MetaViewportOverride aMetaViewportOverride)2295 nsDocShell::SetMetaViewportOverride(
2296     MetaViewportOverride aMetaViewportOverride) {
2297   // We don't have a way to verify this coming from Javascript, so this check is
2298   // still needed.
2299   if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
2300         aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
2301         aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
2302     return NS_ERROR_INVALID_ARG;
2303   }
2304 
2305   mMetaViewportOverride = aMetaViewportOverride;
2306 
2307   // Inform our presShell that it needs to re-check its need for a viewport
2308   // override.
2309   if (RefPtr<PresShell> presShell = GetPresShell()) {
2310     presShell->UpdateViewportOverridden(true);
2311   }
2312 
2313   return NS_OK;
2314 }
2315 
2316 /* virtual */
ItemType()2317 int32_t nsDocShell::ItemType() { return mItemType; }
2318 
2319 NS_IMETHODIMP
GetItemType(int32_t * aItemType)2320 nsDocShell::GetItemType(int32_t* aItemType) {
2321   NS_ENSURE_ARG_POINTER(aItemType);
2322 
2323   MOZ_DIAGNOSTIC_ASSERT(
2324       (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2325   *aItemType = mItemType;
2326   return NS_OK;
2327 }
2328 
2329 NS_IMETHODIMP
GetInProcessParent(nsIDocShellTreeItem ** aParent)2330 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2331   if (!mParent) {
2332     *aParent = nullptr;
2333   } else {
2334     CallQueryInterface(mParent, aParent);
2335   }
2336   // Note that in the case when the parent is not an nsIDocShellTreeItem we
2337   // don't want to throw; we just want to return null.
2338   return NS_OK;
2339 }
2340 
2341 // With Fission, related nsDocShell objects may exist in a different process. In
2342 // that case, this method will return `nullptr`, despite a parent nsDocShell
2343 // object existing.
2344 //
2345 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2346 // parent entry is not in the current process, and handle the case where the
2347 // parent nsDocShell is inaccessible.
GetInProcessParentDocshell()2348 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2349   nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2350   return docshell.forget().downcast<nsDocShell>();
2351 }
2352 
MaybeCreateInitialClientSource(nsIPrincipal * aPrincipal)2353 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2354   MOZ_ASSERT(!mIsBeingDestroyed);
2355 
2356   // If there is an existing document then there is no need to create
2357   // a client for a future initial about:blank document.
2358   if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
2359       mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
2360     MOZ_DIAGNOSTIC_ASSERT(mScriptGlobal->GetCurrentInnerWindowInternal()
2361                               ->GetClientInfo()
2362                               .isSome());
2363     MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2364     return;
2365   }
2366 
2367   // Don't recreate the initial client source.  We call this multiple times
2368   // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
2369   if (mInitialClientSource) {
2370     return;
2371   }
2372 
2373   // Don't pre-allocate the client when we are sandboxed.  The inherited
2374   // principal does not take sandboxing into account.
2375   // TODO: Refactor sandboxing principal code out so we can use it here.
2376   if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2377     return;
2378   }
2379 
2380   nsIPrincipal* principal =
2381       aPrincipal ? aPrincipal : GetInheritedPrincipal(false);
2382 
2383   // Sometimes there is no principal available when we are called from
2384   // CreateAboutBlankContentViewer.  For example, sometimes the principal
2385   // is only extracted from the load context after the document is created
2386   // in Document::ResetToURI().  Ideally we would do something similar
2387   // here, but for now lets just avoid the issue by not preallocating the
2388   // client.
2389   if (!principal) {
2390     return;
2391   }
2392 
2393   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2394   if (!win) {
2395     return;
2396   }
2397 
2398   mInitialClientSource = ClientManager::CreateSource(
2399       ClientType::Window, win->EventTargetFor(TaskCategory::Other), principal);
2400   MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2401 
2402   // Mark the initial client as execution ready, but owned by the docshell.
2403   // If the client is actually used this will cause ClientSource to force
2404   // the creation of the initial about:blank by calling
2405   // nsDocShell::GetDocument().
2406   mInitialClientSource->DocShellExecutionReady(this);
2407 
2408   // Next, check to see if the parent is controlled.
2409   nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2410   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2411   nsPIDOMWindowInner* parentInner =
2412       parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2413   if (!parentInner) {
2414     return;
2415   }
2416 
2417   nsCOMPtr<nsIURI> uri;
2418   MOZ_ALWAYS_SUCCEEDS(
2419       NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank")));
2420 
2421   // We're done if there is no parent controller or if this docshell
2422   // is not permitted to control for some reason.
2423   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2424   if (controller.isNothing() ||
2425       !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2426     return;
2427   }
2428 
2429   mInitialClientSource->InheritController(controller.ref());
2430 }
2431 
GetInitialClientInfo() const2432 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2433   if (mInitialClientSource) {
2434     Maybe<ClientInfo> result;
2435     result.emplace(mInitialClientSource->Info());
2436     return result;
2437   }
2438 
2439   nsGlobalWindowInner* innerWindow =
2440       mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2441   Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2442 
2443   if (!doc || !doc->IsInitialDocument()) {
2444     return Maybe<ClientInfo>();
2445   }
2446 
2447   return innerWindow->GetClientInfo();
2448 }
2449 
RecomputeCanExecuteScripts()2450 void nsDocShell::RecomputeCanExecuteScripts() {
2451   bool old = mCanExecuteScripts;
2452   RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2453 
2454   // If we have no tree owner, that means that we've been detached from the
2455   // docshell tree (this is distinct from having no parent docshell, which
2456   // is the case for root docshells). It would be nice to simply disallow
2457   // script in detached docshells, but bug 986542 demonstrates that this
2458   // behavior breaks at least one website.
2459   //
2460   // So instead, we use our previous value, unless mAllowJavascript has been
2461   // explicitly set to false.
2462   if (!mTreeOwner) {
2463     mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
2464     // If scripting has been explicitly disabled on our docshell, we're done.
2465   } else if (!mAllowJavascript) {
2466     mCanExecuteScripts = false;
2467     // If we have a parent, inherit.
2468   } else if (parent) {
2469     mCanExecuteScripts = parent->mCanExecuteScripts;
2470     // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2471     // script. Allow.
2472   } else {
2473     mCanExecuteScripts = true;
2474   }
2475 
2476   // Inform our active DOM window.
2477   //
2478   // This will pass the outer, which will be in the scope of the active inner.
2479   if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
2480     xpc::Scriptability& scriptability =
2481         xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
2482     scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
2483   }
2484 
2485   // If our value has changed, our children might be affected. Recompute their
2486   // value as well.
2487   if (old != mCanExecuteScripts) {
2488     nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2489     while (iter.HasMore()) {
2490       static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
2491     }
2492   }
2493 }
2494 
SetDocLoaderParent(nsDocLoader * aParent)2495 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2496   bool wasFrame = IsFrame();
2497 
2498   nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2499   NS_ENSURE_SUCCESS(rv, rv);
2500 
2501   nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2502   if (wasFrame != IsFrame() && priorityGroup) {
2503     priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2504   }
2505 
2506   // Curse ambiguous nsISupports inheritance!
2507   nsISupports* parent = GetAsSupports(aParent);
2508 
2509   // If parent is another docshell, we inherit all their flags for
2510   // allowing plugins, scripting etc.
2511   bool value;
2512   nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2513 
2514   if (parentAsDocShell) {
2515     if (mAllowJavascript &&
2516         NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
2517       SetAllowJavascript(value);
2518     }
2519     if (mAllowMetaRedirects &&
2520         NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2521       SetAllowMetaRedirects(value);
2522     }
2523     if (mAllowSubframes &&
2524         NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2525       SetAllowSubframes(value);
2526     }
2527     if (mAllowImages &&
2528         NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2529       SetAllowImages(value);
2530     }
2531     SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2532     if (mAllowWindowControl &&
2533         NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2534       SetAllowWindowControl(value);
2535     }
2536     if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
2537       SetIsActive(value);
2538     }
2539     if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2540       value = false;
2541     }
2542     SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2543     SetAffectPrivateSessionLifetime(
2544         parentAsDocShell->GetAffectPrivateSessionLifetime());
2545 
2546     SetTouchEventsOverride(parentAsDocShell->GetTouchEventsOverride());
2547 
2548     // We don't need to inherit metaViewportOverride, because the viewport
2549     // is only relevant for the outermost nsDocShell, not for any iframes
2550     // like this that might be embedded within it.
2551   }
2552 
2553   nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2554   if (parentURIListener) {
2555     mContentListener->SetParentContentListener(parentURIListener);
2556   }
2557 
2558   // Our parent has changed. Recompute scriptability.
2559   RecomputeCanExecuteScripts();
2560 
2561   // Inform windows when they're being removed from their parent.
2562   if (!aParent) {
2563     MaybeClearStorageAccessFlag();
2564   }
2565 
2566   return NS_OK;
2567 }
2568 
MaybeClearStorageAccessFlag()2569 void nsDocShell::MaybeClearStorageAccessFlag() {
2570   if (mScriptGlobal) {
2571     // Tell our window that the parent has now changed.
2572     mScriptGlobal->ParentWindowChanged();
2573 
2574     // Tell all of our children about the change recursively as well.
2575     nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2576     while (iter.HasMore()) {
2577       nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
2578       if (child) {
2579         static_cast<nsDocShell*>(child.get())->MaybeClearStorageAccessFlag();
2580       }
2581     }
2582   }
2583 }
2584 
2585 NS_IMETHODIMP
GetInProcessSameTypeParent(nsIDocShellTreeItem ** aParent)2586 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2587   if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2588     *aParent = do_AddRef(parentBC->GetDocShell()).take();
2589   }
2590   return NS_OK;
2591 }
2592 
2593 NS_IMETHODIMP
GetSameTypeInProcessParentIgnoreBrowserBoundaries(nsIDocShell ** aParent)2594 nsDocShell::GetSameTypeInProcessParentIgnoreBrowserBoundaries(
2595     nsIDocShell** aParent) {
2596   NS_ENSURE_ARG_POINTER(aParent);
2597   *aParent = nullptr;
2598 
2599   nsCOMPtr<nsIDocShellTreeItem> parent =
2600       do_QueryInterface(GetAsSupports(mParent));
2601   if (!parent) {
2602     return NS_OK;
2603   }
2604 
2605   if (parent->ItemType() == mItemType) {
2606     nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
2607     parentDS.forget(aParent);
2608   }
2609   return NS_OK;
2610 }
2611 
2612 NS_IMETHODIMP
GetInProcessRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)2613 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2614   NS_ENSURE_ARG_POINTER(aRootTreeItem);
2615 
2616   RefPtr<nsDocShell> root = this;
2617   RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2618   while (parent) {
2619     root = parent;
2620     parent = root->GetInProcessParentDocshell();
2621   }
2622 
2623   root.forget(aRootTreeItem);
2624   return NS_OK;
2625 }
2626 
2627 NS_IMETHODIMP
GetInProcessSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)2628 nsDocShell::GetInProcessSameTypeRootTreeItem(
2629     nsIDocShellTreeItem** aRootTreeItem) {
2630   NS_ENSURE_ARG_POINTER(aRootTreeItem);
2631   *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2632 
2633   nsCOMPtr<nsIDocShellTreeItem> parent;
2634   NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2635                     NS_ERROR_FAILURE);
2636   while (parent) {
2637     *aRootTreeItem = parent;
2638     NS_ENSURE_SUCCESS(
2639         (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2640         NS_ERROR_FAILURE);
2641   }
2642   NS_ADDREF(*aRootTreeItem);
2643   return NS_OK;
2644 }
2645 
2646 NS_IMETHODIMP
GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)2647 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2648   NS_ENSURE_ARG_POINTER(aTreeOwner);
2649 
2650   *aTreeOwner = mTreeOwner;
2651   NS_IF_ADDREF(*aTreeOwner);
2652   return NS_OK;
2653 }
2654 
2655 NS_IMETHODIMP
SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)2656 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2657   if (mIsBeingDestroyed && aTreeOwner) {
2658     return NS_ERROR_FAILURE;
2659   }
2660 
2661   // Don't automatically set the progress based on the tree owner for frames
2662   if (!IsFrame()) {
2663     nsCOMPtr<nsIWebProgress> webProgress =
2664         do_QueryInterface(GetAsSupports(this));
2665 
2666     if (webProgress) {
2667       nsCOMPtr<nsIWebProgressListener> oldListener =
2668           do_QueryInterface(mTreeOwner);
2669       nsCOMPtr<nsIWebProgressListener> newListener =
2670           do_QueryInterface(aTreeOwner);
2671 
2672       if (oldListener) {
2673         webProgress->RemoveProgressListener(oldListener);
2674       }
2675 
2676       if (newListener) {
2677         webProgress->AddProgressListener(newListener,
2678                                          nsIWebProgress::NOTIFY_ALL);
2679       }
2680     }
2681   }
2682 
2683   mTreeOwner = aTreeOwner;  // Weak reference per API
2684 
2685   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2686   while (iter.HasMore()) {
2687     nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
2688     NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2689 
2690     if (child->ItemType() == mItemType) {
2691       child->SetTreeOwner(aTreeOwner);
2692     }
2693   }
2694 
2695   // If we're in the content process and have had a TreeOwner set on us, extract
2696   // our BrowserChild actor. If we've already had our BrowserChild set, assert
2697   // that it hasn't changed.
2698   if (mTreeOwner && XRE_IsContentProcess()) {
2699     nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2700     MOZ_ASSERT(newBrowserChild,
2701                "No BrowserChild actor for tree owner in Content!");
2702 
2703     if (mBrowserChild) {
2704       nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2705           do_QueryReferent(mBrowserChild);
2706       MOZ_RELEASE_ASSERT(
2707           oldBrowserChild == newBrowserChild,
2708           "Cannot cahnge BrowserChild during nsDocShell lifetime!");
2709     } else {
2710       mBrowserChild = do_GetWeakReference(newBrowserChild);
2711     }
2712   }
2713 
2714   // Our tree owner has changed. Recompute scriptability.
2715   //
2716   // Note that this is near-redundant with the recomputation in
2717   // SetDocLoaderParent(), but not so for the root DocShell, where the call to
2718   // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
2719   // and we never set another parent. Given that this is neither expensive nor
2720   // performance-critical, let's be safe and unconditionally recompute this
2721   // state whenever dependent state changes.
2722   RecomputeCanExecuteScripts();
2723 
2724   return NS_OK;
2725 }
2726 
SetChildOffset(int32_t aChildOffset)2727 void nsDocShell::SetChildOffset(int32_t aChildOffset) {
2728   mChildOffset = aChildOffset;
2729 }
2730 
GetChildOffset()2731 int32_t nsDocShell::GetChildOffset() { return mChildOffset; }
2732 
2733 NS_IMETHODIMP
GetHistoryID(nsID ** aID)2734 nsDocShell::GetHistoryID(nsID** aID) {
2735   *aID = mHistoryID.Clone();
2736   return NS_OK;
2737 }
2738 
HistoryID()2739 const nsID nsDocShell::HistoryID() { return mHistoryID; }
2740 
2741 NS_IMETHODIMP
GetIsInUnload(bool * aIsInUnload)2742 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2743   *aIsInUnload = mFiredUnloadEvent;
2744   return NS_OK;
2745 }
2746 
2747 NS_IMETHODIMP
GetInProcessChildCount(int32_t * aChildCount)2748 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2749   NS_ENSURE_ARG_POINTER(aChildCount);
2750   *aChildCount = mChildList.Length();
2751   return NS_OK;
2752 }
2753 
2754 NS_IMETHODIMP
AddChild(nsIDocShellTreeItem * aChild)2755 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2756   NS_ENSURE_ARG_POINTER(aChild);
2757 
2758   RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2759   NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2760 
2761   // Make sure we're not creating a loop in the docshell tree
2762   nsDocLoader* ancestor = this;
2763   do {
2764     if (childAsDocLoader == ancestor) {
2765       return NS_ERROR_ILLEGAL_VALUE;
2766     }
2767     ancestor = ancestor->GetParent();
2768   } while (ancestor);
2769 
2770   // Make sure to remove the child from its current parent.
2771   nsDocLoader* childsParent = childAsDocLoader->GetParent();
2772   if (childsParent) {
2773     nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2774     NS_ENSURE_SUCCESS(rv, rv);
2775   }
2776 
2777   // Make sure to clear the treeowner in case this child is a different type
2778   // from us.
2779   aChild->SetTreeOwner(nullptr);
2780 
2781   nsresult res = AddChildLoader(childAsDocLoader);
2782   NS_ENSURE_SUCCESS(res, res);
2783   NS_ASSERTION(!mChildList.IsEmpty(),
2784                "child list must not be empty after a successful add");
2785 
2786   nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
2787   bool dynamic = false;
2788   childDocShell->GetCreatedDynamically(&dynamic);
2789   if (!dynamic) {
2790     nsCOMPtr<nsISHEntry> currentSH;
2791     bool oshe = false;
2792     GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
2793     if (currentSH) {
2794       currentSH->HasDynamicallyAddedChild(&dynamic);
2795     }
2796   }
2797   childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
2798 
2799   /* Set the child's global history if the parent has one */
2800   if (mBrowsingContext->GetUseGlobalHistory()) {
2801     // childDocShell->SetUseGlobalHistory(true);
2802     // this should be set through BC inherit
2803     MOZ_ASSERT(nsDocShell::Cast(childDocShell)
2804                    ->mBrowsingContext->GetUseGlobalHistory());
2805   }
2806 
2807   if (aChild->ItemType() != mItemType) {
2808     return NS_OK;
2809   }
2810 
2811   aChild->SetTreeOwner(mTreeOwner);
2812 
2813   nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2814   if (!childAsDocShell) {
2815     return NS_OK;
2816   }
2817 
2818   // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2819 
2820   // Now take this document's charset and set the child's parentCharset field
2821   // to it. We'll later use that field, in the loading process, for the
2822   // charset choosing algorithm.
2823   // If we fail, at any point, we just return NS_OK.
2824   // This code has some performance impact. But this will be reduced when
2825   // the current charset will finally be stored as an Atom, avoiding the
2826   // alias resolution extra look-up.
2827 
2828   // we are NOT going to propagate the charset is this Chrome's docshell
2829   if (mItemType == nsIDocShellTreeItem::typeChrome) {
2830     return NS_OK;
2831   }
2832 
2833   // get the parent's current charset
2834   if (!mContentViewer) {
2835     return NS_OK;
2836   }
2837   Document* doc = mContentViewer->GetDocument();
2838   if (!doc) {
2839     return NS_OK;
2840   }
2841 
2842   const Encoding* parentCS = doc->GetDocumentCharacterSet();
2843   int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2844   // set the child's parentCharset
2845   childAsDocShell->SetParentCharset(parentCS, charsetSource,
2846                                     doc->NodePrincipal());
2847 
2848   // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2849   //        NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2850 
2851   return NS_OK;
2852 }
2853 
2854 NS_IMETHODIMP
RemoveChild(nsIDocShellTreeItem * aChild)2855 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
2856   NS_ENSURE_ARG_POINTER(aChild);
2857 
2858   RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2859   NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2860 
2861   nsresult rv = RemoveChildLoader(childAsDocLoader);
2862   NS_ENSURE_SUCCESS(rv, rv);
2863 
2864   aChild->SetTreeOwner(nullptr);
2865 
2866   return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
2867 }
2868 
2869 NS_IMETHODIMP
GetInProcessChildAt(int32_t aIndex,nsIDocShellTreeItem ** aChild)2870 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
2871   NS_ENSURE_ARG_POINTER(aChild);
2872 
2873   RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
2874   NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
2875 
2876   child.forget(aChild);
2877 
2878   return NS_OK;
2879 }
2880 
GetInProcessChildAt(int32_t aIndex)2881 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
2882 #ifdef DEBUG
2883   if (aIndex < 0) {
2884     NS_WARNING("Negative index passed to GetChildAt");
2885   } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
2886     NS_WARNING("Too large an index passed to GetChildAt");
2887   }
2888 #endif
2889 
2890   nsIDocumentLoader* child = ChildAt(aIndex);
2891 
2892   // child may be nullptr here.
2893   return static_cast<nsDocShell*>(child);
2894 }
2895 
2896 NS_IMETHODIMP
AddChildSHEntry(nsISHEntry * aCloneRef,nsISHEntry * aNewEntry,int32_t aChildOffset,uint32_t aLoadType,bool aCloneChildren)2897 nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
2898                             int32_t aChildOffset, uint32_t aLoadType,
2899                             bool aCloneChildren) {
2900   nsresult rv = NS_OK;
2901 
2902   if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
2903     /* You get here if you are currently building a
2904      * hierarchy ie.,you just visited a frameset page
2905      */
2906     if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
2907       rv = mLSHE->AddChild(aNewEntry, aChildOffset);
2908     }
2909   } else if (!aCloneRef) {
2910     /* This is an initial load in some subframe.  Just append it if we can */
2911     if (mOSHE) {
2912       rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
2913     }
2914   } else {
2915     rv = AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset, aLoadType,
2916                                  aCloneChildren);
2917   }
2918   return rv;
2919 }
2920 
AddChildSHEntryInternal(nsISHEntry * aCloneRef,nsISHEntry * aNewEntry,int32_t aChildOffset,uint32_t aLoadType,bool aCloneChildren)2921 nsresult nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
2922                                              nsISHEntry* aNewEntry,
2923                                              int32_t aChildOffset,
2924                                              uint32_t aLoadType,
2925                                              bool aCloneChildren) {
2926   nsresult rv = NS_OK;
2927   if (GetSessionHistory()) {
2928     rv = GetSessionHistory()->LegacySHistory()->AddChildSHEntryHelper(
2929         aCloneRef, aNewEntry, mBrowsingContext, aCloneChildren);
2930   } else {
2931     /* Just pass this along */
2932     nsCOMPtr<nsIDocShell> parent =
2933         do_QueryInterface(GetAsSupports(mParent), &rv);
2934     if (parent) {
2935       rv = static_cast<nsDocShell*>(parent.get())
2936                ->AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset,
2937                                          aLoadType, aCloneChildren);
2938     }
2939   }
2940   return rv;
2941 }
2942 
AddChildSHEntryToParent(nsISHEntry * aNewEntry,int32_t aChildOffset,bool aCloneChildren)2943 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
2944                                              int32_t aChildOffset,
2945                                              bool aCloneChildren) {
2946   /* You will get here when you are in a subframe and
2947    * a new url has been loaded on you.
2948    * The mOSHE in this subframe will be the previous url's
2949    * mOSHE. This mOSHE will be used as the identification
2950    * for this subframe in the  CloneAndReplace function.
2951    */
2952 
2953   // In this case, we will end up calling AddEntry, which increases the
2954   // current index by 1
2955   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
2956   if (rootSH) {
2957     mPreviousEntryIndex = rootSH->Index();
2958   }
2959 
2960   nsresult rv;
2961   nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
2962   if (parent) {
2963     rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
2964                                  aCloneChildren);
2965   }
2966 
2967   if (rootSH) {
2968     mLoadedEntryIndex = rootSH->Index();
2969 
2970     if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
2971       MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
2972               ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
2973                mLoadedEntryIndex));
2974     }
2975   }
2976 
2977   return rv;
2978 }
2979 
2980 NS_IMETHODIMP
RemoveFromSessionHistory()2981 nsDocShell::RemoveFromSessionHistory() {
2982   RefPtr<ChildSHistory> sessionHistory =
2983       mBrowsingContext->Top()->GetChildSessionHistory();
2984   if (!sessionHistory) {
2985     return NS_OK;
2986   }
2987   int32_t index = sessionHistory->Index();
2988   AutoTArray<nsID, 16> ids({mHistoryID});
2989   sessionHistory->LegacySHistory()->RemoveEntries(ids, index);
2990   return NS_OK;
2991 }
2992 
2993 NS_IMETHODIMP
SetCreatedDynamically(bool aDynamic)2994 nsDocShell::SetCreatedDynamically(bool aDynamic) {
2995   mDynamicallyCreated = aDynamic;
2996   return NS_OK;
2997 }
2998 
2999 NS_IMETHODIMP
GetCreatedDynamically(bool * aDynamic)3000 nsDocShell::GetCreatedDynamically(bool* aDynamic) {
3001   *aDynamic = mDynamicallyCreated;
3002   return NS_OK;
3003 }
3004 
3005 NS_IMETHODIMP
GetCurrentSHEntry(nsISHEntry ** aEntry,bool * aOSHE)3006 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3007   *aOSHE = false;
3008   *aEntry = nullptr;
3009   if (mLSHE) {
3010     NS_ADDREF(*aEntry = mLSHE);
3011   } else if (mOSHE) {
3012     NS_ADDREF(*aEntry = mOSHE);
3013     *aOSHE = true;
3014   }
3015   return NS_OK;
3016 }
3017 
SynchronizeLayoutHistoryState()3018 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
3019   if (mOSHE) {
3020     mOSHE->SynchronizeLayoutHistoryState();
3021   }
3022   return NS_OK;
3023 }
3024 
SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags)3025 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
3026   if (mLoadGroup) {
3027     mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
3028   } else {
3029     NS_WARNING(
3030         "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3031         "propagate the mode to");
3032   }
3033 }
3034 
GetScriptGlobalObject()3035 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3036   NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3037   return mScriptGlobal;
3038 }
3039 
GetDocument()3040 Document* nsDocShell::GetDocument() {
3041   NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
3042   return mContentViewer->GetDocument();
3043 }
3044 
GetExtantDocument()3045 Document* nsDocShell::GetExtantDocument() {
3046   return mContentViewer ? mContentViewer->GetDocument() : nullptr;
3047 }
3048 
GetWindow()3049 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3050   if (NS_FAILED(EnsureScriptEnvironment())) {
3051     return nullptr;
3052   }
3053   return mScriptGlobal;
3054 }
3055 
3056 NS_IMETHODIMP
GetDomWindow(mozIDOMWindowProxy ** aWindow)3057 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3058   NS_ENSURE_ARG_POINTER(aWindow);
3059 
3060   nsresult rv = EnsureScriptEnvironment();
3061   NS_ENSURE_SUCCESS(rv, rv);
3062 
3063   RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3064   window.forget(aWindow);
3065   return NS_OK;
3066 }
3067 
3068 NS_IMETHODIMP
GetMessageManager(ContentFrameMessageManager ** aMessageManager)3069 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3070   RefPtr<ContentFrameMessageManager> mm;
3071   if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3072     mm = browserChild->GetMessageManager();
3073   } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3074     mm = win->GetMessageManager();
3075   }
3076   mm.forget(aMessageManager);
3077   return NS_OK;
3078 }
3079 
3080 NS_IMETHODIMP
GetIsNavigating(bool * aOut)3081 nsDocShell::GetIsNavigating(bool* aOut) {
3082   *aOut = mIsNavigating;
3083   return NS_OK;
3084 }
3085 
3086 NS_IMETHODIMP
SetDeviceSizeIsPageSize(bool aValue)3087 nsDocShell::SetDeviceSizeIsPageSize(bool aValue) {
3088   if (mDeviceSizeIsPageSize != aValue) {
3089     mDeviceSizeIsPageSize = aValue;
3090     RefPtr<nsPresContext> presContext = GetPresContext();
3091     if (presContext) {
3092       presContext->MediaFeatureValuesChanged(
3093           {MediaFeatureChangeReason::DeviceSizeIsPageSizeChange});
3094     }
3095   }
3096   return NS_OK;
3097 }
3098 
3099 NS_IMETHODIMP
GetDeviceSizeIsPageSize(bool * aValue)3100 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) {
3101   *aValue = mDeviceSizeIsPageSize;
3102   return NS_OK;
3103 }
3104 
ClearFrameHistory(nsISHEntry * aEntry)3105 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3106   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3107   if (!rootSH || !aEntry) {
3108     return;
3109   }
3110 
3111   rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3112 }
3113 
3114 //-------------------------------------
3115 //-- Helper Method for Print discovery
3116 //-------------------------------------
IsPrintingOrPP(bool aDisplayErrorDialog)3117 bool nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog) {
3118   if (mIsPrintingOrPP && aDisplayErrorDialog) {
3119     DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3120   }
3121 
3122   return mIsPrintingOrPP;
3123 }
3124 
IsNavigationAllowed(bool aDisplayPrintErrorDialog,bool aCheckIfUnloadFired)3125 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3126                                      bool aCheckIfUnloadFired) {
3127   bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
3128                    (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3129   if (!isAllowed) {
3130     return false;
3131   }
3132   if (!mContentViewer) {
3133     return true;
3134   }
3135   bool firingBeforeUnload;
3136   mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3137   return !firingBeforeUnload;
3138 }
3139 
3140 //*****************************************************************************
3141 // nsDocShell::nsIWebNavigation
3142 //*****************************************************************************
3143 
3144 NS_IMETHODIMP
GetCanGoBack(bool * aCanGoBack)3145 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3146   *aCanGoBack = false;
3147   if (!IsNavigationAllowed(false)) {
3148     return NS_OK;  // JS may not handle returning of an error code
3149   }
3150   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3151   if (rootSH) {
3152     *aCanGoBack = rootSH->CanGo(-1);
3153     return NS_OK;
3154   }
3155   return NS_ERROR_FAILURE;
3156 }
3157 
3158 NS_IMETHODIMP
GetCanGoForward(bool * aCanGoForward)3159 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3160   *aCanGoForward = false;
3161   if (!IsNavigationAllowed(false)) {
3162     return NS_OK;  // JS may not handle returning of an error code
3163   }
3164   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3165   if (rootSH) {
3166     *aCanGoForward = rootSH->CanGo(1);
3167     return NS_OK;
3168   }
3169   return NS_ERROR_FAILURE;
3170 }
3171 
3172 NS_IMETHODIMP
GoBack()3173 nsDocShell::GoBack() {
3174   if (!IsNavigationAllowed()) {
3175     return NS_OK;  // JS may not handle returning of an error code
3176   }
3177 
3178   auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3179   mIsNavigating = true;
3180 
3181   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3182   NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3183   ErrorResult rv;
3184   rootSH->Go(-1, rv);
3185   return rv.StealNSResult();
3186 }
3187 
3188 NS_IMETHODIMP
GoForward()3189 nsDocShell::GoForward() {
3190   if (!IsNavigationAllowed()) {
3191     return NS_OK;  // JS may not handle returning of an error code
3192   }
3193 
3194   auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3195   mIsNavigating = true;
3196 
3197   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3198   NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3199   ErrorResult rv;
3200   rootSH->Go(1, rv);
3201   return rv.StealNSResult();
3202 }
3203 
3204 // XXX(nika): We may want to stop exposing this API in the child process? Going
3205 // to a specific index from multiple different processes could definitely race.
3206 NS_IMETHODIMP
GotoIndex(int32_t aIndex)3207 nsDocShell::GotoIndex(int32_t aIndex) {
3208   if (!IsNavigationAllowed()) {
3209     return NS_OK;  // JS may not handle returning of an error code
3210   }
3211 
3212   auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3213   mIsNavigating = true;
3214 
3215   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3216   NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3217   return rootSH->LegacySHistory()->GotoIndex(aIndex);
3218 }
3219 
LoadURI(const nsAString & aURI,const LoadURIOptions & aLoadURIOptions)3220 nsresult nsDocShell::LoadURI(const nsAString& aURI,
3221                              const LoadURIOptions& aLoadURIOptions) {
3222   if (!IsNavigationAllowed()) {
3223     return NS_OK;  // JS may not handle returning of an error code
3224   }
3225 
3226   RefPtr<nsDocShellLoadState> loadState;
3227   nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3228       mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3229 
3230   uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3231   if (NS_ERROR_MALFORMED_URI == rv) {
3232     if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURI).get(), nullptr) &&
3233         (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3234       return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3235     }
3236   }
3237 
3238   if (NS_FAILED(rv) || !loadState) {
3239     return NS_ERROR_FAILURE;
3240   }
3241 
3242   return LoadURI(loadState, true);
3243 }
3244 
3245 NS_IMETHODIMP
LoadURIFromScript(const nsAString & aURI,JS::Handle<JS::Value> aLoadURIOptions,JSContext * aCx)3246 nsDocShell::LoadURIFromScript(const nsAString& aURI,
3247                               JS::Handle<JS::Value> aLoadURIOptions,
3248                               JSContext* aCx) {
3249   // generate dictionary for aLoadURIOptions and forward call
3250   LoadURIOptions loadURIOptions;
3251   if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3252     return NS_ERROR_INVALID_ARG;
3253   }
3254   return LoadURI(aURI, loadURIOptions);
3255 }
3256 
UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent)3257 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3258   // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3259   // such as the content-chrome boundary, don't fire the error event.
3260   if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3261     return;
3262   }
3263 
3264   // If embedder is same-process, then unblocking the load event is already
3265   // handled by nsDocLoader. Fire the error event on our embedder element if
3266   // requested.
3267   //
3268   // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3269   // more like the remote case when in-process.
3270   RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3271   if (element) {
3272     if (aFireFrameErrorEvent) {
3273       if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3274         if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3275           fl->FireErrorEvent();
3276         }
3277       }
3278     }
3279     return;
3280   }
3281 
3282   // If we have a cross-process parent document, we must notify it that we no
3283   // longer block its load event.  This is necessary for OOP sub-documents
3284   // because error documents do not result in a call to
3285   // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3286   // (Obviously, we must do this before any of the returns below.)
3287   RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3288   if (browserChild) {
3289     mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3290         aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3291                              : EmbedderElementEventType::NoEvent);
3292   }
3293 }
3294 
3295 NS_IMETHODIMP
DisplayLoadError(nsresult aError,nsIURI * aURI,const char16_t * aURL,nsIChannel * aFailedChannel,bool * aDisplayedErrorPage)3296 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3297                              const char16_t* aURL, nsIChannel* aFailedChannel,
3298                              bool* aDisplayedErrorPage) {
3299   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3300           ("DOCSHELL %p DisplayLoadError %s\n", this,
3301            aURI ? aURI->GetSpecOrDefault().get() : ""));
3302 
3303   *aDisplayedErrorPage = false;
3304   // Get prompt and string bundle services
3305   nsCOMPtr<nsIPrompt> prompter;
3306   nsCOMPtr<nsIStringBundle> stringBundle;
3307   GetPromptAndStringBundle(getter_AddRefs(prompter),
3308                            getter_AddRefs(stringBundle));
3309 
3310   NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3311   NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3312 
3313   const char* error = nullptr;
3314   // The key used to select the appropriate error message from the properties
3315   // file.
3316   const char* errorDescriptionID = nullptr;
3317   AutoTArray<nsString, 3> formatStrs;
3318   bool addHostPort = false;
3319   nsresult rv = NS_OK;
3320   nsAutoString messageStr;
3321   nsAutoCString cssClass;
3322   nsAutoCString errorPage;
3323 
3324   errorPage.AssignLiteral("neterror");
3325 
3326   // Turn the error code into a human readable error message.
3327   if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3328     NS_ENSURE_ARG_POINTER(aURI);
3329 
3330     // Extract the schemes into a comma delimited list.
3331     nsAutoCString scheme;
3332     aURI->GetScheme(scheme);
3333     CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3334     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3335     while (nestedURI) {
3336       nsCOMPtr<nsIURI> tempURI;
3337       nsresult rv2;
3338       rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3339       if (NS_SUCCEEDED(rv2) && tempURI) {
3340         tempURI->GetScheme(scheme);
3341         formatStrs[0].AppendLiteral(", ");
3342         AppendASCIItoUTF16(scheme, formatStrs[0]);
3343       }
3344       nestedURI = do_QueryInterface(tempURI);
3345     }
3346     error = "unknownProtocolFound";
3347   } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3348     NS_ENSURE_ARG_POINTER(aURI);
3349     error = "fileNotFound";
3350   } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3351     NS_ENSURE_ARG_POINTER(aURI);
3352     error = "fileAccessDenied";
3353   } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3354     NS_ENSURE_ARG_POINTER(aURI);
3355     // Get the host
3356     nsAutoCString host;
3357     nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3358     innermostURI->GetHost(host);
3359     CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3360     errorDescriptionID = "dnsNotFound2";
3361     error = "dnsNotFound";
3362   } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3363              NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3364     NS_ENSURE_ARG_POINTER(aURI);
3365     addHostPort = true;
3366     error = "connectionFailure";
3367   } else if (NS_ERROR_NET_INTERRUPT == aError) {
3368     NS_ENSURE_ARG_POINTER(aURI);
3369     addHostPort = true;
3370     error = "netInterrupt";
3371   } else if (NS_ERROR_NET_TIMEOUT == aError ||
3372              NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError) {
3373     NS_ENSURE_ARG_POINTER(aURI);
3374     // Get the host
3375     nsAutoCString host;
3376     aURI->GetHost(host);
3377     CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3378     error = "netTimeout";
3379   } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3380              NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError ||
3381              NS_ERROR_CSP_NAVIGATE_TO_VIOLATION == aError) {
3382     // CSP error
3383     cssClass.AssignLiteral("neterror");
3384     error = "cspBlocked";
3385   } else if (NS_ERROR_XFO_VIOLATION == aError) {
3386     // XFO error
3387     cssClass.AssignLiteral("neterror");
3388     error = "xfoBlocked";
3389   } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3390     nsCOMPtr<nsINSSErrorsService> nsserr =
3391         do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3392 
3393     uint32_t errorClass;
3394     if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3395       errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3396     }
3397 
3398     nsCOMPtr<nsISupports> securityInfo;
3399     nsCOMPtr<nsITransportSecurityInfo> tsi;
3400     if (aFailedChannel) {
3401       aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
3402     }
3403     tsi = do_QueryInterface(securityInfo);
3404     if (tsi) {
3405       uint32_t securityState;
3406       tsi->GetSecurityState(&securityState);
3407       if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3408         error = "sslv3Used";
3409         addHostPort = true;
3410       } else if (securityState &
3411                  nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3412         error = "weakCryptoUsed";
3413         addHostPort = true;
3414       }
3415     } else {
3416       // No channel, let's obtain the generic error message
3417       if (nsserr) {
3418         nsserr->GetErrorMessage(aError, messageStr);
3419       }
3420     }
3421     // We don't have a message string here anymore but DisplayLoadError
3422     // requires a non-empty messageStr.
3423     messageStr.Truncate();
3424     messageStr.AssignLiteral(u" ");
3425     if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3426       error = "nssBadCert";
3427 
3428       // If this is an HTTP Strict Transport Security host or a pinned host
3429       // and the certificate is bad, don't allow overrides (RFC 6797 section
3430       // 12.1).
3431       uint32_t flags =
3432           UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
3433       bool isStsHost = false;
3434       bool isPinnedHost = false;
3435       if (XRE_IsParentProcess()) {
3436         nsCOMPtr<nsISiteSecurityService> sss =
3437             do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3438         NS_ENSURE_SUCCESS(rv, rv);
3439         rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
3440                               GetOriginAttributes(), nullptr, nullptr,
3441                               &isStsHost);
3442         NS_ENSURE_SUCCESS(rv, rv);
3443         rv = sss->IsSecureURI(nsISiteSecurityService::STATIC_PINNING, aURI,
3444                               flags, GetOriginAttributes(), nullptr, nullptr,
3445                               &isPinnedHost);
3446         NS_ENSURE_SUCCESS(rv, rv);
3447       } else {
3448         mozilla::dom::ContentChild* cc =
3449             mozilla::dom::ContentChild::GetSingleton();
3450         cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
3451                             GetOriginAttributes(), &isStsHost);
3452         cc->SendIsSecureURI(nsISiteSecurityService::STATIC_PINNING, aURI, flags,
3453                             GetOriginAttributes(), &isPinnedHost);
3454       }
3455 
3456       if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3457                                false)) {
3458         cssClass.AssignLiteral("expertBadCert");
3459       }
3460 
3461       // HSTS/pinning takes precedence over the expert bad cert pref. We
3462       // never want to show the "Add Exception" button for these sites.
3463       // In the future we should differentiate between an HSTS host and a
3464       // pinned host and display a more informative message to the user.
3465       if (isStsHost || isPinnedHost) {
3466         cssClass.AssignLiteral("badStsCert");
3467       }
3468 
3469       // See if an alternate cert error page is registered
3470       nsAutoCString alternateErrorPage;
3471       nsresult rv = Preferences::GetCString(
3472           "security.alternate_certificate_error_page", alternateErrorPage);
3473       if (NS_SUCCEEDED(rv)) {
3474         errorPage.Assign(alternateErrorPage);
3475       }
3476     } else {
3477       error = "nssFailure2";
3478     }
3479   } else if (NS_ERROR_PHISHING_URI == aError ||
3480              NS_ERROR_MALWARE_URI == aError ||
3481              NS_ERROR_UNWANTED_URI == aError ||
3482              NS_ERROR_HARMFUL_URI == aError) {
3483     nsAutoCString host;
3484     aURI->GetHost(host);
3485     CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3486 
3487     // Malware and phishing detectors may want to use an alternate error
3488     // page, but if the pref's not set, we'll fall back on the standard page
3489     nsAutoCString alternateErrorPage;
3490     nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3491                                           alternateErrorPage);
3492     if (NS_SUCCEEDED(rv)) {
3493       errorPage.Assign(alternateErrorPage);
3494     }
3495 
3496     if (NS_ERROR_PHISHING_URI == aError) {
3497       error = "deceptiveBlocked";
3498     } else if (NS_ERROR_MALWARE_URI == aError) {
3499       error = "malwareBlocked";
3500     } else if (NS_ERROR_UNWANTED_URI == aError) {
3501       error = "unwantedBlocked";
3502     } else if (NS_ERROR_HARMFUL_URI == aError) {
3503       error = "harmfulBlocked";
3504     }
3505 
3506     cssClass.AssignLiteral("blacklist");
3507   } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3508     errorPage.AssignLiteral("tabcrashed");
3509     error = "tabcrashed";
3510 
3511     RefPtr<EventTarget> handler = mChromeEventHandler;
3512     if (handler) {
3513       nsCOMPtr<Element> element = do_QueryInterface(handler);
3514       element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
3515     }
3516 
3517     // DisplayLoadError requires a non-empty messageStr to proceed and call
3518     // LoadErrorPage. If the page doesn't have a title, we will use a blank
3519     // space which will be trimmed and thus treated as empty by the front-end.
3520     if (messageStr.IsEmpty()) {
3521       messageStr.AssignLiteral(u" ");
3522     }
3523   } else if (NS_ERROR_FRAME_CRASHED == aError) {
3524     errorPage.AssignLiteral("framecrashed");
3525     error = "framecrashed";
3526     messageStr.AssignLiteral(u" ");
3527   } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3528     errorPage.AssignLiteral("restartrequired");
3529     error = "restartrequired";
3530 
3531     // DisplayLoadError requires a non-empty messageStr to proceed and call
3532     // LoadErrorPage. If the page doesn't have a title, we will use a blank
3533     // space which will be trimmed and thus treated as empty by the front-end.
3534     if (messageStr.IsEmpty()) {
3535       messageStr.AssignLiteral(u" ");
3536     }
3537   } else {
3538     // Errors requiring simple formatting
3539     switch (aError) {
3540       case NS_ERROR_MALFORMED_URI:
3541         // URI is malformed
3542         error = "malformedURI";
3543         errorDescriptionID = "malformedURI2";
3544         break;
3545       case NS_ERROR_REDIRECT_LOOP:
3546         // Doc failed to load because the server generated too many redirects
3547         error = "redirectLoop";
3548         break;
3549       case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3550         // Doc failed to load because PSM is not installed
3551         error = "unknownSocketType";
3552         break;
3553       case NS_ERROR_NET_RESET:
3554         // Doc failed to load because the server kept reseting the connection
3555         // before we could read any data from it
3556         error = "netReset";
3557         break;
3558       case NS_ERROR_DOCUMENT_NOT_CACHED:
3559         // Doc failed to load because the cache does not contain a copy of
3560         // the document.
3561         error = "notCached";
3562         break;
3563       case NS_ERROR_OFFLINE:
3564         // Doc failed to load because we are offline.
3565         error = "netOffline";
3566         break;
3567       case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3568         // Doc navigation attempted while Printing or Print Preview
3569         error = "isprinting";
3570         break;
3571       case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3572         // Port blocked for security reasons
3573         addHostPort = true;
3574         error = "deniedPortAccess";
3575         break;
3576       case NS_ERROR_UNKNOWN_PROXY_HOST:
3577         // Proxy hostname could not be resolved.
3578         error = "proxyResolveFailure";
3579         break;
3580       case NS_ERROR_PROXY_CONNECTION_REFUSED:
3581       case NS_ERROR_PROXY_FORBIDDEN:
3582       case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3583       case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3584       case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3585         // Proxy connection was refused.
3586         error = "proxyConnectFailure";
3587         break;
3588       case NS_ERROR_INVALID_CONTENT_ENCODING:
3589         // Bad Content Encoding.
3590         error = "contentEncodingError";
3591         break;
3592       case NS_ERROR_REMOTE_XUL:
3593         error = "remoteXUL";
3594         break;
3595       case NS_ERROR_UNSAFE_CONTENT_TYPE:
3596         // Channel refused to load from an unrecognized content type.
3597         error = "unsafeContentType";
3598         break;
3599       case NS_ERROR_CORRUPTED_CONTENT:
3600         // Broken Content Detected. e.g. Content-MD5 check failure.
3601         error = "corruptedContentErrorv2";
3602         break;
3603       case NS_ERROR_INTERCEPTION_FAILED:
3604         // ServiceWorker intercepted request, but something went wrong.
3605         error = "corruptedContentErrorv2";
3606         break;
3607       case NS_ERROR_NET_INADEQUATE_SECURITY:
3608         // Server negotiated bad TLS for HTTP/2.
3609         error = "inadequateSecurityError";
3610         addHostPort = true;
3611         break;
3612       case NS_ERROR_BLOCKED_BY_POLICY:
3613         // Page blocked by policy
3614         error = "blockedByPolicy";
3615         break;
3616       case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3617       case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3618         // HTTP/2 or HTTP/3 stack detected a protocol error
3619         error = "networkProtocolError";
3620         break;
3621 
3622       default:
3623         break;
3624     }
3625   }
3626 
3627   // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3628   // caused this error, we replace the error-page with about:httpsonlyerror
3629   if (aFailedChannel && nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aError)) {
3630     nsCOMPtr<nsILoadInfo> loadInfo = aFailedChannel->LoadInfo();
3631     uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
3632     if ((httpsOnlyStatus &
3633          nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED) &&
3634         !(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT)) {
3635       errorPage.AssignLiteral("httpsonlyerror");
3636     }
3637   }
3638 
3639   if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3640     nsCOMPtr<nsIURI> errorPageURI;
3641     rv = loadURIDelegate->HandleLoadError(aURI, aError,
3642                                           NS_ERROR_GET_MODULE(aError),
3643                                           getter_AddRefs(errorPageURI));
3644     // If the docshell is going away there's no point in showing an error page.
3645     if (NS_FAILED(rv) || mIsBeingDestroyed) {
3646       *aDisplayedErrorPage = false;
3647       return NS_OK;
3648     }
3649 
3650     if (errorPageURI) {
3651       *aDisplayedErrorPage =
3652           NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3653       return NS_OK;
3654     }
3655   }
3656 
3657   // Test if the error should be displayed
3658   if (!error) {
3659     return NS_OK;
3660   }
3661 
3662   if (!errorDescriptionID) {
3663     errorDescriptionID = error;
3664   }
3665 
3666   Telemetry::AccumulateCategoricalKeyed(
3667       IsFrame() ? NS_LITERAL_CSTRING("frame") : NS_LITERAL_CSTRING("top"),
3668       mozilla::dom::LoadErrorToTelemetryLabel(aError));
3669 
3670   // Test if the error needs to be formatted
3671   if (!messageStr.IsEmpty()) {
3672     // already obtained message
3673   } else {
3674     if (addHostPort) {
3675       // Build up the host:port string.
3676       nsAutoCString hostport;
3677       if (aURI) {
3678         aURI->GetHostPort(hostport);
3679       } else {
3680         hostport.Assign('?');
3681       }
3682       CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3683     }
3684 
3685     nsAutoCString spec;
3686     rv = NS_ERROR_NOT_AVAILABLE;
3687     auto& nextFormatStr = *formatStrs.AppendElement();
3688     if (aURI) {
3689       // displaying "file://" is aesthetically unpleasing and could even be
3690       // confusing to the user
3691       if (SchemeIsFile(aURI)) {
3692         aURI->GetPathQueryRef(spec);
3693       } else {
3694         aURI->GetSpec(spec);
3695       }
3696 
3697       nsCOMPtr<nsITextToSubURI> textToSubURI(
3698           do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3699       if (NS_SUCCEEDED(rv)) {
3700         rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3701       }
3702     } else {
3703       spec.Assign('?');
3704     }
3705     if (NS_FAILED(rv)) {
3706       CopyUTF8toUTF16(spec, nextFormatStr);
3707     }
3708     rv = NS_OK;
3709 
3710     nsAutoString str;
3711     rv =
3712         stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3713     NS_ENSURE_SUCCESS(rv, rv);
3714     messageStr.Assign(str);
3715   }
3716 
3717   // Display the error as a page or an alert prompt
3718   NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3719 
3720   if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3721       SchemeIsHTTPS(aURI)) {
3722     // Maybe TLS intolerant. Treat this as an SSL error.
3723     error = "nssFailure2";
3724   }
3725 
3726   if (mUseErrorPages) {
3727     // Display an error page
3728     nsresult loadedPage =
3729         LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3730                       cssClass.get(), aFailedChannel);
3731     *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3732   } else {
3733     // The prompter reqires that our private window has a document (or it
3734     // asserts). Satisfy that assertion now since GetDoc will force
3735     // creation of one if it hasn't already been created.
3736     if (mScriptGlobal) {
3737       Unused << mScriptGlobal->GetDoc();
3738     }
3739 
3740     // Display a message box
3741     prompter->Alert(nullptr, messageStr.get());
3742   }
3743 
3744   return NS_OK;
3745 }
3746 
3747 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3748 
LoadErrorPage(nsIURI * aURI,const char16_t * aURL,const char * aErrorPage,const char * aErrorType,const char16_t * aDescription,const char * aCSSClass,nsIChannel * aFailedChannel)3749 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3750                                    const char* aErrorPage,
3751                                    const char* aErrorType,
3752                                    const char16_t* aDescription,
3753                                    const char* aCSSClass,
3754                                    nsIChannel* aFailedChannel) {
3755   MOZ_ASSERT(!mIsBeingDestroyed);
3756 
3757 #if defined(DEBUG)
3758   if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3759     nsAutoCString chanName;
3760     if (aFailedChannel) {
3761       aFailedChannel->GetName(chanName);
3762     } else {
3763       chanName.AssignLiteral("<no channel>");
3764     }
3765 
3766     MOZ_LOG(gDocShellLog, LogLevel::Debug,
3767             ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3768              this, aURI ? aURI->GetSpecOrDefault().get() : "",
3769              NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3770   }
3771 #endif
3772 
3773   nsAutoCString url;
3774   if (aURI) {
3775     nsresult rv = aURI->GetSpec(url);
3776     NS_ENSURE_SUCCESS(rv, rv);
3777   } else if (aURL) {
3778     CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3779   } else {
3780     return NS_ERROR_INVALID_POINTER;
3781   }
3782 
3783   // Create a URL to pass all the error information through to the page.
3784 
3785 #undef SAFE_ESCAPE
3786 #define SAFE_ESCAPE(output, input, params)             \
3787   if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3788     return NS_ERROR_OUT_OF_MEMORY;                     \
3789   }
3790 
3791   nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3792   SAFE_ESCAPE(escapedUrl, url, url_Path);
3793   SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3794   SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3795               url_Path);
3796   if (aCSSClass) {
3797     nsCString cssClass(aCSSClass);
3798     SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3799   }
3800   nsCString errorPageUrl("about:");
3801   errorPageUrl.AppendASCII(aErrorPage);
3802   errorPageUrl.AppendLiteral("?e=");
3803 
3804   errorPageUrl.AppendASCII(escapedError.get());
3805   errorPageUrl.AppendLiteral("&u=");
3806   errorPageUrl.AppendASCII(escapedUrl.get());
3807   if ((strcmp(aErrorPage, "blocked") == 0) &&
3808       Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
3809     errorPageUrl.AppendLiteral("&o=1");
3810   }
3811   if (!escapedCSSClass.IsEmpty()) {
3812     errorPageUrl.AppendLiteral("&s=");
3813     errorPageUrl.AppendASCII(escapedCSSClass.get());
3814   }
3815   errorPageUrl.AppendLiteral("&c=UTF-8");
3816 
3817   nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
3818   int32_t cpsState;
3819   if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
3820       cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
3821     errorPageUrl.AppendLiteral("&captive=true");
3822   }
3823 
3824   // netError.xhtml's getDescription only handles the "d" parameter at the
3825   // end of the URL, so append it last.
3826   errorPageUrl.AppendLiteral("&d=");
3827   errorPageUrl.AppendASCII(escapedDescription.get());
3828 
3829   nsCOMPtr<nsIURI> errorPageURI;
3830   nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3831   NS_ENSURE_SUCCESS(rv, rv);
3832 
3833   return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
3834 }
3835 
LoadErrorPage(nsIURI * aErrorURI,nsIURI * aFailedURI,nsIChannel * aFailedChannel)3836 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
3837                                    nsIChannel* aFailedChannel) {
3838   mFailedChannel = aFailedChannel;
3839   mFailedURI = aFailedURI;
3840   mFailedLoadType = mLoadType;
3841 
3842   if (mLSHE) {
3843     // Abandon mLSHE's BFCache entry and create a new one.  This way, if
3844     // we go back or forward to another SHEntry with the same doc
3845     // identifier, the error page won't persist.
3846     mLSHE->AbandonBFCacheEntry();
3847   }
3848 
3849   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
3850   loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
3851   loadState->SetLoadType(LOAD_ERROR_PAGE);
3852   loadState->SetFirstParty(true);
3853   loadState->SetSourceBrowsingContext(mBrowsingContext);
3854   loadState->SetHasValidUserGestureActivation(
3855       mBrowsingContext &&
3856       mBrowsingContext->HasValidTransientUserGestureActivation());
3857 
3858   return InternalLoad(loadState, nullptr, nullptr);
3859 }
3860 
3861 NS_IMETHODIMP
Reload(uint32_t aReloadFlags)3862 nsDocShell::Reload(uint32_t aReloadFlags) {
3863   if (!IsNavigationAllowed()) {
3864     return NS_OK;  // JS may not handle returning of an error code
3865   }
3866   nsresult rv;
3867   NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
3868                "Reload command not updated to use load flags!");
3869   NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
3870                "Don't pass these flags to Reload");
3871 
3872   uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3873   NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3874 
3875   // Send notifications to the HistoryListener if any, about the impending
3876   // reload
3877   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3878   bool canReload = true;
3879   if (rootSH) {
3880     rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
3881   }
3882 
3883   if (!canReload) {
3884     return NS_OK;
3885   }
3886 
3887   /* If you change this part of code, make sure bug 45297 does not re-occur */
3888   if (mOSHE) {
3889     rv = LoadHistoryEntry(mOSHE, loadType);
3890   } else if (mLSHE) {  // In case a reload happened before the current load is
3891                        // done
3892     rv = LoadHistoryEntry(mLSHE, loadType);
3893   } else {
3894     RefPtr<Document> doc(GetDocument());
3895 
3896     if (!doc) {
3897       return NS_OK;
3898     }
3899 
3900     // Do not inherit owner from document
3901     uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
3902     nsAutoString srcdoc;
3903     nsIURI* baseURI = nullptr;
3904     nsCOMPtr<nsIURI> originalURI;
3905     nsCOMPtr<nsIURI> resultPrincipalURI;
3906     bool loadReplace = false;
3907 
3908     nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
3909     nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
3910 
3911     nsAutoString contentTypeHint;
3912     doc->GetContentType(contentTypeHint);
3913 
3914     if (doc->IsSrcdocDocument()) {
3915       doc->GetSrcdocData(srcdoc);
3916       flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
3917       baseURI = doc->GetBaseURI();
3918     } else {
3919       srcdoc = VoidString();
3920     }
3921     nsCOMPtr<nsIChannel> chan = doc->GetChannel();
3922     if (chan) {
3923       uint32_t loadFlags;
3924       chan->GetLoadFlags(&loadFlags);
3925       loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
3926       nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
3927       if (httpChan) {
3928         httpChan->GetOriginalURI(getter_AddRefs(originalURI));
3929       }
3930 
3931       nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
3932       loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
3933     }
3934 
3935     if (!triggeringPrincipal) {
3936       MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
3937       return NS_ERROR_FAILURE;
3938     }
3939 
3940     // Stack variables to ensure changes to the member variables don't affect to
3941     // the call.
3942     nsCOMPtr<nsIURI> currentURI = mCurrentURI;
3943 
3944     // Reload always rewrites result principal URI.
3945     Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
3946     emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
3947 
3948     RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
3949     loadState->SetReferrerInfo(mReferrerInfo);
3950     loadState->SetOriginalURI(originalURI);
3951     loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
3952     loadState->SetLoadReplace(loadReplace);
3953     loadState->SetTriggeringPrincipal(triggeringPrincipal);
3954     loadState->SetPrincipalToInherit(triggeringPrincipal);
3955     loadState->SetCsp(csp);
3956     loadState->SetLoadFlags(flags);
3957     loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
3958     loadState->SetLoadType(loadType);
3959     loadState->SetFirstParty(true);
3960     loadState->SetSrcdocData(srcdoc);
3961     loadState->SetSourceBrowsingContext(mBrowsingContext);
3962     loadState->SetBaseURI(baseURI);
3963     loadState->SetHasValidUserGestureActivation(
3964         mBrowsingContext &&
3965         mBrowsingContext->HasValidTransientUserGestureActivation());
3966     rv = InternalLoad(loadState, nullptr, nullptr);
3967   }
3968 
3969   return rv;
3970 }
3971 
3972 NS_IMETHODIMP
Stop(uint32_t aStopFlags)3973 nsDocShell::Stop(uint32_t aStopFlags) {
3974   // Revoke any pending event related to content viewer restoration
3975   mRestorePresentationEvent.Revoke();
3976 
3977   if (mLoadType == LOAD_ERROR_PAGE) {
3978     if (mLSHE) {
3979       // Since error page loads never unset mLSHE, do so now
3980       SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
3981     }
3982 
3983     mFailedChannel = nullptr;
3984     mFailedURI = nullptr;
3985   }
3986 
3987   if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
3988     // Stop the document loading and animations
3989     if (mContentViewer) {
3990       nsCOMPtr<nsIContentViewer> cv = mContentViewer;
3991       cv->Stop();
3992     }
3993   } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
3994     // Stop the document loading only
3995     if (mContentViewer) {
3996       RefPtr<Document> doc = mContentViewer->GetDocument();
3997       if (doc) {
3998         doc->StopDocumentLoad();
3999       }
4000     }
4001   }
4002 
4003   if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4004     // Suspend any timers that were set for this loader.  We'll clear
4005     // them out for good in CreateContentViewer.
4006     if (mRefreshURIList) {
4007       SuspendRefreshURIs();
4008       mSavedRefreshURIList.swap(mRefreshURIList);
4009       mRefreshURIList = nullptr;
4010     }
4011 
4012     // XXXbz We could also pass |this| to nsIURILoader::Stop.  That will
4013     // just call Stop() on us as an nsIDocumentLoader... We need fewer
4014     // redundant apis!
4015     Stop();
4016   }
4017 
4018   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
4019   while (iter.HasMore()) {
4020     nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
4021     if (shellAsNav) {
4022       shellAsNav->Stop(aStopFlags);
4023     }
4024   }
4025 
4026   return NS_OK;
4027 }
4028 
4029 NS_IMETHODIMP
GetDocument(Document ** aDocument)4030 nsDocShell::GetDocument(Document** aDocument) {
4031   NS_ENSURE_ARG_POINTER(aDocument);
4032   NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4033 
4034   RefPtr<Document> doc = mContentViewer->GetDocument();
4035   if (!doc) {
4036     return NS_ERROR_NOT_AVAILABLE;
4037   }
4038 
4039   doc.forget(aDocument);
4040   return NS_OK;
4041 }
4042 
4043 NS_IMETHODIMP
GetCurrentURI(nsIURI ** aURI)4044 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4045   NS_ENSURE_ARG_POINTER(aURI);
4046 
4047   nsCOMPtr<nsIURI> uri = mCurrentURI;
4048   uri.forget(aURI);
4049   return NS_OK;
4050 }
4051 
4052 NS_IMETHODIMP
GetSessionHistoryXPCOM(nsISupports ** aSessionHistory)4053 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4054   NS_ENSURE_ARG_POINTER(aSessionHistory);
4055   RefPtr<ChildSHistory> shistory = GetSessionHistory();
4056   shistory.forget(aSessionHistory);
4057   return NS_OK;
4058 }
4059 
4060 //*****************************************************************************
4061 // nsDocShell::nsIWebPageDescriptor
4062 //*****************************************************************************
4063 
4064 NS_IMETHODIMP
LoadPage(nsISupports * aPageDescriptor,uint32_t aDisplayType)4065 nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType) {
4066   nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
4067 
4068   // Currently, the opaque 'page descriptor' is an nsISHEntry...
4069   if (!shEntryIn) {
4070     return NS_ERROR_INVALID_POINTER;
4071   }
4072 
4073   // Now clone shEntryIn, since we might end up modifying it later on, and we
4074   // want a page descriptor to be reusable.
4075   nsCOMPtr<nsISHEntry> shEntry;
4076   nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
4077   NS_ENSURE_SUCCESS(rv, rv);
4078 
4079   // Give our cloned shEntry a new bfcache entry so this load is independent
4080   // of all other loads.  (This is important, in particular, for bugs 582795
4081   // and 585298.)
4082   rv = shEntry->AbandonBFCacheEntry();
4083   NS_ENSURE_SUCCESS(rv, rv);
4084 
4085   //
4086   // load the page as view-source
4087   //
4088   if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
4089     nsCString spec, newSpec;
4090 
4091     // Create a new view-source URI and replace the original.
4092     nsCOMPtr<nsIURI> oldUri = shEntry->GetURI();
4093 
4094     oldUri->GetSpec(spec);
4095     newSpec.AppendLiteral("view-source:");
4096     newSpec.Append(spec);
4097 
4098     nsCOMPtr<nsIURI> newUri;
4099     rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
4100     if (NS_FAILED(rv)) {
4101       return rv;
4102     }
4103     shEntry->SetURI(newUri);
4104     shEntry->SetOriginalURI(nullptr);
4105     shEntry->SetResultPrincipalURI(nullptr);
4106     // shEntry's current triggering principal is whoever loaded that page
4107     // initially. But now we're doing another load of the page, via an API that
4108     // is only exposed to system code.  The triggering principal for this load
4109     // should be the system principal.
4110     shEntry->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4111   }
4112 
4113   rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
4114   return rv;
4115 }
4116 
4117 NS_IMETHODIMP
GetCurrentDescriptor(nsISupports ** aPageDescriptor)4118 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4119   MOZ_ASSERT(aPageDescriptor, "Null out param?");
4120 
4121   *aPageDescriptor = nullptr;
4122 
4123   nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4124   if (src) {
4125     nsCOMPtr<nsISHEntry> dest;
4126 
4127     nsresult rv = src->Clone(getter_AddRefs(dest));
4128     if (NS_FAILED(rv)) {
4129       return rv;
4130     }
4131 
4132     // null out inappropriate cloned attributes...
4133     dest->SetParent(nullptr);
4134     dest->SetIsSubFrame(false);
4135 
4136     return CallQueryInterface(dest, aPageDescriptor);
4137   }
4138 
4139   return NS_ERROR_NOT_AVAILABLE;
4140 }
4141 
4142 //*****************************************************************************
4143 // nsDocShell::nsIBaseWindow
4144 //*****************************************************************************
4145 
4146 NS_IMETHODIMP
InitWindow(nativeWindow aParentNativeWindow,nsIWidget * aParentWidget,int32_t aX,int32_t aY,int32_t aWidth,int32_t aHeight)4147 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4148                        nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4149                        int32_t aWidth, int32_t aHeight) {
4150   SetParentWidget(aParentWidget);
4151   SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4152 
4153   return NS_OK;
4154 }
4155 
4156 NS_IMETHODIMP
Create()4157 nsDocShell::Create() {
4158   if (mCreated) {
4159     // We've already been created
4160     return NS_OK;
4161   }
4162 
4163   NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4164                "Unexpected item type in docshell");
4165 
4166   NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
4167   mCreated = true;
4168 
4169   mDisableMetaRefreshWhenInactive =
4170       Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
4171                            mDisableMetaRefreshWhenInactive);
4172 
4173   mDeviceSizeIsPageSize = Preferences::GetBool(
4174       "docshell.device_size_is_page_size", mDeviceSizeIsPageSize);
4175 
4176   nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4177   if (serv) {
4178     const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
4179                                                : NS_CHROME_WEBNAVIGATION_CREATE;
4180     serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4181   }
4182 
4183   return NS_OK;
4184 }
4185 
4186 NS_IMETHODIMP
Destroy()4187 nsDocShell::Destroy() {
4188   // XXX: We allow this function to be called just once.  If you are going to
4189   // reset new variables in this function, please make sure the variables will
4190   // never be re-initialized.  Adding assertions to check |mIsBeingDestroyed|
4191   // in the setter functions for the variables would be enough.
4192   if (mIsBeingDestroyed) {
4193     return NS_ERROR_DOCSHELL_DYING;
4194   }
4195 
4196   NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4197                "Unexpected item type in docshell");
4198 
4199   nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4200   if (serv) {
4201     const char* msg = mItemType == typeContent
4202                           ? NS_WEBNAVIGATION_DESTROY
4203                           : NS_CHROME_WEBNAVIGATION_DESTROY;
4204     serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4205   }
4206 
4207   mIsBeingDestroyed = true;
4208 
4209   // Brak the cycle with the initial client, if present.
4210   mInitialClientSource.reset();
4211 
4212   // Make sure we don't record profile timeline markers anymore
4213   SetRecordProfileTimelineMarkers(false);
4214 
4215   // Make sure to blow away our mLoadingURI just in case.  No loads
4216   // from inside this pagehide.
4217   mLoadingURI = nullptr;
4218 
4219   // Fire unload event before we blow anything away.
4220   (void)FirePageHideNotification(true);
4221 
4222   // Clear pointers to any detached nsEditorData that's lying
4223   // around in shistory entries. Breaks cycle. See bug 430921.
4224   if (mOSHE) {
4225     mOSHE->SetEditorData(nullptr);
4226   }
4227   if (mLSHE) {
4228     mLSHE->SetEditorData(nullptr);
4229   }
4230 
4231   // Note: mContentListener can be null if Init() failed and we're being
4232   // called from the destructor.
4233   if (mContentListener) {
4234     mContentListener->DropDocShellReference();
4235     mContentListener->SetParentContentListener(nullptr);
4236     // Note that we do NOT set mContentListener to null here; that
4237     // way if someone tries to do a load in us after this point
4238     // the nsDSURIContentListener will block it.  All of which
4239     // means that we should do this before calling Stop(), of
4240     // course.
4241   }
4242 
4243   // Stop any URLs that are currently being loaded...
4244   Stop(nsIWebNavigation::STOP_ALL);
4245 
4246   mEditorData = nullptr;
4247 
4248   // Save the state of the current document, before destroying the window.
4249   // This is needed to capture the state of a frameset when the new document
4250   // causes the frameset to be destroyed...
4251   PersistLayoutHistoryState();
4252 
4253   // Remove this docshell from its parent's child list
4254   nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4255       do_QueryInterface(GetAsSupports(mParent));
4256   if (docShellParentAsItem) {
4257     docShellParentAsItem->RemoveChild(this);
4258   }
4259 
4260   if (mContentViewer) {
4261     mContentViewer->Close(nullptr);
4262     mContentViewer->Destroy();
4263     mContentViewer = nullptr;
4264   }
4265 
4266   nsDocLoader::Destroy();
4267 
4268   mParentWidget = nullptr;
4269   mCurrentURI = nullptr;
4270 
4271   if (mScriptGlobal) {
4272     mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4273     mScriptGlobal = nullptr;
4274   }
4275 
4276   if (GetSessionHistory()) {
4277     // We want to destroy these content viewers now rather than
4278     // letting their destruction wait for the session history
4279     // entries to get garbage collected.  (Bug 488394)
4280     GetSessionHistory()->EvictLocalContentViewers();
4281   }
4282 
4283   if (mWillChangeProcess) {
4284     mBrowsingContext->PrepareForProcessChange();
4285   }
4286 
4287   SetTreeOwner(nullptr);
4288 
4289   mBrowserChild = nullptr;
4290 
4291   mChromeEventHandler = nullptr;
4292 
4293   // Cancel any timers that were set for this docshell; this is needed
4294   // to break the cycle between us and the timers.
4295   CancelRefreshURITimers();
4296 
4297   if (UsePrivateBrowsing() && mAffectPrivateSessionLifetime) {
4298     DecreasePrivateDocShellCount();
4299   }
4300 
4301   return NS_OK;
4302 }
4303 
4304 NS_IMETHODIMP
GetUnscaledDevicePixelsPerCSSPixel(double * aScale)4305 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
4306   if (mParentWidget) {
4307     *aScale = mParentWidget->GetDefaultScale().scale;
4308     return NS_OK;
4309   }
4310 
4311   nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4312   if (ownerWindow) {
4313     return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
4314   }
4315 
4316   *aScale = 1.0;
4317   return NS_OK;
4318 }
4319 
4320 NS_IMETHODIMP
GetDevicePixelsPerDesktopPixel(double * aScale)4321 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4322   if (mParentWidget) {
4323     *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4324     return NS_OK;
4325   }
4326 
4327   nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4328   if (ownerWindow) {
4329     return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4330   }
4331 
4332   *aScale = 1.0;
4333   return NS_OK;
4334 }
4335 
4336 NS_IMETHODIMP
SetPosition(int32_t aX,int32_t aY)4337 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4338   mBounds.MoveTo(aX, aY);
4339 
4340   if (mContentViewer) {
4341     NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4342   }
4343 
4344   return NS_OK;
4345 }
4346 
4347 NS_IMETHODIMP
SetPositionDesktopPix(int32_t aX,int32_t aY)4348 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4349   nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4350   if (ownerWindow) {
4351     return ownerWindow->SetPositionDesktopPix(aX, aY);
4352   }
4353 
4354   double scale = 1.0;
4355   GetDevicePixelsPerDesktopPixel(&scale);
4356   return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4357 }
4358 
4359 NS_IMETHODIMP
GetPosition(int32_t * aX,int32_t * aY)4360 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4361   return GetPositionAndSize(aX, aY, nullptr, nullptr);
4362 }
4363 
4364 NS_IMETHODIMP
SetSize(int32_t aWidth,int32_t aHeight,bool aRepaint)4365 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4366   int32_t x = 0, y = 0;
4367   GetPosition(&x, &y);
4368   return SetPositionAndSize(x, y, aWidth, aHeight,
4369                             aRepaint ? nsIBaseWindow::eRepaint : 0);
4370 }
4371 
4372 NS_IMETHODIMP
GetSize(int32_t * aWidth,int32_t * aHeight)4373 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4374   return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4375 }
4376 
4377 NS_IMETHODIMP
SetPositionAndSize(int32_t aX,int32_t aY,int32_t aWidth,int32_t aHeight,uint32_t aFlags)4378 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4379                                int32_t aHeight, uint32_t aFlags) {
4380   mBounds.SetRect(aX, aY, aWidth, aHeight);
4381 
4382   // Hold strong ref, since SetBounds can make us null out mContentViewer
4383   nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
4384   if (viewer) {
4385     uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4386                            ? nsIContentViewer::eDelayResize
4387                            : 0;
4388     // XXX Border figured in here or is that handled elsewhere?
4389     nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4390     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4391   }
4392 
4393   return NS_OK;
4394 }
4395 
4396 NS_IMETHODIMP
GetPositionAndSize(int32_t * aX,int32_t * aY,int32_t * aWidth,int32_t * aHeight)4397 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4398                                int32_t* aHeight) {
4399   if (mParentWidget) {
4400     // ensure size is up-to-date if window has changed resolution
4401     LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4402     SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4403   }
4404 
4405   // We should really consider just getting this information from
4406   // our window instead of duplicating the storage and code...
4407   if (aWidth || aHeight) {
4408     // Caller wants to know our size; make sure to give them up to
4409     // date information.
4410     RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4411     if (doc) {
4412       doc->FlushPendingNotifications(FlushType::Layout);
4413     }
4414   }
4415 
4416   DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4417   return NS_OK;
4418 }
4419 
DoGetPositionAndSize(int32_t * aX,int32_t * aY,int32_t * aWidth,int32_t * aHeight)4420 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4421                                       int32_t* aHeight) {
4422   if (aX) {
4423     *aX = mBounds.X();
4424   }
4425   if (aY) {
4426     *aY = mBounds.Y();
4427   }
4428   if (aWidth) {
4429     *aWidth = mBounds.Width();
4430   }
4431   if (aHeight) {
4432     *aHeight = mBounds.Height();
4433   }
4434 }
4435 
4436 NS_IMETHODIMP
Repaint(bool aForce)4437 nsDocShell::Repaint(bool aForce) {
4438   PresShell* presShell = GetPresShell();
4439   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4440 
4441   RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4442   NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4443 
4444   viewManager->InvalidateAllViews();
4445   return NS_OK;
4446 }
4447 
4448 NS_IMETHODIMP
GetParentWidget(nsIWidget ** aParentWidget)4449 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4450   NS_ENSURE_ARG_POINTER(aParentWidget);
4451 
4452   *aParentWidget = mParentWidget;
4453   NS_IF_ADDREF(*aParentWidget);
4454 
4455   return NS_OK;
4456 }
4457 
4458 NS_IMETHODIMP
SetParentWidget(nsIWidget * aParentWidget)4459 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4460   MOZ_ASSERT(!mIsBeingDestroyed);
4461   mParentWidget = aParentWidget;
4462 
4463   return NS_OK;
4464 }
4465 
4466 NS_IMETHODIMP
GetParentNativeWindow(nativeWindow * aParentNativeWindow)4467 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4468   NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4469 
4470   if (mParentWidget) {
4471     *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4472   } else {
4473     *aParentNativeWindow = nullptr;
4474   }
4475 
4476   return NS_OK;
4477 }
4478 
4479 NS_IMETHODIMP
SetParentNativeWindow(nativeWindow aParentNativeWindow)4480 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4481   return NS_ERROR_NOT_IMPLEMENTED;
4482 }
4483 
4484 NS_IMETHODIMP
GetNativeHandle(nsAString & aNativeHandle)4485 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4486   // the nativeHandle should be accessed from nsIAppWindow
4487   return NS_ERROR_NOT_IMPLEMENTED;
4488 }
4489 
4490 NS_IMETHODIMP
GetVisibility(bool * aVisibility)4491 nsDocShell::GetVisibility(bool* aVisibility) {
4492   NS_ENSURE_ARG_POINTER(aVisibility);
4493 
4494   *aVisibility = false;
4495 
4496   if (!mContentViewer) {
4497     return NS_OK;
4498   }
4499 
4500   PresShell* presShell = GetPresShell();
4501   if (!presShell) {
4502     return NS_OK;
4503   }
4504 
4505   // get the view manager
4506   nsViewManager* vm = presShell->GetViewManager();
4507   NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4508 
4509   // get the root view
4510   nsView* view = vm->GetRootView();  // views are not ref counted
4511   NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4512 
4513   // if our root view is hidden, we are not visible
4514   if (view->GetVisibility() == nsViewVisibility_kHide) {
4515     return NS_OK;
4516   }
4517 
4518   // otherwise, we must walk up the document and view trees checking
4519   // for a hidden view, unless we're an off screen browser, which
4520   // would make this test meaningless.
4521 
4522   RefPtr<nsDocShell> docShell = this;
4523   RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4524   while (parentItem) {
4525     // Null-check for crash in bug 267804
4526     if (!parentItem->GetPresShell()) {
4527       MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4528       return NS_OK;
4529     }
4530 
4531     vm = docShell->GetPresShell()->GetViewManager();
4532     if (vm) {
4533       view = vm->GetRootView();
4534     }
4535 
4536     if (view) {
4537       view = view->GetParent();  // anonymous inner view
4538       if (view) {
4539         view = view->GetParent();  // subdocumentframe's view
4540       }
4541     }
4542 
4543     nsIFrame* frame = view ? view->GetFrame() : nullptr;
4544     bool isDocShellOffScreen = false;
4545     docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
4546     if (frame &&
4547         !frame->IsVisibleConsideringAncestors(
4548             nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
4549         !isDocShellOffScreen) {
4550       return NS_OK;
4551     }
4552 
4553     docShell = parentItem;
4554     parentItem = docShell->GetInProcessParentDocshell();
4555   }
4556 
4557   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4558   if (!treeOwnerAsWin) {
4559     *aVisibility = true;
4560     return NS_OK;
4561   }
4562 
4563   // Check with the tree owner as well to give embedders a chance to
4564   // expose visibility as well.
4565   return treeOwnerAsWin->GetVisibility(aVisibility);
4566 }
4567 
4568 NS_IMETHODIMP
SetIsOffScreenBrowser(bool aIsOffScreen)4569 nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen) {
4570   mIsOffScreenBrowser = aIsOffScreen;
4571   return NS_OK;
4572 }
4573 
4574 NS_IMETHODIMP
GetIsOffScreenBrowser(bool * aIsOffScreen)4575 nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen) {
4576   *aIsOffScreen = mIsOffScreenBrowser;
4577   return NS_OK;
4578 }
4579 
4580 NS_IMETHODIMP
SetSuspendMediaWhenInactive(bool aSuspendMediaWhenInactive)4581 nsDocShell::SetSuspendMediaWhenInactive(bool aSuspendMediaWhenInactive) {
4582   mSuspendMediaWhenInactive = aSuspendMediaWhenInactive;
4583   return NS_OK;
4584 }
4585 
4586 NS_IMETHODIMP
GetSuspendMediaWhenInactive(bool * aSuspendMediaWhenInactive)4587 nsDocShell::GetSuspendMediaWhenInactive(bool* aSuspendMediaWhenInactive) {
4588   *aSuspendMediaWhenInactive = mSuspendMediaWhenInactive;
4589   return NS_OK;
4590 }
4591 
4592 NS_IMETHODIMP
SetIsActive(bool aIsActive)4593 nsDocShell::SetIsActive(bool aIsActive) {
4594   // Keep track ourselves.
4595   mBrowsingContext->SetIsActive(aIsActive);
4596 
4597   // Tell the PresShell about it.
4598   if (RefPtr<PresShell> presShell = GetPresShell()) {
4599     presShell->SetIsActive(aIsActive);
4600   }
4601 
4602   // Tell the window about it
4603   if (mScriptGlobal) {
4604     mScriptGlobal->SetIsBackground(!aIsActive);
4605     if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4606       // Update orientation when the top-level browsing context becomes active.
4607       if (aIsActive) {
4608         if (mBrowsingContext->IsTop()) {
4609           // We only care about the top-level browsing context.
4610           uint16_t orientation = mBrowsingContext->GetOrientationLock();
4611           ScreenOrientation::UpdateActiveOrientationLock(orientation);
4612         }
4613       }
4614 
4615       doc->PostVisibilityUpdateEvent();
4616     }
4617   }
4618 
4619   // Tell the nsDOMNavigationTiming about it
4620   RefPtr<nsDOMNavigationTiming> timing = mTiming;
4621   if (!timing && mContentViewer) {
4622     Document* doc = mContentViewer->GetDocument();
4623     if (doc) {
4624       timing = doc->GetNavigationTiming();
4625     }
4626   }
4627   if (timing) {
4628     timing->NotifyDocShellStateChanged(
4629         aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
4630                   : nsDOMNavigationTiming::DocShellState::eInactive);
4631   }
4632 
4633   // Recursively tell all of our children, but don't tell <iframe mozbrowser>
4634   // children; they handle their state separately.
4635   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
4636   while (iter.HasMore()) {
4637     nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
4638     if (!docshell) {
4639       continue;
4640     }
4641 
4642     docshell->SetIsActive(aIsActive);
4643   }
4644 
4645   // Restart or stop meta refresh timers if necessary
4646   if (mDisableMetaRefreshWhenInactive) {
4647     if (mBrowsingContext->GetIsActive()) {
4648       ResumeRefreshURIs();
4649     } else {
4650       SuspendRefreshURIs();
4651     }
4652   }
4653 
4654   return NS_OK;
4655 }
4656 
4657 NS_IMETHODIMP
GetIsActive(bool * aIsActive)4658 nsDocShell::GetIsActive(bool* aIsActive) {
4659   *aIsActive = mBrowsingContext->GetIsActive();
4660   return NS_OK;
4661 }
4662 
4663 NS_IMETHODIMP
SetIsAppTab(bool aIsAppTab)4664 nsDocShell::SetIsAppTab(bool aIsAppTab) {
4665   mIsAppTab = aIsAppTab;
4666   return NS_OK;
4667 }
4668 
4669 NS_IMETHODIMP
GetIsAppTab(bool * aIsAppTab)4670 nsDocShell::GetIsAppTab(bool* aIsAppTab) {
4671   *aIsAppTab = mIsAppTab;
4672   return NS_OK;
4673 }
4674 
4675 NS_IMETHODIMP
SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)4676 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4677   if (!mWillChangeProcess) {
4678     mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4679   } else {
4680     // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4681     // shutdown. Sorry DevTools, your DocShell is in another process.
4682     NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4683   }
4684   return NS_OK;
4685 }
4686 
4687 NS_IMETHODIMP
GetDefaultLoadFlags(uint32_t * aDefaultLoadFlags)4688 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4689   *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4690   return NS_OK;
4691 }
4692 
4693 NS_IMETHODIMP
SetMixedContentChannel(nsIChannel * aMixedContentChannel)4694 nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel) {
4695 #ifdef DEBUG
4696   // if the channel is non-null
4697   if (aMixedContentChannel) {
4698     NS_WARNING_ASSERTION(mBrowsingContext->IsTop(),
4699                          "Setting mMixedContentChannel on a docshell that is "
4700                          "not the root docshell");
4701   }
4702 #endif
4703   mMixedContentChannel = aMixedContentChannel;
4704   return NS_OK;
4705 }
4706 
4707 NS_IMETHODIMP
GetFailedChannel(nsIChannel ** aFailedChannel)4708 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4709   NS_ENSURE_ARG_POINTER(aFailedChannel);
4710   Document* doc = GetDocument();
4711   if (!doc) {
4712     *aFailedChannel = nullptr;
4713     return NS_OK;
4714   }
4715   NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4716   return NS_OK;
4717 }
4718 
4719 NS_IMETHODIMP
GetMixedContentChannel(nsIChannel ** aMixedContentChannel)4720 nsDocShell::GetMixedContentChannel(nsIChannel** aMixedContentChannel) {
4721   NS_ENSURE_ARG_POINTER(aMixedContentChannel);
4722   NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
4723   return NS_OK;
4724 }
4725 
4726 NS_IMETHODIMP
SetVisibility(bool aVisibility)4727 nsDocShell::SetVisibility(bool aVisibility) {
4728   // Show()/Hide() may change mContentViewer.
4729   nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4730   if (!cv) {
4731     return NS_OK;
4732   }
4733   if (aVisibility) {
4734     cv->Show();
4735   } else {
4736     cv->Hide();
4737   }
4738 
4739   return NS_OK;
4740 }
4741 
4742 NS_IMETHODIMP
GetEnabled(bool * aEnabled)4743 nsDocShell::GetEnabled(bool* aEnabled) {
4744   NS_ENSURE_ARG_POINTER(aEnabled);
4745   *aEnabled = true;
4746   return NS_ERROR_NOT_IMPLEMENTED;
4747 }
4748 
4749 NS_IMETHODIMP
SetEnabled(bool aEnabled)4750 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4751 
4752 NS_IMETHODIMP
SetFocus()4753 nsDocShell::SetFocus() { return NS_OK; }
4754 
4755 NS_IMETHODIMP
GetMainWidget(nsIWidget ** aMainWidget)4756 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4757   // We don't create our own widget, so simply return the parent one.
4758   return GetParentWidget(aMainWidget);
4759 }
4760 
4761 NS_IMETHODIMP
GetTitle(nsAString & aTitle)4762 nsDocShell::GetTitle(nsAString& aTitle) {
4763   aTitle = mTitle;
4764   return NS_OK;
4765 }
4766 
4767 NS_IMETHODIMP
SetTitle(const nsAString & aTitle)4768 nsDocShell::SetTitle(const nsAString& aTitle) {
4769   // Avoid unnecessary updates of the title if the URI and the title haven't
4770   // changed.
4771   if (mTitleValidForCurrentURI && mTitle == aTitle) {
4772     return NS_OK;
4773   }
4774 
4775   // Store local title
4776   mTitle = aTitle;
4777   mTitleValidForCurrentURI = true;
4778 
4779   // When title is set on the top object it should then be passed to the
4780   // tree owner.
4781   if (mBrowsingContext->IsTop()) {
4782     nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4783     if (treeOwnerAsWin) {
4784       treeOwnerAsWin->SetTitle(aTitle);
4785     }
4786   }
4787 
4788   if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4789     UpdateGlobalHistoryTitle(mCurrentURI);
4790   }
4791 
4792   // Update SessionHistory with the document's title.
4793   if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
4794       mLoadType != LOAD_ERROR_PAGE) {
4795     mOSHE->SetTitle(mTitle);
4796   }
4797 
4798   return NS_OK;
4799 }
4800 
GetCurScrollPos()4801 nsPoint nsDocShell::GetCurScrollPos() {
4802   nsPoint scrollPos;
4803   if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
4804     scrollPos = sf->GetVisualViewportOffset();
4805   }
4806   return scrollPos;
4807 }
4808 
SetCurScrollPosEx(int32_t aCurHorizontalPos,int32_t aCurVerticalPos)4809 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
4810                                        int32_t aCurVerticalPos) {
4811   nsIScrollableFrame* sf = GetRootScrollFrame();
4812   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4813 
4814   ScrollMode scrollMode =
4815       sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
4816 
4817   nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
4818   sf->ScrollTo(targetPos, scrollMode);
4819 
4820   // Set the visual viewport offset as well.
4821 
4822   RefPtr<PresShell> presShell = GetPresShell();
4823   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4824 
4825   nsPresContext* presContext = presShell->GetPresContext();
4826   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
4827 
4828   // Only the root content document can have a distinct visual viewport offset.
4829   if (!presContext->IsRootContentDocument()) {
4830     return NS_OK;
4831   }
4832 
4833   // Not on a platform with a distinct visual viewport - don't bother setting
4834   // the visual viewport offset.
4835   if (!presShell->IsVisualViewportSizeSet()) {
4836     return NS_OK;
4837   }
4838 
4839   presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
4840                             scrollMode);
4841 
4842   return NS_OK;
4843 }
4844 
SetScrollbarPreference(mozilla::ScrollbarPreference aPref)4845 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
4846   if (mScrollbarPref == aPref) {
4847     return;
4848   }
4849   mScrollbarPref = aPref;
4850   auto* ps = GetPresShell();
4851   if (!ps) {
4852     return;
4853   }
4854   nsIFrame* scrollFrame = ps->GetRootScrollFrame();
4855   if (!scrollFrame) {
4856     return;
4857   }
4858   ps->FrameNeedsReflow(scrollFrame, IntrinsicDirty::StyleChange,
4859                        NS_FRAME_IS_DIRTY);
4860 }
4861 
4862 //*****************************************************************************
4863 // nsDocShell::nsIRefreshURI
4864 //*****************************************************************************
4865 
4866 NS_IMETHODIMP
RefreshURI(nsIURI * aURI,nsIPrincipal * aPrincipal,int32_t aDelay,bool aRepeat,bool aMetaRefresh)4867 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDelay,
4868                        bool aRepeat, bool aMetaRefresh) {
4869   MOZ_ASSERT(!mIsBeingDestroyed);
4870 
4871   NS_ENSURE_ARG(aURI);
4872 
4873   /* Check if Meta refresh/redirects are permitted. Some
4874    * embedded applications may not want to do this.
4875    * Must do this before sending out NOTIFY_REFRESH events
4876    * because listeners may have side effects (e.g. displaying a
4877    * button to manually trigger the refresh later).
4878    */
4879   bool allowRedirects = true;
4880   GetAllowMetaRedirects(&allowRedirects);
4881   if (!allowRedirects) {
4882     return NS_OK;
4883   }
4884 
4885   // If any web progress listeners are listening for NOTIFY_REFRESH events,
4886   // give them a chance to block this refresh.
4887   bool sameURI;
4888   nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
4889   if (NS_FAILED(rv)) {
4890     sameURI = false;
4891   }
4892   if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
4893     return NS_OK;
4894   }
4895 
4896   nsCOMPtr<nsITimerCallback> refreshTimer =
4897       new nsRefreshTimer(this, aURI, aPrincipal, aDelay, aRepeat, aMetaRefresh);
4898 
4899   BusyFlags busyFlags = GetBusyFlags();
4900 
4901   if (!mRefreshURIList) {
4902     mRefreshURIList = nsArray::Create();
4903   }
4904 
4905   if (busyFlags & BUSY_FLAGS_BUSY ||
4906       (!mBrowsingContext->GetIsActive() && mDisableMetaRefreshWhenInactive)) {
4907     // We don't  want to create the timer right now. Instead queue up the
4908     // request and trigger the timer in EndPageLoad() or whenever we become
4909     // active.
4910     mRefreshURIList->AppendElement(refreshTimer);
4911   } else {
4912     // There is no page loading going on right now.  Create the
4913     // timer and fire it right away.
4914     nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
4915     NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
4916 
4917     nsCOMPtr<nsITimer> timer;
4918     MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
4919                                                nsITimer::TYPE_ONE_SHOT));
4920 
4921     mRefreshURIList->AppendElement(timer);  // owning timer ref
4922   }
4923   return NS_OK;
4924 }
4925 
ForceRefreshURIFromTimer(nsIURI * aURI,nsIPrincipal * aPrincipal,int32_t aDelay,bool aMetaRefresh,nsITimer * aTimer)4926 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
4927                                               nsIPrincipal* aPrincipal,
4928                                               int32_t aDelay, bool aMetaRefresh,
4929                                               nsITimer* aTimer) {
4930   MOZ_ASSERT(aTimer, "Must have a timer here");
4931 
4932   // Remove aTimer from mRefreshURIList if needed
4933   if (mRefreshURIList) {
4934     uint32_t n = 0;
4935     mRefreshURIList->GetLength(&n);
4936 
4937     for (uint32_t i = 0; i < n; ++i) {
4938       nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
4939       if (timer == aTimer) {
4940         mRefreshURIList->RemoveElementAt(i);
4941         break;
4942       }
4943     }
4944   }
4945 
4946   return ForceRefreshURI(aURI, aPrincipal, aDelay, aMetaRefresh);
4947 }
4948 
4949 NS_IMETHODIMP
ForceRefreshURI(nsIURI * aURI,nsIPrincipal * aPrincipal,int32_t aDelay,bool aMetaRefresh)4950 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
4951                             int32_t aDelay, bool aMetaRefresh) {
4952   NS_ENSURE_ARG(aURI);
4953 
4954   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
4955   loadState->SetOriginalURI(mCurrentURI);
4956   loadState->SetResultPrincipalURI(aURI);
4957   loadState->SetResultPrincipalURIIsSome(true);
4958   loadState->SetKeepResultPrincipalURIIfSet(true);
4959 
4960   // Set the triggering pricipal to aPrincipal if available, or current
4961   // document's principal otherwise.
4962   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
4963   RefPtr<Document> doc = GetDocument();
4964   if (!principal) {
4965     if (!doc) {
4966       return NS_ERROR_FAILURE;
4967     }
4968     principal = doc->NodePrincipal();
4969   }
4970   loadState->SetTriggeringPrincipal(principal);
4971   if (doc) {
4972     loadState->SetCsp(doc->GetCsp());
4973     loadState->SetHasValidUserGestureActivation(
4974         doc->HasValidTransientUserGestureActivation());
4975   }
4976 
4977   loadState->SetPrincipalIsExplicit(true);
4978 
4979   /* Check if this META refresh causes a redirection
4980    * to another site.
4981    */
4982   bool equalUri = false;
4983   nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
4984 
4985   nsCOMPtr<nsIReferrerInfo> referrerInfo;
4986   if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
4987       aDelay <= REFRESH_REDIRECT_TIMER) {
4988     /* It is a META refresh based redirection within the threshold time
4989      * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
4990      * Pass a REPLACE flag to LoadURI().
4991      */
4992     loadState->SetLoadType(LOAD_NORMAL_REPLACE);
4993 
4994     /* For redirects we mimic HTTP, which passes the
4995      * original referrer.
4996      * We will pass in referrer but will not send to server
4997      */
4998     if (mReferrerInfo) {
4999       referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5000                          ->CloneWithNewSendReferrer(false);
5001     }
5002   } else {
5003     loadState->SetLoadType(LOAD_REFRESH);
5004     /* We do need to pass in a referrer, but we don't want it to
5005      * be sent to the server.
5006      * For most refreshes the current URI is an appropriate
5007      * internal referrer.
5008      */
5009     referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5010   }
5011 
5012   loadState->SetReferrerInfo(referrerInfo);
5013   loadState->SetLoadFlags(
5014       nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5015   loadState->SetFirstParty(true);
5016 
5017   /*
5018    * LoadURI(...) will cancel all refresh timers... This causes the
5019    * Timer and its refreshData instance to be released...
5020    */
5021   LoadURI(loadState, false);
5022 
5023   return NS_OK;
5024 }
5025 
SetupRefreshURIFromHeader(nsIURI * aBaseURI,nsIPrincipal * aPrincipal,uint64_t aInnerWindowID,const nsACString & aHeader)5026 nsresult nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
5027                                                nsIPrincipal* aPrincipal,
5028                                                uint64_t aInnerWindowID,
5029                                                const nsACString& aHeader) {
5030   // Refresh headers are parsed with the following format in mind
5031   // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
5032   // By the time we are here, the following is true:
5033   // header = "REFRESH"
5034   // content = "5; URL=http://uri" // note the URL attribute is
5035   // optional, if it is absent, the currently loaded url is used.
5036   // Also note that the seconds and URL separator can be either
5037   // a ';' or a ','. The ',' separator should be illegal but CNN
5038   // is using it.
5039   //
5040   // We need to handle the following strings, where
5041   //  - X is a set of digits
5042   //  - URI is either a relative or absolute URI
5043   //
5044   // Note that URI should start with "url=" but we allow omission
5045   //
5046   // "" || ";" || ","
5047   //  empty string. use the currently loaded URI
5048   //  and refresh immediately.
5049   // "X" || "X;" || "X,"
5050   //  Refresh the currently loaded URI in X seconds.
5051   // "X; URI" || "X, URI"
5052   //  Refresh using URI as the destination in X seconds.
5053   // "URI" || "; URI" || ", URI"
5054   //  Refresh immediately using URI as the destination.
5055   //
5056   // Currently, anything immediately following the URI, if
5057   // separated by any char in the set "'\"\t\r\n " will be
5058   // ignored. So "10; url=go.html ; foo=bar" will work,
5059   // and so will "10; url='go.html'; foo=bar". However,
5060   // "10; url=go.html; foo=bar" will result in the uri
5061   // "go.html;" since ';' and ',' are valid uri characters.
5062   //
5063   // Note that we need to remove any tokens wrapping the URI.
5064   // These tokens currently include spaces, double and single
5065   // quotes.
5066 
5067   // when done, seconds is 0 or the given number of seconds
5068   //            uriAttrib is empty or the URI specified
5069   MOZ_ASSERT(aPrincipal);
5070 
5071   nsAutoCString uriAttrib;
5072   int32_t seconds = 0;
5073   bool specifiesSeconds = false;
5074 
5075   nsACString::const_iterator iter, tokenStart, doneIterating;
5076 
5077   aHeader.BeginReading(iter);
5078   aHeader.EndReading(doneIterating);
5079 
5080   // skip leading whitespace
5081   while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
5082     ++iter;
5083   }
5084 
5085   tokenStart = iter;
5086 
5087   // skip leading + and -
5088   if (iter != doneIterating && (*iter == '-' || *iter == '+')) {
5089     ++iter;
5090   }
5091 
5092   // parse number
5093   while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
5094     seconds = seconds * 10 + (*iter - '0');
5095     specifiesSeconds = true;
5096     ++iter;
5097   }
5098 
5099   if (iter != doneIterating) {
5100     // if we started with a '-', number is negative
5101     if (*tokenStart == '-') {
5102       seconds = -seconds;
5103     }
5104 
5105     // skip to next ';' or ','
5106     nsACString::const_iterator iterAfterDigit = iter;
5107     while (iter != doneIterating && !(*iter == ';' || *iter == ',')) {
5108       if (specifiesSeconds) {
5109         // Non-whitespace characters here mean that the string is
5110         // malformed but tolerate sites that specify a decimal point,
5111         // even though meta refresh only works on whole seconds.
5112         if (iter == iterAfterDigit && !nsCRT::IsAsciiSpace(*iter) &&
5113             *iter != '.') {
5114           // The characters between the seconds and the next
5115           // section are just garbage!
5116           //   e.g. content="2a0z+,URL=http://www.mozilla.org/"
5117           // Just ignore this redirect.
5118           return NS_ERROR_FAILURE;
5119         } else if (nsCRT::IsAsciiSpace(*iter)) {
5120           // We've had at least one whitespace so tolerate the mistake
5121           // and drop through.
5122           // e.g. content="10 foo"
5123           ++iter;
5124           break;
5125         }
5126       }
5127       ++iter;
5128     }
5129 
5130     // skip any remaining whitespace
5131     while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
5132       ++iter;
5133     }
5134 
5135     // skip ';' or ','
5136     if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
5137       ++iter;
5138     }
5139 
5140     // skip whitespace
5141     while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
5142       ++iter;
5143     }
5144   }
5145 
5146   // possible start of URI
5147   tokenStart = iter;
5148 
5149   // skip "url = " to real start of URI
5150   if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
5151     ++iter;
5152     if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
5153       ++iter;
5154       if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
5155         ++iter;
5156 
5157         // skip whitespace
5158         while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
5159           ++iter;
5160         }
5161 
5162         if (iter != doneIterating && *iter == '=') {
5163           ++iter;
5164 
5165           // skip whitespace
5166           while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
5167             ++iter;
5168           }
5169 
5170           // found real start of URI
5171           tokenStart = iter;
5172         }
5173       }
5174     }
5175   }
5176 
5177   // skip a leading '"' or '\''.
5178 
5179   bool isQuotedURI = false;
5180   if (tokenStart != doneIterating &&
5181       (*tokenStart == '"' || *tokenStart == '\'')) {
5182     isQuotedURI = true;
5183     ++tokenStart;
5184   }
5185 
5186   // set iter to start of URI
5187   iter = tokenStart;
5188 
5189   // tokenStart here points to the beginning of URI
5190 
5191   // grab the rest of the URI
5192   while (iter != doneIterating) {
5193     if (isQuotedURI && (*iter == '"' || *iter == '\'')) {
5194       break;
5195     }
5196     ++iter;
5197   }
5198 
5199   // move iter one back if the last character is a '"' or '\''
5200   if (iter != tokenStart && isQuotedURI) {
5201     --iter;
5202     if (!(*iter == '"' || *iter == '\'')) {
5203       ++iter;
5204     }
5205   }
5206 
5207   // URI is whatever's contained from tokenStart to iter.
5208   // note: if tokenStart == doneIterating, so is iter.
5209 
5210   nsresult rv = NS_OK;
5211 
5212   nsCOMPtr<nsIURI> uri;
5213   bool specifiesURI = false;
5214   if (tokenStart == iter) {
5215     uri = aBaseURI;
5216   } else {
5217     uriAttrib = Substring(tokenStart, iter);
5218     // NS_NewURI takes care of any whitespace surrounding the URL
5219     rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
5220     specifiesURI = true;
5221   }
5222 
5223   // No URI or seconds were specified
5224   if (!specifiesSeconds && !specifiesURI) {
5225     // Do nothing because the alternative is to spin around in a refresh
5226     // loop forever!
5227     return NS_ERROR_FAILURE;
5228   }
5229 
5230   if (NS_SUCCEEDED(rv)) {
5231     nsCOMPtr<nsIScriptSecurityManager> securityManager(
5232         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
5233     if (NS_SUCCEEDED(rv)) {
5234       rv = securityManager->CheckLoadURIWithPrincipal(
5235           aPrincipal, uri,
5236           nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5237           aInnerWindowID);
5238 
5239       if (NS_SUCCEEDED(rv)) {
5240         bool isjs = true;
5241         rv = NS_URIChainHasFlags(
5242             uri, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5243         NS_ENSURE_SUCCESS(rv, rv);
5244 
5245         if (isjs) {
5246           return NS_ERROR_FAILURE;
5247         }
5248       }
5249 
5250       if (NS_SUCCEEDED(rv)) {
5251         // Since we can't travel back in time yet, just pretend
5252         // negative numbers do nothing at all.
5253         if (seconds < 0) {
5254           return NS_ERROR_FAILURE;
5255         }
5256 
5257         rv = RefreshURI(uri, aPrincipal, seconds * 1000, false, true);
5258       }
5259     }
5260   }
5261   return rv;
5262 }
5263 
5264 NS_IMETHODIMP
SetupRefreshURI(nsIChannel * aChannel)5265 nsDocShell::SetupRefreshURI(nsIChannel* aChannel) {
5266   nsresult rv;
5267   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
5268   if (NS_SUCCEEDED(rv)) {
5269     nsAutoCString refreshHeader;
5270     rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
5271                                         refreshHeader);
5272 
5273     if (!refreshHeader.IsEmpty()) {
5274       nsCOMPtr<nsIScriptSecurityManager> secMan =
5275           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
5276       NS_ENSURE_SUCCESS(rv, rv);
5277 
5278       nsCOMPtr<nsIPrincipal> principal;
5279       rv = secMan->GetChannelResultPrincipal(aChannel,
5280                                              getter_AddRefs(principal));
5281       NS_ENSURE_SUCCESS(rv, rv);
5282 
5283       SetupReferrerInfoFromChannel(aChannel);
5284       // We have no idea what window id to use for error reporting
5285       // here, so just pass 0.
5286       rv = SetupRefreshURIFromHeader(mCurrentURI, principal, 0, refreshHeader);
5287       if (NS_SUCCEEDED(rv)) {
5288         return NS_REFRESHURI_HEADER_FOUND;
5289       }
5290     }
5291   }
5292   return rv;
5293 }
5294 
DoCancelRefreshURITimers(nsIMutableArray * aTimerList)5295 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5296   if (!aTimerList) {
5297     return;
5298   }
5299 
5300   uint32_t n = 0;
5301   aTimerList->GetLength(&n);
5302 
5303   while (n) {
5304     nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5305 
5306     aTimerList->RemoveElementAt(n);  // bye bye owning timer ref
5307 
5308     if (timer) {
5309       timer->Cancel();
5310     }
5311   }
5312 }
5313 
5314 NS_IMETHODIMP
CancelRefreshURITimers()5315 nsDocShell::CancelRefreshURITimers() {
5316   DoCancelRefreshURITimers(mRefreshURIList);
5317   DoCancelRefreshURITimers(mSavedRefreshURIList);
5318   mRefreshURIList = nullptr;
5319   mSavedRefreshURIList = nullptr;
5320 
5321   return NS_OK;
5322 }
5323 
5324 NS_IMETHODIMP
GetRefreshPending(bool * aResult)5325 nsDocShell::GetRefreshPending(bool* aResult) {
5326   if (!mRefreshURIList) {
5327     *aResult = false;
5328     return NS_OK;
5329   }
5330 
5331   uint32_t count;
5332   nsresult rv = mRefreshURIList->GetLength(&count);
5333   if (NS_SUCCEEDED(rv)) {
5334     *aResult = (count != 0);
5335   }
5336   return rv;
5337 }
5338 
5339 NS_IMETHODIMP
SuspendRefreshURIs()5340 nsDocShell::SuspendRefreshURIs() {
5341   if (mRefreshURIList) {
5342     uint32_t n = 0;
5343     mRefreshURIList->GetLength(&n);
5344 
5345     for (uint32_t i = 0; i < n; ++i) {
5346       nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5347       if (!timer) {
5348         continue;  // this must be a nsRefreshURI already
5349       }
5350 
5351       // Replace this timer object with a nsRefreshTimer object.
5352       nsCOMPtr<nsITimerCallback> callback;
5353       timer->GetCallback(getter_AddRefs(callback));
5354 
5355       timer->Cancel();
5356 
5357       mRefreshURIList->ReplaceElementAt(callback, i);
5358     }
5359   }
5360 
5361   // Suspend refresh URIs for our child shells as well.
5362   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5363   while (iter.HasMore()) {
5364     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
5365     if (shell) {
5366       shell->SuspendRefreshURIs();
5367     }
5368   }
5369 
5370   return NS_OK;
5371 }
5372 
5373 NS_IMETHODIMP
ResumeRefreshURIs()5374 nsDocShell::ResumeRefreshURIs() {
5375   RefreshURIFromQueue();
5376 
5377   // Resume refresh URIs for our child shells as well.
5378   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5379   while (iter.HasMore()) {
5380     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
5381     if (shell) {
5382       shell->ResumeRefreshURIs();
5383     }
5384   }
5385 
5386   return NS_OK;
5387 }
5388 
RefreshURIFromQueue()5389 nsresult nsDocShell::RefreshURIFromQueue() {
5390   if (!mRefreshURIList) {
5391     return NS_OK;
5392   }
5393   uint32_t n = 0;
5394   mRefreshURIList->GetLength(&n);
5395 
5396   while (n) {
5397     nsCOMPtr<nsITimerCallback> refreshInfo =
5398         do_QueryElementAt(mRefreshURIList, --n);
5399 
5400     if (refreshInfo) {
5401       // This is the nsRefreshTimer object, waiting to be
5402       // setup in a timer object and fired.
5403       // Create the timer and  trigger it.
5404       uint32_t delay = static_cast<nsRefreshTimer*>(
5405                            static_cast<nsITimerCallback*>(refreshInfo))
5406                            ->GetDelay();
5407       nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5408       if (win) {
5409         nsCOMPtr<nsITimer> timer;
5410         NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5411                                 nsITimer::TYPE_ONE_SHOT);
5412 
5413         if (timer) {
5414           // Replace the nsRefreshTimer element in the queue with
5415           // its corresponding timer object, so that in case another
5416           // load comes through before the timer can go off, the timer will
5417           // get cancelled in CancelRefreshURITimer()
5418           mRefreshURIList->ReplaceElementAt(timer, n);
5419         }
5420       }
5421     }
5422   }
5423 
5424   return NS_OK;
5425 }
5426 
Embed(nsIContentViewer * aContentViewer,WindowGlobalChild * aWindowActor)5427 nsresult nsDocShell::Embed(nsIContentViewer* aContentViewer,
5428                            WindowGlobalChild* aWindowActor) {
5429   // Save the LayoutHistoryState of the previous document, before
5430   // setting up new document
5431   PersistLayoutHistoryState();
5432 
5433   nsresult rv = SetupNewViewer(aContentViewer, aWindowActor);
5434   NS_ENSURE_SUCCESS(rv, rv);
5435 
5436   // XXX What if SetupNewViewer fails?
5437   if (mLSHE) {
5438     // Restore the editing state, if it's stored in session history.
5439     if (mLSHE->HasDetachedEditor()) {
5440       ReattachEditorToWindow(mLSHE);
5441     }
5442     // Set history.state
5443     SetDocCurrentStateObj(mLSHE);
5444 
5445     SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5446     if (StaticPrefs::fission_sessionHistoryInParent()) {
5447       mActiveEntry = nullptr;
5448       mLoadingEntry.swap(mActiveEntry);
5449       if (XRE_IsParentProcess()) {
5450         mBrowsingContext->Canonical()->SessionHistoryCommit(mLoadingEntryId);
5451       } else {
5452         ContentChild* cc = ContentChild::GetSingleton();
5453         mozilla::Unused << cc->SendHistoryCommit(mBrowsingContext,
5454                                                  mLoadingEntryId);
5455       }
5456       mLoadingEntryId = 0;
5457     }
5458   }
5459 
5460   bool updateHistory = true;
5461 
5462   // Determine if this type of load should update history
5463   switch (mLoadType) {
5464     case LOAD_NORMAL_REPLACE:
5465     case LOAD_STOP_CONTENT_AND_REPLACE:
5466     case LOAD_RELOAD_BYPASS_CACHE:
5467     case LOAD_RELOAD_BYPASS_PROXY:
5468     case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5469     case LOAD_REPLACE_BYPASS_CACHE:
5470       updateHistory = false;
5471       break;
5472     default:
5473       break;
5474   }
5475 
5476   if (!updateHistory) {
5477     SetLayoutHistoryState(nullptr);
5478   }
5479 
5480   return NS_OK;
5481 }
5482 
5483 //*****************************************************************************
5484 // nsDocShell::nsIWebProgressListener
5485 //*****************************************************************************
5486 
5487 NS_IMETHODIMP
OnProgressChange(nsIWebProgress * aProgress,nsIRequest * aRequest,int32_t aCurSelfProgress,int32_t aMaxSelfProgress,int32_t aCurTotalProgress,int32_t aMaxTotalProgress)5488 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5489                              int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5490                              int32_t aCurTotalProgress,
5491                              int32_t aMaxTotalProgress) {
5492   return NS_OK;
5493 }
5494 
5495 NS_IMETHODIMP
OnStateChange(nsIWebProgress * aProgress,nsIRequest * aRequest,uint32_t aStateFlags,nsresult aStatus)5496 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5497                           uint32_t aStateFlags, nsresult aStatus) {
5498   if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5499     // Save timing statistics.
5500     nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5501     nsCOMPtr<nsIURI> uri;
5502     channel->GetURI(getter_AddRefs(uri));
5503     nsAutoCString aURI;
5504     uri->GetAsciiSpec(aURI);
5505 
5506     if (this == aProgress) {
5507       mozilla::Unused << MaybeInitTiming();
5508       mTiming->NotifyFetchStart(uri,
5509                                 ConvertLoadTypeToNavigationType(mLoadType));
5510       // If we are starting a DocumentChannel, we need to pass the timing
5511       // statistics so that should a process switch occur, the starting type can
5512       // be passed to the new DocShell running in the other content process.
5513       if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5514         docChannel->SetNavigationTiming(mTiming);
5515       }
5516     }
5517 
5518     // Page has begun to load
5519     mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5520 
5521     if ((aStateFlags & STATE_RESTORING) == 0) {
5522       // Show the progress cursor if the pref is set
5523       if (StaticPrefs::ui_use_activity_cursor()) {
5524         nsCOMPtr<nsIWidget> mainWidget;
5525         GetMainWidget(getter_AddRefs(mainWidget));
5526         if (mainWidget) {
5527           mainWidget->SetCursor(eCursor_spinning, nullptr, 0, 0);
5528         }
5529       }
5530     }
5531   } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5532     // Page is loading
5533     mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5534   } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5535     // Page has finished loading
5536     mBusyFlags = BUSY_FLAGS_NONE;
5537 
5538     // Hide the progress cursor if the pref is set
5539     if (StaticPrefs::ui_use_activity_cursor()) {
5540       nsCOMPtr<nsIWidget> mainWidget;
5541       GetMainWidget(getter_AddRefs(mainWidget));
5542       if (mainWidget) {
5543         mainWidget->SetCursor(eCursor_standard, nullptr, 0, 0);
5544       }
5545     }
5546   }
5547   if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5548     nsCOMPtr<nsIWebProgress> webProgress =
5549         do_QueryInterface(GetAsSupports(this));
5550     // Is the document stop notification for this document?
5551     if (aProgress == webProgress.get()) {
5552       nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5553       EndPageLoad(aProgress, channel, aStatus);
5554     }
5555   }
5556   // note that redirect state changes will go through here as well, but it
5557   // is better to handle those in OnRedirectStateChange where more
5558   // information is available.
5559   return NS_OK;
5560 }
5561 
5562 NS_IMETHODIMP
OnLocationChange(nsIWebProgress * aProgress,nsIRequest * aRequest,nsIURI * aURI,uint32_t aFlags)5563 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5564                              nsIURI* aURI, uint32_t aFlags) {
5565   if (XRE_IsParentProcess()) {
5566     // Since we've now changed Documents, notify the BrowsingContext that we've
5567     // changed. Ideally we'd just let the BrowsingContext do this when it
5568     // changes the current window global, but that happens before this and we
5569     // have a lot of tests that depend on the specific ordering of messages.
5570     if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) {
5571       GetBrowsingContext()
5572           ->Canonical()
5573           ->UpdateSecurityStateForLocationOrMixedContentChange();
5574     }
5575   }
5576   return NS_OK;
5577 }
5578 
OnRedirectStateChange(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aRedirectFlags,uint32_t aStateFlags)5579 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5580                                        nsIChannel* aNewChannel,
5581                                        uint32_t aRedirectFlags,
5582                                        uint32_t aStateFlags) {
5583   NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5584                "Calling OnRedirectStateChange when there is no redirect");
5585 
5586   // If mixed content is allowed for the old channel, we forward
5587   // the permission to the new channel if it has the same origin
5588   // as the old one.
5589   if (mMixedContentChannel && mMixedContentChannel == aOldChannel) {
5590     nsresult rv =
5591         nsContentUtils::CheckSameOrigin(mMixedContentChannel, aNewChannel);
5592     if (NS_SUCCEEDED(rv)) {
5593       SetMixedContentChannel(aNewChannel);  // Same origin: forward permission.
5594     } else {
5595       SetMixedContentChannel(
5596           nullptr);  // Different origin: clear mMixedContentChannel.
5597     }
5598   }
5599 
5600   if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5601     return;  // not a toplevel document
5602   }
5603 
5604   nsCOMPtr<nsIURI> oldURI, newURI;
5605   aOldChannel->GetURI(getter_AddRefs(oldURI));
5606   aNewChannel->GetURI(getter_AddRefs(newURI));
5607   if (!oldURI || !newURI) {
5608     return;
5609   }
5610 
5611   // DocumentChannel adds redirect chain to global history in the parent
5612   // process. The redirect chain can't be queried from the content process, so
5613   // there's no need to update global history here.
5614   RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5615   if (!docChannel) {
5616     // Below a URI visit is saved (see AddURIVisit method doc).
5617     // The visit chain looks something like:
5618     //   ...
5619     //   Site N - 1
5620     //                =>  Site N
5621     //   (redirect to =>) Site N + 1 (we are here!)
5622 
5623     // Get N - 1 and transition type
5624     nsCOMPtr<nsIURI> previousURI;
5625     uint32_t previousFlags = 0;
5626     ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5627 
5628     if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5629         net::ChannelIsPost(aOldChannel)) {
5630       // 1. Internal redirects are ignored because they are specific to the
5631       //    channel implementation.
5632       // 2. POSTs are not saved by global history.
5633       //
5634       // Regardless, we need to propagate the previous visit to the new
5635       // channel.
5636       SaveLastVisit(aNewChannel, previousURI, previousFlags);
5637     } else {
5638       // Get the HTTP response code, if available.
5639       uint32_t responseStatus = 0;
5640       nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5641       if (httpChannel) {
5642         Unused << httpChannel->GetResponseStatus(&responseStatus);
5643       }
5644 
5645       // Add visit N -1 => N
5646       AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5647 
5648       // Since N + 1 could be the final destination, we will not save N => N + 1
5649       // here.  OnNewURI will do that, so we will cache it.
5650       SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5651     }
5652   }
5653 
5654   // check if the new load should go through the application cache.
5655   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
5656       do_QueryInterface(aNewChannel);
5657   if (appCacheChannel && !docChannel) {
5658     if (GeckoProcessType_Default != XRE_GetProcessType()) {
5659       // Permission will be checked in the parent process.
5660       appCacheChannel->SetChooseApplicationCache(true);
5661     } else {
5662       nsCOMPtr<nsIScriptSecurityManager> secMan =
5663           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
5664 
5665       if (secMan) {
5666         nsCOMPtr<nsIPrincipal> principal;
5667         secMan->GetDocShellContentPrincipal(newURI, this,
5668                                             getter_AddRefs(principal));
5669         appCacheChannel->SetChooseApplicationCache(
5670             NS_ShouldCheckAppCache(principal));
5671       }
5672     }
5673   }
5674 
5675   if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5676       mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5677     mLoadType = LOAD_NORMAL_REPLACE;
5678     SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5679   }
5680 }
5681 
5682 NS_IMETHODIMP
OnStatusChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsresult aStatus,const char16_t * aMessage)5683 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5684                            nsresult aStatus, const char16_t* aMessage) {
5685   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5686   return NS_OK;
5687 }
5688 
5689 NS_IMETHODIMP
OnSecurityChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aState)5690 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5691                              uint32_t aState) {
5692   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5693   return NS_OK;
5694 }
5695 
5696 NS_IMETHODIMP
OnContentBlockingEvent(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aEvent)5697 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5698                                    nsIRequest* aRequest, uint32_t aEvent) {
5699   MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5700   return NS_OK;
5701 }
5702 
KeywordToURI(const nsACString & aKeyword,bool aIsPrivateContext,nsIInputStream ** aPostData)5703 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5704     const nsACString& aKeyword, bool aIsPrivateContext,
5705     nsIInputStream** aPostData) {
5706   nsCOMPtr<nsIURIFixupInfo> info;
5707   if (XRE_IsContentProcess()) {
5708     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
5709     if (!contentChild) {
5710       return nullptr;
5711     }
5712 
5713     info = do_CreateInstance("@mozilla.org/docshell/uri-fixup-info;1");
5714     if (NS_WARN_IF(!info)) {
5715       return nullptr;
5716     }
5717     RefPtr<nsIInputStream> postData;
5718     RefPtr<nsIURI> uri;
5719     nsAutoString providerName;
5720     nsAutoCString keyword(aKeyword);
5721     // TODO (Bug 1375244): This synchronous IPC messaging should be changed.
5722     if (contentChild->SendKeywordToURI(keyword, aIsPrivateContext,
5723                                        &providerName, &postData, &uri)) {
5724       NS_ConvertUTF8toUTF16 keywordW(aKeyword);
5725       info->SetKeywordAsSent(keywordW);
5726       info->SetKeywordProviderName(providerName);
5727       if (aPostData) {
5728         postData.forget(aPostData);
5729       }
5730       info->SetPreferredURI(uri);
5731     }
5732   } else {
5733     nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5734     if (uriFixup) {
5735       uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, aPostData,
5736                              getter_AddRefs(info));
5737     }
5738   }
5739   return info.forget();
5740 }
5741 
EndPageLoad(nsIWebProgress * aProgress,nsIChannel * aChannel,nsresult aStatus)5742 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
5743                                  nsIChannel* aChannel, nsresult aStatus) {
5744   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
5745           ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
5746            static_cast<uint32_t>(aStatus)));
5747   if (!aChannel) {
5748     return NS_ERROR_NULL_POINTER;
5749   }
5750 
5751   // Make sure to discard the initial client if we never created the initial
5752   // about:blank document.  Do this before possibly returning from the method
5753   // due to an error.
5754   mInitialClientSource.reset();
5755 
5756   nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
5757   if (reporter) {
5758     nsCOMPtr<nsILoadGroup> loadGroup;
5759     aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
5760     if (loadGroup) {
5761       reporter->FlushConsoleReports(loadGroup);
5762     } else {
5763       reporter->FlushConsoleReports(GetDocument());
5764     }
5765   }
5766 
5767   nsCOMPtr<nsIURI> url;
5768   nsresult rv = aChannel->GetURI(getter_AddRefs(url));
5769   if (NS_FAILED(rv)) {
5770     return rv;
5771   }
5772 
5773   nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
5774   if (timingChannel) {
5775     TimeStamp channelCreationTime;
5776     rv = timingChannel->GetChannelCreation(&channelCreationTime);
5777     if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
5778       Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
5779                                      channelCreationTime);
5780     }
5781   }
5782 
5783   // Timing is picked up by the window, we don't need it anymore
5784   mTiming = nullptr;
5785 
5786   // clean up reload state for meta charset
5787   if (eCharsetReloadRequested == mCharsetReloadState) {
5788     mCharsetReloadState = eCharsetReloadStopOrigional;
5789   } else {
5790     mCharsetReloadState = eCharsetReloadInit;
5791   }
5792 
5793   // Save a pointer to the currently-loading history entry.
5794   // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
5795   // entry further down in this method.
5796   nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
5797   mozilla::Unused << loadingSHE;  // XXX: Not sure if we need this anymore
5798 
5799   //
5800   // one of many safeguards that prevent death and destruction if
5801   // someone is so very very rude as to bring this window down
5802   // during this load handler.
5803   //
5804   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
5805 
5806   // Notify the ContentViewer that the Document has finished loading.  This
5807   // will cause any OnLoad(...) and PopState(...) handlers to fire.
5808   if (!mEODForCurrentDocument && mContentViewer) {
5809     mIsExecutingOnLoadHandler = true;
5810     nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
5811     contentViewer->LoadComplete(aStatus);
5812     mIsExecutingOnLoadHandler = false;
5813 
5814     mEODForCurrentDocument = true;
5815 
5816     // If all documents have completed their loading
5817     // favor native event dispatch priorities
5818     // over performance
5819     if (--gNumberOfDocumentsLoading == 0) {
5820       // Hint to use normal native event dispatch priorities
5821       FavorPerformanceHint(false);
5822     }
5823   }
5824   /* Check if the httpChannel has any cache-control related response headers,
5825    * like no-store, no-cache. If so, update SHEntry so that
5826    * when a user goes back/forward to this page, we appropriately do
5827    * form value restoration or load from server.
5828    */
5829   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
5830   if (!httpChannel) {
5831     // HttpChannel could be hiding underneath a Multipart channel.
5832     GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
5833   }
5834 
5835   if (httpChannel) {
5836     // figure out if SH should be saving layout state.
5837     bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
5838     if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
5839         (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
5840       mLSHE->SetSaveLayoutStateFlag(false);
5841     }
5842   }
5843 
5844   // Clear mLSHE after calling the onLoadHandlers. This way, if the
5845   // onLoadHandler tries to load something different in
5846   // itself or one of its children, we can deal with it appropriately.
5847   if (mLSHE) {
5848     mLSHE->SetLoadType(LOAD_HISTORY);
5849 
5850     // Clear the mLSHE reference to indicate document loading is done one
5851     // way or another.
5852     SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5853   }
5854   // if there's a refresh header in the channel, this method
5855   // will set it up for us.
5856   if (mBrowsingContext->GetIsActive() || !mDisableMetaRefreshWhenInactive)
5857     RefreshURIFromQueue();
5858 
5859   // Test whether this is the top frame or a subframe
5860   bool isTopFrame = mBrowsingContext->IsTop();
5861 
5862   //
5863   // If the page load failed, then deal with the error condition...
5864   // Errors are handled as follows:
5865   //   1. Check to see if it's a file not found error or bad content
5866   //      encoding error.
5867   //   2. Send the URI to a keyword server (if enabled)
5868   //   3. If the error was DNS failure, then add www and .com to the URI
5869   //      (if appropriate).
5870   //   4. If the www .com additions don't work, try those with an HTTPS scheme
5871   //      (if appropriate).
5872   //   5. Throw an error dialog box...
5873   //
5874   if (url && NS_FAILED(aStatus)) {
5875     if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
5876         aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
5877         aStatus == NS_ERROR_CORRUPTED_CONTENT ||
5878         aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
5879       UnblockEmbedderLoadEventForFailure();
5880       DisplayLoadError(aStatus, url, nullptr, aChannel);
5881       return NS_OK;
5882     }
5883 
5884     // Handle iframe document not loading error because source was
5885     // a tracking URL. We make a note of this iframe node by including
5886     // it in a dedicated array of blocked tracking nodes under its parent
5887     // document. (document of parent window of blocked document)
5888     if (!isTopFrame &&
5889         UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aStatus)) {
5890       UnblockEmbedderLoadEventForFailure();
5891 
5892       // We don't really need to add the blocked node if we are not testing.
5893       // This could save a IPC here.
5894       if (!StaticPrefs::
5895               privacy_trackingprotection_testing_report_blocked_node()) {
5896         return NS_OK;
5897       }
5898 
5899       RefPtr<BrowsingContext> bc = GetBrowsingContext();
5900       RefPtr<BrowsingContext> parentBC = bc->GetParent();
5901 
5902       if (!parentBC) {
5903         return NS_OK;
5904       }
5905 
5906       if (parentBC->IsInProcess()) {
5907         // We can directly add the blocked node in the parent document if the
5908         // parent is in the same process.
5909         nsCOMPtr<nsPIDOMWindowOuter> parentOuter = parentBC->GetDOMWindow();
5910 
5911         if (!parentOuter) {
5912           return NS_OK;
5913         }
5914 
5915         nsCOMPtr<nsPIDOMWindowInner> parentInner =
5916             parentOuter->GetCurrentInnerWindow();
5917 
5918         if (!parentInner) {
5919           return NS_OK;
5920         }
5921 
5922         RefPtr<Document> parentDoc;
5923         parentDoc = parentInner->GetExtantDoc();
5924         if (!parentDoc) {
5925           return NS_OK;
5926         }
5927 
5928         parentDoc->AddBlockedNodeByClassifier(bc->GetEmbedderElement());
5929       } else {
5930         // If the parent is out-of-process, we send to the process of the
5931         // document which embeds the frame to add the blocked node there.
5932         RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
5933         if (browserChild) {
5934           Unused << browserChild->SendReportBlockedEmbedderNodeByClassifier();
5935         }
5936       }
5937 
5938       return NS_OK;
5939     }
5940 
5941     if ((aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET ||
5942          aStatus == NS_ERROR_CONNECTION_REFUSED) &&
5943         ((mLoadType == LOAD_NORMAL && isTopFrame) || mAllowKeywordFixup)) {
5944       //
5945       // Try and make an alternative URI from the old one
5946       //
5947       nsCOMPtr<nsIURI> newURI;
5948       nsCOMPtr<nsIInputStream> newPostData;
5949 
5950       nsAutoCString oldSpec;
5951       url->GetSpec(oldSpec);
5952 
5953       //
5954       // First try keyword fixup
5955       //
5956       nsAutoString keywordProviderName, keywordAsSent;
5957       if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
5958         // we should only perform a keyword search under the following
5959         // conditions:
5960         // (0) Pref keyword.enabled is true
5961         // (1) the url scheme is http (or https)
5962         // (2) the url does not have a protocol scheme
5963         // If we don't enforce such a policy, then we end up doing
5964         // keyword searchs on urls we don't intend like imap, file,
5965         // mailbox, etc. This could lead to a security problem where we
5966         // send data to the keyword server that we shouldn't be.
5967         // Someone needs to clean up keywords in general so we can
5968         // determine on a per url basis if we want keywords
5969         // enabled...this is just a bandaid...
5970         nsAutoCString scheme;
5971         Unused << url->GetScheme(scheme);
5972         if (Preferences::GetBool("keyword.enabled", false) &&
5973             StringBeginsWith(scheme, NS_LITERAL_CSTRING("http"))) {
5974           bool attemptFixup = false;
5975           nsAutoCString host;
5976           Unused << url->GetHost(host);
5977           if (host.FindChar('.') == kNotFound) {
5978             attemptFixup = true;
5979           } else {
5980             // For domains with dots, we check the public suffix validity.
5981             nsCOMPtr<nsIEffectiveTLDService> tldService =
5982                 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
5983             if (tldService) {
5984               nsAutoCString suffix;
5985               attemptFixup =
5986                   NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
5987                   suffix.IsEmpty();
5988             }
5989           }
5990           if (attemptFixup) {
5991             nsCOMPtr<nsIURIFixupInfo> info;
5992             // only send non-qualified hosts to the keyword server
5993             if (!mOriginalUriString.IsEmpty()) {
5994               info = KeywordToURI(mOriginalUriString, UsePrivateBrowsing(),
5995                                   getter_AddRefs(newPostData));
5996             } else {
5997               //
5998               // If this string was passed through nsStandardURL by
5999               // chance, then it may have been converted from UTF-8 to
6000               // ACE, which would result in a completely bogus keyword
6001               // query.  Here we try to recover the original Unicode
6002               // value, but this is not 100% correct since the value may
6003               // have been normalized per the IDN normalization rules.
6004               //
6005               // Since we don't have access to the exact original string
6006               // that was entered by the user, this will just have to do.
6007               bool isACE;
6008               nsAutoCString utf8Host;
6009               nsCOMPtr<nsIIDNService> idnSrv =
6010                   do_GetService(NS_IDNSERVICE_CONTRACTID);
6011               if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) &&
6012                   isACE &&
6013                   NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6014                 info = KeywordToURI(utf8Host, UsePrivateBrowsing(),
6015                                     getter_AddRefs(newPostData));
6016               } else {
6017                 info = KeywordToURI(host, UsePrivateBrowsing(),
6018                                     getter_AddRefs(newPostData));
6019               }
6020             }
6021             if (info) {
6022               info->GetPreferredURI(getter_AddRefs(newURI));
6023               if (newURI) {
6024                 info->GetKeywordAsSent(keywordAsSent);
6025                 info->GetKeywordProviderName(keywordProviderName);
6026               }
6027             }
6028           }
6029         }
6030       }
6031 
6032       //
6033       // Now try change the address, e.g. turn http://foo into
6034       // http://www.foo.com, and if that doesn't work try https with
6035       // https://foo and https://www.foo.com.
6036       //
6037       if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6038         bool doCreateAlternate = true;
6039 
6040         // Skip fixup for anything except a normal document load
6041         // operation on the topframe.
6042 
6043         if (mLoadType != LOAD_NORMAL || !isTopFrame) {
6044           doCreateAlternate = false;
6045         } else {
6046           // Test if keyword lookup produced a new URI or not
6047           if (newURI) {
6048             bool sameURI = false;
6049             url->Equals(newURI, &sameURI);
6050             if (!sameURI) {
6051               // Keyword lookup made a new URI so no need to try
6052               // an alternate one.
6053               doCreateAlternate = false;
6054             }
6055           }
6056 
6057           if (doCreateAlternate) {
6058             nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6059             // Skip doing this if our channel was redirected, because we
6060             // shouldn't be guessing things about the post-redirect URI.
6061             if (!info->RedirectChain().IsEmpty()) {
6062               doCreateAlternate = false;
6063             }
6064           }
6065         }
6066         if (doCreateAlternate) {
6067           newURI = nullptr;
6068           newPostData = nullptr;
6069           keywordProviderName.Truncate();
6070           keywordAsSent.Truncate();
6071           nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6072           if (uriFixup) {
6073             uriFixup->CreateFixupURI(
6074                 oldSpec, nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
6075                 getter_AddRefs(newPostData), getter_AddRefs(newURI));
6076           }
6077         }
6078       } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6079                  Preferences::GetBool("browser.fixup.fallback-to-https",
6080                                       false)) {
6081         // Try HTTPS, since http didn't work
6082         if (SchemeIsHTTP(url)) {
6083           int32_t port = 0;
6084           url->GetPort(&port);
6085 
6086           // Fall back to HTTPS only if port is default
6087           if (port == -1) {
6088             newURI = nullptr;
6089             newPostData = nullptr;
6090             Unused << NS_MutateURI(url)
6091                           .SetScheme(NS_LITERAL_CSTRING("https"))
6092                           .Finalize(getter_AddRefs(newURI));
6093           }
6094         }
6095       }
6096 
6097       // Did we make a new URI that is different to the old one? If so
6098       // load it.
6099       //
6100       if (newURI) {
6101         // Make sure the new URI is different from the old one,
6102         // otherwise there's little point trying to load it again.
6103         bool sameURI = false;
6104         url->Equals(newURI, &sameURI);
6105         if (!sameURI) {
6106           nsAutoCString newSpec;
6107           newURI->GetSpec(newSpec);
6108           NS_ConvertUTF8toUTF16 newSpecW(newSpec);
6109 
6110           // This notification is meant for Firefox Health Report so it
6111           // can increment counts from the search engine
6112           MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6113 
6114           nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6115           MOZ_ASSERT(loadInfo, "loadInfo is required on all channels");
6116           nsCOMPtr<nsIPrincipal> triggeringPrincipal =
6117               loadInfo->TriggeringPrincipal();
6118 
6119           // If the new URI is HTTP, it may not work and we may want to fall
6120           // back to HTTPS, so kick off a speculative connect to get that
6121           // started.  Even if we don't adjust to HTTPS here, there could be a
6122           // redirect to HTTPS coming so this could speed things up.
6123           if (SchemeIsHTTP(url)) {
6124             int32_t port = 0;
6125             rv = url->GetPort(&port);
6126 
6127             // only do this if the port is default.
6128             if (NS_SUCCEEDED(rv) && port == -1) {
6129               nsCOMPtr<nsIURI> httpsURI;
6130               rv = NS_MutateURI(url)
6131                        .SetScheme(NS_LITERAL_CSTRING("https"))
6132                        .Finalize(getter_AddRefs(httpsURI));
6133 
6134               if (NS_SUCCEEDED(rv)) {
6135                 nsCOMPtr<nsIIOService> ios = do_GetIOService();
6136                 if (ios) {
6137                   nsCOMPtr<nsISpeculativeConnect> speculativeService =
6138                       do_QueryInterface(ios);
6139                   if (speculativeService) {
6140                     speculativeService->SpeculativeConnect(
6141                         httpsURI, triggeringPrincipal, nullptr);
6142                   }
6143                 }
6144               }
6145             }
6146           }
6147 
6148           LoadURIOptions loadURIOptions;
6149           loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
6150           loadURIOptions.mCsp = loadInfo->GetCspToInherit();
6151           loadURIOptions.mPostData = newPostData;
6152           return LoadURI(newSpecW, loadURIOptions);
6153         }
6154       }
6155     }
6156 
6157     // Well, fixup didn't work :-(
6158     // It is time to throw an error dialog box, and be done with it...
6159 
6160     // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6161     // the error event to our embedder, since tests are relying on this.
6162     // The error event is usually fired by the caller of InternalLoad, but
6163     // this particular error can happen asynchronously.
6164     // Bug 1629201 is filed for having much clearer decision making around
6165     // which cases need error events.
6166     bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6167                                 aStatus == NS_ERROR_CONTENT_BLOCKED);
6168     UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6169 
6170     // Errors to be shown only on top-level frames
6171     if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6172          aStatus == NS_ERROR_CONNECTION_REFUSED ||
6173          aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6174          aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6175          aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6176          aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6177          aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6178          aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6179          aStatus == NS_ERROR_MALFORMED_URI ||
6180          aStatus == NS_ERROR_BLOCKED_BY_POLICY) &&
6181         (isTopFrame || mUseErrorPages)) {
6182       DisplayLoadError(aStatus, url, nullptr, aChannel);
6183     } else if (aStatus == NS_ERROR_NET_TIMEOUT ||
6184                aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6185                aStatus == NS_ERROR_REDIRECT_LOOP ||
6186                aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6187                aStatus == NS_ERROR_NET_INTERRUPT ||
6188                aStatus == NS_ERROR_NET_RESET ||
6189                aStatus == NS_ERROR_PROXY_BAD_GATEWAY ||
6190                aStatus == NS_ERROR_OFFLINE || aStatus == NS_ERROR_MALWARE_URI ||
6191                aStatus == NS_ERROR_PHISHING_URI ||
6192                aStatus == NS_ERROR_UNWANTED_URI ||
6193                aStatus == NS_ERROR_HARMFUL_URI ||
6194                aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6195                aStatus == NS_ERROR_REMOTE_XUL ||
6196                aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6197                aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6198                aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6199                aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6200                NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6201       // Errors to be shown for any frame
6202       DisplayLoadError(aStatus, url, nullptr, aChannel);
6203     } else if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6204       // For unknown protocols we only display an error if the load is triggered
6205       // by the browser itself, or we're replacing the initial document (and
6206       // nothing else). Showing the error for page-triggered navigations causes
6207       // annoying behavior for users, see bug 1528305.
6208       //
6209       // We could, maybe, try to detect if this is in response to some user
6210       // interaction (like clicking a link, or something else) and maybe show
6211       // the error page in that case. But this allows for ctrl+clicking and such
6212       // to see the error page.
6213       nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6214       Document* doc = GetDocument();
6215       if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6216           StaticPrefs::dom_no_unknown_protocol_error_enabled() && doc &&
6217           !doc->IsInitialDocument()) {
6218         nsTArray<nsString> params;
6219         if (NS_FAILED(NS_GetSanitizedURIStringFromURI(
6220                 url, *params.AppendElement()))) {
6221           params.LastElement().AssignLiteral(u"(unknown uri)");
6222         }
6223         nsContentUtils::ReportToConsole(
6224             nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), doc,
6225             nsContentUtils::eDOM_PROPERTIES,
6226             "UnknownProtocolNavigationPrevented", params);
6227       } else {
6228         DisplayLoadError(aStatus, url, nullptr, aChannel);
6229       }
6230     } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6231       // Non-caching channels will simply return NS_ERROR_OFFLINE.
6232       // Caching channels would have to look at their flags to work
6233       // out which error to return. Or we can fix up the error here.
6234       if (!(mLoadType & LOAD_CMD_HISTORY)) {
6235         aStatus = NS_ERROR_OFFLINE;
6236       }
6237       DisplayLoadError(aStatus, url, nullptr, aChannel);
6238     }
6239   } else if (url && NS_SUCCEEDED(aStatus)) {
6240     // If we have a host
6241     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6242     PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6243   }
6244 
6245   return NS_OK;
6246 }
6247 
6248 //*****************************************************************************
6249 // nsDocShell: Content Viewer Management
6250 //*****************************************************************************
6251 
EnsureContentViewer()6252 nsresult nsDocShell::EnsureContentViewer() {
6253   if (mContentViewer) {
6254     return NS_OK;
6255   }
6256   if (mIsBeingDestroyed) {
6257     return NS_ERROR_FAILURE;
6258   }
6259 
6260   nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6261   nsCOMPtr<nsIURI> baseURI;
6262   nsIPrincipal* principal = GetInheritedPrincipal(false);
6263   nsIPrincipal* storagePrincipal = GetInheritedPrincipal(false, true);
6264 
6265   nsCOMPtr<nsIDocShellTreeItem> parentItem;
6266   GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6267   if (parentItem) {
6268     if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6269       nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6270       if (parentElement) {
6271         baseURI = parentElement->GetBaseURI();
6272         cspToInheritForAboutBlank = parentElement->GetCsp();
6273       }
6274     }
6275   }
6276 
6277   nsresult rv = CreateAboutBlankContentViewer(
6278       principal, storagePrincipal, cspToInheritForAboutBlank, baseURI);
6279 
6280   NS_ENSURE_STATE(mContentViewer);
6281 
6282   if (NS_SUCCEEDED(rv)) {
6283     RefPtr<Document> doc(GetDocument());
6284     NS_ASSERTION(doc,
6285                  "Should have doc if CreateAboutBlankContentViewer "
6286                  "succeeded!");
6287 
6288     doc->SetIsInitialDocument(true);
6289 
6290     // Documents created using EnsureContentViewer may be transient
6291     // placeholders created by framescripts before content has a
6292     // chance to load. In some cases, window.open(..., "noopener")
6293     // will create such a document and then synchronously tear it
6294     // down, firing a "pagehide" event. Doing so violates our
6295     // assertions about DocGroups. It's easier to silence the
6296     // assertion here than to avoid creating the extra document.
6297     doc->IgnoreDocGroupMismatches();
6298   }
6299 
6300   return rv;
6301 }
6302 
CreateAboutBlankContentViewer(nsIPrincipal * aPrincipal,nsIPrincipal * aStoragePrincipal,nsIContentSecurityPolicy * aCSP,nsIURI * aBaseURI,bool aTryToSaveOldPresentation,bool aCheckPermitUnload,WindowGlobalChild * aActor)6303 nsresult nsDocShell::CreateAboutBlankContentViewer(
6304     nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal,
6305     nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI,
6306     bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6307     WindowGlobalChild* aActor) {
6308   RefPtr<Document> blankDoc;
6309   nsCOMPtr<nsIContentViewer> viewer;
6310   nsresult rv = NS_ERROR_FAILURE;
6311 
6312   MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6313 
6314   /* mCreatingDocument should never be true at this point. However, it's
6315      a theoretical possibility. We want to know about it and make it stop,
6316      and this sounds like a job for an assertion. */
6317   NS_ASSERTION(!mCreatingDocument,
6318                "infinite(?) loop creating document averted");
6319   if (mCreatingDocument) {
6320     return NS_ERROR_FAILURE;
6321   }
6322 
6323   // mContentViewer->PermitUnload may release |this| docshell.
6324   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6325 
6326   AutoRestore<bool> creatingDocument(mCreatingDocument);
6327   mCreatingDocument = true;
6328 
6329   if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6330       mItemType != typeChrome) {
6331     MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6332                mBrowsingContext->OriginAttributesRef());
6333   }
6334 
6335   // Make sure timing is created.  But first record whether we had it
6336   // already, so we don't clobber the timing for an in-progress load.
6337   bool hadTiming = mTiming;
6338   bool toBeReset = MaybeInitTiming();
6339   if (mContentViewer) {
6340     if (aCheckPermitUnload) {
6341       // We've got a content viewer already. Make sure the user
6342       // permits us to discard the current document and replace it
6343       // with about:blank. And also ensure we fire the unload events
6344       // in the current document.
6345 
6346       // Unload gets fired first for
6347       // document loaded from the session history.
6348       mTiming->NotifyBeforeUnload();
6349 
6350       bool okToUnload;
6351       rv = mContentViewer->PermitUnload(&okToUnload);
6352 
6353       if (NS_SUCCEEDED(rv) && !okToUnload) {
6354         // The user chose not to unload the page, interrupt the load.
6355         MaybeResetInitTiming(toBeReset);
6356         return NS_ERROR_FAILURE;
6357       }
6358       if (mTiming) {
6359         mTiming->NotifyUnloadAccepted(mCurrentURI);
6360       }
6361     }
6362 
6363     mSavingOldViewer = aTryToSaveOldPresentation &&
6364                        CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
6365 
6366     // Make sure to blow away our mLoadingURI just in case.  No loads
6367     // from inside this pagehide.
6368     mLoadingURI = nullptr;
6369 
6370     // Stop any in-progress loading, so that we don't accidentally trigger any
6371     // PageShow notifications from Embed() interrupting our loading below.
6372     Stop();
6373 
6374     // Notify the current document that it is about to be unloaded!!
6375     //
6376     // It is important to fire the unload() notification *before* any state
6377     // is changed within the DocShell - otherwise, javascript will get the
6378     // wrong information :-(
6379     //
6380     (void)FirePageHideNotification(!mSavingOldViewer);
6381     // pagehide notification might destroy this docshell.
6382     if (mIsBeingDestroyed) {
6383       return NS_ERROR_DOCSHELL_DYING;
6384     }
6385   }
6386 
6387   // Now make sure we don't think we're in the middle of firing unload after
6388   // this point.  This will make us fire unload when the about:blank document
6389   // unloads... but that's ok, more or less.  Would be nice if it fired load
6390   // too, of course.
6391   mFiredUnloadEvent = false;
6392 
6393   nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6394       nsContentUtils::FindInternalContentViewer(
6395           NS_LITERAL_CSTRING("text/html"));
6396 
6397   if (docFactory) {
6398     nsCOMPtr<nsIPrincipal> principal, storagePrincipal;
6399     const uint32_t sandboxFlags =
6400         mBrowsingContext->GetHasLoadedNonInitialDocument()
6401             ? mBrowsingContext->GetSandboxFlags()
6402             : mBrowsingContext->GetInitialSandboxFlags();
6403     // If we're sandboxed, then create a new null principal. We skip
6404     // this if we're being created from WindowGlobalChild, since in
6405     // that case we already have a null principal if required.
6406     // We can't compare againt the BrowsingContext sandbox flag, since
6407     // the value was taken when the load initiated and may have since
6408     // changed.
6409     if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6410       if (aPrincipal) {
6411         principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6412       } else {
6413         principal = NullPrincipal::CreateWithInheritedAttributes(this);
6414       }
6415       storagePrincipal = principal;
6416     } else {
6417       principal = aPrincipal;
6418       storagePrincipal = aStoragePrincipal;
6419     }
6420 
6421     MaybeCreateInitialClientSource(principal);
6422 
6423     // generate (about:blank) document to load
6424     blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6425                                                  storagePrincipal, this);
6426     if (blankDoc) {
6427       // Hack: manually set the CSP for the new document
6428       // Please create an actual copy of the CSP (do not share the same
6429       // reference) otherwise appending a new policy within the new
6430       // document will be incorrectly propagated to the opening doc.
6431       if (aCSP) {
6432         RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6433         cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6434         blankDoc->SetCsp(cspToInherit);
6435       }
6436 
6437       // Hack: set the base URI manually, since this document never
6438       // got Reset() with a channel.
6439       blankDoc->SetBaseURI(aBaseURI);
6440 
6441       // Copy our sandbox flags to the document. These are immutable
6442       // after being set here.
6443       blankDoc->SetSandboxFlags(sandboxFlags);
6444 
6445       // create a content viewer for us and the new document
6446       docFactory->CreateInstanceForDocument(
6447           NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6448           getter_AddRefs(viewer));
6449 
6450       // hook 'em up
6451       if (viewer) {
6452         viewer->SetContainer(this);
6453         rv = Embed(viewer, aActor);
6454         NS_ENSURE_SUCCESS(rv, rv);
6455 
6456         SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
6457         rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6458       }
6459     }
6460   }
6461 
6462   // The transient about:blank viewer doesn't have a session history entry.
6463   SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6464 
6465   // Clear out our mTiming like we would in EndPageLoad, if we didn't
6466   // have one before entering this function.
6467   if (!hadTiming) {
6468     mTiming = nullptr;
6469     mBlankTiming = true;
6470   }
6471 
6472   return rv;
6473 }
6474 
6475 NS_IMETHODIMP
CreateAboutBlankContentViewer(nsIPrincipal * aPrincipal,nsIPrincipal * aStoragePrincipal,nsIContentSecurityPolicy * aCSP)6476 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
6477                                           nsIPrincipal* aStoragePrincipal,
6478                                           nsIContentSecurityPolicy* aCSP) {
6479   return CreateAboutBlankContentViewer(aPrincipal, aStoragePrincipal, aCSP,
6480                                        nullptr);
6481 }
6482 
CreateContentViewerForActor(WindowGlobalChild * aWindowActor)6483 nsresult nsDocShell::CreateContentViewerForActor(
6484     WindowGlobalChild* aWindowActor) {
6485   MOZ_ASSERT(aWindowActor);
6486 
6487   // FIXME: WindowGlobalChild should provide the StoragePrincipal.
6488   nsresult rv = CreateAboutBlankContentViewer(
6489       aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6490       /* aCsp */ nullptr,
6491       /* aBaseURI */ nullptr,
6492       /* aTryToSaveOldPresentation */ true,
6493       /* aCheckPermitUnload */ true, aWindowActor);
6494   if (NS_SUCCEEDED(rv)) {
6495     RefPtr<Document> doc(GetDocument());
6496     MOZ_ASSERT(
6497         doc,
6498         "Should have a document if CreateAboutBlankContentViewer succeeded");
6499     MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6500                "New document should be in the same global as our actor");
6501 
6502     // FIXME: We may want to support non-initial documents here.
6503     doc->SetIsInitialDocument(true);
6504   }
6505 
6506   return rv;
6507 }
6508 
CanSavePresentation(uint32_t aLoadType,nsIRequest * aNewRequest,Document * aNewDocument)6509 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6510                                      nsIRequest* aNewRequest,
6511                                      Document* aNewDocument) {
6512   if (!mOSHE) {
6513     return false;  // no entry to save into
6514   }
6515 
6516   nsCOMPtr<nsIContentViewer> viewer = mOSHE->GetContentViewer();
6517   if (viewer) {
6518     NS_WARNING("mOSHE already has a content viewer!");
6519     return false;
6520   }
6521 
6522   // Only save presentation for "normal" loads and link loads.  Anything else
6523   // probably wants to refetch the page, so caching the old presentation
6524   // would be incorrect.
6525   if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6526       aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6527       aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6528       aLoadType != LOAD_ERROR_PAGE) {
6529     return false;
6530   }
6531 
6532   // If the session history entry has the saveLayoutState flag set to false,
6533   // then we should not cache the presentation.
6534   if (!mOSHE->GetSaveLayoutStateFlag()) {
6535     return false;
6536   }
6537 
6538   // If the document is not done loading, don't cache it.
6539   if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6540     MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6541             ("Blocked due to document still loading"));
6542     return false;
6543   }
6544 
6545   if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6546     return false;
6547   }
6548 
6549   // Avoid doing the work of saving the presentation state in the case where
6550   // the content viewer cache is disabled.
6551   if (nsSHistory::GetMaxTotalViewers() == 0) {
6552     return false;
6553   }
6554 
6555   // Don't cache the content viewer if we're in a subframe.
6556   if (mBrowsingContext->GetParent()) {
6557     return false;  // this is a subframe load
6558   }
6559 
6560   // If the document does not want its presentation cached, then don't.
6561   RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6562 
6563   uint16_t bfCacheCombo = 0;
6564   bool canSavePresentation =
6565       doc->CanSavePresentation(aNewRequest, bfCacheCombo);
6566   MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6567   if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6568     auto* browsingContextGroup = mBrowsingContext->Group();
6569     nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6570         browsingContextGroup->Toplevels();
6571 
6572     for (const auto& browsingContext : topLevelContext) {
6573       if (browsingContext != mBrowsingContext) {
6574         bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6575         break;
6576       }
6577     }
6578   }
6579   ReportBFCacheComboTelemetry(bfCacheCombo);
6580 
6581   return doc && canSavePresentation;
6582 }
6583 
ReportBFCacheComboTelemetry(uint16_t aCombo)6584 void nsDocShell::ReportBFCacheComboTelemetry(uint16_t aCombo) {
6585   switch (aCombo) {
6586     case BFCACHE_SUCCESS:
6587       Telemetry::AccumulateCategorical(
6588           Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6589       break;
6590     case SUCCESS_NOT_ONLY_TOPLEVEL:
6591       Telemetry::AccumulateCategorical(
6592           Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6593       Telemetry::AccumulateCategorical(
6594           Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6595       break;
6596     case UNLOAD:
6597       Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6598       break;
6599     case UNLOAD_REQUEST:
6600       Telemetry::AccumulateCategorical(
6601           Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6602       break;
6603     case REQUEST:
6604       Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6605       break;
6606     case UNLOAD_REQUEST_PEER:
6607       Telemetry::AccumulateCategorical(
6608           Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6609       break;
6610     case UNLOAD_REQUEST_PEER_MSE:
6611       Telemetry::AccumulateCategorical(
6612           Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6613       break;
6614     case UNLOAD_REQUEST_MSE:
6615       Telemetry::AccumulateCategorical(
6616           Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6617       break;
6618     case SUSPENDED_UNLOAD_REQUEST_PEER:
6619       Telemetry::AccumulateCategorical(
6620           Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6621       break;
6622     case REMOTE_SUBFRAMES:
6623       Telemetry::AccumulateCategorical(
6624           Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6625       break;
6626     default:
6627       Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6628       break;
6629   }
6630 };
6631 
ReattachEditorToWindow(nsISHEntry * aSHEntry)6632 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6633   MOZ_ASSERT(!mIsBeingDestroyed);
6634 
6635   NS_ASSERTION(!mEditorData,
6636                "Why reattach an editor when we already have one?");
6637   NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6638                "Reattaching when there's not a detached editor.");
6639 
6640   if (mEditorData || !aSHEntry) {
6641     return;
6642   }
6643 
6644   mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6645   if (mEditorData) {
6646 #ifdef DEBUG
6647     nsresult rv =
6648 #endif
6649         mEditorData->ReattachToWindow(this);
6650     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
6651   }
6652 }
6653 
DetachEditorFromWindow()6654 void nsDocShell::DetachEditorFromWindow() {
6655   if (!mEditorData || mEditorData->WaitingForLoad()) {
6656     // If there's nothing to detach, or if the editor data is actually set
6657     // up for the _new_ page that's coming in, don't detach.
6658     return;
6659   }
6660 
6661   NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6662                "Detaching editor when it's already detached.");
6663 
6664   nsresult res = mEditorData->DetachFromWindow();
6665   NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6666 
6667   if (NS_SUCCEEDED(res)) {
6668     // Make mOSHE hold the owning ref to the editor data.
6669     if (mOSHE) {
6670       MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
6671                  "We should not set the editor data again once after we "
6672                  "detached the editor data during destroying this docshell");
6673       mOSHE->SetEditorData(mEditorData.release());
6674     } else {
6675       mEditorData = nullptr;
6676     }
6677   }
6678 
6679 #ifdef DEBUG
6680   {
6681     bool isEditable;
6682     GetEditable(&isEditable);
6683     NS_ASSERTION(!isEditable,
6684                  "Window is still editable after detaching editor.");
6685   }
6686 #endif  // DEBUG
6687 }
6688 
CaptureState()6689 nsresult nsDocShell::CaptureState() {
6690   if (!mOSHE || mOSHE == mLSHE) {
6691     // No entry to save into, or we're replacing the existing entry.
6692     return NS_ERROR_FAILURE;
6693   }
6694 
6695   if (!mScriptGlobal) {
6696     return NS_ERROR_FAILURE;
6697   }
6698 
6699   nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
6700   NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
6701 
6702   if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
6703     nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
6704     nsAutoCString spec;
6705     if (uri) {
6706       uri->GetSpec(spec);
6707     }
6708     MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6709             ("Saving presentation into session history, URI: %s", spec.get()));
6710   }
6711 
6712   mOSHE->SetWindowState(windowState);
6713 
6714   // Suspend refresh URIs and save off the timer queue
6715   mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6716 
6717   // Capture the current content viewer bounds.
6718   if (mContentViewer) {
6719     nsIntRect bounds;
6720     mContentViewer->GetBounds(bounds);
6721     mOSHE->SetViewerBounds(bounds);
6722   }
6723 
6724   // Capture the docshell hierarchy.
6725   mOSHE->ClearChildShells();
6726 
6727   uint32_t childCount = mChildList.Length();
6728   for (uint32_t i = 0; i < childCount; ++i) {
6729     nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6730     NS_ASSERTION(childShell, "null child shell");
6731 
6732     mOSHE->AddChildShell(childShell);
6733   }
6734 
6735   return NS_OK;
6736 }
6737 
6738 NS_IMETHODIMP
Run()6739 nsDocShell::RestorePresentationEvent::Run() {
6740   if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
6741     NS_WARNING("RestoreFromHistory failed");
6742   }
6743   return NS_OK;
6744 }
6745 
6746 NS_IMETHODIMP
BeginRestore(nsIContentViewer * aContentViewer,bool aTop)6747 nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop) {
6748   nsresult rv;
6749   if (!aContentViewer) {
6750     rv = EnsureContentViewer();
6751     NS_ENSURE_SUCCESS(rv, rv);
6752 
6753     aContentViewer = mContentViewer;
6754   }
6755 
6756   // Dispatch events for restoring the presentation.  We try to simulate
6757   // the progress notifications loading the document would cause, so we add
6758   // the document's channel to the loadgroup to initiate stateChange
6759   // notifications.
6760 
6761   RefPtr<Document> doc = aContentViewer->GetDocument();
6762   if (doc) {
6763     nsIChannel* channel = doc->GetChannel();
6764     if (channel) {
6765       mEODForCurrentDocument = false;
6766       mIsRestoringDocument = true;
6767       mLoadGroup->AddRequest(channel, nullptr);
6768       mIsRestoringDocument = false;
6769     }
6770   }
6771 
6772   if (!aTop) {
6773     // This point corresponds to us having gotten OnStartRequest or
6774     // STATE_START, so do the same thing that CreateContentViewer does at
6775     // this point to ensure that unload/pagehide events for this document
6776     // will fire when it's unloaded again.
6777     mFiredUnloadEvent = false;
6778 
6779     // For non-top frames, there is no notion of making sure that the
6780     // previous document is in the domwindow when STATE_START notifications
6781     // happen.  We can just call BeginRestore for all of the child shells
6782     // now.
6783     rv = BeginRestoreChildren();
6784     NS_ENSURE_SUCCESS(rv, rv);
6785   }
6786 
6787   return NS_OK;
6788 }
6789 
BeginRestoreChildren()6790 nsresult nsDocShell::BeginRestoreChildren() {
6791   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6792   while (iter.HasMore()) {
6793     nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
6794     if (child) {
6795       nsresult rv = child->BeginRestore(nullptr, false);
6796       NS_ENSURE_SUCCESS(rv, rv);
6797     }
6798   }
6799   return NS_OK;
6800 }
6801 
6802 NS_IMETHODIMP
FinishRestore()6803 nsDocShell::FinishRestore() {
6804   // First we call finishRestore() on our children.  In the simulated load,
6805   // all of the child frames finish loading before the main document.
6806 
6807   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6808   while (iter.HasMore()) {
6809     nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
6810     if (child) {
6811       child->FinishRestore();
6812     }
6813   }
6814 
6815   if (mOSHE && mOSHE->HasDetachedEditor()) {
6816     ReattachEditorToWindow(mOSHE);
6817   }
6818 
6819   RefPtr<Document> doc = GetDocument();
6820   if (doc) {
6821     // Finally, we remove the request from the loadgroup.  This will
6822     // cause onStateChange(STATE_STOP) to fire, which will fire the
6823     // pageshow event to the chrome.
6824 
6825     nsIChannel* channel = doc->GetChannel();
6826     if (channel) {
6827       mIsRestoringDocument = true;
6828       mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
6829       mIsRestoringDocument = false;
6830     }
6831   }
6832 
6833   return NS_OK;
6834 }
6835 
6836 NS_IMETHODIMP
GetRestoringDocument(bool * aRestoring)6837 nsDocShell::GetRestoringDocument(bool* aRestoring) {
6838   *aRestoring = mIsRestoringDocument;
6839   return NS_OK;
6840 }
6841 
RestorePresentation(nsISHEntry * aSHEntry,bool * aRestoring)6842 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
6843                                          bool* aRestoring) {
6844   MOZ_ASSERT(!mIsBeingDestroyed);
6845 
6846   NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
6847                "RestorePresentation should only be called for history loads");
6848 
6849   nsCOMPtr<nsIContentViewer> viewer = aSHEntry->GetContentViewer();
6850 
6851   nsAutoCString spec;
6852   if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
6853     nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
6854     if (uri) {
6855       uri->GetSpec(spec);
6856     }
6857   }
6858 
6859   *aRestoring = false;
6860 
6861   if (!viewer) {
6862     MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6863             ("no saved presentation for uri: %s", spec.get()));
6864     return NS_OK;
6865   }
6866 
6867   // We need to make sure the content viewer's container is this docshell.
6868   // In subframe navigation, it's possible for the docshell that the
6869   // content viewer was originally loaded into to be replaced with a
6870   // different one.  We don't currently support restoring the presentation
6871   // in that case.
6872 
6873   nsCOMPtr<nsIDocShell> container;
6874   viewer->GetContainer(getter_AddRefs(container));
6875   if (!::SameCOMIdentity(container, GetAsSupports(this))) {
6876     MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6877             ("No valid container, clearing presentation"));
6878     aSHEntry->SetContentViewer(nullptr);
6879     return NS_ERROR_FAILURE;
6880   }
6881 
6882   NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
6883 
6884   MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6885           ("restoring presentation from session history: %s", spec.get()));
6886 
6887   SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
6888 
6889   // Post an event that will remove the request after we've returned
6890   // to the event loop.  This mimics the way it is called by nsIChannel
6891   // implementations.
6892 
6893   // Revoke any pending restore (just in case)
6894   NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
6895                "should only have one RestorePresentationEvent");
6896   mRestorePresentationEvent.Revoke();
6897 
6898   RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
6899   nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(evt));
6900   if (NS_SUCCEEDED(rv)) {
6901     mRestorePresentationEvent = evt.get();
6902     // The rest of the restore processing will happen on our event
6903     // callback.
6904     *aRestoring = true;
6905   }
6906 
6907   return rv;
6908 }
6909 
6910 namespace {
6911 class MOZ_STACK_CLASS PresentationEventForgetter {
6912  public:
PresentationEventForgetter(nsRevocableEventPtr<nsDocShell::RestorePresentationEvent> & aRestorePresentationEvent)6913   explicit PresentationEventForgetter(
6914       nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
6915           aRestorePresentationEvent)
6916       : mRestorePresentationEvent(aRestorePresentationEvent),
6917         mEvent(aRestorePresentationEvent.get()) {}
6918 
~PresentationEventForgetter()6919   ~PresentationEventForgetter() { Forget(); }
6920 
Forget()6921   void Forget() {
6922     if (mRestorePresentationEvent.get() == mEvent) {
6923       mRestorePresentationEvent.Forget();
6924       mEvent = nullptr;
6925     }
6926   }
6927 
6928  private:
6929   nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
6930       mRestorePresentationEvent;
6931   RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
6932 };
6933 
6934 }  // namespace
6935 
SandboxFlagsImplyCookies(const uint32_t & aSandboxFlags)6936 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
6937   return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
6938 }
6939 
RestoreFromHistory()6940 nsresult nsDocShell::RestoreFromHistory() {
6941   MOZ_ASSERT(mRestorePresentationEvent.IsPending());
6942   PresentationEventForgetter forgetter(mRestorePresentationEvent);
6943 
6944   // This section of code follows the same ordering as CreateContentViewer.
6945   if (!mLSHE) {
6946     return NS_ERROR_FAILURE;
6947   }
6948 
6949   nsCOMPtr<nsIContentViewer> viewer = mLSHE->GetContentViewer();
6950   if (!viewer) {
6951     return NS_ERROR_FAILURE;
6952   }
6953 
6954   if (mSavingOldViewer) {
6955     // We determined that it was safe to cache the document presentation
6956     // at the time we initiated the new load.  We need to check whether
6957     // it's still safe to do so, since there may have been DOM mutations
6958     // or new requests initiated.
6959     RefPtr<Document> doc = viewer->GetDocument();
6960     nsIRequest* request = nullptr;
6961     if (doc) {
6962       request = doc->GetChannel();
6963     }
6964     mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
6965   }
6966 
6967   nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
6968   nsCOMPtr<nsIContentViewer> newCv(viewer);
6969   float overrideDPPX = 0.0f;
6970 
6971   bool styleDisabled = false;
6972   if (oldCv && newCv) {
6973     oldCv->GetOverrideDPPX(&overrideDPPX);
6974     oldCv->GetAuthorStyleDisabled(&styleDisabled);
6975   }
6976 
6977   // Protect against mLSHE going away via a load triggered from
6978   // pagehide or unload.
6979   nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
6980 
6981   // Make sure to blow away our mLoadingURI just in case.  No loads
6982   // from inside this pagehide.
6983   mLoadingURI = nullptr;
6984 
6985   // Notify the old content viewer that it's being hidden.
6986   FirePageHideNotification(!mSavingOldViewer);
6987   // pagehide notification might destroy this docshell.
6988   if (mIsBeingDestroyed) {
6989     return NS_ERROR_DOCSHELL_DYING;
6990   }
6991 
6992   // If mLSHE was changed as a result of the pagehide event, then
6993   // something else was loaded.  Don't finish restoring.
6994   if (mLSHE != origLSHE) {
6995     return NS_OK;
6996   }
6997 
6998   // Add the request to our load group.  We do this before swapping out
6999   // the content viewers so that consumers of STATE_START can access
7000   // the old document.  We only deal with the toplevel load at this time --
7001   // to be consistent with normal document loading, subframes cannot start
7002   // loading until after data arrives, which is after STATE_START completes.
7003 
7004   RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7005       mRestorePresentationEvent.get();
7006   Stop();
7007   // Make sure we're still restoring the same presentation.
7008   // If we aren't, docshell is in process doing another load already.
7009   NS_ENSURE_STATE(currentPresentationRestoration ==
7010                   mRestorePresentationEvent.get());
7011   BeginRestore(viewer, true);
7012   NS_ENSURE_STATE(currentPresentationRestoration ==
7013                   mRestorePresentationEvent.get());
7014   forgetter.Forget();
7015 
7016   // Set mFiredUnloadEvent = false so that the unload handler for the
7017   // *new* document will fire.
7018   mFiredUnloadEvent = false;
7019 
7020   mURIResultedInDocument = true;
7021   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7022   if (rootSH) {
7023     mPreviousEntryIndex = rootSH->Index();
7024     rootSH->LegacySHistory()->UpdateIndex();
7025     mLoadedEntryIndex = rootSH->Index();
7026     MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7027             ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7028              mLoadedEntryIndex));
7029   }
7030 
7031   // Rather than call Embed(), we will retrieve the viewer from the session
7032   // history entry and swap it in.
7033   // XXX can we refactor this so that we can just call Embed()?
7034   PersistLayoutHistoryState();
7035   nsresult rv;
7036   if (mContentViewer) {
7037     if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7038       if (mOSHE) {
7039         mOSHE->SyncPresentationState();
7040       }
7041       mSavingOldViewer = false;
7042     }
7043   }
7044 
7045   mSavedRefreshURIList = nullptr;
7046 
7047   // In cases where we use a transient about:blank viewer between loads,
7048   // we never show the transient viewer, so _its_ previous viewer is never
7049   // unhooked from the view hierarchy.  Destroy any such previous viewer now,
7050   // before we grab the root view sibling, so that we don't grab a view
7051   // that's about to go away.
7052 
7053   if (mContentViewer) {
7054     // Make sure to hold a strong ref to previousViewer here while we
7055     // drop the reference to it from mContentViewer.
7056     nsCOMPtr<nsIContentViewer> previousViewer =
7057         mContentViewer->GetPreviousViewer();
7058     if (previousViewer) {
7059       mContentViewer->SetPreviousViewer(nullptr);
7060       previousViewer->Destroy();
7061     }
7062   }
7063 
7064   // Save off the root view's parent and sibling so that we can insert the
7065   // new content viewer's root view at the same position.  Also save the
7066   // bounds of the root view's widget.
7067 
7068   nsView* rootViewSibling = nullptr;
7069   nsView* rootViewParent = nullptr;
7070   nsIntRect newBounds(0, 0, 0, 0);
7071 
7072   PresShell* oldPresShell = GetPresShell();
7073   if (oldPresShell) {
7074     nsViewManager* vm = oldPresShell->GetViewManager();
7075     if (vm) {
7076       nsView* oldRootView = vm->GetRootView();
7077 
7078       if (oldRootView) {
7079         rootViewSibling = oldRootView->GetNextSibling();
7080         rootViewParent = oldRootView->GetParent();
7081 
7082         mContentViewer->GetBounds(newBounds);
7083       }
7084     }
7085   }
7086 
7087   nsCOMPtr<nsIContent> container;
7088   RefPtr<Document> sibling;
7089   if (rootViewParent && rootViewParent->GetParent()) {
7090     nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7091     container = frame ? frame->GetContent() : nullptr;
7092   }
7093   if (rootViewSibling) {
7094     nsIFrame* frame = rootViewSibling->GetFrame();
7095     sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7096   }
7097 
7098   // Transfer ownership to mContentViewer.  By ensuring that either the
7099   // docshell or the session history, but not both, have references to the
7100   // content viewer, we prevent the viewer from being torn down after
7101   // Destroy() is called.
7102 
7103   if (mContentViewer) {
7104     mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7105     viewer->SetPreviousViewer(mContentViewer);
7106   }
7107   if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
7108     // We don't plan to save a viewer in mOSHE; tell it to drop
7109     // any other state it's holding.
7110     mOSHE->SyncPresentationState();
7111   }
7112 
7113   // Order the mContentViewer setup just like Embed does.
7114   mContentViewer = nullptr;
7115 
7116   // Now that we're about to switch documents, forget all of our children.
7117   // Note that we cached them as needed up in CaptureState above.
7118   DestroyChildren();
7119 
7120   mContentViewer.swap(viewer);
7121 
7122   // Grab all of the related presentation from the SHEntry now.
7123   // Clearing the viewer from the SHEntry will clear all of this state.
7124   nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7125   mLSHE->SetWindowState(nullptr);
7126 
7127   bool sticky = mLSHE->GetSticky();
7128 
7129   RefPtr<Document> document = mContentViewer->GetDocument();
7130 
7131   nsCOMArray<nsIDocShellTreeItem> childShells;
7132   int32_t i = 0;
7133   nsCOMPtr<nsIDocShellTreeItem> child;
7134   while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7135          child) {
7136     childShells.AppendObject(child);
7137   }
7138 
7139   // get the previous content viewer size
7140   nsIntRect oldBounds(0, 0, 0, 0);
7141   mLSHE->GetViewerBounds(oldBounds);
7142 
7143   // Restore the refresh URI list.  The refresh timers will be restarted
7144   // when EndPageLoad() is called.
7145   nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7146 
7147   // Reattach to the window object.
7148   mIsRestoringDocument = true;  // for MediaDocument::BecomeInteractive
7149   rv = mContentViewer->Open(windowState, mLSHE);
7150   mIsRestoringDocument = false;
7151 
7152   // Hack to keep nsDocShellEditorData alive across the
7153   // SetContentViewer(nullptr) call below.
7154   UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7155 
7156   // Now remove it from the cached presentation.
7157   mLSHE->SetContentViewer(nullptr);
7158   mEODForCurrentDocument = false;
7159 
7160   mLSHE->SetEditorData(data.release());
7161 
7162 #ifdef DEBUG
7163   {
7164     nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7165     nsCOMPtr<nsIDocShellTreeItem> childShell;
7166     mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7167     NS_ASSERTION(!refreshURIs && !childShell,
7168                  "SHEntry should have cleared presentation state");
7169   }
7170 #endif
7171 
7172   // Restore the sticky state of the viewer.  The viewer has set this state
7173   // on the history entry in Destroy() just before marking itself non-sticky,
7174   // to avoid teardown of the presentation.
7175   mContentViewer->SetSticky(sticky);
7176 
7177   NS_ENSURE_SUCCESS(rv, rv);
7178 
7179   // mLSHE is now our currently-loaded document.
7180   SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7181 
7182   // We aren't going to restore any items from the LayoutHistoryState,
7183   // but we don't want them to stay around in case the page is reloaded.
7184   SetLayoutHistoryState(nullptr);
7185 
7186   // This is the end of our Embed() replacement
7187 
7188   mSavingOldViewer = false;
7189   mEODForCurrentDocument = false;
7190 
7191   // Tell the event loop to favor plevents over user events, see comments
7192   // in CreateContentViewer.
7193   if (++gNumberOfDocumentsLoading == 1) {
7194     FavorPerformanceHint(true);
7195   }
7196 
7197   if (oldCv && newCv) {
7198     newCv->SetOverrideDPPX(overrideDPPX);
7199     newCv->SetAuthorStyleDisabled(styleDisabled);
7200   }
7201 
7202   if (document) {
7203     RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7204     if (parent) {
7205       RefPtr<Document> d = parent->GetDocument();
7206       if (d) {
7207         if (d->EventHandlingSuppressed()) {
7208           document->SuppressEventHandling(d->EventHandlingSuppressed());
7209         }
7210       }
7211     }
7212 
7213     // Use the uri from the mLSHE we had when we entered this function
7214     // (which need not match the document's URI if anchors are involved),
7215     // since that's the history entry we're loading.  Note that if we use
7216     // origLSHE we don't have to worry about whether the entry in question
7217     // is still mLSHE or whether it's now mOSHE.
7218     nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7219     SetCurrentURI(uri, document->GetChannel(), true, 0);
7220   }
7221 
7222   // This is the end of our CreateContentViewer() replacement.
7223   // Now we simulate a load.  First, we restore the state of the javascript
7224   // window object.
7225   nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7226   NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7227 
7228   // Now, dispatch a title change event which would happen as the
7229   // <head> is parsed.
7230   document->NotifyPossibleTitleChange(false);
7231 
7232   // Now we simulate appending child docshells for subframes.
7233   for (i = 0; i < childShells.Count(); ++i) {
7234     nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7235     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7236 
7237     // Make sure to not clobber the state of the child.  Since AddChild
7238     // always clobbers it, save it off first.
7239     bool allowJavascript;
7240     childShell->GetAllowJavascript(&allowJavascript);
7241 
7242     bool allowRedirects;
7243     childShell->GetAllowMetaRedirects(&allowRedirects);
7244 
7245     bool allowSubframes;
7246     childShell->GetAllowSubframes(&allowSubframes);
7247 
7248     bool allowImages;
7249     childShell->GetAllowImages(&allowImages);
7250 
7251     bool allowMedia = childShell->GetAllowMedia();
7252 
7253     bool allowDNSPrefetch;
7254     childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7255 
7256     bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7257     bool allowContentRetargetingOnChildren =
7258         childShell->GetAllowContentRetargetingOnChildren();
7259 
7260     // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7261     // the child inherits our state. Among other things, this means that the
7262     // child inherits our mPrivateBrowsingId, which is what we want.
7263     AddChild(childItem);
7264 
7265     childShell->SetAllowJavascript(allowJavascript);
7266     childShell->SetAllowMetaRedirects(allowRedirects);
7267     childShell->SetAllowSubframes(allowSubframes);
7268     childShell->SetAllowImages(allowImages);
7269     childShell->SetAllowMedia(allowMedia);
7270     childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7271     childShell->SetAllowContentRetargeting(allowContentRetargeting);
7272     childShell->SetAllowContentRetargetingOnChildren(
7273         allowContentRetargetingOnChildren);
7274 
7275     rv = childShell->BeginRestore(nullptr, false);
7276     NS_ENSURE_SUCCESS(rv, rv);
7277   }
7278 
7279   // Make sure to restore the window state after adding the child shells back
7280   // to the tree.  This is necessary for Thaw() and Resume() to propagate
7281   // properly.
7282   rv = privWin->RestoreWindowState(windowState);
7283   NS_ENSURE_SUCCESS(rv, rv);
7284 
7285   RefPtr<PresShell> presShell = GetPresShell();
7286 
7287   // We may be displayed on a different monitor (or in a different
7288   // HiDPI mode) than when we got into the history list.  So we need
7289   // to check if this has happened. See bug 838239.
7290 
7291   // Because the prescontext normally handles resolution changes via
7292   // a runnable (see nsPresContext::UIResolutionChanged), its device
7293   // context won't be -immediately- updated as a result of calling
7294   // presShell->BackingScaleFactorChanged().
7295 
7296   // But we depend on that device context when adjusting the view size
7297   // via mContentViewer->SetBounds(newBounds) below. So we need to
7298   // explicitly tell it to check for changed resolution here.
7299   if (presShell) {
7300     RefPtr<nsPresContext> pc = presShell->GetPresContext();
7301     if (pc->DeviceContext()->CheckDPIChange()) {
7302       presShell->BackingScaleFactorChanged();
7303     }
7304     // Recompute zoom and text-zoom and such.
7305     pc->RecomputeBrowsingContextDependentData();
7306   }
7307 
7308   nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7309   nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7310 
7311   // Insert the new root view at the correct location in the view tree.
7312   if (container) {
7313     nsSubDocumentFrame* subDocFrame =
7314         do_QueryFrame(container->GetPrimaryFrame());
7315     rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7316   } else {
7317     rootViewParent = nullptr;
7318   }
7319   if (sibling && sibling->GetPresShell() &&
7320       sibling->GetPresShell()->GetViewManager()) {
7321     rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7322   } else {
7323     rootViewSibling = nullptr;
7324   }
7325   if (rootViewParent && newRootView &&
7326       newRootView->GetParent() != rootViewParent) {
7327     nsViewManager* parentVM = rootViewParent->GetViewManager();
7328     if (parentVM) {
7329       // InsertChild(parent, child, sib, true) inserts the child after
7330       // sib in content order, which is before sib in view order. BUT
7331       // when sib is null it inserts at the end of the the document
7332       // order, i.e., first in view order.  But when oldRootSibling is
7333       // null, the old root as at the end of the view list --- last in
7334       // content order --- and we want to call InsertChild(parent, child,
7335       // nullptr, false) in that case.
7336       parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7337                             rootViewSibling ? true : false);
7338 
7339       NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7340                    "error in InsertChild");
7341     }
7342   }
7343 
7344   nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7345 
7346   // If parent is suspended, increase suspension count.
7347   // This can't be done as early as event suppression since this
7348   // depends on docshell tree.
7349   privWinInner->SyncStateFromParentWindow();
7350 
7351   // Now that all of the child docshells have been put into place, we can
7352   // restart the timers for the window and all of the child frames.
7353   privWinInner->Resume();
7354 
7355   // Now that we have found the inner window of the page restored
7356   // from the history, we have to  make sure that
7357   // performance.navigation.type is 2.
7358   privWinInner->GetPerformance()->GetDOMTiming()->NotifyRestoreStart();
7359 
7360   // Restore the refresh URI list.  The refresh timers will be restarted
7361   // when EndPageLoad() is called.
7362   mRefreshURIList = refreshURIList;
7363 
7364   // Meta-refresh timers have been restarted for this shell, but not
7365   // for our children.  Walk the child shells and restart their timers.
7366   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7367   while (iter.HasMore()) {
7368     nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7369     if (child) {
7370       child->ResumeRefreshURIs();
7371     }
7372   }
7373 
7374   // Make sure this presentation is the same size as the previous
7375   // presentation.  If this is not the same size we showed it at last time,
7376   // then we need to resize the widget.
7377 
7378   // XXXbryner   This interacts poorly with Firefox's infobar.  If the old
7379   // presentation had the infobar visible, then we will resize the new
7380   // presentation to that smaller size.  However, firing the locationchanged
7381   // event will hide the infobar, which will immediately resize the window
7382   // back to the larger size.  A future optimization might be to restore
7383   // the presentation at the "wrong" size, then fire the locationchanged
7384   // event and check whether the docshell's new size is the same as the
7385   // cached viewer size (skipping the resize if they are equal).
7386 
7387   if (newRootView) {
7388     if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7389       MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7390               ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7391                newBounds.width, newBounds.height));
7392       mContentViewer->SetBounds(newBounds);
7393     } else {
7394       nsIScrollableFrame* rootScrollFrame =
7395           presShell->GetRootScrollFrameAsScrollable();
7396       if (rootScrollFrame) {
7397         rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7398       }
7399     }
7400   }
7401 
7402   // The FinishRestore call below can kill these, null them out so we don't
7403   // have invalid pointer lying around.
7404   newRootView = rootViewSibling = rootViewParent = nullptr;
7405   newVM = nullptr;
7406 
7407   // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7408   // update it.
7409   if (oldPresShell && presShell &&
7410       presShell->IsUnderHiddenEmbedderElement() !=
7411           oldPresShell->IsUnderHiddenEmbedderElement()) {
7412     presShell->SetIsUnderHiddenEmbedderElement(
7413         oldPresShell->IsUnderHiddenEmbedderElement());
7414   }
7415 
7416   // Simulate the completion of the load.
7417   nsDocShell::FinishRestore();
7418 
7419   // Restart plugins, and paint the content.
7420   if (presShell) {
7421     presShell->Thaw();
7422   }
7423 
7424   return privWin->FireDelayedDOMEvents();
7425 }
7426 
CreateContentViewer(const nsACString & aContentType,nsIRequest * aRequest,nsIStreamListener ** aContentHandler)7427 nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
7428                                          nsIRequest* aRequest,
7429                                          nsIStreamListener** aContentHandler) {
7430   if (DocGroup::TryToLoadIframesInBackground()) {
7431     ResetToFirstLoad();
7432   }
7433 
7434   *aContentHandler = nullptr;
7435 
7436   if (!mTreeOwner || mIsBeingDestroyed) {
7437     // If we don't have a tree owner, then we're in the process of being
7438     // destroyed. Rather than continue trying to load something, just give up.
7439     return NS_ERROR_DOCSHELL_DYING;
7440   }
7441 
7442   // Can we check the content type of the current content viewer
7443   // and reuse it without destroying it and re-creating it?
7444 
7445   NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7446 
7447   // Instantiate the content viewer object
7448   nsCOMPtr<nsIContentViewer> viewer;
7449   nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
7450                                     aContentHandler, getter_AddRefs(viewer));
7451 
7452   if (NS_FAILED(rv)) {
7453     return rv;
7454   }
7455 
7456   // Notify the current document that it is about to be unloaded!!
7457   //
7458   // It is important to fire the unload() notification *before* any state
7459   // is changed within the DocShell - otherwise, javascript will get the
7460   // wrong information :-(
7461   //
7462 
7463   if (mSavingOldViewer) {
7464     // We determined that it was safe to cache the document presentation
7465     // at the time we initiated the new load.  We need to check whether
7466     // it's still safe to do so, since there may have been DOM mutations
7467     // or new requests initiated.
7468     RefPtr<Document> doc = viewer->GetDocument();
7469     mSavingOldViewer = CanSavePresentation(mLoadType, aRequest, doc);
7470   }
7471 
7472   NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7473 
7474   nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7475   if (aOpenedChannel) {
7476     aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7477   }
7478   FirePageHideNotification(!mSavingOldViewer);
7479   if (mIsBeingDestroyed) {
7480     // Force to stop the newly created orphaned viewer.
7481     viewer->Stop();
7482     return NS_ERROR_DOCSHELL_DYING;
7483   }
7484   mLoadingURI = nullptr;
7485 
7486   // Set mFiredUnloadEvent = false so that the unload handler for the
7487   // *new* document will fire.
7488   mFiredUnloadEvent = false;
7489 
7490   // we've created a new document so go ahead and call
7491   // OnNewURI(), but don't fire OnLocationChange()
7492   // notifications before we've called Embed(). See bug 284993.
7493   mURIResultedInDocument = true;
7494   bool errorOnLocationChangeNeeded = false;
7495   nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7496   nsCOMPtr<nsIURI> failedURI;
7497 
7498   if (mLoadType == LOAD_ERROR_PAGE) {
7499     // We need to set the SH entry and our current URI here and not
7500     // at the moment we load the page. We want the same behavior
7501     // of Stop() as for a normal page load. See bug 514232 for details.
7502 
7503     // Revert mLoadType to load type to state the page load failed,
7504     // following function calls need it.
7505     mLoadType = mFailedLoadType;
7506 
7507     Document* doc = viewer->GetDocument();
7508     if (doc) {
7509       doc->SetFailedChannel(failedChannel);
7510     }
7511 
7512     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7513     if (failedChannel) {
7514       // Make sure we have a URI to set currentURI.
7515       NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7516     } else {
7517       // if there is no failed channel we have to explicitly provide
7518       // a triggeringPrincipal for the history entry.
7519       triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7520     }
7521 
7522     if (!failedURI) {
7523       failedURI = mFailedURI;
7524     }
7525     if (!failedURI) {
7526       // We need a URI object to store a session history entry, so make up a URI
7527       NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7528     }
7529 
7530     // When we don't have failedURI, something wrong will happen. See
7531     // bug 291876.
7532     MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7533 
7534     mFailedChannel = nullptr;
7535     mFailedURI = nullptr;
7536 
7537     // Create an shistory entry for the old load.
7538     if (failedURI) {
7539       errorOnLocationChangeNeeded =
7540           OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7541                    nullptr, mLoadType, nullptr, false, false, false);
7542     }
7543 
7544     // Be sure to have a correct mLSHE, it may have been cleared by
7545     // EndPageLoad. See bug 302115.
7546     ChildSHistory* shistory = GetSessionHistory();
7547     if (shistory && !mLSHE) {
7548       int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7549       if (idx == -1) {
7550         idx = shistory->Index();
7551       }
7552       shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7553     }
7554 
7555     mLoadType = LOAD_ERROR_PAGE;
7556   }
7557 
7558   nsCOMPtr<nsIURI> finalURI;
7559   // If this a redirect, use the final url (uri)
7560   // else use the original url
7561   //
7562   // Note that this should match what documents do (see Document::Reset).
7563   NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7564 
7565   bool onLocationChangeNeeded = false;
7566   if (finalURI) {
7567     // Pass false for aCloneSHChildren, since we're loading a new page here.
7568     onLocationChangeNeeded =
7569         OnNewURI(finalURI, aOpenedChannel, nullptr, nullptr, nullptr, mLoadType,
7570                  nullptr, false, true, false);
7571   }
7572 
7573   // let's try resetting the load group if we need to...
7574   nsCOMPtr<nsILoadGroup> currentLoadGroup;
7575   NS_ENSURE_SUCCESS(
7576       aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7577       NS_ERROR_FAILURE);
7578 
7579   if (currentLoadGroup != mLoadGroup) {
7580     nsLoadFlags loadFlags = 0;
7581 
7582     // Cancel any URIs that are currently loading...
7583     // XXX: Need to do this eventually      Stop();
7584     //
7585     // Retarget the document to this loadgroup...
7586     //
7587     /* First attach the channel to the right loadgroup
7588      * and then remove from the old loadgroup. This
7589      * puts the notifications in the right order and
7590      * we don't null-out mLSHE in OnStateChange() for
7591      * all redirected urls
7592      */
7593     aOpenedChannel->SetLoadGroup(mLoadGroup);
7594 
7595     // Mark the channel as being a document URI...
7596     aOpenedChannel->GetLoadFlags(&loadFlags);
7597     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7598     nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7599     if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7600       loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7601     }
7602 
7603     aOpenedChannel->SetLoadFlags(loadFlags);
7604 
7605     mLoadGroup->AddRequest(aRequest, nullptr);
7606     if (currentLoadGroup) {
7607       currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7608     }
7609 
7610     // Update the notification callbacks, so that progress and
7611     // status information are sent to the right docshell...
7612     aOpenedChannel->SetNotificationCallbacks(this);
7613   }
7614 
7615   if (DocGroup::TryToLoadIframesInBackground()) {
7616     if ((!mContentViewer || GetDocument()->IsInitialDocument()) && IsFrame()) {
7617       // At this point, we know we just created a new iframe document based on
7618       // the response from the server, and we check if it's a cross-domain
7619       // iframe
7620 
7621       RefPtr<Document> newDoc = viewer->GetDocument();
7622 
7623       RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7624       nsCOMPtr<nsIPrincipal> parentPrincipal =
7625           parent->GetDocument()->NodePrincipal();
7626       nsCOMPtr<nsIPrincipal> thisPrincipal = newDoc->NodePrincipal();
7627 
7628       SiteIdentifier parentSite;
7629       SiteIdentifier thisSite;
7630 
7631       nsresult rv =
7632           BasePrincipal::Cast(parentPrincipal)->GetSiteIdentifier(parentSite);
7633       NS_ENSURE_SUCCESS(rv, rv);
7634 
7635       rv = BasePrincipal::Cast(thisPrincipal)->GetSiteIdentifier(thisSite);
7636       NS_ENSURE_SUCCESS(rv, rv);
7637 
7638       if (!parentSite.Equals(thisSite)) {
7639 #ifdef MOZ_GECKO_PROFILER
7640         nsCOMPtr<nsIURI> prinURI;
7641         BasePrincipal::Cast(thisPrincipal)->GetURI(getter_AddRefs(prinURI));
7642         nsPrintfCString marker("Iframe loaded in background: %s",
7643                                prinURI->GetSpecOrDefault().get());
7644         TimeStamp now = TimeStamp::Now();
7645         profiler_add_text_marker("Background Iframe", marker,
7646                                  JS::ProfilingCategoryPair::DOM, now, now,
7647                                  Nothing());
7648 #endif
7649         SetBackgroundLoadIframe();
7650       }
7651     }
7652   }
7653 
7654   NS_ENSURE_SUCCESS(Embed(viewer), NS_ERROR_FAILURE);
7655 
7656   if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
7657     mBrowsingContext->SetHasLoadedNonInitialDocument(true);
7658   }
7659 
7660   if (TreatAsBackgroundLoad()) {
7661     nsCOMPtr<nsIRunnable> triggerParentCheckDocShell =
7662         NewRunnableMethod("nsDocShell::TriggerParentCheckDocShellIsEmpty", this,
7663                           &nsDocShell::TriggerParentCheckDocShellIsEmpty);
7664     nsresult rv = NS_DispatchToCurrentThread(triggerParentCheckDocShell);
7665     NS_ENSURE_SUCCESS(rv, rv);
7666   }
7667 
7668   mSavedRefreshURIList = nullptr;
7669   mSavingOldViewer = false;
7670   mEODForCurrentDocument = false;
7671 
7672   // if this document is part of a multipart document,
7673   // the ID can be used to distinguish it from the other parts.
7674   nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
7675   if (multiPartChannel) {
7676     if (PresShell* presShell = GetPresShell()) {
7677       if (Document* doc = presShell->GetDocument()) {
7678         uint32_t partID;
7679         multiPartChannel->GetPartID(&partID);
7680         doc->SetPartID(partID);
7681       }
7682     }
7683   }
7684 
7685   // Give hint to native plevent dispatch mechanism. If a document
7686   // is loading the native plevent dispatch mechanism should favor
7687   // performance over normal native event dispatch priorities.
7688   if (++gNumberOfDocumentsLoading == 1) {
7689     // Hint to favor performance for the plevent notification mechanism.
7690     // We want the pages to load as fast as possible even if its means
7691     // native messages might be starved.
7692     FavorPerformanceHint(true);
7693   }
7694 
7695   if (errorOnLocationChangeNeeded) {
7696     FireOnLocationChange(this, failedChannel, failedURI,
7697                          LOCATION_CHANGE_ERROR_PAGE);
7698   } else if (onLocationChangeNeeded) {
7699     uint32_t locationFlags =
7700         (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
7701     FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
7702   }
7703 
7704   return NS_OK;
7705 }
7706 
NewContentViewerObj(const nsACString & aContentType,nsIRequest * aRequest,nsILoadGroup * aLoadGroup,nsIStreamListener ** aContentHandler,nsIContentViewer ** aViewer)7707 nsresult nsDocShell::NewContentViewerObj(const nsACString& aContentType,
7708                                          nsIRequest* aRequest,
7709                                          nsILoadGroup* aLoadGroup,
7710                                          nsIStreamListener** aContentHandler,
7711                                          nsIContentViewer** aViewer) {
7712   nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7713 
7714   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
7715       nsContentUtils::FindInternalContentViewer(aContentType);
7716   if (!docLoaderFactory) {
7717     return NS_ERROR_FAILURE;
7718   }
7719 
7720   // Now create an instance of the content viewer nsLayoutDLF makes the
7721   // determination if it should be a "view-source" instead of "view"
7722   nsresult rv = docLoaderFactory->CreateInstance(
7723       "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
7724       aContentHandler, aViewer);
7725   NS_ENSURE_SUCCESS(rv, rv);
7726 
7727   (*aViewer)->SetContainer(this);
7728   return NS_OK;
7729 }
7730 
SetupNewViewer(nsIContentViewer * aNewViewer,WindowGlobalChild * aWindowActor)7731 nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
7732                                     WindowGlobalChild* aWindowActor) {
7733   MOZ_ASSERT(!mIsBeingDestroyed);
7734 
7735   //
7736   // Copy content viewer state from previous or parent content viewer.
7737   //
7738   // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7739   //
7740   // Do NOT to maintain a reference to the old content viewer outside
7741   // of this "copying" block, or it will not be destroyed until the end of
7742   // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7743   //
7744   // In this block of code, if we get an error result, we return it
7745   // but if we get a null pointer, that's perfectly legal for parent
7746   // and parentContentViewer.
7747   //
7748 
7749   int32_t x = 0;
7750   int32_t y = 0;
7751   int32_t cx = 0;
7752   int32_t cy = 0;
7753 
7754   // This will get the size from the current content viewer or from the
7755   // Init settings
7756   DoGetPositionAndSize(&x, &y, &cx, &cy);
7757 
7758   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7759   NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
7760                     NS_ERROR_FAILURE);
7761   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7762 
7763   const Encoding* forceCharset = nullptr;
7764   const Encoding* hintCharset = nullptr;
7765   int32_t hintCharsetSource = kCharsetUninitialized;
7766   float overrideDPPX = 1.0;
7767   bool styleDisabled = false;
7768   // |newMUDV| also serves as a flag to set the data from the above vars
7769   nsCOMPtr<nsIContentViewer> newCv;
7770 
7771   if (mContentViewer || parent) {
7772     nsCOMPtr<nsIContentViewer> oldCv;
7773     if (mContentViewer) {
7774       // Get any interesting state from old content viewer
7775       // XXX: it would be far better to just reuse the document viewer ,
7776       //      since we know we're just displaying the same document as before
7777       oldCv = mContentViewer;
7778 
7779       // Tell the old content viewer to hibernate in session history when
7780       // it is destroyed.
7781 
7782       if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7783         if (mOSHE) {
7784           mOSHE->SyncPresentationState();
7785         }
7786         mSavingOldViewer = false;
7787       }
7788     } else {
7789       // No old content viewer, so get state from parent's content viewer
7790       parent->GetContentViewer(getter_AddRefs(oldCv));
7791     }
7792 
7793     if (oldCv) {
7794       newCv = aNewViewer;
7795       if (newCv) {
7796         forceCharset = oldCv->GetForceCharset();
7797         hintCharset = oldCv->GetHintCharset();
7798         NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSetSource(&hintCharsetSource),
7799                           NS_ERROR_FAILURE);
7800         NS_ENSURE_SUCCESS(oldCv->GetOverrideDPPX(&overrideDPPX),
7801                           NS_ERROR_FAILURE);
7802         NS_ENSURE_SUCCESS(oldCv->GetAuthorStyleDisabled(&styleDisabled),
7803                           NS_ERROR_FAILURE);
7804       }
7805     }
7806   }
7807 
7808   nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
7809   bool isActive = false;
7810   // Ensure that the content viewer is destroyed *after* the GC - bug 71515
7811   nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
7812   if (contentViewer) {
7813     // Stop any activity that may be happening in the old document before
7814     // releasing it...
7815     contentViewer->Stop();
7816 
7817     // Try to extract the canvas background color from the old
7818     // presentation shell, so we can use it for the next document.
7819     if (PresShell* presShell = contentViewer->GetPresShell()) {
7820       bgcolor = presShell->GetCanvasBackground();
7821       isActive = presShell->IsActive();
7822     }
7823 
7824     contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7825     aNewViewer->SetPreviousViewer(contentViewer);
7826   }
7827   if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
7828     // We don't plan to save a viewer in mOSHE; tell it to drop
7829     // any other state it's holding.
7830     mOSHE->SyncPresentationState();
7831   }
7832 
7833   mContentViewer = nullptr;
7834 
7835   // Now that we're about to switch documents, forget all of our children.
7836   // Note that we cached them as needed up in CaptureState above.
7837   DestroyChildren();
7838 
7839   mContentViewer = aNewViewer;
7840 
7841   nsCOMPtr<nsIWidget> widget;
7842   NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
7843 
7844   nsIntRect bounds(x, y, cx, cy);
7845 
7846   mContentViewer->SetNavigationTiming(mTiming);
7847 
7848   if (NS_FAILED(mContentViewer->Init(widget, bounds, aWindowActor))) {
7849     mContentViewer = nullptr;
7850     NS_WARNING("ContentViewer Initialization failed");
7851     return NS_ERROR_FAILURE;
7852   }
7853 
7854   // If we have old state to copy, set the old state onto the new content
7855   // viewer
7856   if (newCv) {
7857     newCv->SetForceCharset(forceCharset);
7858     newCv->SetHintCharset(hintCharset);
7859     NS_ENSURE_SUCCESS(newCv->SetHintCharacterSetSource(hintCharsetSource),
7860                       NS_ERROR_FAILURE);
7861     NS_ENSURE_SUCCESS(newCv->SetOverrideDPPX(overrideDPPX), NS_ERROR_FAILURE);
7862     NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
7863                       NS_ERROR_FAILURE);
7864   }
7865 
7866   // Stuff the bgcolor from the old pres shell into the new
7867   // pres shell. This improves page load continuity.
7868   if (RefPtr<PresShell> presShell = mContentViewer->GetPresShell()) {
7869     presShell->SetCanvasBackground(bgcolor);
7870     if (isActive) {
7871       presShell->SetIsActive(isActive);
7872     }
7873   }
7874 
7875   // XXX: It looks like the LayoutState gets restored again in Embed()
7876   //      right after the call to SetupNewViewer(...)
7877 
7878   // We don't show the mContentViewer yet, since we want to draw the old page
7879   // until we have enough of the new page to show.  Just return with the new
7880   // viewer still set to hidden.
7881 
7882   return NS_OK;
7883 }
7884 
SetDocCurrentStateObj(nsISHEntry * aShEntry)7885 nsresult nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry) {
7886   NS_ENSURE_STATE(mContentViewer);
7887   RefPtr<Document> document = GetDocument();
7888   NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
7889 
7890   nsCOMPtr<nsIStructuredCloneContainer> scContainer;
7891   if (aShEntry) {
7892     scContainer = aShEntry->GetStateData();
7893 
7894     // If aShEntry is null, just set the document's state object to null.
7895   }
7896 
7897   // It's OK for scContainer too be null here; that just means there's no
7898   // state data associated with this history entry.
7899   document->SetStateObject(scContainer);
7900 
7901   return NS_OK;
7902 }
7903 
CheckLoadingPermissions()7904 nsresult nsDocShell::CheckLoadingPermissions() {
7905   // This method checks whether the caller may load content into
7906   // this docshell. Even though we've done our best to hide windows
7907   // from code that doesn't have the right to access them, it's
7908   // still possible for an evil site to open a window and access
7909   // frames in the new window through window.frames[] (which is
7910   // allAccess for historic reasons), so we still need to do this
7911   // check on load.
7912   nsresult rv = NS_OK;
7913 
7914   if (!IsFrame()) {
7915     // We're not a frame. Permit all loads.
7916     return rv;
7917   }
7918 
7919   // Note - The check for a current JSContext here isn't necessarily sensical.
7920   // It's just designed to preserve the old semantics during a mass-conversion
7921   // patch.
7922   if (!nsContentUtils::GetCurrentJSContext()) {
7923     return NS_OK;
7924   }
7925 
7926   // Check if the caller is from the same origin as this docshell,
7927   // or any of its ancestors.
7928   nsCOMPtr<nsIDocShellTreeItem> item(this);
7929   do {
7930     nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
7931     nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
7932 
7933     nsIPrincipal* p;
7934     if (!sop || !(p = sop->GetPrincipal())) {
7935       return NS_ERROR_UNEXPECTED;
7936     }
7937 
7938     if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
7939       // Same origin, permit load
7940       return NS_OK;
7941     }
7942 
7943     nsCOMPtr<nsIDocShellTreeItem> tmp;
7944     item->GetInProcessSameTypeParent(getter_AddRefs(tmp));
7945     item.swap(tmp);
7946   } while (item);
7947 
7948   return NS_ERROR_DOM_PROP_ACCESS_DENIED;
7949 }
7950 
7951 //*****************************************************************************
7952 // nsDocShell: Site Loading
7953 //*****************************************************************************
7954 
CopyFavicon(nsIURI * aOldURI,nsIURI * aNewURI,nsIPrincipal * aLoadingPrincipal,bool aInPrivateBrowsing)7955 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
7956                              nsIPrincipal* aLoadingPrincipal,
7957                              bool aInPrivateBrowsing) {
7958   if (XRE_IsContentProcess()) {
7959     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
7960     if (contentChild) {
7961       contentChild->SendCopyFavicon(aOldURI, aNewURI,
7962                                     IPC::Principal(aLoadingPrincipal),
7963                                     aInPrivateBrowsing);
7964     }
7965     return;
7966   }
7967 
7968 #ifdef MOZ_PLACES
7969   nsCOMPtr<nsIFaviconService> favSvc =
7970       do_GetService("@mozilla.org/browser/favicon-service;1");
7971   if (favSvc) {
7972     favSvc->CopyFavicons(aOldURI, aNewURI,
7973                          aInPrivateBrowsing
7974                              ? nsIFaviconService::FAVICON_LOAD_PRIVATE
7975                              : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
7976                          nullptr);
7977   }
7978 #endif
7979 }
7980 
7981 class InternalLoadEvent : public Runnable {
7982  public:
InternalLoadEvent(nsDocShell * aDocShell,nsDocShellLoadState * aLoadState)7983   InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
7984       : mozilla::Runnable("InternalLoadEvent"),
7985         mDocShell(aDocShell),
7986         mLoadState(aLoadState) {
7987     // For events, both target and filename should be the version of "null" they
7988     // expect. By the time the event is fired, both window targeting and file
7989     // downloading have been handled, so we should never have an internal load
7990     // event that retargets or had a download.
7991     mLoadState->SetTarget(EmptyString());
7992     mLoadState->SetFileName(VoidString());
7993   }
7994 
7995   NS_IMETHOD
Run()7996   Run() override {
7997 #ifndef ANDROID
7998     MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
7999                "InternalLoadEvent: Should always have a principal here");
8000 #endif
8001     return mDocShell->InternalLoad(mLoadState, nullptr, nullptr);
8002   }
8003 
8004  private:
8005   RefPtr<nsDocShell> mDocShell;
8006   RefPtr<nsDocShellLoadState> mLoadState;
8007 };
8008 
8009 /**
8010  * Returns true if we started an asynchronous load (i.e., from the network), but
8011  * the document we're loading there hasn't yet become this docshell's active
8012  * document.
8013  *
8014  * When JustStartedNetworkLoad is true, you should be careful about modifying
8015  * mLoadType and mLSHE.  These are both set when the asynchronous load first
8016  * starts, and the load expects that, when it eventually runs InternalLoad,
8017  * mLoadType and mLSHE will have their original values.
8018  */
JustStartedNetworkLoad()8019 bool nsDocShell::JustStartedNetworkLoad() {
8020   return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8021 }
8022 
8023 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8024 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8025 // <embed>, etc)
8026 //
8027 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8028 // later when we call DoURILoad.
DetermineContentType()8029 uint32_t nsDocShell::DetermineContentType() {
8030   if (!IsFrame()) {
8031     return nsIContentPolicy::TYPE_DOCUMENT;
8032   }
8033 
8034   nsCOMPtr<Element> requestingElement =
8035       mScriptGlobal->GetFrameElementInternal();
8036   if (requestingElement) {
8037     return requestingElement->IsHTMLElement(nsGkAtoms::iframe)
8038                ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8039                : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8040   }
8041   // If we have lost our frame element by now, just assume we're
8042   // an iframe since that's more common.
8043   return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8044 }
8045 
PerformRetargeting(nsDocShellLoadState * aLoadState,nsIDocShell ** aDocShell,nsIRequest ** aRequest)8046 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState,
8047                                         nsIDocShell** aDocShell,
8048                                         nsIRequest** aRequest) {
8049   MOZ_ASSERT(aLoadState, "need a load state!");
8050   MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8051 
8052   nsresult rv = NS_OK;
8053   RefPtr<BrowsingContext> targetContext;
8054 
8055   // Only _self, _parent, and _top are supported in noopener case.  But we
8056   // have to be careful to not apply that to the noreferrer case.  See bug
8057   // 1358469.
8058   bool allowNamedTarget =
8059       !aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8060       aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8061   if (allowNamedTarget ||
8062       aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8063       aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8064       aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8065     targetContext = mBrowsingContext->FindWithName(
8066         aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8067   }
8068 
8069   if (!targetContext) {
8070     // If the targetContext doesn't exist, then this is a new docShell and we
8071     // should consider this a TYPE_DOCUMENT load
8072     //
8073     // For example, when target="_blank"
8074 
8075     // If there's no targetContext, that means we are about to create a new
8076     // window. Perform a content policy check before creating the window. Please
8077     // note for all other docshell loads content policy checks are performed
8078     // within the contentSecurityManager when the channel is about to be
8079     // openend.
8080     nsISupports* requestingContext = nullptr;
8081     if (XRE_IsContentProcess()) {
8082       // In e10s the child process doesn't have access to the element that
8083       // contains the browsing context (because that element is in the chrome
8084       // process). So we just pass mScriptGlobal.
8085       requestingContext = ToSupports(mScriptGlobal);
8086     } else {
8087       // This is for loading non-e10s tabs and toplevel windows of various
8088       // sorts.
8089       // For the toplevel window cases, requestingElement will be null.
8090       nsCOMPtr<Element> requestingElement =
8091           mScriptGlobal->GetFrameElementInternal();
8092       requestingContext = requestingElement;
8093     }
8094 
8095     // Ideally we should use the same loadinfo as within DoURILoad which
8096     // should match this one when both are applicable.
8097     nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new LoadInfo(
8098         mScriptGlobal, aLoadState->TriggeringPrincipal(), requestingContext,
8099         nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8100 
8101     // Since Content Policy checks are performed within docShell as well as
8102     // the ContentSecurityManager we need a reliable way to let certain
8103     // nsIContentPolicy consumers ignore duplicate calls.
8104     secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8105 
8106     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8107     rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8108                                    EmptyCString(),  // mime guess
8109                                    &shouldLoad);
8110 
8111     if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8112       if (NS_SUCCEEDED(rv)) {
8113         if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8114           return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8115         }
8116         if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8117           return NS_ERROR_BLOCKED_BY_POLICY;
8118         }
8119       }
8120 
8121       return NS_ERROR_CONTENT_BLOCKED;
8122     }
8123   }
8124 
8125   //
8126   // Resolve the window target before going any further...
8127   // If the load has been targeted to another DocShell, then transfer the
8128   // load to it...
8129   //
8130 
8131   // We've already done our owner-inheriting.  Mask out that bit, so we
8132   // don't try inheriting an owner from the target window if we came up
8133   // with a null owner above.
8134   aLoadState->UnsetLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8135 
8136   if (!targetContext) {
8137     // If the docshell's document is sandboxed, only open a new window
8138     // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8139     // (i.e. if allow-popups is specified)
8140     NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8141     Document* doc = mContentViewer->GetDocument();
8142 
8143     const bool isDocumentAuxSandboxed =
8144         doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8145 
8146     if (isDocumentAuxSandboxed) {
8147       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8148     }
8149 
8150     nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8151     NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8152 
8153     RefPtr<BrowsingContext> newBC;
8154     nsAutoCString spec;
8155     aLoadState->URI()->GetSpec(spec);
8156 
8157     // If we are a noopener load, we just hand the whole thing over to our
8158     // window.
8159     if (aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER)) {
8160       // Various asserts that we know to hold because NO_OPENER loads can only
8161       // happen for links.
8162       MOZ_ASSERT(!aLoadState->LoadReplace());
8163       MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8164                  aLoadState->TriggeringPrincipal());
8165       MOZ_ASSERT(aLoadState->LoadFlags() == INTERNAL_LOAD_FLAGS_NO_OPENER ||
8166                  aLoadState->LoadFlags() ==
8167                      (INTERNAL_LOAD_FLAGS_NO_OPENER |
8168                       INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
8169       MOZ_ASSERT(!aLoadState->PostDataStream());
8170       MOZ_ASSERT(!aLoadState->HeadersStream());
8171       // If OnLinkClickSync was invoked inside the onload handler, the load
8172       // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8173       // LOAD_LINK.
8174       MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8175                  aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8176       MOZ_ASSERT(!aLoadState->SHEntry());
8177       MOZ_ASSERT(aLoadState->FirstParty());  // Windowwatcher will assume this.
8178 
8179       RefPtr<nsDocShellLoadState> loadState =
8180           new nsDocShellLoadState(aLoadState->URI());
8181 
8182       // Set up our loadinfo so it will do the load as much like we would have
8183       // as possible.
8184       loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8185       loadState->SetOriginalURI(aLoadState->OriginalURI());
8186 
8187       Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8188       aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8189 
8190       loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8191       loadState->SetKeepResultPrincipalURIIfSet(
8192           aLoadState->KeepResultPrincipalURIIfSet());
8193       // LoadReplace will always be false due to asserts above, skip setting
8194       // it.
8195       loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8196       loadState->SetCsp(aLoadState->Csp());
8197       loadState->SetInheritPrincipal(
8198           aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8199       // Explicit principal because we do not want any guesses as to what the
8200       // principal to inherit is: it should be aTriggeringPrincipal.
8201       loadState->SetPrincipalIsExplicit(true);
8202       loadState->SetLoadType(LOAD_LINK);
8203       loadState->SetForceAllowDataURI(
8204           aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8205 
8206       loadState->SetHasValidUserGestureActivation(
8207           aLoadState->HasValidUserGestureActivation());
8208 
8209       rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8210                      aLoadState->Target(),  // window name
8211                      EmptyString(),         // Features
8212                      loadState,
8213                      true,  // aForceNoOpener
8214                      getter_AddRefs(newBC));
8215       MOZ_ASSERT(!newBC);
8216       return rv;
8217     }
8218 
8219     rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8220                              aLoadState->Target(),  // window name
8221                              EmptyString(),         // Features
8222                              getter_AddRefs(newBC));
8223 
8224     // In some cases the Open call doesn't actually result in a new
8225     // window being opened.  We can detect these cases by examining the
8226     // document in |newBC|, if any.
8227     nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8228         newBC ? newBC->GetDOMWindow() : nullptr;
8229     if (piNewWin) {
8230       RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8231       if (!newDoc || newDoc->IsInitialDocument()) {
8232         aLoadState->SetLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8233       }
8234     }
8235 
8236     if (newBC) {
8237       targetContext = newBC;
8238     }
8239   }
8240 
8241   NS_ENSURE_SUCCESS(rv, rv);
8242   NS_ENSURE_TRUE(targetContext, rv);
8243   //
8244   // Transfer the load to the target BrowsingContext... Pass empty string as the
8245   // window target name from to prevent recursive retargeting!
8246   //
8247   // No window target
8248   aLoadState->SetTarget(EmptyString());
8249   // No forced download
8250   aLoadState->SetFileName(VoidString());
8251   return targetContext->InternalLoad(aLoadState, aDocShell, aRequest);
8252 }
8253 
IsSameDocumentNavigation(nsDocShellLoadState * aLoadState,SameDocumentNavigationState & aState)8254 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8255                                           SameDocumentNavigationState& aState) {
8256   MOZ_ASSERT(aLoadState);
8257   if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8258         aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8259         LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8260                             LOAD_FLAGS_REPLACE_HISTORY) ||
8261         aLoadState->LoadType() == LOAD_HISTORY ||
8262         aLoadState->LoadType() == LOAD_LINK)) {
8263     return false;
8264   }
8265 
8266   nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8267 
8268   nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8269   if (NS_SUCCEEDED(rvURINew)) {
8270     rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8271   }
8272 
8273   if (currentURI && NS_SUCCEEDED(rvURINew)) {
8274     nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8275     if (NS_SUCCEEDED(rvURIOld)) {
8276       rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8277     }
8278     if (NS_SUCCEEDED(rvURIOld)) {
8279       if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8280                                                 &aState.mSameExceptHashes))) {
8281         aState.mSameExceptHashes = false;
8282       }
8283     }
8284   }
8285 
8286   if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8287     // Maybe aLoadState->URI() came from the exposable form of currentURI?
8288     nsCOMPtr<nsIURI> currentExposableURI =
8289         nsIOService::CreateExposableURI(currentURI);
8290     nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8291     if (NS_SUCCEEDED(rvURIOld)) {
8292       rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8293     }
8294     if (NS_SUCCEEDED(rvURIOld)) {
8295       if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8296               aLoadState->URI(), &aState.mSameExceptHashes))) {
8297         aState.mSameExceptHashes = false;
8298       }
8299     }
8300   }
8301 
8302   if (mOSHE && aLoadState->SHEntry()) {
8303     // We're doing a history load.
8304 
8305     mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8306                               &aState.mHistoryNavBetweenSameDoc);
8307 
8308 #ifdef DEBUG
8309     if (aState.mHistoryNavBetweenSameDoc) {
8310       nsCOMPtr<nsIInputStream> currentPostData = mOSHE->GetPostData();
8311       NS_ASSERTION(currentPostData == aLoadState->PostDataStream(),
8312                    "Different POST data for entries for the same page?");
8313     }
8314 #endif
8315   }
8316 
8317   // A same document navigation happens when we navigate between two SHEntries
8318   // for the same document. We do a same document navigation under two
8319   // circumstances. Either
8320   //
8321   //  a) we're navigating between two different SHEntries which share a
8322   //     document, or
8323   //
8324   //  b) we're navigating to a new shentry whose URI differs from the
8325   //     current URI only in its hash, the new hash is non-empty, and
8326   //     we're not doing a POST.
8327   //
8328   // The restriction that the SHEntries in (a) must be different ensures
8329   // that history.go(0) and the like trigger full refreshes, rather than
8330   // same document navigations.
8331   bool doSameDocumentNavigation =
8332       (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8333       (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8334        aState.mSameExceptHashes && aState.mNewURIHasRef);
8335 
8336   return doSameDocumentNavigation;
8337 }
8338 
HandleSameDocumentNavigation(nsDocShellLoadState * aLoadState,SameDocumentNavigationState & aState)8339 nsresult nsDocShell::HandleSameDocumentNavigation(
8340     nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState) {
8341 #ifdef DEBUG
8342   SameDocumentNavigationState state;
8343   MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8344 #endif
8345 
8346   nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8347 
8348   // Save the position of the scrollers.
8349   nsPoint scrollPos = GetCurScrollPos();
8350 
8351   // Reset mLoadType to its original value once we exit this block, because this
8352   // same document navigation might have started after a normal, network load,
8353   // and we don't want to clobber its load type. See bug 737307.
8354   AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8355 
8356   // If a non-same-document-navigation (i.e., a network load) is pending, make
8357   // this a replacement load, so that we don't add a SHEntry here and the
8358   // network load goes into the SHEntry it expects to.
8359   if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8360     mLoadType = LOAD_NORMAL_REPLACE;
8361   } else {
8362     mLoadType = aLoadState->LoadType();
8363   }
8364 
8365   mURIResultedInDocument = true;
8366 
8367   nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8368 
8369   // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8370   // History loads, SetCurrentURI() called from OnNewURI() will send proper
8371   // onLocationChange() notifications to the browser to update back/forward
8372   // buttons.
8373   SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8374                              Nothing());
8375 
8376   // Set the doc's URI according to the new history entry's URI.
8377   RefPtr<Document> doc = GetDocument();
8378   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8379   doc->SetDocumentURI(aLoadState->URI());
8380 
8381   /* This is a anchor traversal within the same page.
8382    * call OnNewURI() so that, this traversal will be
8383    * recorded in session and global history.
8384    */
8385   nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8386       newURIStoragePrincipalToInherit;
8387   nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8388   if (mOSHE) {
8389     newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8390     newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8391     newURIStoragePrincipalToInherit = mOSHE->GetStoragePrincipalToInherit();
8392     newCsp = mOSHE->GetCsp();
8393   } else {
8394     newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8395     newURIPrincipalToInherit = doc->NodePrincipal();
8396     newURIStoragePrincipalToInherit = doc->IntrinsicStoragePrincipal();
8397     newCsp = doc->GetCsp();
8398   }
8399   // Pass true for aCloneSHChildren, since we're not
8400   // changing documents here, so all of our subframes are
8401   // still relevant to the new session history entry.
8402   //
8403   // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8404   // flag on firing onLocationChange(...).
8405   // Anyway, aCloneSHChildren param is simply reflecting
8406   // doSameDocumentNavigation in this scope.
8407   OnNewURI(aLoadState->URI(), nullptr, newURITriggeringPrincipal,
8408            newURIPrincipalToInherit, newURIStoragePrincipalToInherit, mLoadType,
8409            newCsp, true, true, true);
8410 
8411   nsCOMPtr<nsIInputStream> postData;
8412   uint32_t cacheKey = 0;
8413 
8414   bool scrollRestorationIsManual = false;
8415   if (mOSHE) {
8416     /* save current position of scroller(s) (bug 59774) */
8417     mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8418     DebugOnly<nsresult> rv =
8419         mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
8420     MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
8421     // Get the postdata and page ident from the current page, if
8422     // the new load is being done via normal means.  Note that
8423     // "normal means" can be checked for just by checking for
8424     // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8425     // above -- it filters out some LOAD_CMD_NORMAL cases that we
8426     // wouldn't want here.
8427     if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8428       postData = mOSHE->GetPostData();
8429       cacheKey = mOSHE->GetCacheKey();
8430 
8431       // Link our new SHEntry to the old SHEntry's back/forward
8432       // cache data, since the two SHEntries correspond to the
8433       // same document.
8434       if (mLSHE) {
8435         if (!aLoadState->SHEntry()) {
8436           // If we're not doing a history load, scroll restoration
8437           // should be inherited from the previous session history entry.
8438           mLSHE->SetScrollRestorationIsManual(scrollRestorationIsManual);
8439         }
8440         mLSHE->AdoptBFCacheEntry(mOSHE);
8441       }
8442     }
8443   }
8444 
8445   // If we're doing a history load, use its scroll restoration state.
8446   if (aLoadState->SHEntry()) {
8447     DebugOnly<nsresult> rv =
8448         aLoadState->SHEntry()->GetScrollRestorationIsManual(
8449             &scrollRestorationIsManual);
8450     MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
8451   }
8452 
8453   /* Assign mLSHE to mOSHE. This will either be a new entry created
8454    * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8455    * loads.
8456    */
8457   if (mLSHE) {
8458     SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8459     // Save the postData obtained from the previous page
8460     // in to the session history entry created for the
8461     // anchor page, so that any history load of the anchor
8462     // page will restore the appropriate postData.
8463     if (postData) {
8464       mOSHE->SetPostData(postData);
8465     }
8466 
8467     // Make sure we won't just repost without hitting the
8468     // cache first
8469     if (cacheKey != 0) {
8470       mOSHE->SetCacheKey(cacheKey);
8471     }
8472   }
8473 
8474   /* Restore the original LSHE if we were loading something
8475    * while same document navigation was initiated.
8476    */
8477   SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
8478   /* Set the title for the SH entry for this target url. so that
8479    * SH menus in go/back/forward buttons won't be empty for this.
8480    */
8481   ChildSHistory* shistory = GetSessionHistory();
8482   if (shistory) {
8483     int32_t index = shistory->Index();
8484     nsCOMPtr<nsISHEntry> shEntry;
8485     shistory->LegacySHistory()->GetEntryAtIndex(index, getter_AddRefs(shEntry));
8486     NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
8487     shEntry->SetTitle(mTitle);
8488   }
8489 
8490   /* Set the title for the Global History entry for this anchor url.
8491    */
8492   UpdateGlobalHistoryTitle(aLoadState->URI());
8493 
8494   SetDocCurrentStateObj(mOSHE);
8495 
8496   // Inform the favicon service that the favicon for oldURI also
8497   // applies to aLoadState->URI().
8498   CopyFavicon(currentURI, aLoadState->URI(), doc->NodePrincipal(),
8499               UsePrivateBrowsing());
8500 
8501   RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
8502   RefPtr<nsGlobalWindowInner> win =
8503       scriptGlobal ? scriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
8504 
8505   // ScrollToAnchor doesn't necessarily cause us to scroll the window;
8506   // the function decides whether a scroll is appropriate based on the
8507   // arguments it receives.  But even if we don't end up scrolling,
8508   // ScrollToAnchor performs other important tasks, such as informing
8509   // the presShell that we have a new hash.  See bug 680257.
8510   nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
8511                                aState.mNewHash, aLoadState->LoadType());
8512   NS_ENSURE_SUCCESS(rv, rv);
8513 
8514   /* restore previous position of scroller(s), if we're moving
8515    * back in history (bug 59774)
8516    */
8517   nscoord bx = 0;
8518   nscoord by = 0;
8519   bool needsScrollPosUpdate = false;
8520   if (mOSHE &&
8521       (aLoadState->LoadType() == LOAD_HISTORY ||
8522        aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
8523       !scrollRestorationIsManual) {
8524     needsScrollPosUpdate = true;
8525     mOSHE->GetScrollPosition(&bx, &by);
8526   }
8527 
8528   // Dispatch the popstate and hashchange events, as appropriate.
8529   //
8530   // The event dispatch below can cause us to re-enter script and
8531   // destroy the docshell, nulling out mScriptGlobal. Hold a stack
8532   // reference to avoid null derefs. See bug 914521.
8533   if (win) {
8534     // Fire a hashchange event URIs differ, and only in their hashes.
8535     bool doHashchange = aState.mSameExceptHashes &&
8536                         (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
8537                          !aState.mCurrentHash.Equals(aState.mNewHash));
8538 
8539     if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
8540       win->DispatchSyncPopState();
8541     }
8542 
8543     if (needsScrollPosUpdate && win->HasActiveDocument()) {
8544       SetCurScrollPosEx(bx, by);
8545     }
8546 
8547     if (doHashchange) {
8548       // Note that currentURI hasn't changed because it's on the
8549       // stack, so we can just use it directly as the old URI.
8550       win->DispatchAsyncHashchange(currentURI, aLoadState->URI());
8551     }
8552   }
8553 
8554   return NS_OK;
8555 }
8556 
InternalLoad(nsDocShellLoadState * aLoadState,nsIDocShell ** aDocShell,nsIRequest ** aRequest)8557 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
8558                                   nsIDocShell** aDocShell,
8559                                   nsIRequest** aRequest) {
8560   MOZ_ASSERT(aLoadState, "need a load state!");
8561   MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
8562              "need a valid TriggeringPrincipal");
8563 
8564   if (!aLoadState->TriggeringPrincipal()) {
8565     MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
8566     return NS_ERROR_FAILURE;
8567   }
8568   if (mBrowsingContext->PendingInitialization()) {
8569     return NS_ERROR_NOT_AVAILABLE;
8570   }
8571 
8572   mOriginalUriString.Truncate();
8573 
8574   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
8575           ("DOCSHELL %p InternalLoad %s\n", this,
8576            aLoadState->URI()->GetSpecOrDefault().get()));
8577 
8578   // Initialize aDocShell/aRequest
8579   if (aDocShell) {
8580     *aDocShell = nullptr;
8581   }
8582   if (aRequest) {
8583     *aRequest = nullptr;
8584   }
8585 
8586   NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
8587 
8588   // Cancel loads coming from Docshells that are being destroyed.
8589   if (mIsBeingDestroyed) {
8590     return NS_ERROR_NOT_AVAILABLE;
8591   }
8592 
8593   nsresult rv = EnsureScriptEnvironment();
8594   if (NS_FAILED(rv)) {
8595     return rv;
8596   }
8597 
8598   // If we have a target to move to, do that now.
8599   if (!aLoadState->Target().IsEmpty()) {
8600     return PerformRetargeting(aLoadState, aDocShell, aRequest);
8601   }
8602 
8603   // If we don't have a target, we're loading into ourselves, and our load
8604   // delegate may want to intercept that load.
8605   SameDocumentNavigationState sameDocumentNavigationState;
8606   bool sameDocument =
8607       IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
8608       !aLoadState->GetPendingRedirectedChannel();
8609 
8610   // Note: We do this check both here and in BrowsingContext::
8611   // LoadURI/InternalLoad, since document-specific sandbox flags are only
8612   // available in the process triggering the load, and we don't want the target
8613   // process to have to trust the triggering process to do the appropriate
8614   // checks for the BrowsingContext's sandbox flags.
8615   MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
8616 
8617   NS_ENSURE_STATE(!HasUnloadedParent());
8618 
8619   rv = CheckLoadingPermissions();
8620   if (NS_FAILED(rv)) {
8621     return rv;
8622   }
8623 
8624   if (mFiredUnloadEvent) {
8625     if (IsOKToLoadURI(aLoadState->URI())) {
8626       MOZ_ASSERT(aLoadState->Target().IsEmpty(),
8627                  "Shouldn't have a window target here!");
8628 
8629       // If this is a replace load, make whatever load triggered
8630       // the unload event also a replace load, so we don't
8631       // create extra history entries.
8632       if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8633                               LOAD_FLAGS_REPLACE_HISTORY)) {
8634         mLoadType = LOAD_NORMAL_REPLACE;
8635       }
8636 
8637       // Do this asynchronously
8638       nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
8639       return Dispatch(TaskCategory::Other, ev.forget());
8640     }
8641 
8642     // Just ignore this load attempt
8643     return NS_OK;
8644   }
8645 
8646   // If we are loading a URI that should inherit a security context (basically
8647   // javascript: at this point), and the caller has said that principal
8648   // inheritance is allowed, there are a few possible cases:
8649   //
8650   // 1) We are provided with the principal to inherit. In that case, we just use
8651   //    it.
8652   //
8653   // 2) The load is coming from some other application. In this case we don't
8654   //    want to inherit from whatever document we have loaded now, since the
8655   //    load is unrelated to it.
8656   //
8657   // 3) It's a load from our application, but does not provide an explicit
8658   //    principal to inherit. In that case, we want to inherit the principal of
8659   //    our current document, or of our parent document (if any) if we don't
8660   //    have a current document.
8661   {
8662     bool inherits;
8663 
8664     if (aLoadState->LoadType() != LOAD_NORMAL_EXTERNAL &&
8665         !aLoadState->PrincipalToInherit() &&
8666         (aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
8667         NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
8668             aLoadState->URI(), &inherits)) &&
8669         inherits) {
8670       aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
8671     }
8672     // If principalToInherit is still null (e.g. if some of the conditions of
8673     // were not satisfied), then no inheritance of any sort will happen: the
8674     // load will just get a principal based on the URI being loaded.
8675   }
8676 
8677   // If this docshell is owned by a frameloader, make sure to cancel
8678   // possible frameloader initialization before loading a new page.
8679   nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
8680   if (parent) {
8681     RefPtr<Document> doc = parent->GetDocument();
8682     if (doc) {
8683       doc->TryCancelFrameLoaderInitialization(this);
8684     }
8685   }
8686 
8687   // Before going any further vet loads initiated by external programs.
8688   if (aLoadState->LoadType() == LOAD_NORMAL_EXTERNAL) {
8689     // Disallow external chrome: loads targetted at content windows
8690     if (SchemeIsChrome(aLoadState->URI())) {
8691       NS_WARNING("blocked external chrome: url -- use '--chrome' option");
8692       return NS_ERROR_FAILURE;
8693     }
8694 
8695     // clear the decks to prevent context bleed-through (bug 298255)
8696     rv = CreateAboutBlankContentViewer(nullptr, nullptr, nullptr, nullptr);
8697     if (NS_FAILED(rv)) {
8698       return NS_ERROR_FAILURE;
8699     }
8700 
8701     // reset loadType so we don't have to add lots of tests for
8702     // LOAD_NORMAL_EXTERNAL after this point
8703     aLoadState->SetLoadType(LOAD_NORMAL);
8704   }
8705 
8706   mAllowKeywordFixup =
8707       aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
8708   mURIResultedInDocument = false;  // reset the clock...
8709 
8710   // See if this is actually a load between two history entries for the same
8711   // document. If the process fails, or if we successfully navigate within the
8712   // same document, return.
8713   if (sameDocument) {
8714     return HandleSameDocumentNavigation(aLoadState,
8715                                         sameDocumentNavigationState);
8716   }
8717 
8718   // mContentViewer->PermitUnload can destroy |this| docShell, which
8719   // causes the next call of CanSavePresentation to crash.
8720   // Hold onto |this| until we return, to prevent a crash from happening.
8721   // (bug#331040)
8722   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
8723 
8724   // Don't init timing for javascript:, since it generally doesn't
8725   // actually start a load or anything.  If it does, we'll init
8726   // timing then, from OnStateChange.
8727 
8728   // XXXbz mTiming should know what channel it's for, so we don't
8729   // need this hackery.
8730   bool toBeReset = false;
8731   bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
8732 
8733   if (!isJavaScript) {
8734     toBeReset = MaybeInitTiming();
8735   }
8736   bool isNotDownload = aLoadState->FileName().IsVoid();
8737   if (mTiming && isNotDownload) {
8738     mTiming->NotifyBeforeUnload();
8739   }
8740   // Check if the page doesn't want to be unloaded. The javascript:
8741   // protocol handler deals with this for javascript: URLs.
8742   if (!isJavaScript && isNotDownload && mContentViewer) {
8743     bool okToUnload;
8744     rv = mContentViewer->PermitUnload(&okToUnload);
8745 
8746     if (NS_SUCCEEDED(rv) && !okToUnload) {
8747       // The user chose not to unload the page, interrupt the
8748       // load.
8749       MaybeResetInitTiming(toBeReset);
8750       return NS_OK;
8751     }
8752   }
8753 
8754   if (mTiming && isNotDownload) {
8755     mTiming->NotifyUnloadAccepted(mCurrentURI);
8756   }
8757 
8758   // Check if the webbrowser chrome wants the load to proceed; this can be
8759   // used to cancel attempts to load URIs in the wrong process. testing
8760   // GetPendingRedirectedChannel() helps to avoid revisiting an earlier
8761   // redirect decision.
8762   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
8763   if (browserChrome3 && !aLoadState->GetPendingRedirectedChannel()) {
8764     bool shouldLoad;
8765     rv = browserChrome3->ShouldLoadURI(
8766         this, aLoadState->URI(), aLoadState->GetReferrerInfo(),
8767         !!aLoadState->PostDataStream(), aLoadState->TriggeringPrincipal(),
8768         aLoadState->Csp(), &shouldLoad);
8769     if (NS_SUCCEEDED(rv) && !shouldLoad) {
8770       return NS_OK;
8771     }
8772   }
8773 
8774   // In e10s, in the parent process, we refuse to load anything other than
8775   // "safe" resources that we ship or trust enough to give "special" URLs.
8776   // Similar check will be performed by the ParentProcessDocumentChannel if in
8777   // use.
8778   if (XRE_IsE10sParentProcess() &&
8779       !DocumentChannel::CanUseDocumentChannel(aLoadState) &&
8780       !CanLoadInParentProcess(aLoadState->URI())) {
8781     return NS_ERROR_FAILURE;
8782   }
8783 
8784   // Whenever a top-level browsing context is navigated, the user agent MUST
8785   // lock the orientation of the document to the document's default
8786   // orientation. We don't explicitly check for a top-level browsing context
8787   // here because orientation is only set on top-level browsing contexts.
8788   if (mBrowsingContext->GetOrientationLock() != hal::eScreenOrientation_None) {
8789     MOZ_ASSERT(mBrowsingContext->IsTop());
8790     mBrowsingContext->SetOrientationLock(hal::eScreenOrientation_None);
8791     if (mBrowsingContext->GetIsActive()) {
8792       ScreenOrientation::UpdateActiveOrientationLock(
8793           hal::eScreenOrientation_None);
8794     }
8795   }
8796 
8797   // Check for saving the presentation here, before calling Stop().
8798   // This is necessary so that we can catch any pending requests.
8799   // Since the new request has not been created yet, we pass null for the
8800   // new request parameter.
8801   // Also pass nullptr for the document, since it doesn't affect the return
8802   // value for our purposes here.
8803   bool savePresentation =
8804       CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr);
8805 
8806   // Don't stop current network activity for javascript: URL's since
8807   // they might not result in any data, and thus nothing should be
8808   // stopped in those cases. In the case where they do result in
8809   // data, the javascript: URL channel takes care of stopping
8810   // current network activity.
8811   if (!isJavaScript && isNotDownload) {
8812     // Stop any current network activity.
8813     // Also stop content if this is a zombie doc. otherwise
8814     // the onload will be delayed by other loads initiated in the
8815     // background by the first document that
8816     // didn't fully load before the next load was initiated.
8817     // If not a zombie, don't stop content until data
8818     // starts arriving from the new URI...
8819 
8820     if ((mContentViewer && mContentViewer->GetPreviousViewer()) ||
8821         LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
8822       rv = Stop(nsIWebNavigation::STOP_ALL);
8823     } else {
8824       rv = Stop(nsIWebNavigation::STOP_NETWORK);
8825     }
8826 
8827     if (NS_FAILED(rv)) {
8828       return rv;
8829     }
8830   }
8831 
8832   mLoadType = aLoadState->LoadType();
8833 
8834   // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
8835   // been called. But when loading an error page, do not clear the
8836   // mLSHE for the real page.
8837   if (mLoadType != LOAD_ERROR_PAGE) {
8838     SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8839                                Nothing());
8840     if (aLoadState->SHEntry()) {
8841       // We're making history navigation or a reload. Make sure our history ID
8842       // points to the same ID as SHEntry's docshell ID.
8843       aLoadState->SHEntry()->GetDocshellID(mHistoryID);
8844       mBrowsingContext->SetHistoryID(mHistoryID);
8845     }
8846   }
8847 
8848   mSavingOldViewer = savePresentation;
8849 
8850   // If we have a saved content viewer in history, restore and show it now.
8851   if (aLoadState->SHEntry() && (mLoadType & LOAD_CMD_HISTORY)) {
8852     // https://html.spec.whatwg.org/#history-traversal:
8853     // To traverse the history
8854     // "If entry has a different Document object than the current entry, then
8855     // run the following substeps: Remove any tasks queued by the history
8856     // traversal task source..."
8857     // Same document object case was handled already above with
8858     // HandleSameDocumentNavigation call.
8859     RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
8860     if (shistory) {
8861       shistory->RemovePendingHistoryNavigations();
8862     }
8863 
8864     // It's possible that the previous viewer of mContentViewer is the
8865     // viewer that will end up in aLoadState->SHEntry() when it gets closed.  If
8866     // that's the case, we need to go ahead and force it into its shentry so we
8867     // can restore it.
8868     if (mContentViewer) {
8869       nsCOMPtr<nsIContentViewer> prevViewer =
8870           mContentViewer->GetPreviousViewer();
8871       if (prevViewer) {
8872 #ifdef DEBUG
8873         nsCOMPtr<nsIContentViewer> prevPrevViewer =
8874             prevViewer->GetPreviousViewer();
8875         NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
8876 #endif
8877         nsCOMPtr<nsISHEntry> viewerEntry;
8878         prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
8879         if (viewerEntry == aLoadState->SHEntry()) {
8880           // Make sure this viewer ends up in the right place
8881           mContentViewer->SetPreviousViewer(nullptr);
8882           prevViewer->Destroy();
8883         }
8884       }
8885     }
8886     nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
8887     bool restoring;
8888     rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
8889     if (restoring) {
8890       Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
8891       return rv;
8892     }
8893     Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
8894 
8895     // We failed to restore the presentation, so clean up.
8896     // Both the old and new history entries could potentially be in
8897     // an inconsistent state.
8898     if (NS_FAILED(rv)) {
8899       if (oldEntry) {
8900         oldEntry->SyncPresentationState();
8901       }
8902 
8903       aLoadState->SHEntry()->SyncPresentationState();
8904     }
8905   }
8906 
8907   bool isTopLevelDoc = mBrowsingContext->IsTopContent();
8908 
8909   OriginAttributes attrs = GetOriginAttributes();
8910   attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
8911 
8912   PredictorLearn(aLoadState->URI(), nullptr,
8913                  nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
8914   PredictorPredict(aLoadState->URI(), nullptr,
8915                    nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
8916 
8917   nsCOMPtr<nsIRequest> req;
8918   rv = DoURILoad(aLoadState, aDocShell, getter_AddRefs(req));
8919   if (req && aRequest) {
8920     NS_ADDREF(*aRequest = req);
8921   }
8922 
8923   if (NS_FAILED(rv)) {
8924     nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
8925     UnblockEmbedderLoadEventForFailure();
8926     if (DisplayLoadError(rv, aLoadState->URI(), nullptr, chan) &&
8927         aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
8928       return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
8929     }
8930 
8931     // We won't report any error if this is an unknown protocol error. The
8932     // reason behind this is that it will allow enumeration of external
8933     // protocols if we report an error for each unknown protocol.
8934     if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
8935       return NS_OK;
8936     }
8937   }
8938 
8939   return rv;
8940 }
8941 
8942 /* static */
CanLoadInParentProcess(nsIURI * aURI)8943 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
8944   nsCOMPtr<nsIURI> uri = aURI;
8945   // In e10s, in the parent process, we refuse to load anything other than
8946   // "safe" resources that we ship or trust enough to give "special" URLs.
8947   bool canLoadInParent = false;
8948   if (NS_SUCCEEDED(NS_URIChainHasFlags(
8949           uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
8950       canLoadInParent) {
8951     // We allow UI resources.
8952     return true;
8953   }
8954   // For about: and extension-based URIs, which don't get
8955   // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
8956   while (uri && uri->SchemeIs("view-source")) {
8957     nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
8958     if (nested) {
8959       nested->GetInnerURI(getter_AddRefs(uri));
8960     } else {
8961       break;
8962     }
8963   }
8964   // Allow about: URIs, and allow moz-extension ones if we're running
8965   // extension content in the parent process.
8966   if (!uri || uri->SchemeIs("about") ||
8967       (!StaticPrefs::extensions_webextensions_remote() &&
8968        uri->SchemeIs("moz-extension"))) {
8969     return true;
8970   }
8971   nsAutoCString scheme;
8972   uri->GetScheme(scheme);
8973   // Allow ext+foo URIs (extension-registered custom protocols). See
8974   // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
8975   if (StringBeginsWith(scheme, NS_LITERAL_CSTRING("ext+")) &&
8976       !StaticPrefs::extensions_webextensions_remote()) {
8977     return true;
8978   }
8979   // Final exception for some legacy automated tests:
8980   if (xpc::IsInAutomation() &&
8981       StaticPrefs::security_allow_unsafe_parent_loads()) {
8982     return true;
8983   }
8984   return false;
8985 }
8986 
GetInheritedPrincipal(bool aConsiderCurrentDocument,bool aConsiderStoragePrincipal)8987 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
8988     bool aConsiderCurrentDocument, bool aConsiderStoragePrincipal) {
8989   RefPtr<Document> document;
8990   bool inheritedFromCurrent = false;
8991 
8992   if (aConsiderCurrentDocument && mContentViewer) {
8993     document = mContentViewer->GetDocument();
8994     inheritedFromCurrent = true;
8995   }
8996 
8997   if (!document) {
8998     nsCOMPtr<nsIDocShellTreeItem> parentItem;
8999     GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9000     if (parentItem) {
9001       document = parentItem->GetDocument();
9002     }
9003   }
9004 
9005   if (!document) {
9006     if (!aConsiderCurrentDocument) {
9007       return nullptr;
9008     }
9009 
9010     // Make sure we end up with _something_ as the principal no matter
9011     // what.If this fails, we'll just get a null docViewer and bail.
9012     EnsureContentViewer();
9013     if (!mContentViewer) {
9014       return nullptr;
9015     }
9016     document = mContentViewer->GetDocument();
9017   }
9018 
9019   //-- Get the document's principal
9020   if (document) {
9021     nsIPrincipal* docPrincipal = aConsiderStoragePrincipal
9022                                      ? document->IntrinsicStoragePrincipal()
9023                                      : document->NodePrincipal();
9024 
9025     // Don't allow loads in typeContent docShells to inherit the system
9026     // principal from existing documents.
9027     if (inheritedFromCurrent && mItemType == typeContent &&
9028         docPrincipal->IsSystemPrincipal()) {
9029       return nullptr;
9030     }
9031 
9032     return docPrincipal;
9033   }
9034 
9035   return nullptr;
9036 }
9037 
CreateRealChannelForDocument(nsIChannel ** aChannel,nsIURI * aURI,nsILoadInfo * aLoadInfo,nsIInterfaceRequestor * aCallbacks,nsLoadFlags aLoadFlags,const nsAString & aSrcdoc,nsIURI * aBaseURI)9038 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9039     nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9040     nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9041     const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9042   nsCOMPtr<nsIChannel> channel;
9043   if (aSrcdoc.IsVoid()) {
9044     MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9045                                   nullptr,  // PerformanceStorage
9046                                   nullptr,  // loadGroup
9047                                   aCallbacks, aLoadFlags));
9048 
9049     if (aBaseURI) {
9050       nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9051       if (vsc) {
9052         MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9053       }
9054     }
9055   } else if (SchemeIsViewSource(aURI)) {
9056     nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9057     if (!vsh) {
9058       return NS_ERROR_FAILURE;
9059     }
9060 
9061     MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9062                                   getter_AddRefs(channel)));
9063   } else {
9064     MOZ_TRY(NS_NewInputStreamChannelInternal(
9065         getter_AddRefs(channel), aURI, aSrcdoc, NS_LITERAL_CSTRING("text/html"),
9066         aLoadInfo, true));
9067     nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9068     MOZ_ASSERT(isc);
9069     isc->SetBaseURI(aBaseURI);
9070   }
9071 
9072   if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9073     nsresult rv = channel->SetLoadFlags(aLoadFlags);
9074     NS_ENSURE_SUCCESS(rv, rv);
9075   }
9076 
9077   channel.forget(aChannel);
9078   return NS_OK;
9079 }
9080 
CreateAndConfigureRealChannelForLoadState(BrowsingContext * aBrowsingContext,nsDocShellLoadState * aLoadState,LoadInfo * aLoadInfo,nsIInterfaceRequestor * aCallbacks,nsDocShell * aDocShell,const OriginAttributes & aOriginAttributes,nsLoadFlags aLoadFlags,uint32_t aCacheKey,nsresult & aRv,nsIChannel ** aChannel)9081 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9082     BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9083     LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9084     nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9085     nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9086     nsIChannel** aChannel) {
9087   MOZ_ASSERT(aLoadInfo);
9088 
9089   nsString srcdoc = VoidString();
9090   bool isSrcdoc = aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9091   if (isSrcdoc) {
9092     srcdoc = aLoadState->SrcdocData();
9093   }
9094 
9095   if (aLoadState->PrincipalToInherit()) {
9096     aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9097   }
9098   aLoadInfo->SetLoadTriggeredFromExternal(aLoadState->LoadType() ==
9099                                           LOAD_NORMAL_EXTERNAL);
9100   aLoadInfo->SetForceAllowDataURI(
9101       aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9102   aLoadInfo->SetOriginalFrameSrcLoad(
9103       aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9104 
9105   bool inheritAttrs = false;
9106   if (aLoadState->PrincipalToInherit()) {
9107     inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9108         aLoadState->PrincipalToInherit(), aLoadState->URI(),
9109         true,  // aInheritForAboutBlank
9110         isSrcdoc);
9111   }
9112 
9113   OriginAttributes attrs;
9114 
9115   // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9116   // true. Otherwise we just use the origin attributes from docshell.
9117   if (inheritAttrs) {
9118     MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9119                "We should have PrincipalToInherit here.");
9120     attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9121     // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9122     // have the same origin attributes with docshell.
9123     MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9124                   attrs == aOriginAttributes);
9125   } else {
9126     attrs = aOriginAttributes;
9127     attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9128                               aLoadState->URI());
9129   }
9130 
9131   aRv = aLoadInfo->SetOriginAttributes(attrs);
9132   if (NS_WARN_IF(NS_FAILED(aRv))) {
9133     return false;
9134   }
9135 
9136   if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9137     aLoadInfo->SetIsFromProcessingFrameAttributes();
9138   }
9139 
9140   // Propagate the IsFormSubmission flag to the loadInfo.
9141   if (aLoadState->IsFormSubmission()) {
9142     aLoadInfo->SetIsFormSubmission(true);
9143   }
9144 
9145   // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9146   // HTTPS by default. This behavior can be disabled through the loadinfo flag
9147   // HTTPS_ONLY_EXEMPT.
9148   if (aLoadState->IsHttpsOnlyModeUpgradeExempt() &&
9149       mozilla::StaticPrefs::dom_security_https_only_mode()) {
9150     uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
9151     httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
9152     aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
9153   }
9154 
9155   nsCOMPtr<nsIChannel> channel;
9156   aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9157                                      aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9158                                      aLoadState->BaseURI());
9159   NS_ENSURE_SUCCESS(aRv, false);
9160 
9161   if (!channel) {
9162     return false;
9163   }
9164 
9165   if (nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
9166           do_QueryInterface(channel)) {
9167     // Any document load should not inherit application cache.
9168     appCacheChannel->SetInheritApplicationCache(false);
9169 
9170     // Loads with the correct permissions should check for a matching
9171     // application cache.
9172     if (GeckoProcessType_Default != XRE_GetProcessType()) {
9173       // Permission will be checked in the parent process
9174       appCacheChannel->SetChooseApplicationCache(true);
9175     } else if (aDocShell) {
9176       // TODO: Figure out how to handle this in the parent,
9177       // on behalf of a content process.
9178       nsCOMPtr<nsIScriptSecurityManager> secMan =
9179           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
9180 
9181       if (secMan) {
9182         nsCOMPtr<nsIPrincipal> principal;
9183         secMan->GetDocShellContentPrincipal(aLoadState->URI(), aDocShell,
9184                                             getter_AddRefs(principal));
9185         appCacheChannel->SetChooseApplicationCache(
9186             NS_ShouldCheckAppCache(principal));
9187       }
9188     }
9189   }
9190 
9191   // hack
9192   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9193   nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9194       do_QueryInterface(channel));
9195   nsCOMPtr<nsIURI> referrer;
9196   nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9197   if (referrerInfo) {
9198     referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9199   }
9200   if (httpChannelInternal) {
9201     if (aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9202       aRv = httpChannelInternal->SetThirdPartyFlags(
9203           nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9204       MOZ_ASSERT(NS_SUCCEEDED(aRv));
9205     }
9206     if (aLoadState->FirstParty()) {
9207       aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9208       MOZ_ASSERT(NS_SUCCEEDED(aRv));
9209     } else {
9210       aRv = httpChannelInternal->SetDocumentURI(referrer);
9211       MOZ_ASSERT(NS_SUCCEEDED(aRv));
9212     }
9213     aRv = httpChannelInternal->SetRedirectMode(
9214         nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9215     MOZ_ASSERT(NS_SUCCEEDED(aRv));
9216   }
9217 
9218   if (httpChannel) {
9219     if (aLoadState->HeadersStream()) {
9220       aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9221     }
9222     // Set the referrer explicitly
9223     // Referrer is currenly only set for link clicks here.
9224     if (referrerInfo) {
9225       aRv = httpChannel->SetReferrerInfo(referrerInfo);
9226       MOZ_ASSERT(NS_SUCCEEDED(aRv));
9227     }
9228 
9229     // Mark the http channel as UrgentStart for top level document loading in
9230     // active tab.
9231     if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9232       nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9233       if (cos) {
9234         cos->AddClassFlags(nsIClassOfService::UrgentStart);
9235       }
9236     }
9237   }
9238 
9239   channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
9240                                                     : aLoadState->URI());
9241 
9242   const nsACString& typeHint = aLoadState->TypeHint();
9243   if (!typeHint.IsVoid()) {
9244     channel->SetContentType(typeHint);
9245   }
9246 
9247   const nsAString& fileName = aLoadState->FileName();
9248   if (!fileName.IsVoid()) {
9249     aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9250     NS_ENSURE_SUCCESS(aRv, false);
9251     if (!fileName.IsEmpty()) {
9252       aRv = channel->SetContentDispositionFilename(fileName);
9253       NS_ENSURE_SUCCESS(aRv, false);
9254     }
9255   }
9256 
9257   if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
9258     nsCOMPtr<nsIURI> referrer;
9259     nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9260     if (referrerInfo) {
9261       referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9262     }
9263     // save true referrer for those who need it (e.g. xpinstall whitelisting)
9264     // Currently only http and ftp channels support this.
9265     props->SetPropertyAsInterface(
9266         NS_LITERAL_STRING("docshell.internalReferrer"), referrer);
9267 
9268     if (aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
9269       props->SetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
9270                                true);
9271     }
9272   }
9273 
9274   nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
9275   auto loadType = aLoadState->LoadType();
9276 
9277   // figure out if we need to set the post data stream on the channel...
9278   if (aLoadState->PostDataStream()) {
9279     if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
9280             do_QueryInterface(channel)) {
9281       // XXX it's a bit of a hack to rewind the postdata stream here but
9282       // it has to be done in case the post data is being reused multiple
9283       // times.
9284       nsCOMPtr<nsISeekableStream> postDataSeekable =
9285           do_QueryInterface(aLoadState->PostDataStream());
9286       if (postDataSeekable) {
9287         aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
9288         NS_ENSURE_SUCCESS(aRv, false);
9289       }
9290 
9291       // we really need to have a content type associated with this stream!!
9292       postChannel->SetUploadStream(aLoadState->PostDataStream(), EmptyCString(),
9293                                    -1);
9294     }
9295 
9296     /* If there is a valid postdata *and* it is a History Load,
9297      * set up the cache key on the channel, to retrieve the
9298      * data *only* from the cache. If it is a normal reload, the
9299      * cache is free to go to the server for updated postdata.
9300      */
9301     if (cacheChannel && aCacheKey != 0) {
9302       if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
9303         cacheChannel->SetCacheKey(aCacheKey);
9304         uint32_t loadFlags;
9305         if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
9306           channel->SetLoadFlags(loadFlags |
9307                                 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
9308         }
9309       } else if (loadType == LOAD_RELOAD_NORMAL) {
9310         cacheChannel->SetCacheKey(aCacheKey);
9311       }
9312     }
9313   } else {
9314     /* If there is no postdata, set the cache key on the channel, and
9315      * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
9316      * will be free to get it from net if it is not found in cache.
9317      * New cache may use it creatively on CGI pages with GET
9318      * method and even on those that say "no-cache"
9319      */
9320     if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
9321         loadType == LOAD_RELOAD_CHARSET_CHANGE ||
9322         loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
9323         loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
9324       if (cacheChannel && aCacheKey != 0) {
9325         cacheChannel->SetCacheKey(aCacheKey);
9326       }
9327     }
9328   }
9329 
9330   if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
9331     // Allow execution against our context if the principals match
9332     scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
9333   }
9334 
9335   if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
9336     timedChannel->SetTimingEnabled(true);
9337 
9338     auto& embedderElementType = aBrowsingContext->GetEmbedderElementType();
9339     if (embedderElementType) {
9340       timedChannel->SetInitiatorType(*embedderElementType);
9341     }
9342   }
9343 
9344   if (httpChannelInternal && aBrowsingContext->GetSandboxFlags() != 0) {
9345     httpChannelInternal->SetHasNonEmptySandboxingFlag(true);
9346   }
9347 
9348   nsCOMPtr<nsIURI> rpURI;
9349   aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
9350   Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
9351   aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
9352   if (originalResultPrincipalURI &&
9353       (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
9354     // Unconditionally override, we want the replay to be equal to what has
9355     // been captured.
9356     aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
9357   }
9358 
9359   if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
9360     // The LOAD_REPLACE flag and its handling here will be removed as part
9361     // of bug 1319110.  For now preserve its restoration here to not break
9362     // any code expecting it being set specially on redirected channels.
9363     // If the flag has originally been set to change result of
9364     // NS_GetFinalChannelURI it won't have any effect and also won't cause
9365     // any harm.
9366     uint32_t loadFlags;
9367     aRv = channel->GetLoadFlags(&loadFlags);
9368     NS_ENSURE_SUCCESS(aRv, false);
9369     channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
9370   }
9371 
9372   nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
9373   if (csp) {
9374     // Navigational requests that are same origin need to be upgraded in case
9375     // upgrade-insecure-requests is present. Please note that for document
9376     // navigations that bit is re-computed in case we encounter a server
9377     // side redirect so the navigation is not same-origin anymore.
9378     bool upgradeInsecureRequests = false;
9379     csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
9380     if (upgradeInsecureRequests) {
9381       // only upgrade if the navigation is same origin
9382       nsCOMPtr<nsIPrincipal> resultPrincipal;
9383       aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
9384           channel, getter_AddRefs(resultPrincipal));
9385       NS_ENSURE_SUCCESS(aRv, false);
9386       if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
9387               aLoadState->TriggeringPrincipal(), resultPrincipal)) {
9388         aLoadInfo->SetUpgradeInsecureRequests(true);
9389       }
9390     }
9391 
9392     // For document loads we store the CSP that potentially needs to
9393     // be inherited by the new document, e.g. in case we are loading
9394     // an opaque origin like a data: URI. The actual inheritance
9395     // check happens within Document::InitCSP().
9396     // Please create an actual copy of the CSP (do not share the same
9397     // reference) otherwise a Meta CSP of an opaque origin will
9398     // incorrectly be propagated to the embedding document.
9399     RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
9400     cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
9401     aLoadInfo->SetCSPToInherit(cspToInherit);
9402   }
9403 
9404   channel.forget(aChannel);
9405   return true;
9406 }
9407 
DoURILoad(nsDocShellLoadState * aLoadState,nsIDocShell ** aDocShell,nsIRequest ** aRequest)9408 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
9409                                nsIDocShell** aDocShell, nsIRequest** aRequest) {
9410   // Double-check that we're still around to load this URI.
9411   if (mIsBeingDestroyed) {
9412     // Return NS_OK despite not doing anything to avoid throwing exceptions
9413     // from nsLocation::SetHref if the unload handler of the existing page
9414     // tears us down.
9415     return NS_OK;
9416   }
9417 
9418   nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
9419   if (NS_WARN_IF(!uriLoader)) {
9420     return NS_ERROR_UNEXPECTED;
9421   }
9422 
9423   nsresult rv;
9424   uint32_t contentPolicyType = DetermineContentType();
9425 
9426   if (IsFrame()) {
9427     MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
9428                    contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
9429                "DoURILoad thinks this is a frame and InternalLoad does not");
9430 
9431     if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
9432       // Only allow URLs able to return data in iframes.
9433       bool doesNotReturnData = false;
9434       NS_URIChainHasFlags(aLoadState->URI(),
9435                           nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
9436                           &doesNotReturnData);
9437       if (doesNotReturnData) {
9438         bool popupBlocked = true;
9439 
9440         // Let's consider external protocols as popups and let's check if the
9441         // page is allowed to open them without abuse regardless of allowed
9442         // events
9443         if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
9444           nsCOMPtr<nsINode> loadingNode =
9445               mScriptGlobal->GetFrameElementInternal();
9446           popupBlocked = !PopupBlocker::TryUsePopupOpeningToken(
9447               loadingNode ? loadingNode->NodePrincipal() : nullptr);
9448         } else if (mBrowsingContext->GetIsActive() &&
9449                    PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
9450           popupBlocked = false;
9451         } else {
9452           nsCOMPtr<nsINode> loadingNode =
9453               mScriptGlobal->GetFrameElementInternal();
9454           if (loadingNode) {
9455             popupBlocked = !PopupBlocker::CanShowPopupByPermission(
9456                 loadingNode->NodePrincipal());
9457           }
9458         }
9459 
9460         // No error must be returned when iframes are blocked.
9461         if (popupBlocked) {
9462           return NS_OK;
9463         }
9464       }
9465     }
9466 
9467     // Only allow view-source scheme in top-level docshells. view-source is
9468     // the only scheme to which this applies at the moment due to potential
9469     // timing attacks to read data from cross-origin iframes. If this widens
9470     // we should add a protocol flag for whether the scheme is allowed in
9471     // frames and use something like nsNetUtil::NS_URIChainHasFlags.
9472     nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
9473     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
9474     while (nestedURI) {
9475       // view-source should always be an nsINestedURI, loop and check the
9476       // scheme on this and all inner URIs that are also nested URIs.
9477       if (SchemeIsViewSource(tempURI)) {
9478         return NS_ERROR_UNKNOWN_PROTOCOL;
9479       }
9480       nestedURI->GetInnerURI(getter_AddRefs(tempURI));
9481       nestedURI = do_QueryInterface(tempURI);
9482     }
9483   } else {
9484     MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
9485                "DoURILoad thinks this is a document and InternalLoad does not");
9486   }
9487 
9488   // FIXME We still have a ton of codepaths that don't pass through
9489   //       DocumentLoadListener, so probably need to create session history info
9490   //       in more places.
9491   if (aLoadState->GetSessionHistoryID() != 0) {
9492     mLoadingEntry =
9493         MakeUnique<SessionHistoryInfo>(aLoadState->GetSessionHistoryInfo());
9494     mLoadingEntryId = aLoadState->GetSessionHistoryID();
9495   }
9496 
9497   // open a channel for the url
9498 
9499   // If we have a pending channel, use the channel we've already created here.
9500   // We don't need to set up load flags for our channel, as it has already been
9501   // created.
9502 
9503   if (nsCOMPtr<nsIChannel> channel =
9504           aLoadState->GetPendingRedirectedChannel()) {
9505     MOZ_ASSERT(!aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC),
9506                "pending channel for srcdoc load?");
9507 
9508     // If we have a request outparameter, shove our channel into it.
9509     if (aRequest) {
9510       nsCOMPtr<nsIRequest> outRequest = channel;
9511       outRequest.forget(aRequest);
9512     }
9513 
9514     rv = OpenInitializedChannel(channel, uriLoader,
9515                                 nsIURILoader::REDIRECTED_CHANNEL);
9516 
9517     // If the channel load failed, we failed and nsIWebProgress just ain't
9518     // gonna happen.
9519     if (NS_SUCCEEDED(rv) && aDocShell) {
9520       nsCOMPtr<nsIDocShell> self = this;
9521       self.forget(aDocShell);
9522     }
9523     return rv;
9524   }
9525 
9526   // There are two cases we care about:
9527   // * Top-level load: In this case, loadingNode is null, but loadingWindow
9528   //   is our mScriptGlobal. We pass null for loadingPrincipal in this case.
9529   // * Subframe load: loadingWindow is null, but loadingNode is the frame
9530   //   element for the load. loadingPrincipal is the NodePrincipal of the
9531   //   frame element.
9532   nsCOMPtr<nsINode> loadingNode;
9533   nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
9534   nsCOMPtr<nsIPrincipal> loadingPrincipal;
9535   nsCOMPtr<nsISupports> topLevelLoadingContext;
9536 
9537   if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
9538     loadingNode = nullptr;
9539     loadingPrincipal = nullptr;
9540     loadingWindow = mScriptGlobal;
9541     if (XRE_IsContentProcess()) {
9542       // In e10s the child process doesn't have access to the element that
9543       // contains the browsing context (because that element is in the chrome
9544       // process).
9545       nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
9546       topLevelLoadingContext = ToSupports(browserChild);
9547     } else {
9548       // This is for loading non-e10s tabs and toplevel windows of various
9549       // sorts.
9550       // For the toplevel window cases, requestingElement will be null.
9551       nsCOMPtr<Element> requestingElement =
9552           loadingWindow->GetFrameElementInternal();
9553       topLevelLoadingContext = requestingElement;
9554     }
9555   } else {
9556     loadingWindow = nullptr;
9557     loadingNode = mScriptGlobal->GetFrameElementInternal();
9558     if (loadingNode) {
9559       // If we have a loading node, then use that as our loadingPrincipal.
9560       loadingPrincipal = loadingNode->NodePrincipal();
9561 #ifdef DEBUG
9562       // Get the docshell type for requestingElement.
9563       RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
9564       nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
9565       // requestingElement docshell type = current docshell type.
9566       MOZ_ASSERT(
9567           mItemType == elementDocShell->ItemType(),
9568           "subframes should have the same docshell type as their parent");
9569 #endif
9570     } else {
9571       if (mIsBeingDestroyed) {
9572         // If this isn't a top-level load and mScriptGlobal's frame element is
9573         // null, then the element got removed from the DOM while we were trying
9574         // to load this resource. This docshell is scheduled for destruction
9575         // already, so bail out here.
9576         return NS_OK;
9577       }
9578       // If we are not being destroyed and we do not have access to the loading
9579       // node, then we are a remote subframe. Set the loading principal
9580       // to be a null principal and then set it correctly in the parent.
9581       loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
9582     }
9583   }
9584 
9585   if (!aLoadState->TriggeringPrincipal()) {
9586     MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
9587     return NS_ERROR_FAILURE;
9588   }
9589 
9590   // We want to inherit aLoadState->PrincipalToInherit() when:
9591   // 1. ChannelShouldInheritPrincipal returns true.
9592   // 2. aLoadState->URI() is not data: URI, or data: URI is not
9593   //    configured as unique opaque origin.
9594   bool inheritPrincipal = false;
9595 
9596   if (aLoadState->PrincipalToInherit()) {
9597     bool isSrcdoc = aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9598     bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9599         aLoadState->PrincipalToInherit(), aLoadState->URI(),
9600         true,  // aInheritForAboutBlank
9601         isSrcdoc);
9602 
9603     bool isURIUniqueOrigin =
9604         StaticPrefs::security_data_uri_unique_opaque_origin() &&
9605         SchemeIsData(aLoadState->URI());
9606     inheritPrincipal = inheritAttrs && !isURIUniqueOrigin;
9607   }
9608 
9609   uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
9610   nsSecurityFlags securityFlags =
9611       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
9612 
9613   if (mLoadType == LOAD_ERROR_PAGE) {
9614     securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
9615   }
9616 
9617   if (inheritPrincipal) {
9618     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
9619   }
9620 
9621   // Must never have a parent for TYPE_DOCUMENT loads
9622   MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
9623                 !mBrowsingContext->GetParent());
9624   // Subdocuments must have a parent
9625   MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
9626                 mBrowsingContext->GetParent());
9627 
9628   RefPtr<LoadInfo> loadInfo =
9629       (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
9630           ? new LoadInfo(loadingWindow, aLoadState->TriggeringPrincipal(),
9631                          topLevelLoadingContext, securityFlags, sandboxFlags)
9632           : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
9633                          loadingNode, securityFlags, contentPolicyType,
9634                          Maybe<mozilla::dom::ClientInfo>(),
9635                          Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
9636                          sandboxFlags);
9637 
9638   // in case this docshell load was triggered by a valid transient user gesture,
9639   // or also the load originates from external, then we pass that information on
9640   // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
9641   if (mBrowsingContext->HasValidTransientUserGestureActivation() ||
9642       aLoadState->HasValidUserGestureActivation() ||
9643       aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9644     loadInfo->SetHasValidUserGestureActivation(true);
9645   }
9646 
9647   // if this is an iframe load then store if it's the inital frame src load
9648   if (nsContentUtils::InternalContentPolicyTypeToExternal(contentPolicyType) ==
9649       nsIContentPolicy::TYPE_SUBDOCUMENT) {
9650     loadInfo->SetOriginalFrameSrcLoad(
9651         aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9652   }
9653 
9654   /* Get the cache Key from SH */
9655   uint32_t cacheKey = 0;
9656   if (mLSHE) {
9657     cacheKey = mLSHE->GetCacheKey();
9658   } else if (mOSHE) {  // for reload cases
9659     cacheKey = mOSHE->GetCacheKey();
9660   }
9661 
9662   bool uriModified = mLSHE ? mLSHE->GetURIWasModified() : false;
9663   bool isXFOError = false;
9664   if (mFailedChannel) {
9665     nsresult status;
9666     mFailedChannel->GetStatus(&status);
9667     isXFOError = status == NS_ERROR_XFO_VIOLATION;
9668   }
9669 
9670   nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
9671       mBrowsingContext, Some(uriModified), Some(isXFOError));
9672 
9673   nsCOMPtr<nsIChannel> channel;
9674   if (DocumentChannel::CanUseDocumentChannel(aLoadState)) {
9675     channel = DocumentChannel::CreateDocumentChannel(aLoadState, loadInfo,
9676                                                      loadFlags, this, cacheKey,
9677                                                      uriModified, isXFOError);
9678     MOZ_ASSERT(channel);
9679   } else if (!CreateAndConfigureRealChannelForLoadState(
9680                  mBrowsingContext, aLoadState, loadInfo, this, this,
9681                  GetOriginAttributes(), loadFlags, cacheKey, rv,
9682                  getter_AddRefs(channel))) {
9683     return rv;
9684   }
9685 
9686   // Make sure to give the caller a channel if we managed to create one
9687   // This is important for correct error page/session history interaction
9688   if (aRequest) {
9689     NS_ADDREF(*aRequest = channel);
9690   }
9691 
9692   nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
9693   if (csp) {
9694     // Check CSP navigate-to
9695     bool allowsNavigateTo = false;
9696     rv = csp->GetAllowsNavigateTo(aLoadState->URI(),
9697                                   aLoadState->IsFormSubmission(),
9698                                   false, /* aWasRedirected */
9699                                   false, /* aEnforceWhitelist */
9700                                   &allowsNavigateTo);
9701     NS_ENSURE_SUCCESS(rv, rv);
9702 
9703     if (!allowsNavigateTo) {
9704       return NS_ERROR_CSP_NAVIGATE_TO_VIOLATION;
9705     }
9706   }
9707 
9708   const nsACString& typeHint = aLoadState->TypeHint();
9709   if (!typeHint.IsVoid()) {
9710     mContentTypeHint = typeHint;
9711   } else {
9712     mContentTypeHint.Truncate();
9713   }
9714 
9715   // Load attributes depend on load type...
9716   if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
9717     // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
9718     // only want to force cache load for this channel, not the whole
9719     // loadGroup.
9720     nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
9721     if (cachingChannel) {
9722       cachingChannel->SetAllowStaleCacheContent(true);
9723     }
9724   }
9725 
9726   uint32_t openFlags =
9727       nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
9728   rv = OpenInitializedChannel(channel, uriLoader, openFlags);
9729 
9730   //
9731   // If the channel load failed, we failed and nsIWebProgress just ain't
9732   // gonna happen.
9733   //
9734   if (NS_SUCCEEDED(rv)) {
9735     if (aDocShell) {
9736       *aDocShell = this;
9737       NS_ADDREF(*aDocShell);
9738     }
9739   }
9740 
9741   return rv;
9742 }
9743 
AppendSegmentToString(nsIInputStream * aIn,void * aClosure,const char * aFromRawSegment,uint32_t aToOffset,uint32_t aCount,uint32_t * aWriteCount)9744 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
9745                                       const char* aFromRawSegment,
9746                                       uint32_t aToOffset, uint32_t aCount,
9747                                       uint32_t* aWriteCount) {
9748   // aFromSegment now contains aCount bytes of data.
9749 
9750   nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
9751   buf->Append(aFromRawSegment, aCount);
9752 
9753   // Indicate that we have consumed all of aFromSegment
9754   *aWriteCount = aCount;
9755   return NS_OK;
9756 }
9757 
AddHeadersToChannel(nsIInputStream * aHeadersData,nsIChannel * aGenericChannel)9758 /* static */ nsresult nsDocShell::AddHeadersToChannel(
9759     nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
9760   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
9761   NS_ENSURE_STATE(httpChannel);
9762 
9763   uint32_t numRead;
9764   nsAutoCString headersString;
9765   nsresult rv = aHeadersData->ReadSegments(
9766       AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
9767   NS_ENSURE_SUCCESS(rv, rv);
9768 
9769   // used during the manipulation of the String from the InputStream
9770   nsAutoCString headerName;
9771   nsAutoCString headerValue;
9772   int32_t crlf;
9773   int32_t colon;
9774 
9775   //
9776   // Iterate over the headersString: for each "\r\n" delimited chunk,
9777   // add the value as a header to the nsIHttpChannel
9778   //
9779 
9780   static const char kWhitespace[] = "\b\t\r\n ";
9781   while (true) {
9782     crlf = headersString.Find("\r\n");
9783     if (crlf == kNotFound) {
9784       return NS_OK;
9785     }
9786 
9787     const nsACString& oneHeader = StringHead(headersString, crlf);
9788 
9789     colon = oneHeader.FindChar(':');
9790     if (colon == kNotFound) {
9791       return NS_ERROR_UNEXPECTED;
9792     }
9793 
9794     headerName = StringHead(oneHeader, colon);
9795     headerValue = Substring(oneHeader, colon + 1);
9796 
9797     headerName.Trim(kWhitespace);
9798     headerValue.Trim(kWhitespace);
9799 
9800     headersString.Cut(0, crlf + 2);
9801 
9802     //
9803     // FINALLY: we can set the header!
9804     //
9805 
9806     rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
9807     NS_ENSURE_SUCCESS(rv, rv);
9808   }
9809 
9810   MOZ_ASSERT_UNREACHABLE("oops");
9811   return NS_ERROR_UNEXPECTED;
9812 }
9813 
ComputeURILoaderFlags(BrowsingContext * aBrowsingContext,uint32_t aLoadType)9814 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
9815     BrowsingContext* aBrowsingContext, uint32_t aLoadType) {
9816   MOZ_ASSERT(aBrowsingContext);
9817 
9818   uint32_t openFlags = 0;
9819   if (aLoadType == LOAD_LINK) {
9820     openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
9821   }
9822   if (!aBrowsingContext->GetAllowContentRetargeting()) {
9823     openFlags |= nsIURILoader::DONT_RETARGET;
9824   }
9825 
9826   return openFlags;
9827 }
9828 
OpenInitializedChannel(nsIChannel * aChannel,nsIURILoader * aURILoader,uint32_t aOpenFlags)9829 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
9830                                             nsIURILoader* aURILoader,
9831                                             uint32_t aOpenFlags) {
9832   nsresult rv = NS_OK;
9833 
9834   if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
9835       mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
9836     rv = SetMixedContentChannel(aChannel);
9837     NS_ENSURE_SUCCESS(rv, rv);
9838   } else if (mMixedContentChannel) {
9839     /*
9840      * If the user "Disables Protection on This Page", we call
9841      * SetMixedContentChannel for the first time, otherwise
9842      * mMixedContentChannel is still null.
9843      * Later, if the new channel passes a same orign check, we remember the
9844      * users decision by calling SetMixedContentChannel using the new channel.
9845      * This way, the user does not have to click the disable protection button
9846      * over and over for browsing the same site.
9847      */
9848     rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, aChannel);
9849     if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(aChannel))) {
9850       SetMixedContentChannel(nullptr);
9851     }
9852   }
9853 
9854   // If anything fails here, make sure to clear our initial ClientSource.
9855   auto cleanupInitialClient =
9856       MakeScopeExit([&] { mInitialClientSource.reset(); });
9857 
9858   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
9859   NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
9860 
9861   MaybeCreateInitialClientSource();
9862 
9863   if (aOpenFlags & nsIURILoader::REDIRECTED_CHANNEL) {
9864     nsCOMPtr<nsILoadInfo> loadInfo;
9865     aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
9866 
9867     LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
9868     if (loadInfo->GetExternalContentPolicyType() ==
9869         nsIContentPolicy::TYPE_DOCUMENT) {
9870       li->UpdateBrowsingContextID(mBrowsingContext->Id());
9871     } else if (loadInfo->GetExternalContentPolicyType() ==
9872                nsIContentPolicy::TYPE_SUBDOCUMENT) {
9873       li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
9874     }
9875   }
9876   // TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
9877 
9878   // Let the client channel helper know if we are using DocumentChannel,
9879   // since redirects get handled in the parent process in that case.
9880   RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
9881   if (docChannel && XRE_IsContentProcess()) {
9882     // Tell the content process nsDocumentOpenInfo to not try to do
9883     // any sort of targeting.
9884     aOpenFlags |= nsIURILoader::DONT_RETARGET;
9885   }
9886 
9887   // Since we are loading a document we need to make sure the proper reserved
9888   // and initial client data is stored on the nsILoadInfo.  The
9889   // ClientChannelHelper does this and ensures that it is propagated properly
9890   // on redirects.  We pass no reserved client here so that the helper will
9891   // create the reserved ClientSource if necessary.
9892   Maybe<ClientInfo> noReservedClient;
9893   if (docChannel) {
9894     // When using DocumentChannel, all redirect handling is done in the parent,
9895     // so we just need the child variant to watch for the internal redirect
9896     // to the final channel.
9897     rv = AddClientChannelHelperInChild(
9898         aChannel, win->EventTargetFor(TaskCategory::Other));
9899     docChannel->SetInitialClientInfo(GetInitialClientInfo());
9900   } else if (aOpenFlags & nsIURILoader::REDIRECTED_CHANNEL) {
9901     // If we did a process switch, then we should have an existing allocated
9902     // ClientInfo, so we just need to allocate a corresponding ClientSource.
9903     CreateReservedSourceIfNeeded(aChannel,
9904                                  win->EventTargetFor(TaskCategory::Other));
9905     rv = NS_OK;
9906   } else {
9907     rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
9908                                 GetInitialClientInfo(),
9909                                 win->EventTargetFor(TaskCategory::Other));
9910   }
9911   NS_ENSURE_SUCCESS(rv, rv);
9912 
9913   rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
9914   NS_ENSURE_SUCCESS(rv, rv);
9915 
9916   // We're about to load a new page and it may take time before necko
9917   // gives back any data, so main thread might have a chance to process a
9918   // collector slice
9919   nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
9920 
9921   // Success.  Keep the initial ClientSource if it exists.
9922   cleanupInitialClient.release();
9923 
9924   return NS_OK;
9925 }
9926 
ScrollToAnchor(bool aCurHasRef,bool aNewHasRef,nsACString & aNewHash,uint32_t aLoadType)9927 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
9928                                     nsACString& aNewHash, uint32_t aLoadType) {
9929   if (!mCurrentURI) {
9930     return NS_OK;
9931   }
9932 
9933   RefPtr<PresShell> presShell = GetPresShell();
9934   if (!presShell) {
9935     // If we failed to get the shell, or if there is no shell,
9936     // nothing left to do here.
9937     return NS_OK;
9938   }
9939 
9940   nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
9941   if (rootScroll) {
9942     rootScroll->ClearDidHistoryRestore();
9943   }
9944 
9945   // If we have no new anchor, we do not want to scroll, unless there is a
9946   // current anchor and we are doing a history load.  So return if we have no
9947   // new anchor, and there is no current anchor or the load is not a history
9948   // load.
9949   if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
9950     return NS_OK;
9951   }
9952 
9953   // Both the new and current URIs refer to the same page. We can now
9954   // browse to the hash stored in the new URI.
9955 
9956   if (!aNewHash.IsEmpty()) {
9957     // anchor is there, but if it's a load from history,
9958     // we don't have any anchor jumping to do
9959     bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
9960 
9961     // We assume that the bytes are in UTF-8, as it says in the
9962     // spec:
9963     // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9964 
9965     // We try the UTF-8 string first, and then try the document's
9966     // charset (see below).  If the string is not UTF-8,
9967     // conversion will fail and give us an empty Unicode string.
9968     // In that case, we should just fall through to using the
9969     // page's charset.
9970     nsresult rv = NS_ERROR_FAILURE;
9971     NS_ConvertUTF8toUTF16 uStr(aNewHash);
9972     if (!uStr.IsEmpty()) {
9973       rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
9974     }
9975 
9976     if (NS_FAILED(rv)) {
9977       char* str = ToNewCString(aNewHash, mozilla::fallible);
9978       if (!str) {
9979         return NS_ERROR_OUT_OF_MEMORY;
9980       }
9981       nsUnescape(str);
9982       NS_ConvertUTF8toUTF16 utf16Str(str);
9983       if (!utf16Str.IsEmpty()) {
9984         rv = presShell->GoToAnchor(utf16Str, scroll,
9985                                    ScrollFlags::ScrollSmoothAuto);
9986       }
9987       free(str);
9988     }
9989 
9990     // Above will fail if the anchor name is not UTF-8.  Need to
9991     // convert from document charset to unicode.
9992     if (NS_FAILED(rv)) {
9993       // Get a document charset
9994       NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
9995       Document* doc = mContentViewer->GetDocument();
9996       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
9997       nsAutoCString charset;
9998       doc->GetDocumentCharacterSet()->Name(charset);
9999 
10000       nsCOMPtr<nsITextToSubURI> textToSubURI =
10001           do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10002       NS_ENSURE_SUCCESS(rv, rv);
10003 
10004       // Unescape and convert to unicode
10005       nsAutoString uStr;
10006 
10007       rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
10008       NS_ENSURE_SUCCESS(rv, rv);
10009 
10010       // Ignore return value of GoToAnchor, since it will return an error
10011       // if there is no such anchor in the document, which is actually a
10012       // success condition for us (we want to update the session history
10013       // with the new URI no matter whether we actually scrolled
10014       // somewhere).
10015       //
10016       // When aNewHash contains "%00", unescaped string may be empty.
10017       // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10018       presShell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
10019                             ScrollFlags::ScrollSmoothAuto);
10020     }
10021   } else {
10022     // Tell the shell it's at an anchor, without scrolling.
10023     presShell->GoToAnchor(EmptyString(), false);
10024 
10025     // An empty anchor was found, but if it's a load from history,
10026     // we don't have to jump to the top of the page. Scrollbar
10027     // position will be restored by the caller, based on positions
10028     // stored in session history.
10029     if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
10030       return NS_OK;
10031     }
10032     // An empty anchor. Scroll to the top of the page.  Ignore the
10033     // return value; failure to scroll here (e.g. if there is no
10034     // root scrollframe) is not grounds for canceling the load!
10035     SetCurScrollPosEx(0, 0);
10036   }
10037 
10038   return NS_OK;
10039 }
10040 
SetupReferrerInfoFromChannel(nsIChannel * aChannel)10041 void nsDocShell::SetupReferrerInfoFromChannel(nsIChannel* aChannel) {
10042   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10043   if (httpChannel) {
10044     nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
10045     SetReferrerInfo(referrerInfo);
10046   }
10047 }
10048 
OnNewURI(nsIURI * aURI,nsIChannel * aChannel,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,nsIPrincipal * aStoragePrincipalToInherit,uint32_t aLoadType,nsIContentSecurityPolicy * aCsp,bool aFireOnLocationChange,bool aAddToGlobalHistory,bool aCloneSHChildren)10049 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10050                           nsIPrincipal* aTriggeringPrincipal,
10051                           nsIPrincipal* aPrincipalToInherit,
10052                           nsIPrincipal* aStoragePrincipalToInherit,
10053                           uint32_t aLoadType, nsIContentSecurityPolicy* aCsp,
10054                           bool aFireOnLocationChange, bool aAddToGlobalHistory,
10055                           bool aCloneSHChildren) {
10056   MOZ_ASSERT(aURI, "uri is null");
10057   MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10058 
10059   MOZ_ASSERT(!aPrincipalToInherit ||
10060              (aPrincipalToInherit && aTriggeringPrincipal));
10061 
10062 #if defined(DEBUG)
10063   if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10064     nsAutoCString chanName;
10065     if (aChannel) {
10066       aChannel->GetName(chanName);
10067     } else {
10068       chanName.AssignLiteral("<no channel>");
10069     }
10070 
10071     MOZ_LOG(gDocShellLog, LogLevel::Debug,
10072             ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10073              aURI->GetSpecOrDefault().get(), chanName.get(), aLoadType));
10074   }
10075 #endif
10076 
10077   bool equalUri = false;
10078 
10079   // Get the post data and the HTTP response code from the channel.
10080   uint32_t responseStatus = 0;
10081   nsCOMPtr<nsIInputStream> inputStream;
10082   if (aChannel) {
10083     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10084 
10085     // Check if the HTTPChannel is hiding under a multiPartChannel
10086     if (!httpChannel) {
10087       GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10088     }
10089 
10090     if (httpChannel) {
10091       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10092       if (uploadChannel) {
10093         uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10094       }
10095 
10096       // If the response status indicates an error, unlink this session
10097       // history entry from any entries sharing its document.
10098       nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10099       if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10100         mLSHE->AbandonBFCacheEntry();
10101       }
10102     }
10103   }
10104 
10105   // Determine if this type of load should update history.
10106   bool updateGHistory = ShouldUpdateGlobalHistory(aLoadType);
10107 
10108   // We don't update session history on reload unless we're loading
10109   // an iframe in shift-reload case.
10110   bool updateSHistory =
10111       updateGHistory && (!(aLoadType & LOAD_CMD_RELOAD) ||
10112                          (IsForceReloadType(aLoadType) && IsFrame()));
10113 
10114   // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10115   // in the root browsing context.
10116   RefPtr<ChildSHistory> rootSH =
10117       mBrowsingContext->Top()->GetChildSessionHistory();
10118   if (!rootSH) {
10119     updateSHistory = false;
10120     updateGHistory = false;  // XXX Why global history too?
10121   }
10122 
10123   // Check if the url to be loaded is the same as the one already loaded.
10124   if (mCurrentURI) {
10125     aURI->Equals(mCurrentURI, &equalUri);
10126   }
10127 
10128 #ifdef DEBUG
10129   bool shAvailable = (rootSH != nullptr);
10130 
10131   // XXX This log message is almost useless because |updateSHistory|
10132   //     and |updateGHistory| are not correct at this point.
10133 
10134   MOZ_LOG(gDocShellLog, LogLevel::Debug,
10135           ("  shAvailable=%i updateSHistory=%i updateGHistory=%i"
10136            " equalURI=%i\n",
10137            shAvailable, updateSHistory, updateGHistory, equalUri));
10138 #endif
10139 
10140   /* If the url to be loaded is the same as the one already there,
10141    * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10142    * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10143    * AddToSessionHistory() won't mess with the current SHEntry and
10144    * if this page has any frame children, it also will be handled
10145    * properly. see bug 83684
10146    *
10147    * NB: If mOSHE is null but we have a current URI, then it probably
10148    * means that we must be at the transient about:blank content viewer;
10149    * we should let the normal load continue, since there's nothing to
10150    * replace. Sometimes this happens after a session restore (eg process
10151    * switch) and mCurrentURI is not about:blank; we assume we can let the load
10152    * continue (Bug 1301399).
10153    *
10154    * XXX Hopefully changing the loadType at this time will not hurt
10155    *  anywhere. The other way to take care of sequentially repeating
10156    *  frameset pages is to add new methods to nsIDocShellTreeItem.
10157    * Hopefully I don't have to do that.
10158    */
10159   if (equalUri && mOSHE &&
10160       (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
10161        mLoadType == LOAD_STOP_CONTENT) &&
10162       !inputStream) {
10163     mLoadType = LOAD_NORMAL_REPLACE;
10164   }
10165 
10166   // If this is a refresh to the currently loaded url, we don't
10167   // have to update session or global history.
10168   if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10169     SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
10170   }
10171 
10172   /* If the user pressed shift-reload, cache will create a new cache key
10173    * for the page. Save the new cacheKey in Session History.
10174    * see bug 90098
10175    */
10176   if (aChannel && IsForceReloadType(aLoadType)) {
10177     MOZ_ASSERT(!updateSHistory || IsFrame(),
10178                "We shouldn't be updating session history for forced"
10179                " reloads unless we're in a newly created iframe!");
10180 
10181     nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
10182     uint32_t cacheKey = 0;
10183     // Get the Cache Key and store it in SH.
10184     if (cacheChannel) {
10185       cacheChannel->GetCacheKey(&cacheKey);
10186     }
10187     // If we already have a loading history entry, store the new cache key
10188     // in it.  Otherwise, since we're doing a reload and won't be updating
10189     // our history entry, store the cache key in our current history entry.
10190     if (mLSHE) {
10191       mLSHE->SetCacheKey(cacheKey);
10192     } else if (mOSHE) {
10193       mOSHE->SetCacheKey(cacheKey);
10194     }
10195 
10196     // Since we're force-reloading, clear all the sub frame history.
10197     ClearFrameHistory(mLSHE);
10198     ClearFrameHistory(mOSHE);
10199   }
10200 
10201   // Clear subframe history on refresh.
10202   // XXX: history.go(0) won't go this path as aLoadType is LOAD_HISTORY in
10203   // this case. One should re-validate after bug 1331865 fixed.
10204   if (aLoadType == LOAD_REFRESH) {
10205     ClearFrameHistory(mLSHE);
10206     ClearFrameHistory(mOSHE);
10207   }
10208 
10209   if (updateSHistory) {
10210     // Update session history if necessary...
10211     if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
10212       /* This is  a fresh page getting loaded for the first time
10213        *.Create a Entry for it and add it to SH, if this is the
10214        * rootDocShell
10215        */
10216       (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
10217                                 aPrincipalToInherit, aStoragePrincipalToInherit,
10218                                 aCsp, aCloneSHChildren, getter_AddRefs(mLSHE));
10219     }
10220   } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
10221     // Even if we don't add anything to SHistory, ensure the current index
10222     // points to the same SHEntry as our mLSHE.
10223 
10224     GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(mLSHE);
10225   }
10226 
10227   // If this is a POST request, we do not want to include this in global
10228   // history.
10229   if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
10230       !net::ChannelIsPost(aChannel)) {
10231     nsCOMPtr<nsIURI> previousURI;
10232     uint32_t previousFlags = 0;
10233 
10234     if (aLoadType & LOAD_CMD_RELOAD) {
10235       // On a reload request, we don't set redirecting flags.
10236       previousURI = aURI;
10237     } else {
10238       ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
10239     }
10240 
10241     AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
10242   }
10243 
10244   // If this was a history load or a refresh, or it was a history load but
10245   // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
10246   // in session history.
10247   if (rootSH && ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
10248                  mLoadType == LOAD_NORMAL_REPLACE)) {
10249     mPreviousEntryIndex = rootSH->Index();
10250     rootSH->LegacySHistory()->UpdateIndex();
10251     mLoadedEntryIndex = rootSH->Index();
10252     MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
10253             ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
10254              mLoadedEntryIndex));
10255   }
10256 
10257   // aCloneSHChildren exactly means "we are not loading a new document".
10258   uint32_t locationFlags =
10259       aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
10260 
10261   bool onLocationChangeNeeded =
10262       SetCurrentURI(aURI, aChannel, aFireOnLocationChange, locationFlags);
10263   // Make sure to store the referrer from the channel, if any
10264   SetupReferrerInfoFromChannel(aChannel);
10265   return onLocationChangeNeeded;
10266 }
10267 
SetReferrerInfo(nsIReferrerInfo * aReferrerInfo)10268 void nsDocShell::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
10269   mReferrerInfo = aReferrerInfo;  // This assigment addrefs
10270 }
10271 
10272 //*****************************************************************************
10273 // nsDocShell: Session History
10274 //*****************************************************************************
10275 
10276 NS_IMETHODIMP
AddState(JS::Handle<JS::Value> aData,const nsAString & aTitle,const nsAString & aURL,bool aReplace,JSContext * aCx)10277 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
10278                      const nsAString& aURL, bool aReplace, JSContext* aCx) {
10279   // Implements History.pushState and History.replaceState
10280 
10281   // Here's what we do, roughly in the order specified by HTML5.  The specific
10282   // steps we are executing are at
10283   // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
10284   // and
10285   // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
10286   // This function basically implements #dom-history-pushstate and
10287   // UpdateURLAndHistory implements #url-and-history-update-steps.
10288   //
10289   // A. Serialize aData using structured clone.  This is #dom-history-pushstate
10290   //    step 5.
10291   // B. If the third argument is present, #dom-history-pushstate step 7.
10292   //     7.1. Resolve the url, relative to our document.
10293   //     7.2. If (a) fails, raise a SECURITY_ERR
10294   //     7.4. Compare the resulting absolute URL to the document's address.  If
10295   //          any part of the URLs difer other than the <path>, <query>, and
10296   //          <fragment> components, raise a SECURITY_ERR and abort.
10297   // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
10298   //     Remove from the session history all entries after the current entry,
10299   //     as we would after a regular navigation, and save the current
10300   //     entry's scroll position (bug 590573).
10301   // D. #url-and-history-update-steps step 2.4 or step 3.  As apropriate,
10302   //    either add a state object entry to the session history after the
10303   //    current entry with the following properties, or modify the current
10304   //    session history entry to set
10305   //      a. cloned data as the state object,
10306   //      b. if the third argument was present, the absolute URL found in
10307   //         step 2
10308   //    Also clear the new history entry's POST data (see bug 580069).
10309   // E. If aReplace is false (i.e. we're doing a pushState instead of a
10310   //    replaceState), notify bfcache that we've navigated to a new page.
10311   // F. If the third argument is present, set the document's current address
10312   //    to the absolute URL found in step B.  This is
10313   //    #url-and-history-update-steps step 4.
10314   //
10315   // It's important that this function not run arbitrary scripts after step A
10316   // and before completing step E.  For example, if a script called
10317   // history.back() before we completed step E, bfcache might destroy an
10318   // active content viewer.  Since EvictOutOfRangeContentViewers at the end of
10319   // step E might run script, we can't just put a script blocker around the
10320   // critical section.
10321   //
10322   // Note that we completely ignore the aTitle parameter.
10323 
10324   nsresult rv;
10325 
10326   // Don't clobber the load type of an existing network load.
10327   AutoRestore<uint32_t> loadTypeResetter(mLoadType);
10328 
10329   // pushState effectively becomes replaceState when we've started a network
10330   // load but haven't adopted its document yet.  This mirrors what we do with
10331   // changes to the hash at this stage of the game.
10332   if (JustStartedNetworkLoad()) {
10333     aReplace = true;
10334   }
10335 
10336   RefPtr<Document> document = GetDocument();
10337   NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
10338 
10339   // Step A: Serialize aData using structured clone.
10340   // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
10341   // step 5.
10342   nsCOMPtr<nsIStructuredCloneContainer> scContainer;
10343 
10344   // scContainer->Init might cause arbitrary JS to run, and this code might
10345   // navigate the page we're on, potentially to a different origin! (bug
10346   // 634834)  To protect against this, we abort if our principal changes due
10347   // to the InitFromJSVal() call.
10348   {
10349     RefPtr<Document> origDocument = GetDocument();
10350     if (!origDocument) {
10351       return NS_ERROR_DOM_SECURITY_ERR;
10352     }
10353     nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
10354 
10355     scContainer = new nsStructuredCloneContainer();
10356     rv = scContainer->InitFromJSVal(aData, aCx);
10357     NS_ENSURE_SUCCESS(rv, rv);
10358 
10359     RefPtr<Document> newDocument = GetDocument();
10360     if (!newDocument) {
10361       return NS_ERROR_DOM_SECURITY_ERR;
10362     }
10363     nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
10364 
10365     bool principalsEqual = false;
10366     origPrincipal->Equals(newPrincipal, &principalsEqual);
10367     NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
10368   }
10369 
10370   // Check that the state object isn't too long.
10371   // Default max length: 2097152 (0x200000) bytes.
10372   int32_t maxStateObjSize =
10373       Preferences::GetInt("browser.history.maxStateObjectSize", 2097152);
10374   if (maxStateObjSize < 0) {
10375     maxStateObjSize = 0;
10376   }
10377 
10378   uint64_t scSize;
10379   rv = scContainer->GetSerializedNBytes(&scSize);
10380   NS_ENSURE_SUCCESS(rv, rv);
10381 
10382   NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
10383 
10384   // Step B: Resolve aURL.
10385   // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
10386   // step 7.
10387   bool equalURIs = true;
10388   nsCOMPtr<nsIURI> currentURI;
10389   if (mCurrentURI) {
10390     currentURI = nsIOService::CreateExposableURI(mCurrentURI);
10391   } else {
10392     currentURI = mCurrentURI;
10393   }
10394   nsCOMPtr<nsIURI> newURI;
10395   if (aURL.Length() == 0) {
10396     newURI = currentURI;
10397   } else {
10398     // 7.1: Resolve aURL relative to mURI
10399 
10400     nsIURI* docBaseURI = document->GetDocBaseURI();
10401     if (!docBaseURI) {
10402       return NS_ERROR_FAILURE;
10403     }
10404 
10405     nsAutoCString spec;
10406     docBaseURI->GetSpec(spec);
10407 
10408     rv = NS_NewURI(getter_AddRefs(newURI), aURL,
10409                    document->GetDocumentCharacterSet(), docBaseURI);
10410 
10411     // 7.2: If 2a fails, raise a SECURITY_ERR
10412     if (NS_FAILED(rv)) {
10413       return NS_ERROR_DOM_SECURITY_ERR;
10414     }
10415 
10416     // 7.4 and 7.5: Same-origin check.
10417     if (!nsContentUtils::URIIsLocalFile(newURI)) {
10418       // In addition to checking that the security manager says that
10419       // the new URI has the same origin as our current URI, we also
10420       // check that the two URIs have the same userpass. (The
10421       // security manager says that |http://foo.com| and
10422       // |http://me@foo.com| have the same origin.)  currentURI
10423       // won't contain the password part of the userpass, so this
10424       // means that it's never valid to specify a password in a
10425       // pushState or replaceState URI.
10426 
10427       nsCOMPtr<nsIScriptSecurityManager> secMan =
10428           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10429       NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
10430 
10431       // It's very important that we check that newURI is of the same
10432       // origin as currentURI, not docBaseURI, because a page can
10433       // set docBaseURI arbitrarily to any domain.
10434       nsAutoCString currentUserPass, newUserPass;
10435       NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
10436                         NS_ERROR_FAILURE);
10437       NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
10438       bool isPrivateWin =
10439           document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
10440           0;
10441       if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
10442                                                isPrivateWin)) ||
10443           !currentUserPass.Equals(newUserPass)) {
10444         return NS_ERROR_DOM_SECURITY_ERR;
10445       }
10446     } else {
10447       // It's a file:// URI
10448       nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
10449 
10450       if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
10451                             newURI, false, document->InnerWindowID()))) {
10452         return NS_ERROR_DOM_SECURITY_ERR;
10453       }
10454     }
10455 
10456     if (currentURI) {
10457       currentURI->Equals(newURI, &equalURIs);
10458     } else {
10459       equalURIs = false;
10460     }
10461 
10462   }  // end of same-origin check
10463 
10464   // Step 8: call "URL and history update steps"
10465   rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
10466                            currentURI, equalURIs);
10467   NS_ENSURE_SUCCESS(rv, rv);
10468 
10469   return NS_OK;
10470 }
10471 
UpdateURLAndHistory(Document * aDocument,nsIURI * aNewURI,nsIStructuredCloneContainer * aData,const nsAString & aTitle,bool aReplace,nsIURI * aCurrentURI,bool aEqualURIs)10472 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
10473                                          nsIStructuredCloneContainer* aData,
10474                                          const nsAString& aTitle, bool aReplace,
10475                                          nsIURI* aCurrentURI, bool aEqualURIs) {
10476   // Implements
10477   // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
10478 
10479   // Step 2, if aReplace is false: Create a new entry in the session
10480   // history. This will erase all SHEntries after the new entry and make this
10481   // entry the current one.  This operation may modify mOSHE, which we need
10482   // later, so we keep a reference here.
10483   NS_ENSURE_TRUE(mOSHE || aReplace, NS_ERROR_FAILURE);
10484   nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
10485 
10486   mLoadType = LOAD_PUSHSTATE;
10487 
10488   nsCOMPtr<nsISHEntry> newSHEntry;
10489   if (!aReplace) {
10490     // Step 2.
10491 
10492     // Step 2.2, "Remove any tasks queued by the history traversal task
10493     // source..."
10494     RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
10495     if (shistory) {
10496       shistory->RemovePendingHistoryNavigations();
10497     }
10498 
10499     // Save the current scroll position (bug 590573).  Step 2.3.
10500     nsPoint scrollPos = GetCurScrollPos();
10501     mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
10502 
10503     bool scrollRestorationIsManual;
10504     nsresult rv =
10505         mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
10506     MOZ_ASSERT(NS_SUCCEEDED(rv), "Didn't expect this to fail.");
10507 
10508     nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
10509 
10510     // Since we're not changing which page we have loaded, pass
10511     // true for aCloneChildren.
10512     rv = AddToSessionHistory(aNewURI, nullptr,
10513                              aDocument->NodePrincipal(),  // triggeringPrincipal
10514                              nullptr, nullptr, csp, true,
10515                              getter_AddRefs(newSHEntry));
10516     NS_ENSURE_SUCCESS(rv, rv);
10517 
10518     NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
10519 
10520     // Session history entries created by pushState inherit scroll restoration
10521     // mode from the current entry.
10522     newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
10523 
10524     // Link the new SHEntry to the old SHEntry's BFCache entry, since the
10525     // two entries correspond to the same document.
10526     NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), NS_ERROR_FAILURE);
10527 
10528     // Set the new SHEntry's title (bug 655273).
10529     nsString title;
10530     mOSHE->GetTitle(title);
10531     newSHEntry->SetTitle(title);
10532 
10533     // AddToSessionHistory may not modify mOSHE.  In case it doesn't,
10534     // we'll just set mOSHE here.
10535     mOSHE = newSHEntry;
10536   } else {
10537     // Step 3.
10538     newSHEntry = mOSHE;
10539 
10540     // Since we're not changing which page we have loaded, pass
10541     // true for aCloneChildren.
10542     if (!newSHEntry) {
10543       nsresult rv = AddToSessionHistory(
10544           aNewURI, nullptr,
10545           aDocument->NodePrincipal(),  // triggeringPrincipal
10546           nullptr, nullptr, aDocument->GetCsp(), true,
10547           getter_AddRefs(newSHEntry));
10548       NS_ENSURE_SUCCESS(rv, rv);
10549       mOSHE = newSHEntry;
10550     }
10551     newSHEntry->SetURI(aNewURI);
10552     newSHEntry->SetOriginalURI(aNewURI);
10553     // Setting the resultPrincipalURI to nullptr is fine here: it will cause
10554     // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
10555     // in our case.  We could also set it to aNewURI, with the same result.
10556     newSHEntry->SetResultPrincipalURI(nullptr);
10557     newSHEntry->SetLoadReplace(false);
10558   }
10559 
10560   // Step 2.4 and 3: Modify new/original session history entry and clear its
10561   // POST data, if there is any.
10562   newSHEntry->SetStateData(aData);
10563   newSHEntry->SetPostData(nullptr);
10564 
10565   // If this push/replaceState changed the document's current URI and the new
10566   // URI differs from the old URI in more than the hash, or if the old
10567   // SHEntry's URI was modified in this way by a push/replaceState call
10568   // set URIWasModified to true for the current SHEntry (bug 669671).
10569   bool sameExceptHashes = true;
10570   aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
10571   bool oldURIWasModified = oldOSHE && oldOSHE->GetURIWasModified();
10572   newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
10573 
10574   // Step E as described at the top of AddState: If aReplace is false,
10575   // indicating that we're doing a pushState rather than a replaceState, notify
10576   // bfcache that we've added a page to the history so it can evict content
10577   // viewers if appropriate. Otherwise call ReplaceEntry so that we notify
10578   // nsIHistoryListeners that an entry was replaced.  We may not have a root
10579   // session history if this call is coming from a document.open() in a docshell
10580   // subtree that disables session history.
10581   RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
10582   if (rootSH) {
10583     rootSH->LegacySHistory()->EvictContentViewersOrReplaceEntry(newSHEntry,
10584                                                                 aReplace);
10585   }
10586 
10587   // Step 4: If the document's URI changed, update document's URI and update
10588   // global history.
10589   //
10590   // We need to call FireOnLocationChange so that the browser's address bar
10591   // gets updated and the back button is enabled, but we only need to
10592   // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
10593   // since SetCurrentURI will call FireOnLocationChange for us.
10594   //
10595   // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
10596   // nullptr for aRequest param to FireOnLocationChange(...). Such an update
10597   // notification is allowed only when we know docshell is not loading a new
10598   // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
10599   // FireOnLocationChange(...) breaks security UI.
10600   //
10601   // If the docshell is shutting down, don't update the document URI, as we
10602   // can't load into a docshell that is being destroyed.
10603   if (!aEqualURIs && !mIsBeingDestroyed) {
10604     aDocument->SetDocumentURI(aNewURI);
10605     // We can't trust SetCurrentURI to do always fire locationchange events
10606     // when we expect it to, so we hack around that by doing it ourselves...
10607     SetCurrentURI(aNewURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
10608     if (mLoadType != LOAD_ERROR_PAGE) {
10609       FireDummyOnLocationChange();
10610     }
10611 
10612     AddURIVisit(aNewURI, aCurrentURI, 0);
10613 
10614     // AddURIVisit doesn't set the title for the new URI in global history,
10615     // so do that here.
10616     UpdateGlobalHistoryTitle(aNewURI);
10617 
10618     // Inform the favicon service that our old favicon applies to this new
10619     // URI.
10620     CopyFavicon(aCurrentURI, aNewURI, aDocument->NodePrincipal(),
10621                 UsePrivateBrowsing());
10622   } else {
10623     FireDummyOnLocationChange();
10624   }
10625   aDocument->SetStateObject(aData);
10626 
10627   return NS_OK;
10628 }
10629 
10630 NS_IMETHODIMP
GetCurrentScrollRestorationIsManual(bool * aIsManual)10631 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
10632   *aIsManual = false;
10633   if (mOSHE) {
10634     return mOSHE->GetScrollRestorationIsManual(aIsManual);
10635   }
10636 
10637   return NS_OK;
10638 }
10639 
10640 NS_IMETHODIMP
SetCurrentScrollRestorationIsManual(bool aIsManual)10641 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
10642   if (mOSHE) {
10643     mOSHE->SetScrollRestorationIsManual(aIsManual);
10644   }
10645 
10646   return NS_OK;
10647 }
10648 
ShouldAddToSessionHistory(nsIURI * aURI,nsIChannel * aChannel)10649 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
10650   // I believe none of the about: urls should go in the history. But then
10651   // that could just be me... If the intent is only deny about:blank then we
10652   // should just do a spec compare, rather than two gets of the scheme and
10653   // then the path.  -Gagan
10654   nsresult rv;
10655   nsAutoCString buf;
10656 
10657   rv = aURI->GetScheme(buf);
10658   if (NS_FAILED(rv)) {
10659     return false;
10660   }
10661 
10662   if (buf.EqualsLiteral("about")) {
10663     rv = aURI->GetPathQueryRef(buf);
10664     if (NS_FAILED(rv)) {
10665       return false;
10666     }
10667 
10668     if (buf.EqualsLiteral("blank")) {
10669       return false;
10670     }
10671     // We only want to add about:newtab if it's not privileged:
10672     if (buf.EqualsLiteral("newtab")) {
10673       NS_ENSURE_TRUE(aChannel, false);
10674       nsCOMPtr<nsIPrincipal> resultPrincipal;
10675       rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10676           aChannel, getter_AddRefs(resultPrincipal));
10677       NS_ENSURE_SUCCESS(rv, false);
10678       return !resultPrincipal->IsSystemPrincipal();
10679     }
10680   }
10681 
10682   return true;
10683 }
10684 
AddToSessionHistory(nsIURI * aURI,nsIChannel * aChannel,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,nsIPrincipal * aStoragePrincipalToInherit,nsIContentSecurityPolicy * aCsp,bool aCloneChildren,nsISHEntry ** aNewEntry)10685 nsresult nsDocShell::AddToSessionHistory(
10686     nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
10687     nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aStoragePrincipalToInherit,
10688     nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
10689     nsISHEntry** aNewEntry) {
10690   MOZ_ASSERT(aURI, "uri is null");
10691   MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10692 
10693 #if defined(DEBUG)
10694   if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10695     nsAutoCString chanName;
10696     if (aChannel) {
10697       aChannel->GetName(chanName);
10698     } else {
10699       chanName.AssignLiteral("<no channel>");
10700     }
10701 
10702     MOZ_LOG(gDocShellLog, LogLevel::Debug,
10703             ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
10704              aURI->GetSpecOrDefault().get(), chanName.get()));
10705   }
10706 #endif
10707 
10708   nsresult rv = NS_OK;
10709   nsCOMPtr<nsISHEntry> entry;
10710 
10711   // Get a handle to the root docshell
10712   nsCOMPtr<nsIDocShellTreeItem> root;
10713   GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
10714   /*
10715    * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
10716    * the existing SH entry in the page and replace the url and
10717    * other vitalities.
10718    */
10719   if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
10720       root != static_cast<nsIDocShellTreeItem*>(this)) {
10721     // This is a subframe
10722     entry = mOSHE;
10723     if (entry) {
10724       entry->ClearEntry();
10725     }
10726   }
10727 
10728   // Create a new entry if necessary.
10729   if (!entry) {
10730     nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
10731     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
10732 
10733     RefPtr<ChildSHistory> shistory = webnav->GetSessionHistory();
10734     entry = new nsSHEntry(shistory ? shistory->LegacySHistory() : nullptr);
10735   }
10736 
10737   // Get the post data & referrer
10738   nsCOMPtr<nsIInputStream> inputStream;
10739   nsCOMPtr<nsIURI> originalURI;
10740   nsCOMPtr<nsIURI> resultPrincipalURI;
10741   bool loadReplace = false;
10742   nsCOMPtr<nsIReferrerInfo> referrerInfo;
10743   uint32_t cacheKey = 0;
10744   nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
10745   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
10746   nsCOMPtr<nsIPrincipal> storagePrincipalToInherit = aStoragePrincipalToInherit;
10747   nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
10748   bool expired = false;  // by default the page is not expired
10749   bool discardLayoutState = false;
10750   nsCOMPtr<nsICacheInfoChannel> cacheChannel;
10751   if (aChannel) {
10752     cacheChannel = do_QueryInterface(aChannel);
10753 
10754     /* If there is a caching channel, get the Cache Key and store it
10755      * in SH.
10756      */
10757     if (cacheChannel) {
10758       cacheChannel->GetCacheKey(&cacheKey);
10759     }
10760     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10761 
10762     // Check if the httpChannel is hiding under a multipartChannel
10763     if (!httpChannel) {
10764       GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10765     }
10766     if (httpChannel) {
10767       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10768       if (uploadChannel) {
10769         uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10770       }
10771       httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
10772       uint32_t loadFlags;
10773       aChannel->GetLoadFlags(&loadFlags);
10774       loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
10775       rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
10776       MOZ_ASSERT(NS_SUCCEEDED(rv));
10777 
10778       discardLayoutState = ShouldDiscardLayoutState(httpChannel);
10779     }
10780 
10781     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
10782     if (!triggeringPrincipal) {
10783       triggeringPrincipal = loadInfo->TriggeringPrincipal();
10784     }
10785     if (!csp) {
10786       csp = loadInfo->GetCspToInherit();
10787     }
10788 
10789     loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
10790 
10791     // For now keep storing just the principal in the SHEntry.
10792     if (!principalToInherit) {
10793       if (loadInfo->GetLoadingSandboxed()) {
10794         if (loadInfo->GetLoadingPrincipal()) {
10795           principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
10796               loadInfo->GetLoadingPrincipal());
10797         } else {
10798           // get the OriginAttributes
10799           OriginAttributes attrs;
10800           loadInfo->GetOriginAttributes(&attrs);
10801           principalToInherit = NullPrincipal::Create(attrs);
10802         }
10803       } else {
10804         principalToInherit = loadInfo->PrincipalToInherit();
10805       }
10806     }
10807 
10808     if (!storagePrincipalToInherit) {
10809       // XXXehsan is it correct to fall back to the principal to inherit in all
10810       // cases?  For example, what about the cases where we are using the load
10811       // info's principal to inherit?  Do we need to add a similar concept to
10812       // load info for storage principal?
10813       storagePrincipalToInherit = principalToInherit;
10814     }
10815   }
10816 
10817   nsAutoString srcdoc;
10818   bool srcdocEntry = false;
10819   nsCOMPtr<nsIURI> baseURI;
10820 
10821   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
10822   if (inStrmChan) {
10823     bool isSrcdocChannel;
10824     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
10825     if (isSrcdocChannel) {
10826       inStrmChan->GetSrcdocData(srcdoc);
10827       srcdocEntry = true;
10828       inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
10829     } else {
10830       srcdoc.SetIsVoid(true);
10831     }
10832   }
10833   /* If cache got a 'no-store', ask SH not to store
10834    * HistoryLayoutState. By default, SH will set this
10835    * flag to true and save HistoryLayoutState.
10836    */
10837   bool saveLayoutState = !discardLayoutState;
10838 
10839   if (cacheChannel) {
10840     // Check if the page has expired from cache
10841     uint32_t expTime = 0;
10842     cacheChannel->GetCacheTokenExpirationTime(&expTime);
10843     uint32_t now = PRTimeToSeconds(PR_Now());
10844     if (expTime <= now) {
10845       expired = true;
10846     }
10847   }
10848 
10849   // Title is set in nsDocShell::SetTitle()
10850   entry->Create(aURI,                 // uri
10851                 EmptyString(),        // Title
10852                 inputStream,          // Post data stream
10853                 cacheKey,             // CacheKey
10854                 mContentTypeHint,     // Content-type
10855                 triggeringPrincipal,  // Channel or provided principal
10856                 principalToInherit, storagePrincipalToInherit, csp, HistoryID(),
10857                 mDynamicallyCreated, originalURI, resultPrincipalURI,
10858                 loadReplace, referrerInfo, srcdoc, srcdocEntry, baseURI,
10859                 saveLayoutState, expired);
10860 
10861   if (root == static_cast<nsIDocShellTreeItem*>(this) && GetSessionHistory()) {
10862     bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
10863     Maybe<int32_t> previousEntryIndex;
10864     Maybe<int32_t> loadedEntryIndex;
10865     rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
10866         aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
10867         shouldPersist, &previousEntryIndex, &loadedEntryIndex);
10868 
10869     MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
10870     if (previousEntryIndex.isSome()) {
10871       mPreviousEntryIndex = previousEntryIndex.value();
10872     }
10873     if (loadedEntryIndex.isSome()) {
10874       mLoadedEntryIndex = loadedEntryIndex.value();
10875     }
10876   } else {
10877     // This is a subframe.
10878     if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
10879       rv = AddChildSHEntryToParent(entry, mChildOffset, aCloneChildren);
10880     }
10881   }
10882 
10883   // Return the new SH entry...
10884   if (aNewEntry) {
10885     *aNewEntry = nullptr;
10886     if (NS_SUCCEEDED(rv)) {
10887       entry.forget(aNewEntry);
10888     }
10889   }
10890 
10891   return rv;
10892 }
10893 
LoadHistoryEntry(nsISHEntry * aEntry,uint32_t aLoadType)10894 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) {
10895   if (!IsNavigationAllowed()) {
10896     return NS_OK;
10897   }
10898 
10899   NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
10900   nsresult rv;
10901   RefPtr<nsDocShellLoadState> loadState;
10902   rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
10903   NS_ENSURE_SUCCESS(rv, rv);
10904   // We are setting load type afterwards so we don't have to
10905   // send it in an IPC message
10906   loadState->SetLoadType(aLoadType);
10907 
10908   // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
10909   // that's the only thing holding a ref to aEntry that will cause aEntry to
10910   // die while we're loading it.  So hold a strong ref to aEntry here, just
10911   // in case.
10912   nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
10913 
10914   if (SchemeIsJavascript(loadState->URI())) {
10915     // We're loading a URL that will execute script from inside asyncOpen.
10916     // Replace the current document with about:blank now to prevent
10917     // anything from the current document from leaking into any JavaScript
10918     // code in the URL.
10919     // Don't cache the presentation if we're going to just reload the
10920     // current entry. Caching would lead to trying to save the different
10921     // content viewers in the same nsISHEntry object.
10922     rv = CreateAboutBlankContentViewer(loadState->PrincipalToInherit(),
10923                                        loadState->StoragePrincipalToInherit(),
10924                                        nullptr, nullptr, aEntry != mOSHE);
10925 
10926     if (NS_FAILED(rv)) {
10927       // The creation of the intermittent about:blank content
10928       // viewer failed for some reason (potentially because the
10929       // user prevented it). Interrupt the history load.
10930       return NS_OK;
10931     }
10932 
10933     if (!loadState->TriggeringPrincipal()) {
10934       // Ensure that we have a triggeringPrincipal.  Otherwise javascript:
10935       // URIs will pick it up from the about:blank page we just loaded,
10936       // and we don't really want even that in this case.
10937       nsCOMPtr<nsIPrincipal> principal =
10938           NullPrincipal::CreateWithInheritedAttributes(this);
10939       loadState->SetTriggeringPrincipal(principal);
10940     }
10941   }
10942 
10943   /* If there is a valid postdata *and* the user pressed
10944    * reload or shift-reload, take user's permission before we
10945    * repost the data to the server.
10946    */
10947   if ((aLoadType & LOAD_CMD_RELOAD) && loadState->PostDataStream()) {
10948     bool repost;
10949     rv = ConfirmRepost(&repost);
10950     if (NS_FAILED(rv)) {
10951       return rv;
10952     }
10953 
10954     // If the user pressed cancel in the dialog, return.  We're done here.
10955     if (!repost) {
10956       return NS_BINDING_ABORTED;
10957     }
10958   }
10959 
10960   // If there is no valid triggeringPrincipal, we deny the load
10961   MOZ_ASSERT(loadState->TriggeringPrincipal(),
10962              "need a valid triggeringPrincipal to load from history");
10963   if (!loadState->TriggeringPrincipal()) {
10964     return NS_ERROR_FAILURE;
10965   }
10966 
10967   rv = InternalLoad(loadState,
10968                     nullptr,   // No nsIDocShell
10969                     nullptr);  // No nsIRequest
10970   return rv;
10971 }
10972 
10973 NS_IMETHODIMP
PersistLayoutHistoryState()10974 nsDocShell::PersistLayoutHistoryState() {
10975   nsresult rv = NS_OK;
10976 
10977   if (mOSHE) {
10978     bool scrollRestorationIsManual;
10979     Unused << mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
10980     nsCOMPtr<nsILayoutHistoryState> layoutState;
10981     if (RefPtr<PresShell> presShell = GetPresShell()) {
10982       rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
10983     } else if (scrollRestorationIsManual) {
10984       // Even if we don't have layout anymore, we may want to reset the
10985       // current scroll state in layout history.
10986       GetLayoutHistoryState(getter_AddRefs(layoutState));
10987     }
10988 
10989     if (scrollRestorationIsManual && layoutState) {
10990       layoutState->ResetScrollState();
10991     }
10992   }
10993 
10994   return rv;
10995 }
10996 
SwapHistoryEntries(nsISHEntry * aOldEntry,nsISHEntry * aNewEntry)10997 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
10998                                     nsISHEntry* aNewEntry) {
10999   if (aOldEntry == mOSHE) {
11000     mOSHE = aNewEntry;
11001   }
11002 
11003   if (aOldEntry == mLSHE) {
11004     mLSHE = aNewEntry;
11005   }
11006 }
11007 
SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry * > & aLSHE,const Maybe<nsISHEntry * > & aOSHE)11008 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
11009                                             const Maybe<nsISHEntry*>& aOSHE) {
11010   // We want to hold on to the reference in mLSHE before we update it.
11011   // Otherwise, SetHistoryEntry could release the last reference to
11012   // the entry while aOSHE is pointing to it.
11013   nsCOMPtr<nsISHEntry> deathGripOldLSHE;
11014   if (aLSHE.isSome()) {
11015     deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
11016     MOZ_ASSERT(mLSHE.get() == aLSHE.value());
11017   }
11018   nsCOMPtr<nsISHEntry> deathGripOldOSHE;
11019   if (aOSHE.isSome()) {
11020     deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
11021     MOZ_ASSERT(mOSHE.get() == aOSHE.value());
11022   }
11023 }
11024 
SetHistoryEntry(nsCOMPtr<nsISHEntry> * aPtr,nsISHEntry * aEntry)11025 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
11026     nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
11027   // We need to sync up the docshell and session history trees for
11028   // subframe navigation.  If the load was in a subframe, we forward up to
11029   // the root docshell, which will then recursively sync up all docshells
11030   // to their corresponding entries in the new session history tree.
11031   // If we don't do this, then we can cache a content viewer on the wrong
11032   // cloned entry, and subsequently restore it at the wrong time.
11033   RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
11034   if (topBC->IsDiscarded()) {
11035     topBC = nullptr;
11036   }
11037   RefPtr<BrowsingContext> currBC =
11038       mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
11039   if (topBC && *aPtr) {
11040     (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
11041   }
11042   nsCOMPtr<nsISHEntry> entry(aEntry);
11043   entry.swap(*aPtr);
11044   return entry.forget();
11045 }
11046 
GetRootSessionHistory()11047 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
11048   nsCOMPtr<nsIDocShellTreeItem> root;
11049   nsresult rv = GetInProcessSameTypeRootTreeItem(getter_AddRefs(root));
11050   if (NS_WARN_IF(NS_FAILED(rv))) {
11051     return nullptr;
11052   }
11053   nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
11054   if (!webnav) {
11055     return nullptr;
11056   }
11057   return webnav->GetSessionHistory();
11058 }
11059 
GetHttpChannel(nsIChannel * aChannel,nsIHttpChannel ** aReturn)11060 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
11061                                     nsIHttpChannel** aReturn) {
11062   NS_ENSURE_ARG_POINTER(aReturn);
11063   if (!aChannel) {
11064     return NS_ERROR_FAILURE;
11065   }
11066 
11067   nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
11068   if (multiPartChannel) {
11069     nsCOMPtr<nsIChannel> baseChannel;
11070     multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
11071     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
11072     *aReturn = httpChannel;
11073     NS_IF_ADDREF(*aReturn);
11074   }
11075   return NS_OK;
11076 }
11077 
ShouldDiscardLayoutState(nsIHttpChannel * aChannel)11078 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
11079   // By default layout State will be saved.
11080   if (!aChannel) {
11081     return false;
11082   }
11083 
11084   // figure out if SH should be saving layout state
11085   bool noStore = false;
11086   Unused << aChannel->IsNoStoreResponse(&noStore);
11087   return noStore;
11088 }
11089 
11090 NS_IMETHODIMP
GetEditor(nsIEditor ** aEditor)11091 nsDocShell::GetEditor(nsIEditor** aEditor) {
11092   NS_ENSURE_ARG_POINTER(aEditor);
11093   RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
11094   htmlEditor.forget(aEditor);
11095   return NS_OK;
11096 }
11097 
11098 NS_IMETHODIMP
SetEditor(nsIEditor * aEditor)11099 nsDocShell::SetEditor(nsIEditor* aEditor) {
11100   HTMLEditor* htmlEditor = aEditor ? aEditor->AsHTMLEditor() : nullptr;
11101   // If TextEditor comes, throw an error.
11102   if (aEditor && !htmlEditor) {
11103     return NS_ERROR_INVALID_ARG;
11104   }
11105   return SetHTMLEditorInternal(htmlEditor);
11106 }
11107 
GetHTMLEditorInternal()11108 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
11109   return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
11110 }
11111 
SetHTMLEditorInternal(HTMLEditor * aHTMLEditor)11112 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
11113   if (!aHTMLEditor && !mEditorData) {
11114     return NS_OK;
11115   }
11116 
11117   nsresult rv = EnsureEditorData();
11118   if (NS_FAILED(rv)) {
11119     return rv;
11120   }
11121 
11122   return mEditorData->SetHTMLEditor(aHTMLEditor);
11123 }
11124 
11125 NS_IMETHODIMP
GetEditable(bool * aEditable)11126 nsDocShell::GetEditable(bool* aEditable) {
11127   NS_ENSURE_ARG_POINTER(aEditable);
11128   *aEditable = mEditorData && mEditorData->GetEditable();
11129   return NS_OK;
11130 }
11131 
11132 NS_IMETHODIMP
GetHasEditingSession(bool * aHasEditingSession)11133 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
11134   NS_ENSURE_ARG_POINTER(aHasEditingSession);
11135 
11136   if (mEditorData) {
11137     *aHasEditingSession = !!mEditorData->GetEditingSession();
11138   } else {
11139     *aHasEditingSession = false;
11140   }
11141 
11142   return NS_OK;
11143 }
11144 
11145 NS_IMETHODIMP
MakeEditable(bool aInWaitForUriLoad)11146 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
11147   nsresult rv = EnsureEditorData();
11148   if (NS_FAILED(rv)) {
11149     return rv;
11150   }
11151 
11152   return mEditorData->MakeEditable(aInWaitForUriLoad);
11153 }
11154 
ShouldAddURIVisit(nsIChannel * aChannel)11155 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
11156   bool needToAddURIVisit = true;
11157   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
11158   if (props) {
11159     mozilla::Unused << props->GetPropertyAsBool(
11160         NS_LITERAL_STRING("docshell.needToAddURIVisit"), &needToAddURIVisit);
11161   }
11162 
11163   return needToAddURIVisit;
11164 }
11165 
ExtractLastVisit(nsIChannel * aChannel,nsIURI ** aURI,uint32_t * aChannelRedirectFlags)11166 /* static */ void nsDocShell::ExtractLastVisit(
11167     nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
11168   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
11169   if (!props) {
11170     return;
11171   }
11172 
11173   nsresult rv = props->GetPropertyAsInterface(
11174       NS_LITERAL_STRING("docshell.previousURI"), NS_GET_IID(nsIURI),
11175       reinterpret_cast<void**>(aURI));
11176 
11177   if (NS_FAILED(rv)) {
11178     // There is no last visit for this channel, so this must be the first
11179     // link.  Link the visit to the referrer of this request, if any.
11180     // Treat referrer as null if there is an error getting it.
11181     (void)NS_GetReferrerFromChannel(aChannel, aURI);
11182   } else {
11183     rv = props->GetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
11184                                     aChannelRedirectFlags);
11185 
11186     NS_WARNING_ASSERTION(
11187         NS_SUCCEEDED(rv),
11188         "Could not fetch previous flags, URI will be treated like referrer");
11189   }
11190 }
11191 
SaveLastVisit(nsIChannel * aChannel,nsIURI * aURI,uint32_t aChannelRedirectFlags)11192 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
11193                                uint32_t aChannelRedirectFlags) {
11194   nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
11195   if (!props || !aURI) {
11196     return;
11197   }
11198 
11199   props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
11200                                 aURI);
11201   props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
11202                              aChannelRedirectFlags);
11203 }
11204 
InternalAddURIVisit(nsIURI * aURI,nsIURI * aPreviousURI,uint32_t aChannelRedirectFlags,uint32_t aResponseStatus,BrowsingContext * aBrowsingContext,nsIWidget * aWidget,uint32_t aLoadType)11205 /* static */ void nsDocShell::InternalAddURIVisit(
11206     nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
11207     uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
11208     nsIWidget* aWidget, uint32_t aLoadType) {
11209   MOZ_ASSERT(aURI, "Visited URI is null!");
11210   MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
11211              "Do not add error or bypass pages to global history");
11212 
11213   bool usePrivateBrowsing = false;
11214   aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
11215 
11216   // Only content-type docshells save URI visits.  Also don't do
11217   // anything here if we're not supposed to use global history.
11218   if (!aBrowsingContext->IsContent() ||
11219       !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
11220     return;
11221   }
11222 
11223   nsCOMPtr<IHistory> history = services::GetHistoryService();
11224 
11225   if (history) {
11226     uint32_t visitURIFlags = 0;
11227 
11228     if (aBrowsingContext->IsTop()) {
11229       visitURIFlags |= IHistory::TOP_LEVEL;
11230     }
11231 
11232     if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
11233       visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
11234     } else if (aChannelRedirectFlags &
11235                nsIChannelEventSink::REDIRECT_PERMANENT) {
11236       visitURIFlags |= IHistory::REDIRECT_PERMANENT;
11237     } else {
11238       MOZ_ASSERT(!aChannelRedirectFlags,
11239                  "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
11240                  "if any flags in aChannelRedirectFlags is set.");
11241     }
11242 
11243     if (aResponseStatus >= 300 && aResponseStatus < 400) {
11244       visitURIFlags |= IHistory::REDIRECT_SOURCE;
11245       if (aResponseStatus == 301 || aResponseStatus == 308) {
11246         visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
11247       }
11248     }
11249     // Errors 400-501 and 505 are considered unrecoverable, in the sense a
11250     // simple retry attempt by the user is unlikely to solve them.
11251     // 408 is special cased, since may actually indicate a temporary
11252     // connection problem.
11253     else if (aResponseStatus != 408 &&
11254              ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
11255               aResponseStatus == 505)) {
11256       visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
11257     }
11258 
11259     mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
11260                                          visitURIFlags);
11261   }
11262 }
11263 
AddURIVisit(nsIURI * aURI,nsIURI * aPreviousURI,uint32_t aChannelRedirectFlags,uint32_t aResponseStatus)11264 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
11265                              uint32_t aChannelRedirectFlags,
11266                              uint32_t aResponseStatus) {
11267   nsPIDOMWindowOuter* outer = GetWindow();
11268   nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
11269 
11270   InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
11271                       aResponseStatus, mBrowsingContext, widget, mLoadType);
11272 }
11273 
11274 //*****************************************************************************
11275 // nsDocShell: Helper Routines
11276 //*****************************************************************************
11277 
11278 NS_IMETHODIMP
SetLoadType(uint32_t aLoadType)11279 nsDocShell::SetLoadType(uint32_t aLoadType) {
11280   mLoadType = aLoadType;
11281   return NS_OK;
11282 }
11283 
11284 NS_IMETHODIMP
GetLoadType(uint32_t * aLoadType)11285 nsDocShell::GetLoadType(uint32_t* aLoadType) {
11286   *aLoadType = mLoadType;
11287   return NS_OK;
11288 }
11289 
ConfirmRepost(bool * aRepost)11290 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
11291   if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
11292     *aRepost = true;
11293     return NS_OK;
11294   }
11295 
11296   nsCOMPtr<nsIPrompt> prompter;
11297   CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
11298   if (!prompter) {
11299     return NS_ERROR_NOT_AVAILABLE;
11300   }
11301 
11302   nsCOMPtr<nsIStringBundleService> stringBundleService =
11303       mozilla::services::GetStringBundleService();
11304   if (!stringBundleService) {
11305     return NS_ERROR_FAILURE;
11306   }
11307 
11308   nsCOMPtr<nsIStringBundle> appBundle;
11309   nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
11310                                                   getter_AddRefs(appBundle));
11311   NS_ENSURE_SUCCESS(rv, rv);
11312 
11313   nsCOMPtr<nsIStringBundle> brandBundle;
11314   rv = stringBundleService->CreateBundle(kBrandBundleURL,
11315                                          getter_AddRefs(brandBundle));
11316   NS_ENSURE_SUCCESS(rv, rv);
11317 
11318   NS_ASSERTION(prompter && brandBundle && appBundle,
11319                "Unable to set up repost prompter.");
11320 
11321   AutoTArray<nsString, 1> formatStrings;
11322   rv = brandBundle->GetStringFromName("brandShortName",
11323                                       *formatStrings.AppendElement());
11324 
11325   nsAutoString msgString, button0Title;
11326   if (NS_FAILED(rv)) {  // No brand, use the generic version.
11327     rv = appBundle->GetStringFromName("confirmRepostPrompt", msgString);
11328   } else {
11329     // Brand available - if the app has an override file with formatting, the
11330     // app name will be included. Without an override, the prompt will look
11331     // like the generic version.
11332     rv = appBundle->FormatStringFromName("confirmRepostPrompt", formatStrings,
11333                                          msgString);
11334   }
11335   if (NS_FAILED(rv)) {
11336     return rv;
11337   }
11338 
11339   rv = appBundle->GetStringFromName("resendButton.label", button0Title);
11340   if (NS_FAILED(rv)) {
11341     return rv;
11342   }
11343 
11344   // Make the repost prompt content modal to prevent malicious pages from
11345   // locking up the browser, see bug 1412559 for an example.
11346   if (nsCOMPtr<nsIWritablePropertyBag2> promptBag =
11347           do_QueryInterface(prompter)) {
11348     promptBag->SetPropertyAsUint32(NS_LITERAL_STRING("modalType"),
11349                                    nsIPrompt::MODAL_TYPE_CONTENT);
11350   }
11351 
11352   int32_t buttonPressed;
11353   // The actual value here is irrelevant, but we can't pass an invalid
11354   // bool through XPConnect.
11355   bool checkState = false;
11356   rv = prompter->ConfirmEx(
11357       nullptr, msgString.get(),
11358       (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
11359           (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
11360       button0Title.get(), nullptr, nullptr, nullptr, &checkState,
11361       &buttonPressed);
11362   if (NS_FAILED(rv)) {
11363     return rv;
11364   }
11365 
11366   *aRepost = (buttonPressed == 0);
11367   return NS_OK;
11368 }
11369 
GetPromptAndStringBundle(nsIPrompt ** aPrompt,nsIStringBundle ** aStringBundle)11370 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
11371                                               nsIStringBundle** aStringBundle) {
11372   NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
11373                     NS_ERROR_FAILURE);
11374 
11375   nsCOMPtr<nsIStringBundleService> stringBundleService =
11376       mozilla::services::GetStringBundleService();
11377   NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
11378 
11379   NS_ENSURE_SUCCESS(
11380       stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
11381       NS_ERROR_FAILURE);
11382 
11383   return NS_OK;
11384 }
11385 
GetRootScrollFrame()11386 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
11387   PresShell* presShell = GetPresShell();
11388   NS_ENSURE_TRUE(presShell, nullptr);
11389 
11390   return presShell->GetRootScrollFrameAsScrollable();
11391 }
11392 
EnsureScriptEnvironment()11393 nsresult nsDocShell::EnsureScriptEnvironment() {
11394   if (mScriptGlobal) {
11395     return NS_OK;
11396   }
11397 
11398   if (mIsBeingDestroyed) {
11399     return NS_ERROR_NOT_AVAILABLE;
11400   }
11401 
11402 #ifdef DEBUG
11403   NS_ASSERTION(!mInEnsureScriptEnv,
11404                "Infinite loop! Calling EnsureScriptEnvironment() from "
11405                "within EnsureScriptEnvironment()!");
11406 
11407   // Yeah, this isn't re-entrant safe, but that's ok since if we
11408   // re-enter this method, we'll infinitely loop...
11409   AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
11410   mInEnsureScriptEnv = true;
11411 #endif
11412 
11413   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
11414   NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
11415 
11416   uint32_t chromeFlags;
11417   browserChrome->GetChromeFlags(&chromeFlags);
11418 
11419   // If our window is modal and we're not opened as chrome, make
11420   // this window a modal content window.
11421   mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
11422   MOZ_ASSERT(mScriptGlobal);
11423 
11424   // Ensure the script object is set up to run script.
11425   return mScriptGlobal->EnsureScriptEnvironment();
11426 }
11427 
EnsureEditorData()11428 nsresult nsDocShell::EnsureEditorData() {
11429   MOZ_ASSERT(!mIsBeingDestroyed);
11430 
11431   bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
11432   if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
11433     // We shouldn't recreate the editor data if it already exists, or
11434     // we're shutting down, or we already have a detached editor data
11435     // stored in the session history. We should only have one editordata
11436     // per docshell.
11437     mEditorData = MakeUnique<nsDocShellEditorData>(this);
11438   }
11439 
11440   return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
11441 }
11442 
EnsureFind()11443 nsresult nsDocShell::EnsureFind() {
11444   if (!mFind) {
11445     mFind = new nsWebBrowserFind();
11446   }
11447 
11448   // we promise that the nsIWebBrowserFind that we return has been set
11449   // up to point to the focused, or content window, so we have to
11450   // set that up each time.
11451 
11452   nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
11453   NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
11454 
11455   // default to our window
11456   nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
11457   nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
11458   nsFocusManager::GetFocusedDescendant(ourWindow,
11459                                        nsFocusManager::eIncludeAllDescendants,
11460                                        getter_AddRefs(windowToSearch));
11461 
11462   nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
11463   if (!findInFrames) {
11464     return NS_ERROR_NO_INTERFACE;
11465   }
11466 
11467   nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
11468   if (NS_FAILED(rv)) {
11469     return rv;
11470   }
11471   rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
11472   if (NS_FAILED(rv)) {
11473     return rv;
11474   }
11475 
11476   return NS_OK;
11477 }
11478 
11479 NS_IMETHODIMP
IsBeingDestroyed(bool * aDoomed)11480 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
11481   NS_ENSURE_ARG(aDoomed);
11482   *aDoomed = mIsBeingDestroyed;
11483   return NS_OK;
11484 }
11485 
11486 NS_IMETHODIMP
GetIsExecutingOnLoadHandler(bool * aResult)11487 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
11488   NS_ENSURE_ARG(aResult);
11489   *aResult = mIsExecutingOnLoadHandler;
11490   return NS_OK;
11491 }
11492 
11493 NS_IMETHODIMP
GetLayoutHistoryState(nsILayoutHistoryState ** aLayoutHistoryState)11494 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
11495   if (mOSHE) {
11496     nsCOMPtr<nsILayoutHistoryState> state = mOSHE->GetLayoutHistoryState();
11497     state.forget(aLayoutHistoryState);
11498   }
11499   return NS_OK;
11500 }
11501 
11502 NS_IMETHODIMP
SetLayoutHistoryState(nsILayoutHistoryState * aLayoutHistoryState)11503 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
11504   if (mOSHE) {
11505     mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
11506   }
11507   return NS_OK;
11508 }
11509 
InterfaceRequestorProxy(nsIInterfaceRequestor * aRequestor)11510 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
11511     nsIInterfaceRequestor* aRequestor) {
11512   if (aRequestor) {
11513     mWeakPtr = do_GetWeakReference(aRequestor);
11514   }
11515 }
11516 
~InterfaceRequestorProxy()11517 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
11518   mWeakPtr = nullptr;
11519 }
11520 
NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy,nsIInterfaceRequestor)11521 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
11522 
11523 NS_IMETHODIMP
11524 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
11525                                                   void** aSink) {
11526   NS_ENSURE_ARG_POINTER(aSink);
11527   nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
11528   if (ifReq) {
11529     return ifReq->GetInterface(aIID, aSink);
11530   }
11531   *aSink = nullptr;
11532   return NS_NOINTERFACE;
11533 }
11534 
11535 //*****************************************************************************
11536 // nsDocShell::nsIAuthPromptProvider
11537 //*****************************************************************************
11538 
11539 NS_IMETHODIMP
GetAuthPrompt(uint32_t aPromptReason,const nsIID & aIID,void ** aResult)11540 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
11541                           void** aResult) {
11542   // a priority prompt request will override a false mAllowAuth setting
11543   bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
11544 
11545   if (!mAllowAuth && !priorityPrompt) {
11546     return NS_ERROR_NOT_AVAILABLE;
11547   }
11548 
11549   // we're either allowing auth, or it's a proxy request
11550   nsresult rv;
11551   nsCOMPtr<nsIPromptFactory> wwatch =
11552       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
11553   NS_ENSURE_SUCCESS(rv, rv);
11554 
11555   rv = EnsureScriptEnvironment();
11556   NS_ENSURE_SUCCESS(rv, rv);
11557 
11558   // Get the an auth prompter for our window so that the parenting
11559   // of the dialogs works as it should when using tabs.
11560 
11561   return wwatch->GetPrompt(mScriptGlobal, aIID,
11562                            reinterpret_cast<void**>(aResult));
11563 }
11564 
11565 //*****************************************************************************
11566 // nsDocShell::nsILoadContext
11567 //*****************************************************************************
11568 
11569 NS_IMETHODIMP
GetAssociatedWindow(mozIDOMWindowProxy ** aWindow)11570 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
11571   CallGetInterface(this, aWindow);
11572   return NS_OK;
11573 }
11574 
11575 NS_IMETHODIMP
GetTopWindow(mozIDOMWindowProxy ** aWindow)11576 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
11577   return mBrowsingContext->GetTopWindow(aWindow);
11578 }
11579 
11580 NS_IMETHODIMP
GetTopFrameElement(Element ** aElement)11581 nsDocShell::GetTopFrameElement(Element** aElement) {
11582   return mBrowsingContext->GetTopFrameElement(aElement);
11583 }
11584 
11585 NS_IMETHODIMP
GetUseTrackingProtection(bool * aUseTrackingProtection)11586 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
11587   return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
11588 }
11589 
11590 NS_IMETHODIMP
SetUseTrackingProtection(bool aUseTrackingProtection)11591 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
11592   return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
11593 }
11594 
11595 NS_IMETHODIMP
GetIsContent(bool * aIsContent)11596 nsDocShell::GetIsContent(bool* aIsContent) {
11597   *aIsContent = (mItemType == typeContent);
11598   return NS_OK;
11599 }
11600 
IsOKToLoadURI(nsIURI * aURI)11601 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
11602   MOZ_ASSERT(aURI, "Must have a URI!");
11603 
11604   if (!mFiredUnloadEvent) {
11605     return true;
11606   }
11607 
11608   if (!mLoadingURI) {
11609     return false;
11610   }
11611 
11612   bool isPrivateWin = false;
11613   Document* doc = GetDocument();
11614   if (doc) {
11615     isPrivateWin =
11616         doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
11617   }
11618 
11619   nsCOMPtr<nsIScriptSecurityManager> secMan =
11620       do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11621   return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
11622                        aURI, mLoadingURI, false, isPrivateWin));
11623 }
11624 
11625 //
11626 // Routines for selection and clipboard
11627 //
GetControllerForCommand(const char * aCommand,nsIController ** aResult)11628 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
11629                                              nsIController** aResult) {
11630   NS_ENSURE_ARG_POINTER(aResult);
11631   *aResult = nullptr;
11632 
11633   NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
11634 
11635   nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
11636   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
11637 
11638   return root->GetControllerForCommand(aCommand, false /* for any window */,
11639                                        aResult);
11640 }
11641 
11642 NS_IMETHODIMP
IsCommandEnabled(const char * aCommand,bool * aResult)11643 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
11644   NS_ENSURE_ARG_POINTER(aResult);
11645   *aResult = false;
11646 
11647   nsresult rv = NS_ERROR_FAILURE;
11648 
11649   nsCOMPtr<nsIController> controller;
11650   rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
11651   if (controller) {
11652     rv = controller->IsCommandEnabled(aCommand, aResult);
11653   }
11654 
11655   return rv;
11656 }
11657 
11658 NS_IMETHODIMP
DoCommand(const char * aCommand)11659 nsDocShell::DoCommand(const char* aCommand) {
11660   nsresult rv = NS_ERROR_FAILURE;
11661 
11662   nsCOMPtr<nsIController> controller;
11663   rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
11664   if (controller) {
11665     rv = controller->DoCommand(aCommand);
11666   }
11667 
11668   return rv;
11669 }
11670 
11671 NS_IMETHODIMP
DoCommandWithParams(const char * aCommand,nsICommandParams * aParams)11672 nsDocShell::DoCommandWithParams(const char* aCommand,
11673                                 nsICommandParams* aParams) {
11674   nsCOMPtr<nsIController> controller;
11675   nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
11676   if (NS_WARN_IF(NS_FAILED(rv))) {
11677     return rv;
11678   }
11679 
11680   nsCOMPtr<nsICommandController> commandController =
11681       do_QueryInterface(controller, &rv);
11682   if (NS_WARN_IF(NS_FAILED(rv))) {
11683     return rv;
11684   }
11685 
11686   return commandController->DoCommandWithParams(aCommand, aParams);
11687 }
11688 
EnsureCommandHandler()11689 nsresult nsDocShell::EnsureCommandHandler() {
11690   if (!mCommandManager) {
11691     if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
11692       mCommandManager = new nsCommandManager(domWindow);
11693     }
11694   }
11695   return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
11696 }
11697 
11698 // link handling
11699 
11700 class OnLinkClickEvent : public Runnable {
11701  public:
11702   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent, nsIURI* aURI,
11703                    const nsAString& aTargetSpec, const nsAString& aFileName,
11704                    nsIInputStream* aPostDataStream,
11705                    nsIInputStream* aHeadersDataStream, bool aNoOpenerImplied,
11706                    bool aIsUserTriggered, bool aIsTrusted,
11707                    nsIPrincipal* aTriggeringPrincipal,
11708                    nsIContentSecurityPolicy* aCsp);
11709 
Run()11710   NS_IMETHOD Run() override {
11711     AutoPopupStatePusher popupStatePusher(mPopupState);
11712 
11713     // We need to set up an AutoJSAPI here for the following reason: When we
11714     // do OnLinkClickSync we'll eventually end up in
11715     // nsGlobalWindow::OpenInternal which only does popup blocking if
11716     // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
11717     // we don't look like native code as far as LegacyIsCallerNativeCode() is
11718     // concerned.
11719     AutoJSAPI jsapi;
11720     if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
11721       mHandler->OnLinkClickSync(mContent, mURI, mTargetSpec, mFileName,
11722                                 mPostDataStream, mHeadersDataStream,
11723                                 mNoOpenerImplied, nullptr, nullptr,
11724                                 mIsUserTriggered, mTriggeringPrincipal, mCsp);
11725     }
11726     return NS_OK;
11727   }
11728 
11729  private:
11730   RefPtr<nsDocShell> mHandler;
11731   nsCOMPtr<nsIURI> mURI;
11732   nsString mTargetSpec;
11733   nsString mFileName;
11734   nsCOMPtr<nsIInputStream> mPostDataStream;
11735   nsCOMPtr<nsIInputStream> mHeadersDataStream;
11736   nsCOMPtr<nsIContent> mContent;
11737   PopupBlocker::PopupControlState mPopupState;
11738   bool mNoOpenerImplied;
11739   bool mIsUserTriggered;
11740   bool mIsTrusted;
11741   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
11742   nsCOMPtr<nsIContentSecurityPolicy> mCsp;
11743 };
11744 
OnLinkClickEvent(nsDocShell * aHandler,nsIContent * aContent,nsIURI * aURI,const nsAString & aTargetSpec,const nsAString & aFileName,nsIInputStream * aPostDataStream,nsIInputStream * aHeadersDataStream,bool aNoOpenerImplied,bool aIsUserTriggered,bool aIsTrusted,nsIPrincipal * aTriggeringPrincipal,nsIContentSecurityPolicy * aCsp)11745 OnLinkClickEvent::OnLinkClickEvent(
11746     nsDocShell* aHandler, nsIContent* aContent, nsIURI* aURI,
11747     const nsAString& aTargetSpec, const nsAString& aFileName,
11748     nsIInputStream* aPostDataStream, nsIInputStream* aHeadersDataStream,
11749     bool aNoOpenerImplied, bool aIsUserTriggered, bool aIsTrusted,
11750     nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp)
11751     : mozilla::Runnable("OnLinkClickEvent"),
11752       mHandler(aHandler),
11753       mURI(aURI),
11754       mTargetSpec(aTargetSpec),
11755       mFileName(aFileName),
11756       mPostDataStream(aPostDataStream),
11757       mHeadersDataStream(aHeadersDataStream),
11758       mContent(aContent),
11759       mPopupState(PopupBlocker::GetPopupControlState()),
11760       mNoOpenerImplied(aNoOpenerImplied),
11761       mIsUserTriggered(aIsUserTriggered),
11762       mIsTrusted(aIsTrusted),
11763       mTriggeringPrincipal(aTriggeringPrincipal),
11764       mCsp(aCsp) {}
11765 
OnLinkClick(nsIContent * aContent,nsIURI * aURI,const nsAString & aTargetSpec,const nsAString & aFileName,nsIInputStream * aPostDataStream,nsIInputStream * aHeadersDataStream,bool aIsUserTriggered,bool aIsTrusted,nsIPrincipal * aTriggeringPrincipal,nsIContentSecurityPolicy * aCsp)11766 nsresult nsDocShell::OnLinkClick(
11767     nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
11768     const nsAString& aFileName, nsIInputStream* aPostDataStream,
11769     nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
11770     nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
11771 #ifndef ANDROID
11772   MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
11773 #endif
11774   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
11775 
11776   if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
11777     return NS_OK;
11778   }
11779 
11780   // On history navigation through Back/Forward buttons, don't execute
11781   // automatic JavaScript redirection such as |anchorElement.click()| or
11782   // |formElement.submit()|.
11783   //
11784   // XXX |formElement.submit()| bypasses this checkpoint because it calls
11785   //     nsDocShell::OnLinkClickSync(...) instead.
11786   if (ShouldBlockLoadingForBackButton()) {
11787     return NS_OK;
11788   }
11789 
11790   if (aContent->IsEditable()) {
11791     return NS_OK;
11792   }
11793 
11794   nsresult rv = NS_ERROR_FAILURE;
11795   nsAutoString target;
11796 
11797   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
11798   bool noOpenerImplied = false;
11799   if (browserChrome3) {
11800     rv = browserChrome3->OnBeforeLinkTraversal(aTargetSpec, aURI, aContent,
11801                                                mIsAppTab, target);
11802     if (!aTargetSpec.Equals(target)) {
11803       noOpenerImplied = true;
11804     }
11805   }
11806 
11807   if (NS_FAILED(rv)) {
11808     target = aTargetSpec;
11809   }
11810 
11811   nsCOMPtr<nsIRunnable> ev = new OnLinkClickEvent(
11812       this, aContent, aURI, target, aFileName, aPostDataStream,
11813       aHeadersDataStream, noOpenerImplied, aIsUserTriggered, aIsTrusted,
11814       aTriggeringPrincipal, aCsp);
11815   return Dispatch(TaskCategory::UI, ev.forget());
11816 }
11817 
IsElementAnchorOrArea(nsIContent * aContent)11818 static bool IsElementAnchorOrArea(nsIContent* aContent) {
11819   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
11820   // or XHTML namespace.
11821   return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
11822 }
11823 
OnLinkClickSync(nsIContent * aContent,nsIURI * aURI,const nsAString & aTargetSpec,const nsAString & aFileName,nsIInputStream * aPostDataStream,nsIInputStream * aHeadersDataStream,bool aNoOpenerImplied,nsIDocShell ** aDocShell,nsIRequest ** aRequest,bool aIsUserTriggered,nsIPrincipal * aTriggeringPrincipal,nsIContentSecurityPolicy * aCsp)11824 nsresult nsDocShell::OnLinkClickSync(
11825     nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
11826     const nsAString& aFileName, nsIInputStream* aPostDataStream,
11827     nsIInputStream* aHeadersDataStream, bool aNoOpenerImplied,
11828     nsIDocShell** aDocShell, nsIRequest** aRequest, bool aIsUserTriggered,
11829     nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
11830   // Initialize the DocShell / Request
11831   if (aDocShell) {
11832     *aDocShell = nullptr;
11833   }
11834   if (aRequest) {
11835     *aRequest = nullptr;
11836   }
11837 
11838   if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
11839     return NS_OK;
11840   }
11841 
11842   // XXX When the linking node was HTMLFormElement, it is synchronous event.
11843   //     That is, the caller of this method is not |OnLinkClickEvent::Run()|
11844   //     but |HTMLFormElement::SubmitSubmission(...)|.
11845   if (aContent->IsHTMLElement(nsGkAtoms::form) &&
11846       ShouldBlockLoadingForBackButton()) {
11847     return NS_OK;
11848   }
11849 
11850   if (aContent->IsEditable()) {
11851     return NS_OK;
11852   }
11853 
11854   // if the triggeringPrincipal is not passed explicitly, then we
11855   // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
11856   nsCOMPtr<nsIPrincipal> triggeringPrincipal =
11857       aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
11858 
11859   {
11860     // defer to an external protocol handler if necessary...
11861     nsCOMPtr<nsIExternalProtocolService> extProtService =
11862         do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
11863     if (extProtService) {
11864       nsAutoCString scheme;
11865       aURI->GetScheme(scheme);
11866       if (!scheme.IsEmpty()) {
11867         // if the URL scheme does not correspond to an exposed protocol, then
11868         // we need to hand this link click over to the external protocol
11869         // handler.
11870         bool isExposed;
11871         nsresult rv =
11872             extProtService->IsExposedProtocol(scheme.get(), &isExposed);
11873         if (NS_SUCCEEDED(rv) && !isExposed) {
11874           return extProtService->LoadURI(aURI, triggeringPrincipal,
11875                                          mBrowsingContext);
11876         }
11877       }
11878     }
11879   }
11880 
11881   nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11882   if (!csp) {
11883     // Currently, if no csp is passed explicitly we fall back to querying the
11884     // CSP from the document.
11885     csp = aContent->GetCsp();
11886   }
11887 
11888   uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
11889   bool isElementAnchorOrArea = IsElementAnchorOrArea(aContent);
11890   if (isElementAnchorOrArea) {
11891     MOZ_ASSERT(aContent->IsHTMLElement());
11892     nsAutoString relString;
11893     aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel,
11894                                    relString);
11895     nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
11896         relString);
11897 
11898     bool targetBlank = aTargetSpec.LowerCaseEqualsLiteral("_blank");
11899     bool explicitOpenerSet = false;
11900 
11901     // The opener behaviour follows a hierarchy, such that if a higher
11902     // priority behaviour is specified, it always takes priority. That
11903     // priority is currently: norefrerer > noopener > opener > default
11904 
11905     while (tok.hasMoreTokens()) {
11906       const nsAString& token = tok.nextToken();
11907       if (token.LowerCaseEqualsLiteral("noreferrer")) {
11908         flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
11909                  INTERNAL_LOAD_FLAGS_NO_OPENER;
11910         // noreferrer cannot be overwritten by a 'rel=opener'.
11911         explicitOpenerSet = true;
11912         break;
11913       }
11914 
11915       if (token.LowerCaseEqualsLiteral("noopener")) {
11916         flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
11917         explicitOpenerSet = true;
11918       }
11919 
11920       if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
11921           token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
11922         explicitOpenerSet = true;
11923       }
11924     }
11925 
11926     if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
11927         !explicitOpenerSet && !triggeringPrincipal->IsSystemPrincipal()) {
11928       flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
11929     }
11930 
11931     if (aNoOpenerImplied) {
11932       flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
11933     }
11934   }
11935 
11936   // Get the owner document of the link that was clicked, this will be
11937   // the document that the link is in, or the last document that the
11938   // link was in. From that document, we'll get the URI to use as the
11939   // referrer, since the current URI in this docshell may be a
11940   // new document that we're in the process of loading.
11941   RefPtr<Document> referrerDoc = aContent->OwnerDoc();
11942 
11943   // Now check that the referrerDoc's inner window is the current inner
11944   // window for mScriptGlobal.  If it's not, then we don't want to
11945   // follow this link.
11946   nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
11947   NS_ENSURE_TRUE(referrerInner, NS_ERROR_UNEXPECTED);
11948   if (!mScriptGlobal ||
11949       mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
11950     // We're no longer the current inner window
11951     return NS_OK;
11952   }
11953 
11954   // referrer could be null here in some odd cases, but that's ok,
11955   // we'll just load the link w/o sending a referrer in those cases.
11956 
11957   // If this is an anchor element, grab its type property to use as a hint
11958   nsAutoString typeHint;
11959   RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
11960   if (anchor) {
11961     anchor->GetType(typeHint);
11962     NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
11963     nsAutoCString type, dummy;
11964     NS_ParseRequestContentType(utf8Hint, type, dummy);
11965     CopyUTF8toUTF16(type, typeHint);
11966   }
11967 
11968   // Link click (or form submission) can be triggered inside an onload
11969   // handler, and we don't want to add history entry in this case.
11970   bool inOnLoadHandler = false;
11971   GetIsExecutingOnLoadHandler(&inOnLoadHandler);
11972   uint32_t loadType = inOnLoadHandler ? LOAD_NORMAL_REPLACE : LOAD_LINK;
11973 
11974   nsCOMPtr<nsIReferrerInfo> referrerInfo =
11975       isElementAnchorOrArea ? new ReferrerInfo(*aContent->AsElement())
11976                             : new ReferrerInfo(*referrerDoc);
11977 
11978   RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
11979   loadState->SetReferrerInfo(referrerInfo);
11980   loadState->SetTriggeringPrincipal(triggeringPrincipal);
11981   loadState->SetPrincipalToInherit(aContent->NodePrincipal());
11982   loadState->SetCsp(csp);
11983   loadState->SetLoadFlags(flags);
11984   loadState->SetTarget(aTargetSpec);
11985   loadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
11986   loadState->SetFileName(aFileName);
11987   loadState->SetPostDataStream(aPostDataStream);
11988   loadState->SetHeadersStream(aHeadersDataStream);
11989   loadState->SetLoadType(loadType);
11990   loadState->SetFirstParty(true);
11991   loadState->SetSourceBrowsingContext(mBrowsingContext);
11992   loadState->SetIsFormSubmission(aContent->IsHTMLElement(nsGkAtoms::form));
11993   loadState->SetHasValidUserGestureActivation(
11994       mBrowsingContext &&
11995       mBrowsingContext->HasValidTransientUserGestureActivation());
11996   nsresult rv = InternalLoad(loadState, aDocShell, aRequest);
11997 
11998   if (NS_SUCCEEDED(rv)) {
11999     nsPingListener::DispatchPings(this, aContent, aURI, referrerInfo);
12000   }
12001   return rv;
12002 }
12003 
OnOverLink(nsIContent * aContent,nsIURI * aURI,const nsAString & aTargetSpec)12004 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
12005                                 const nsAString& aTargetSpec) {
12006   if (aContent->IsEditable()) {
12007     return NS_OK;
12008   }
12009 
12010   nsresult rv = NS_ERROR_FAILURE;
12011 
12012   nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
12013   if (!browserChrome) {
12014     return rv;
12015   }
12016 
12017   nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
12018   nsAutoCString spec;
12019   rv = exposableURI->GetDisplaySpec(spec);
12020   NS_ENSURE_SUCCESS(rv, rv);
12021 
12022   NS_ConvertUTF8toUTF16 uStr(spec);
12023 
12024   PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
12025                    aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
12026 
12027   rv = browserChrome->SetLinkStatus(uStr);
12028   return rv;
12029 }
12030 
OnLeaveLink()12031 nsresult nsDocShell::OnLeaveLink() {
12032   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12033   nsresult rv = NS_ERROR_FAILURE;
12034 
12035   if (browserChrome) {
12036     rv = browserChrome->SetLinkStatus(EmptyString());
12037   }
12038   return rv;
12039 }
12040 
ShouldBlockLoadingForBackButton()12041 bool nsDocShell::ShouldBlockLoadingForBackButton() {
12042   if (!(mLoadType & LOAD_CMD_HISTORY) ||
12043       UserActivation::IsHandlingUserInput() ||
12044       !Preferences::GetBool("accessibility.blockjsredirection")) {
12045     return false;
12046   }
12047 
12048   bool canGoForward = false;
12049   GetCanGoForward(&canGoForward);
12050   return canGoForward;
12051 }
12052 
PluginsAllowedInCurrentDoc()12053 bool nsDocShell::PluginsAllowedInCurrentDoc() {
12054   if (!mContentViewer) {
12055     return false;
12056   }
12057 
12058   Document* doc = mContentViewer->GetDocument();
12059   if (!doc) {
12060     return false;
12061   }
12062 
12063   return doc->GetAllowPlugins();
12064 }
12065 
12066 //----------------------------------------------------------------------
12067 // Web Shell Services API
12068 
12069 // This functions is only called when a new charset is detected in loading a
12070 // document.
CharsetChangeReloadDocument(const char * aCharset,int32_t aSource)12071 nsresult nsDocShell::CharsetChangeReloadDocument(const char* aCharset,
12072                                                  int32_t aSource) {
12073   // XXX hack. keep the aCharset and aSource wait to pick it up
12074   nsCOMPtr<nsIContentViewer> cv;
12075   NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
12076   if (cv) {
12077     int32_t hint;
12078     cv->GetHintCharacterSetSource(&hint);
12079     if (aSource > hint) {
12080       nsCString charset(aCharset);
12081       cv->SetHintCharacterSet(charset);
12082       cv->SetHintCharacterSetSource(aSource);
12083       if (eCharsetReloadRequested != mCharsetReloadState) {
12084         mCharsetReloadState = eCharsetReloadRequested;
12085         switch (mLoadType) {
12086           case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
12087             return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
12088                           LOAD_FLAGS_BYPASS_PROXY);
12089           case LOAD_RELOAD_BYPASS_CACHE:
12090             return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
12091           default:
12092             return Reload(LOAD_FLAGS_CHARSET_CHANGE);
12093         }
12094       }
12095     }
12096   }
12097   // return failure if this request is not accepted due to mCharsetReloadState
12098   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
12099 }
12100 
CharsetChangeStopDocumentLoad()12101 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
12102   if (eCharsetReloadRequested != mCharsetReloadState) {
12103     Stop(nsIWebNavigation::STOP_ALL);
12104     return NS_OK;
12105   }
12106   // return failer if this request is not accepted due to mCharsetReloadState
12107   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
12108 }
12109 
SetIsPrinting(bool aIsPrinting)12110 void nsDocShell::SetIsPrinting(bool aIsPrinting) {
12111   mIsPrintingOrPP = aIsPrinting;
12112 }
12113 
12114 NS_IMETHODIMP
InitOrReusePrintPreviewViewer(nsIWebBrowserPrint ** aPrintPreview)12115 nsDocShell::InitOrReusePrintPreviewViewer(nsIWebBrowserPrint** aPrintPreview) {
12116   *aPrintPreview = nullptr;
12117 #if NS_PRINT_PREVIEW
12118   nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
12119   if (!print || !print->IsInitializedForPrintPreview()) {
12120     // XXX: Creating a brand new content viewer to host preview every
12121     // time we enter here seems overwork. We could skip ahead to where
12122     // we QI the mContentViewer if the current URI is either about:blank
12123     // or about:printpreview.
12124     Stop(nsIWebNavigation::STOP_ALL);
12125     nsCOMPtr<nsIPrincipal> principal =
12126         NullPrincipal::CreateWithInheritedAttributes(this);
12127     nsCOMPtr<nsIURI> uri;
12128     NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview"));
12129     // Reuse the null principal for the storage principal.
12130     // XXXehsan is that the right principal to use here?
12131     nsresult rv = CreateAboutBlankContentViewer(principal, principal,
12132                                                 /* aCsp = */ nullptr, uri);
12133     NS_ENSURE_SUCCESS(rv, rv);
12134     // Here we manually set current URI since we have just created a
12135     // brand new content viewer (about:blank) to host preview.
12136     SetCurrentURI(uri, nullptr, true, 0);
12137     print = do_QueryInterface(mContentViewer);
12138     NS_ENSURE_STATE(print);
12139     print->InitializeForPrintPreview();
12140   }
12141   nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
12142   result.forget(aPrintPreview);
12143   return NS_OK;
12144 #else
12145   return NS_ERROR_NOT_IMPLEMENTED;
12146 #endif
12147 }
12148 
ExitPrintPreview()12149 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
12150 #if NS_PRINT_PREVIEW
12151 #  ifdef DEBUG
12152   nsCOMPtr<nsIDocumentViewerPrint> vp = do_QueryInterface(mContentViewer);
12153   MOZ_ASSERT(vp && vp->IsInitializedForPrintPreview());
12154 #  endif
12155   nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mContentViewer);
12156   return viewer->ExitPrintPreview();
12157 #else
12158   return NS_OK;
12159 #endif
12160 }
12161 
12162 #ifdef DEBUG
12163 unsigned long nsDocShell::gNumberOfDocShells = 0;
12164 #endif
12165 
12166 NS_IMETHODIMP
GetCanExecuteScripts(bool * aResult)12167 nsDocShell::GetCanExecuteScripts(bool* aResult) {
12168   *aResult = mCanExecuteScripts;
12169   return NS_OK;
12170 }
12171 
12172 /* [infallible] */
GetIsTopLevelContentDocShell(bool * aIsTopLevelContentDocShell)12173 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
12174     bool* aIsTopLevelContentDocShell) {
12175   *aIsTopLevelContentDocShell = false;
12176 
12177   if (mItemType == typeContent) {
12178     *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
12179   }
12180 
12181   return NS_OK;
12182 }
12183 
12184 // Implements nsILoadContext.originAttributes
12185 NS_IMETHODIMP
GetScriptableOriginAttributes(JSContext * aCx,JS::MutableHandle<JS::Value> aVal)12186 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
12187                                           JS::MutableHandle<JS::Value> aVal) {
12188   return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
12189 }
12190 
12191 // Implements nsIDocShell.GetOriginAttributes()
12192 NS_IMETHODIMP
GetOriginAttributes(JSContext * aCx,JS::MutableHandle<JS::Value> aVal)12193 nsDocShell::GetOriginAttributes(JSContext* aCx,
12194                                 JS::MutableHandle<JS::Value> aVal) {
12195   return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
12196 }
12197 
ServiceWorkerAllowedToControlWindow(nsIPrincipal * aPrincipal,nsIURI * aURI)12198 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
12199                                                      nsIURI* aURI) {
12200   MOZ_ASSERT(aPrincipal);
12201   MOZ_ASSERT(aURI);
12202 
12203   if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
12204     return false;
12205   }
12206 
12207   nsCOMPtr<nsIDocShellTreeItem> parent;
12208   GetInProcessSameTypeParent(getter_AddRefs(parent));
12209   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
12210   nsPIDOMWindowInner* parentInner =
12211       parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
12212 
12213   StorageAccess storage =
12214       StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
12215 
12216   return storage == StorageAccess::eAllow;
12217 }
12218 
SetOriginAttributes(const OriginAttributes & aAttrs)12219 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
12220   MOZ_ASSERT(!mIsBeingDestroyed);
12221   return mBrowsingContext->SetOriginAttributes(aAttrs);
12222 }
12223 
12224 NS_IMETHODIMP
ResumeRedirectedLoad(uint64_t aIdentifier,int32_t aHistoryIndex)12225 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
12226   RefPtr<nsDocShell> self = this;
12227   RefPtr<ChildProcessChannelListener> cpcl =
12228       ChildProcessChannelListener::GetSingleton();
12229 
12230   // Call into InternalLoad with the pending channel when it is received.
12231   cpcl->RegisterCallback(
12232       aIdentifier, [self, aHistoryIndex](
12233                        nsDocShellLoadState* aLoadState,
12234                        nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
12235                            aStreamFilterEndpoints,
12236                        nsDOMNavigationTiming* aTiming) {
12237         MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
12238         if (NS_WARN_IF(self->mIsBeingDestroyed)) {
12239           aLoadState->GetPendingRedirectedChannel()->Cancel(NS_BINDING_ABORTED);
12240           return NS_BINDING_ABORTED;
12241         }
12242 
12243         self->mLoadType = aLoadState->LoadType();
12244         nsCOMPtr<nsIURI> previousURI;
12245         uint32_t previousFlags = 0;
12246         ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
12247                          getter_AddRefs(previousURI), &previousFlags);
12248         self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
12249                             previousURI, previousFlags);
12250 
12251         if (aTiming) {
12252           self->mTiming = new nsDOMNavigationTiming(self, aTiming);
12253         }
12254 
12255         // If we're performing a history load, locate the correct history entry,
12256         // and set the relevant bits on our loadState.
12257         if (aHistoryIndex >= 0 && self->GetSessionHistory()) {
12258           nsCOMPtr<nsISHistory> legacySHistory =
12259               self->GetSessionHistory()->LegacySHistory();
12260 
12261           nsCOMPtr<nsISHEntry> entry;
12262           nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
12263                                                         getter_AddRefs(entry));
12264           if (NS_SUCCEEDED(rv)) {
12265             legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
12266             aLoadState->SetLoadType(LOAD_HISTORY);
12267             aLoadState->SetSHEntry(entry);
12268           }
12269         }
12270 
12271         self->InternalLoad(aLoadState, nullptr, nullptr);
12272 
12273         for (auto& endpoint : aStreamFilterEndpoints) {
12274           extensions::StreamFilterParent::Attach(
12275               aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
12276         }
12277 
12278         // If the channel isn't pending, then it means that InternalLoad
12279         // never connected it, and we shouldn't try to continue. This
12280         // can happen even if InternalLoad returned NS_OK.
12281         bool pending = false;
12282         aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
12283         NS_ASSERTION(pending, "We should have connected the pending channel!");
12284         if (!pending) {
12285           return NS_BINDING_ABORTED;
12286         }
12287         return NS_OK;
12288       });
12289   return NS_OK;
12290 }
12291 
12292 NS_IMETHODIMP
SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,JSContext * aCx)12293 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
12294                                 JSContext* aCx) {
12295   OriginAttributes attrs;
12296   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
12297     return NS_ERROR_INVALID_ARG;
12298   }
12299 
12300   return SetOriginAttributes(attrs);
12301 }
12302 
12303 NS_IMETHODIMP
GetOSHEId(uint32_t * aSHEntryId)12304 nsDocShell::GetOSHEId(uint32_t* aSHEntryId) {
12305   if (mOSHE) {
12306     mOSHE->GetID(aSHEntryId);
12307     return NS_OK;
12308   } else {
12309     return NS_ERROR_FAILURE;
12310   }
12311 }
12312 
12313 NS_IMETHODIMP
GetAsyncPanZoomEnabled(bool * aOut)12314 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
12315   if (PresShell* presShell = GetPresShell()) {
12316     *aOut = presShell->AsyncPanZoomEnabled();
12317     return NS_OK;
12318   }
12319 
12320   // If we don't have a presShell, fall back to the default platform value of
12321   // whether or not APZ is enabled.
12322   *aOut = gfxPlatform::AsyncPanZoomEnabled();
12323   return NS_OK;
12324 }
12325 
HasUnloadedParent()12326 bool nsDocShell::HasUnloadedParent() {
12327   RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
12328   while (parent) {
12329     bool inUnload = false;
12330     parent->GetIsInUnload(&inUnload);
12331     if (inUnload) {
12332       return true;
12333     }
12334     parent = parent->GetInProcessParentDocshell();
12335   }
12336   return false;
12337 }
12338 
12339 /* static */
ShouldUpdateGlobalHistory(uint32_t aLoadType)12340 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
12341   return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
12342            aLoadType & LOAD_CMD_HISTORY);
12343 }
12344 
UpdateGlobalHistoryTitle(nsIURI * aURI)12345 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
12346   if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
12347     return;
12348   }
12349 
12350   // Global history is interested into sub-frame visits only for link-coloring
12351   // purposes, thus title updates are skipped for those.
12352   //
12353   // Moreover, some iframe documents (such as the ones created via
12354   // document.open()) inherit the document uri of the caller, which would cause
12355   // us to override a previously set page title with one from the subframe.
12356   if (IsFrame()) {
12357     return;
12358   }
12359 
12360   if (nsCOMPtr<IHistory> history = services::GetHistoryService()) {
12361     history->SetURITitle(aURI, mTitle);
12362   }
12363 }
12364 
IsInvisible()12365 bool nsDocShell::IsInvisible() { return mInvisible; }
12366 
SetInvisible(bool aInvisible)12367 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
12368 
12369 // The caller owns |aAsyncCause| here.
NotifyJSRunToCompletionStart(const char * aReason,const nsAString & aFunctionName,const nsAString & aFilename,const uint32_t aLineNumber,JS::Handle<JS::Value> aAsyncStack,const char * aAsyncCause)12370 void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
12371                                               const nsAString& aFunctionName,
12372                                               const nsAString& aFilename,
12373                                               const uint32_t aLineNumber,
12374                                               JS::Handle<JS::Value> aAsyncStack,
12375                                               const char* aAsyncCause) {
12376   // If first start, mark interval start.
12377   if (mJSRunToCompletionDepth == 0) {
12378     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
12379     if (timelines && timelines->HasConsumer(this)) {
12380       timelines->AddMarkerForDocShell(
12381           this, mozilla::MakeUnique<JavascriptTimelineMarker>(
12382                     aReason, aFunctionName, aFilename, aLineNumber,
12383                     MarkerTracingType::START, aAsyncStack, aAsyncCause));
12384     }
12385   }
12386 
12387   mJSRunToCompletionDepth++;
12388 }
12389 
NotifyJSRunToCompletionStop()12390 void nsDocShell::NotifyJSRunToCompletionStop() {
12391   mJSRunToCompletionDepth--;
12392 
12393   // If last stop, mark interval end.
12394   if (mJSRunToCompletionDepth == 0) {
12395     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
12396     if (timelines && timelines->HasConsumer(this)) {
12397       timelines->AddMarkerForDocShell(this, "Javascript",
12398                                       MarkerTracingType::END);
12399     }
12400   }
12401 }
12402 
12403 /* static */
MaybeNotifyKeywordSearchLoading(const nsString & aProvider,const nsString & aKeyword)12404 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
12405                                                  const nsString& aKeyword) {
12406   if (aProvider.IsEmpty()) {
12407     return;
12408   }
12409 
12410   if (XRE_IsContentProcess()) {
12411     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
12412     if (contentChild) {
12413       contentChild->SendNotifyKeywordSearchLoading(aProvider, aKeyword);
12414     }
12415     return;
12416   }
12417 
12418   nsCOMPtr<nsISearchService> searchSvc =
12419       do_GetService("@mozilla.org/browser/search-service;1");
12420   if (searchSvc) {
12421     nsCOMPtr<nsISearchEngine> searchEngine;
12422     searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
12423     if (searchEngine) {
12424       nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
12425       if (obsSvc) {
12426         // Note that "keyword-search" refers to a search via the url
12427         // bar, not a bookmarks keyword search.
12428         obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
12429       }
12430     }
12431   }
12432 }
12433 
12434 NS_IMETHODIMP
ShouldPrepareForIntercept(nsIURI * aURI,nsIChannel * aChannel,bool * aShouldIntercept)12435 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
12436                                       bool* aShouldIntercept) {
12437   return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
12438                                                          aShouldIntercept);
12439 }
12440 
12441 NS_IMETHODIMP
ChannelIntercepted(nsIInterceptedChannel * aChannel)12442 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
12443   return mInterceptController->ChannelIntercepted(aChannel);
12444 }
12445 
InFrameSwap()12446 bool nsDocShell::InFrameSwap() {
12447   RefPtr<nsDocShell> shell = this;
12448   do {
12449     if (shell->mInFrameSwap) {
12450       return true;
12451     }
12452     shell = shell->GetInProcessParentDocshell();
12453   } while (shell);
12454   return false;
12455 }
12456 
TakeInitialClientSource()12457 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
12458   return std::move(mInitialClientSource);
12459 }
12460 
12461 NS_IMETHODIMP
IssueWarning(uint32_t aWarning,bool aAsError)12462 nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError) {
12463   if (mContentViewer) {
12464     RefPtr<Document> doc = mContentViewer->GetDocument();
12465     if (doc) {
12466       doc->WarnOnceAbout(Document::DeprecatedOperations(aWarning), aAsError);
12467     }
12468   }
12469   return NS_OK;
12470 }
12471 
12472 NS_IMETHODIMP
GetEditingSession(nsIEditingSession ** aEditSession)12473 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
12474   if (!NS_SUCCEEDED(EnsureEditorData())) {
12475     return NS_ERROR_FAILURE;
12476   }
12477 
12478   *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
12479   return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
12480 }
12481 
12482 NS_IMETHODIMP
GetScriptableBrowserChild(nsIBrowserChild ** aBrowserChild)12483 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
12484   *aBrowserChild = GetBrowserChild().take();
12485   return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
12486 }
12487 
GetBrowserChild()12488 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
12489   nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
12490   return tc.forget();
12491 }
12492 
GetCommandManager()12493 nsCommandManager* nsDocShell::GetCommandManager() {
12494   NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
12495   return mCommandManager;
12496 }
12497 
12498 NS_IMETHODIMP
GetAwaitingLargeAlloc(bool * aResult)12499 nsDocShell::GetAwaitingLargeAlloc(bool* aResult) {
12500   MOZ_ASSERT(aResult);
12501   nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
12502   if (!browserChild) {
12503     *aResult = false;
12504     return NS_OK;
12505   }
12506   *aResult =
12507       static_cast<BrowserChild*>(browserChild.get())->IsAwaitingLargeAlloc();
12508   return NS_OK;
12509 }
12510 
NS_IMETHODIMP_(void)12511 NS_IMETHODIMP_(void)
12512 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
12513   mBrowsingContext->GetOriginAttributes(aAttrs);
12514 }
12515 
GetHTMLEditor()12516 HTMLEditor* nsIDocShell::GetHTMLEditor() {
12517   nsDocShell* docShell = static_cast<nsDocShell*>(this);
12518   return docShell->GetHTMLEditorInternal();
12519 }
12520 
SetHTMLEditor(HTMLEditor * aHTMLEditor)12521 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
12522   nsDocShell* docShell = static_cast<nsDocShell*>(this);
12523   return docShell->SetHTMLEditorInternal(aHTMLEditor);
12524 }
12525 
12526 NS_IMETHODIMP
GetDisplayMode(DisplayMode * aDisplayMode)12527 nsDocShell::GetDisplayMode(DisplayMode* aDisplayMode) {
12528   *aDisplayMode = mDisplayMode;
12529   return NS_OK;
12530 }
12531 
12532 NS_IMETHODIMP
SetDisplayMode(DisplayMode aDisplayMode)12533 nsDocShell::SetDisplayMode(DisplayMode aDisplayMode) {
12534   // We don't have a way to verify this coming from Javascript, so this check
12535   // is still needed.
12536   if (!(aDisplayMode == nsIDocShell::DISPLAY_MODE_BROWSER ||
12537         aDisplayMode == nsIDocShell::DISPLAY_MODE_STANDALONE ||
12538         aDisplayMode == nsIDocShell::DISPLAY_MODE_FULLSCREEN ||
12539         aDisplayMode == nsIDocShell::DISPLAY_MODE_MINIMAL_UI)) {
12540     return NS_ERROR_INVALID_ARG;
12541   }
12542 
12543   if (aDisplayMode != mDisplayMode) {
12544     mDisplayMode = aDisplayMode;
12545 
12546     RefPtr<nsPresContext> presContext = GetPresContext();
12547     presContext->MediaFeatureValuesChangedAllDocuments(
12548         {MediaFeatureChangeReason::DisplayModeChange});
12549   }
12550 
12551   return NS_OK;
12552 }
12553 
12554 #define MATRIX_LENGTH 20
12555 
12556 NS_IMETHODIMP
SetColorMatrix(const nsTArray<float> & aMatrix)12557 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
12558   if (aMatrix.Length() == MATRIX_LENGTH) {
12559     mColorMatrix.reset(new gfx::Matrix5x4());
12560     static_assert(
12561         MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
12562         "Size mismatch for our memcpy");
12563     memcpy(mColorMatrix->components, aMatrix.Elements(),
12564            sizeof(mColorMatrix->components));
12565   } else if (aMatrix.Length() == 0) {
12566     mColorMatrix.reset();
12567   } else {
12568     return NS_ERROR_INVALID_ARG;
12569   }
12570 
12571   PresShell* presShell = GetPresShell();
12572   if (!presShell) {
12573     return NS_ERROR_FAILURE;
12574   }
12575 
12576   nsIFrame* frame = presShell->GetRootFrame();
12577   if (!frame) {
12578     return NS_ERROR_FAILURE;
12579   }
12580 
12581   frame->SchedulePaint();
12582 
12583   return NS_OK;
12584 }
12585 
12586 NS_IMETHODIMP
GetColorMatrix(nsTArray<float> & aMatrix)12587 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
12588   if (mColorMatrix) {
12589     aMatrix.SetLength(MATRIX_LENGTH);
12590     static_assert(
12591         MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
12592         "Size mismatch for our memcpy");
12593     memcpy(aMatrix.Elements(), mColorMatrix->components,
12594            MATRIX_LENGTH * sizeof(float));
12595   }
12596 
12597   return NS_OK;
12598 }
12599 
12600 #undef MATRIX_LENGTH
12601 
12602 NS_IMETHODIMP
GetIsForceReloading(bool * aForceReload)12603 nsDocShell::GetIsForceReloading(bool* aForceReload) {
12604   *aForceReload = IsForceReloading();
12605   return NS_OK;
12606 }
12607 
IsForceReloading()12608 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
12609 
12610 NS_IMETHODIMP
GetBrowsingContextXPCOM(BrowsingContext ** aBrowsingContext)12611 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
12612   *aBrowsingContext = do_AddRef(mBrowsingContext).take();
12613   return NS_OK;
12614 }
12615 
GetBrowsingContext()12616 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
12617 
GetIsAttemptingToNavigate()12618 bool nsDocShell::GetIsAttemptingToNavigate() {
12619   // XXXbz the document.open spec says to abort even if there's just a
12620   // queued navigation task, sort of.  It's not clear whether browsers
12621   // actually do that, and we didn't use to do it, so for now let's
12622   // not do that.
12623   // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
12624   if (mDocumentRequest) {
12625     // There's definitely a navigation in progress.
12626     return true;
12627   }
12628 
12629   // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
12630   // until the script runs, which means they're not sending loadgroup
12631   // notifications and hence not getting set as mDocumentRequest.  Look through
12632   // our loadgroup for document-level javascript: loads.
12633   if (!mLoadGroup) {
12634     return false;
12635   }
12636 
12637   nsCOMPtr<nsISimpleEnumerator> requests;
12638   mLoadGroup->GetRequests(getter_AddRefs(requests));
12639   bool hasMore = false;
12640   while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
12641     nsCOMPtr<nsISupports> elem;
12642     requests->GetNext(getter_AddRefs(elem));
12643     nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
12644     if (!scriptChannel) {
12645       continue;
12646     }
12647 
12648     if (scriptChannel->GetIsDocumentLoad()) {
12649       // This is a javascript: load that might lead to a new document,
12650       // hence a navigation.
12651       return true;
12652     }
12653   }
12654 
12655   return false;
12656 }
12657