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/Encoding.h"
24 #include "mozilla/EventStateManager.h"
25 #include "mozilla/HTMLEditor.h"
26 #include "mozilla/LoadInfo.h"
27 #include "mozilla/Logging.h"
28 #include "mozilla/MediaFeatureChange.h"
29 #include "mozilla/Preferences.h"
30 #include "mozilla/ResultExtensions.h"
31 #include "mozilla/Services.h"
32 #include "mozilla/StartupTimeline.h"
33 #include "mozilla/Telemetry.h"
34 #include "mozilla/Unused.h"
35 
36 #include "mozilla/dom/ClientChannelHelper.h"
37 #include "mozilla/dom/ClientHandle.h"
38 #include "mozilla/dom/ClientInfo.h"
39 #include "mozilla/dom/ClientManager.h"
40 #include "mozilla/dom/ClientSource.h"
41 #include "mozilla/dom/ContentChild.h"
42 #include "mozilla/dom/DocGroup.h"
43 #include "mozilla/dom/Element.h"
44 #include "mozilla/dom/HTMLAnchorElement.h"
45 #include "mozilla/dom/PerformanceNavigation.h"
46 #include "mozilla/dom/PermissionMessageUtils.h"
47 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
48 #include "mozilla/dom/ScreenOrientation.h"
49 #include "mozilla/dom/ScriptSettings.h"
50 #include "mozilla/dom/ServiceWorkerInterceptController.h"
51 #include "mozilla/dom/ServiceWorkerManager.h"
52 #include "mozilla/dom/ServiceWorkerUtils.h"
53 #include "mozilla/dom/TabChild.h"
54 #include "mozilla/dom/TabGroup.h"
55 #include "mozilla/dom/ToJSValue.h"
56 
57 #include "mozilla/net/ReferrerPolicy.h"
58 
59 #include "nsIApplicationCacheChannel.h"
60 #include "nsIApplicationCacheContainer.h"
61 #include "nsIAppShell.h"
62 #include "nsIAsyncVerifyRedirectCallback.h"
63 #include "nsIAuthPrompt.h"
64 #include "nsIAuthPrompt2.h"
65 #include "nsICachingChannel.h"
66 #include "nsICaptivePortalService.h"
67 #include "nsIChannel.h"
68 #include "nsIChannelEventSink.h"
69 #include "nsIClassOfService.h"
70 #include "nsICommandManager.h"
71 #include "nsIConsoleReportCollector.h"
72 #include "nsIContent.h"
73 #include "nsIContentInlines.h"
74 #include "nsIContentSecurityPolicy.h"
75 #include "nsIContentViewer.h"
76 #include "nsIController.h"
77 #include "nsICookieService.h"
78 #include "nsIDocShellTreeItem.h"
79 #include "nsIDocShellTreeOwner.h"
80 #include "nsIDocument.h"
81 #include "nsIDocumentLoaderFactory.h"
82 #include "nsIDOMDocument.h"
83 #include "nsIDOMElement.h"
84 #include "nsIDOMNode.h"
85 #include "nsIDOMStorage.h"
86 #include "nsIDOMWindow.h"
87 #include "nsIEditingSession.h"
88 #include "nsIExternalProtocolService.h"
89 #include "nsIFormPOSTActionChannel.h"
90 #include "nsIFrame.h"
91 #include "nsIGlobalHistory2.h"
92 #include "nsIGlobalObject.h"
93 #include "nsIHttpChannel.h"
94 #include "nsIHttpChannelInternal.h"
95 #include "nsIIDNService.h"
96 #include "nsIInputStreamChannel.h"
97 #include "nsIInterfaceRequestorUtils.h"
98 #include "nsIJARChannel.h"
99 #include "nsILayoutHistoryState.h"
100 #include "nsILoadInfo.h"
101 #include "nsIMultiPartChannel.h"
102 #include "nsINestedURI.h"
103 #include "nsINetworkPredictor.h"
104 #include "nsINode.h"
105 #include "nsINSSErrorsService.h"
106 #include "nsIObserverService.h"
107 #include "nsIOService.h"
108 #include "nsIPrincipal.h"
109 #include "nsIPrivacyTransitionObserver.h"
110 #include "nsIPrompt.h"
111 #include "nsIPromptFactory.h"
112 #include "nsIReflowObserver.h"
113 #include "nsIScriptChannel.h"
114 #include "nsIScriptError.h"
115 #include "nsIScriptObjectPrincipal.h"
116 #include "nsIScriptSecurityManager.h"
117 #include "nsIScrollableFrame.h"
118 #include "nsIScrollObserver.h"
119 #include "nsISecureBrowserUI.h"
120 #include "nsISecurityUITelemetry.h"
121 #include "nsISeekableStream.h"
122 #include "nsISelectionDisplay.h"
123 #include "nsISHContainer.h"
124 #include "nsISHEntry.h"
125 #include "nsISHistory.h"
126 #include "nsISHistoryInternal.h"
127 #include "nsISiteSecurityService.h"
128 #include "nsISocketProvider.h"
129 #include "nsIStringBundle.h"
130 #include "nsIStructuredCloneContainer.h"
131 #include "nsISupportsPrimitives.h"
132 #include "nsITabChild.h"
133 #include "nsITextToSubURI.h"
134 #include "nsITimedChannel.h"
135 #include "nsITimer.h"
136 #include "nsITransportSecurityInfo.h"
137 #include "nsIUploadChannel.h"
138 #include "nsIURIFixup.h"
139 #include "nsIURILoader.h"
140 #include "nsIURIMutator.h"
141 #include "nsIURL.h"
142 #include "nsIViewSourceChannel.h"
143 #include "nsIWebBrowserChrome.h"
144 #include "nsIWebBrowserChrome3.h"
145 #include "nsIWebBrowserChromeFocus.h"
146 #include "nsIWebBrowserFind.h"
147 #include "nsIWebProgress.h"
148 #include "nsIWidget.h"
149 #include "nsIWindowWatcher.h"
150 #include "nsIWritablePropertyBag2.h"
151 #include "nsIWyciwygChannel.h"
152 
153 #include "nsPICommandUpdater.h"
154 #include "nsPIDOMWindow.h"
155 #include "nsPILoadGroupInternal.h"
156 #include "nsPIWindowRoot.h"
157 
158 #include "IHistory.h"
159 #include "IUrlClassifierUITelemetry.h"
160 
161 #include "mozIThirdPartyUtil.h"
162 
163 #include "nsArray.h"
164 #include "nsArrayUtils.h"
165 #include "nsAutoPtr.h"
166 #include "nsCDefaultURIFixup.h"
167 #include "nsCExternalHandlerService.h"
168 #include "nsContentDLF.h"
169 #include "nsContentPolicyUtils.h"  // NS_CheckContentLoadPolicy(...)
170 #include "nsContentSecurityManager.h"
171 #include "nsContentUtils.h"
172 #include "nsCURILoader.h"
173 #include "nsDocShellCID.h"
174 #include "nsDocShellEditorData.h"
175 #include "nsDocShellEnumerator.h"
176 #include "nsDocShellLoadInfo.h"
177 #include "nsDocShellLoadTypes.h"
178 #include "nsDocShellTransferableHooks.h"
179 #include "nsDOMCID.h"
180 #include "nsDOMNavigationTiming.h"
181 #include "nsDSURIContentListener.h"
182 #include "nsError.h"
183 #include "nsEscape.h"
184 #include "nsFocusManager.h"
185 #include "nsGlobalWindow.h"
186 #include "nsJSEnvironment.h"
187 #include "nsNetCID.h"
188 #include "nsNetUtil.h"
189 #include "nsObjectLoadingContent.h"
190 #include "nsPingListener.h"
191 #include "nsPoint.h"
192 #include "nsQueryObject.h"
193 #include "nsRect.h"
194 #include "nsRefreshTimer.h"
195 #include "nsSandboxFlags.h"
196 #include "nsIServiceWorkerManager.h"
197 #include "nsSHistory.h"
198 #include "nsStructuredCloneContainer.h"
199 #include "nsSubDocumentFrame.h"
200 #include "nsView.h"
201 #include "nsViewManager.h"
202 #include "nsViewSourceHandler.h"
203 #include "nsWhitespaceTokenizer.h"
204 #include "nsWidgetsCID.h"
205 #include "nsXULAppAPI.h"
206 
207 #include "GeckoProfiler.h"
208 #include "Navigator.h"
209 #include "NullPrincipal.h"
210 #include "prenv.h"
211 #include "URIUtils.h"
212 
213 #include "timeline/JavascriptTimelineMarker.h"
214 
215 #ifdef MOZ_PLACES
216 #include "nsIFaviconService.h"
217 #include "mozIPlacesPendingOperation.h"
218 #include "mozIAsyncFavicons.h"
219 #endif
220 
221 #if NS_PRINT_PREVIEW
222 #include "nsIDocumentViewerPrint.h"
223 #include "nsIWebBrowserPrint.h"
224 #endif
225 
226 #ifdef MOZ_TOOLKIT_SEARCH
227 #include "nsIBrowserSearchService.h"
228 #endif
229 
230 using namespace mozilla;
231 using namespace mozilla::dom;
232 
233 // Threshold value in ms for META refresh based redirects
234 #define REFRESH_REDIRECT_TIMER 15000
235 
236 // Hint for native dispatch of events on how long to delay after
237 // all documents have loaded in milliseconds before favoring normal
238 // native event dispatch priorites over performance
239 // Can be overridden with docshell.event_starvation_delay_hint pref.
240 #define NS_EVENT_STARVATION_DELAY_HINT 2000
241 
242 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
243 
244 // True means sUseErrorPages has been added to
245 // preferences var cache.
246 static bool gAddedPreferencesVarCache = false;
247 
248 // Number of documents currently loading
249 static int32_t gNumberOfDocumentsLoading = 0;
250 
251 // Global count of existing docshells.
252 static int32_t gDocShellCount = 0;
253 
254 // Global count of docshells with the private attribute set
255 static uint32_t gNumberOfPrivateDocShells = 0;
256 
257 // True means we validate window targets to prevent frameset
258 // spoofing. Initialize this to a non-bolean value so we know to check
259 // the pref on the creation of the first docshell.
260 static uint32_t gValidateOrigin = 0xffffffff;
261 
262 #ifdef DEBUG
263 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
264 #endif
265 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
266 ;
267 
268 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
269 const char kAppstringsBundleURL[] =
270     "chrome://global/locale/appstrings.properties";
271 
272 bool nsDocShell::sUseErrorPages = false;
273 
274 // Global reference to the URI fixup service.
275 nsIURIFixup* nsDocShell::sURIFixup = nullptr;
276 
FavorPerformanceHint(bool aPerfOverStarvation)277 static void FavorPerformanceHint(bool aPerfOverStarvation) {
278   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
279   if (appShell) {
280     appShell->FavorPerformanceHint(
281         aPerfOverStarvation,
282         Preferences::GetUint("docshell.event_starvation_delay_hint",
283                              NS_EVENT_STARVATION_DELAY_HINT));
284   }
285 }
286 
IncreasePrivateDocShellCount()287 static void IncreasePrivateDocShellCount() {
288   gNumberOfPrivateDocShells++;
289   if (gNumberOfPrivateDocShells > 1 || !XRE_IsContentProcess()) {
290     return;
291   }
292 
293   mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
294   cc->SendPrivateDocShellsExist(true);
295 }
296 
DecreasePrivateDocShellCount()297 static void DecreasePrivateDocShellCount() {
298   MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
299   gNumberOfPrivateDocShells--;
300   if (!gNumberOfPrivateDocShells) {
301     if (XRE_IsContentProcess()) {
302       dom::ContentChild* cc = dom::ContentChild::GetSingleton();
303       cc->SendPrivateDocShellsExist(false);
304       return;
305     }
306 
307     nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
308     if (obsvc) {
309       obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
310     }
311   }
312 }
313 
nsDocShell()314 nsDocShell::nsDocShell()
315     : nsDocLoader(),
316       mForcedCharset(nullptr),
317       mParentCharset(nullptr),
318       mTreeOwner(nullptr),
319       mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
320       mCharsetReloadState(eCharsetReloadInit),
321       mOrientationLock(eScreenOrientation_None),
322       mParentCharsetSource(0),
323       mMarginWidth(-1),
324       mMarginHeight(-1),
325       mItemType(typeContent),
326       mPreviousTransIndex(-1),
327       mLoadedTransIndex(-1),
328       mChildOffset(0),
329       mSandboxFlags(0),
330       mBusyFlags(BUSY_FLAGS_NONE),
331       mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
332       mLoadType(0),
333       mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
334       mReferrerPolicy(0),
335       mFailedLoadType(0),
336       mFrameType(FRAME_TYPE_REGULAR),
337       mPrivateBrowsingId(0),
338       mDisplayMode(nsIDocShell::DISPLAY_MODE_BROWSER),
339       mJSRunToCompletionDepth(0),
340       mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE),
341       mFullscreenAllowed(CHECK_ATTRIBUTES),
342       mCreatingDocument(false)
343 #ifdef DEBUG
344       ,
345       mInEnsureScriptEnv(false)
346 #endif
347       ,
348       mCreated(false),
349       mAllowSubframes(true),
350       mAllowPlugins(true),
351       mAllowJavascript(true),
352       mAllowMetaRedirects(true),
353       mAllowImages(true),
354       mAllowMedia(true),
355       mAllowDNSPrefetch(true),
356       mAllowWindowControl(true),
357       mAllowContentRetargeting(true),
358       mAllowContentRetargetingOnChildren(true),
359       mUseErrorPages(false),
360       mObserveErrorPages(true),
361       mAllowAuth(true),
362       mAllowKeywordFixup(false),
363       mIsOffScreenBrowser(false),
364       mIsActive(true),
365       mDisableMetaRefreshWhenInactive(false),
366       mIsAppTab(false),
367       mUseGlobalHistory(false),
368       mUseRemoteTabs(false),
369       mUseTrackingProtection(false),
370       mDeviceSizeIsPageSize(false),
371       mWindowDraggingAllowed(false),
372       mInFrameSwap(false),
373       mInheritPrivateBrowsingId(true),
374       mCanExecuteScripts(false),
375       mFiredUnloadEvent(false),
376       mEODForCurrentDocument(false),
377       mURIResultedInDocument(false),
378       mIsBeingDestroyed(false),
379       mIsExecutingOnLoadHandler(false),
380       mIsPrintingOrPP(false),
381       mSavingOldViewer(false),
382       mDynamicallyCreated(false),
383       mAffectPrivateSessionLifetime(true),
384       mInvisible(false),
385       mHasLoadedNonBlankURI(false),
386       mBlankTiming(false) {
387   AssertOriginAttributesMatchPrivateBrowsing();
388 
389   nsContentUtils::GenerateUUIDInPlace(mHistoryID);
390 
391   if (gDocShellCount++ == 0) {
392     NS_ASSERTION(sURIFixup == nullptr,
393                  "Huh, sURIFixup not null in first nsDocShell ctor!");
394 
395     CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
396   }
397 
398   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
399 
400 #ifdef DEBUG
401   // We're counting the number of |nsDocShells| to help find leaks
402   ++gNumberOfDocShells;
403   if (!PR_GetEnv("MOZ_QUIET")) {
404     printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %s]\n", (void*)this,
405                   gNumberOfDocShells, getpid(),
406                   nsIDToCString(mHistoryID).get());
407   }
408 #endif
409 }
410 
~nsDocShell()411 nsDocShell::~nsDocShell() {
412   MOZ_ASSERT(!mObserved);
413 
414   // Avoid notifying observers while we're in the dtor.
415   mIsBeingDestroyed = true;
416 
417   Destroy();
418 
419   nsCOMPtr<nsISHistoryInternal> shPrivate(do_QueryInterface(mSessionHistory));
420   if (shPrivate) {
421     shPrivate->SetRootDocShell(nullptr);
422   }
423 
424   if (--gDocShellCount == 0) {
425     NS_IF_RELEASE(sURIFixup);
426   }
427 
428   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
429 
430 #ifdef DEBUG
431   // We're counting the number of |nsDocShells| to help find leaks
432   --gNumberOfDocShells;
433   if (!PR_GetEnv("MOZ_QUIET")) {
434     printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s]\n", (void*)this,
435                   gNumberOfDocShells, getpid(),
436                   nsIDToCString(mHistoryID).get());
437   }
438 #endif
439 }
440 
Init()441 nsresult nsDocShell::Init() {
442   MOZ_ASSERT(!mIsBeingDestroyed);
443 
444   nsresult rv = nsDocLoader::Init();
445   NS_ENSURE_SUCCESS(rv, rv);
446 
447   NS_ASSERTION(mLoadGroup, "Something went wrong!");
448 
449   mContentListener = new nsDSURIContentListener(this);
450   rv = mContentListener->Init();
451   NS_ENSURE_SUCCESS(rv, rv);
452 
453   // If parent intercept is not enabled then we must forward to
454   // the network controller from docshell.  We also enable if we're
455   // in the parent process in order to support non-e10s configurations.
456   if (!ServiceWorkerParentInterceptEnabled() || XRE_IsParentProcess()) {
457     mInterceptController = new ServiceWorkerInterceptController();
458   }
459 
460   // We want to hold a strong ref to the loadgroup, so it better hold a weak
461   // ref to us...  use an InterfaceRequestorProxy to do this.
462   nsCOMPtr<nsIInterfaceRequestor> proxy =
463       new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>(this));
464   mLoadGroup->SetNotificationCallbacks(proxy);
465 
466   rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
467   NS_ENSURE_SUCCESS(rv, rv);
468 
469   // Add as |this| a progress listener to itself.  A little weird, but
470   // simpler than reproducing all the listener-notification logic in
471   // overrides of the various methods via which nsDocLoader can be
472   // notified.   Note that this holds an nsWeakPtr to ourselves, so it's ok.
473   return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
474                                        nsIWebProgress::NOTIFY_STATE_NETWORK);
475 }
476 
DestroyChildren()477 void nsDocShell::DestroyChildren() {
478   nsCOMPtr<nsIDocShellTreeItem> shell;
479   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
480   while (iter.HasMore()) {
481     shell = do_QueryObject(iter.GetNext());
482     NS_ASSERTION(shell, "docshell has null child");
483 
484     if (shell) {
485       shell->SetTreeOwner(nullptr);
486     }
487   }
488 
489   nsDocLoader::DestroyChildren();
490 }
491 
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell,nsDocLoader,mSessionStorageManager,mScriptGlobal,mInitialClientSource,mChromeEventHandler)492 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell, nsDocLoader,
493                                    mSessionStorageManager, mScriptGlobal,
494                                    mInitialClientSource, mChromeEventHandler)
495 
496 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
497 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
498 
499 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
500   NS_INTERFACE_MAP_ENTRY(nsIDocShell)
501   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
502   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
503   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
504   NS_INTERFACE_MAP_ENTRY(nsIScrollable)
505   NS_INTERFACE_MAP_ENTRY(nsITextScroll)
506   NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
507   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
508   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
509   NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
510   NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
511   NS_INTERFACE_MAP_ENTRY(nsILoadContext)
512   NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
513   NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
514   NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
515   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
516   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
517                                      mInterceptController)
518   NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
519 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
520 
521 NS_IMETHODIMP
522 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
523   NS_PRECONDITION(aSink, "null out param");
524 
525   *aSink = nullptr;
526 
527   if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
528     NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
529     *aSink = mCommandManager;
530   } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
531     *aSink = mContentListener;
532   } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
533               aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
534               aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
535               aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
536               aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
537              NS_SUCCEEDED(EnsureScriptEnvironment())) {
538     return mScriptGlobal->QueryInterface(aIID, aSink);
539   } else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) &&
540              NS_SUCCEEDED(EnsureContentViewer())) {
541     nsCOMPtr<nsIDOMDocument> doc =
542         do_QueryInterface(mContentViewer->GetDocument());
543     doc.forget(aSink);
544     return *aSink ? NS_OK : NS_NOINTERFACE;
545   } else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
546              NS_SUCCEEDED(EnsureContentViewer())) {
547     nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
548     doc.forget(aSink);
549     return *aSink ? NS_OK : NS_NOINTERFACE;
550   } else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
551     *aSink = nullptr;
552 
553     // Return application cache associated with this docshell, if any
554 
555     nsCOMPtr<nsIContentViewer> contentViewer;
556     GetContentViewer(getter_AddRefs(contentViewer));
557     if (!contentViewer) {
558       return NS_ERROR_NO_INTERFACE;
559     }
560 
561     nsCOMPtr<nsIDocument> doc = contentViewer->GetDocument();
562     NS_ASSERTION(doc, "Should have a document.");
563     if (!doc) {
564       return NS_ERROR_NO_INTERFACE;
565     }
566 
567 #if defined(DEBUG)
568     MOZ_LOG(
569         gDocShellLog, LogLevel::Debug,
570         ("nsDocShell[%p]: returning app cache container %p", this, doc.get()));
571 #endif
572     return doc->QueryInterface(aIID, aSink);
573   } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
574              NS_SUCCEEDED(EnsureScriptEnvironment())) {
575     nsresult rv;
576     nsCOMPtr<nsIWindowWatcher> wwatch =
577         do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
578     NS_ENSURE_SUCCESS(rv, rv);
579 
580     // Get the an auth prompter for our window so that the parenting
581     // of the dialogs works as it should when using tabs.
582     nsIPrompt* prompt;
583     rv = wwatch->GetNewPrompter(mScriptGlobal->AsOuter(), &prompt);
584     NS_ENSURE_SUCCESS(rv, rv);
585 
586     *aSink = prompt;
587     return NS_OK;
588   } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
589              aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
590     return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
591                ? NS_OK
592                : NS_NOINTERFACE;
593   } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
594     nsCOMPtr<nsISHistory> shistory;
595     nsresult rv = GetSessionHistory(getter_AddRefs(shistory));
596     if (NS_SUCCEEDED(rv) && shistory) {
597       shistory.forget(aSink);
598       return NS_OK;
599     }
600     return NS_NOINTERFACE;
601   } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
602     nsresult rv = EnsureFind();
603     if (NS_FAILED(rv)) {
604       return rv;
605     }
606 
607     *aSink = mFind;
608     NS_ADDREF((nsISupports*)*aSink);
609     return NS_OK;
610   } else if (aIID.Equals(NS_GET_IID(nsIEditingSession))) {
611     nsCOMPtr<nsIEditingSession> es;
612     GetEditingSession(getter_AddRefs(es));
613     es.forget(aSink);
614     return *aSink ? NS_OK : NS_NOINTERFACE;
615   } else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList)) &&
616              NS_SUCCEEDED(EnsureTransferableHookData())) {
617     *aSink = mTransferableHookData;
618     NS_ADDREF((nsISupports*)*aSink);
619     return NS_OK;
620   } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
621     nsIPresShell* shell = GetPresShell();
622     if (shell) {
623       return shell->QueryInterface(aIID, aSink);
624     }
625   } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
626     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
627     nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
628     if (NS_SUCCEEDED(rv) && treeOwner) {
629       return treeOwner->QueryInterface(aIID, aSink);
630     }
631   } else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
632     *aSink = GetTabChild().take();
633     return *aSink ? NS_OK : NS_ERROR_FAILURE;
634   } else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
635     nsCOMPtr<nsITabChild> tabChild =
636         do_GetInterface(static_cast<nsIDocShell*>(this));
637     nsCOMPtr<nsIContentFrameMessageManager> mm;
638     if (tabChild) {
639       tabChild->GetMessageManager(getter_AddRefs(mm));
640     } else {
641       if (nsPIDOMWindowOuter* win = GetWindow()) {
642         mm = do_QueryInterface(win->GetParentTarget());
643       }
644     }
645     *aSink = mm.get();
646   } else {
647     return nsDocLoader::GetInterface(aIID, aSink);
648   }
649 
650   NS_IF_ADDREF(((nsISupports*)*aSink));
651   return *aSink ? NS_OK : NS_NOINTERFACE;
652 }
653 
654 NS_IMETHODIMP
LoadURI(nsIURI * aURI,nsIDocShellLoadInfo * aLoadInfo,uint32_t aLoadFlags,bool aFirstParty)655 nsDocShell::LoadURI(nsIURI* aURI, nsIDocShellLoadInfo* aLoadInfo,
656                     uint32_t aLoadFlags, bool aFirstParty) {
657   NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
658                   "Unexpected flags");
659   NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set");
660 
661   // Note: we allow loads to get through here even if mFiredUnloadEvent is
662   // true; that case will get handled in LoadInternal or LoadHistoryEntry,
663   // so we pass false as the second parameter to IsNavigationAllowed.
664   // However, we don't allow the page to change location *in the middle of*
665   // firing beforeunload, so we do need to check if *beforeunload* is currently
666   // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
667   if (!IsNavigationAllowed(true, false)) {
668     return NS_OK;  // JS may not handle returning of an error code
669   }
670 
671   nsCOMPtr<nsIURI> referrer;
672   nsCOMPtr<nsIURI> originalURI;
673   Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
674   bool keepResultPrincipalURIIfSet = false;
675   bool loadReplace = false;
676   bool isFromProcessingFrameAttributes = false;
677   nsCOMPtr<nsIInputStream> postStream;
678   nsCOMPtr<nsIInputStream> headersStream;
679   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
680   bool inheritPrincipal = false;
681   bool principalIsExplicit = false;
682   bool sendReferrer = true;
683   uint32_t referrerPolicy = mozilla::net::RP_Unset;
684   bool isSrcdoc = false;
685   nsCOMPtr<nsISHEntry> shEntry;
686   nsString target;
687   nsAutoString srcdoc;
688   bool forceAllowDataURI = false;
689   bool originalFrameSrc = false;
690   nsCOMPtr<nsIDocShell> sourceDocShell;
691   nsCOMPtr<nsIURI> baseURI;
692 
693   uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
694 
695   NS_ENSURE_ARG(aURI);
696 
697   if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
698       mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
699     StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
700   }
701 
702   // Extract the info from the DocShellLoadInfo struct...
703   if (aLoadInfo) {
704     aLoadInfo->GetReferrer(getter_AddRefs(referrer));
705     aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI));
706     GetMaybeResultPrincipalURI(aLoadInfo, resultPrincipalURI);
707     aLoadInfo->GetKeepResultPrincipalURIIfSet(&keepResultPrincipalURIIfSet);
708     aLoadInfo->GetLoadReplace(&loadReplace);
709     aLoadInfo->GetIsFromProcessingFrameAttributes(
710         &isFromProcessingFrameAttributes);
711     nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
712     aLoadInfo->GetLoadType(&lt);
713     // Get the appropriate loadType from nsIDocShellLoadInfo type
714     loadType = ConvertDocShellInfoLoadTypeToLoadType(lt);
715 
716     aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
717     aLoadInfo->GetInheritPrincipal(&inheritPrincipal);
718     aLoadInfo->GetPrincipalIsExplicit(&principalIsExplicit);
719     aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
720     aLoadInfo->GetTarget(getter_Copies(target));
721     aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
722     aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
723     aLoadInfo->GetSendReferrer(&sendReferrer);
724     aLoadInfo->GetReferrerPolicy(&referrerPolicy);
725     aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
726     aLoadInfo->GetSrcdocData(srcdoc);
727     aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
728     aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
729     aLoadInfo->GetForceAllowDataURI(&forceAllowDataURI);
730     aLoadInfo->GetOriginalFrameSrc(&originalFrameSrc);
731   }
732 
733   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
734           ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
735            aURI->GetSpecOrDefault().get(), aLoadFlags));
736 
737   if (!shEntry && !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
738     // First verify if this is a subframe.
739     nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
740     GetSameTypeParent(getter_AddRefs(parentAsItem));
741     nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
742     uint32_t parentLoadType;
743 
744     if (parentDS && parentDS != static_cast<nsIDocShell*>(this)) {
745       /* OK. It is a subframe. Checkout the
746        * parent's loadtype. If the parent was loaded thro' a history
747        * mechanism, then get the SH entry for the child from the parent.
748        * This is done to restore frameset navigation while going back/forward.
749        * If the parent was loaded through any other loadType, set the
750        * child's loadType too accordingly, so that session history does not
751        * get confused.
752        */
753 
754       // Get the parent's load type
755       parentDS->GetLoadType(&parentLoadType);
756 
757       // Get the ShEntry for the child from the parent
758       nsCOMPtr<nsISHEntry> currentSH;
759       bool oshe = false;
760       parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
761       bool dynamicallyAddedChild = mDynamicallyCreated;
762       if (!dynamicallyAddedChild && !oshe && currentSH) {
763         currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
764       }
765       if (!dynamicallyAddedChild) {
766         // Only use the old SHEntry, if we're sure enough that
767         // it wasn't originally for some other frame.
768         parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
769       }
770 
771       // Make some decisions on the child frame's loadType based on the
772       // parent's loadType, if the subframe hasn't loaded anything into it.
773       //
774       // In some cases privileged scripts may try to get the DOMWindow
775       // reference of this docshell before the loading starts, causing the
776       // initial about:blank content viewer being created and mCurrentURI being
777       // set. To handle this case we check if mCurrentURI is about:blank and
778       // currentSHEntry is null.
779       nsCOMPtr<nsISHEntry> currentChildEntry;
780       GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
781       if (!mCurrentURI ||
782           (NS_IsAboutBlank(mCurrentURI) && !currentChildEntry)) {
783         // This is a newly created frame. Check for exception cases first.
784         // By default the subframe will inherit the parent's loadType.
785         if (shEntry &&
786             (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK ||
787              parentLoadType == LOAD_NORMAL_EXTERNAL)) {
788           // The parent was loaded normally. In this case, this *brand new*
789           // child really shouldn't have a SHEntry. If it does, it could be
790           // because the parent is replacing an existing frame with a new frame,
791           // in the onLoadHandler. We don't want this url to get into session
792           // history. Clear off shEntry, and set load type to
793           // LOAD_BYPASS_HISTORY.
794           bool inOnLoadHandler = false;
795           parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
796           if (inOnLoadHandler) {
797             loadType = LOAD_NORMAL_REPLACE;
798             shEntry = nullptr;
799           }
800         } else if (parentLoadType == LOAD_REFRESH) {
801           // Clear shEntry. For refresh loads, we have to load
802           // what comes thro' the pipe, not what's in history.
803           shEntry = nullptr;
804         } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
805                    (shEntry &&
806                     ((parentLoadType & LOAD_CMD_HISTORY) ||
807                      (parentLoadType == LOAD_RELOAD_NORMAL) ||
808                      (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
809                      (parentLoadType ==
810                       LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
811                      (parentLoadType ==
812                       LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
813           // If the parent url, bypassed history or was loaded from
814           // history, pass on the parent's loadType to the new child
815           // frame too, so that the child frame will also
816           // avoid getting into history.
817           loadType = parentLoadType;
818         } else if (parentLoadType == LOAD_ERROR_PAGE) {
819           // If the parent document is an error page, we don't
820           // want to update global/session history. However,
821           // this child frame is not an error page.
822           loadType = LOAD_BYPASS_HISTORY;
823         } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
824                    (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
825                    (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
826           // the new frame should inherit the parent's load type so that it also
827           // bypasses the cache and/or proxy
828           loadType = parentLoadType;
829         }
830       } else {
831         // This is a pre-existing subframe. If
832         // 1. The load of this frame was not originally initiated by session
833         //    history directly (i.e. (!shEntry) condition succeeded, but it can
834         //    still be a history load on parent which causes this frame being
835         //    loaded), and
836         // 2. mCurrentURI is not null, nor the initial about:blank,
837         // it is possible that a parent's onLoadHandler or even self's
838         // onLoadHandler is loading a new page in this child. Check parent's and
839         // self's busy flag and if it is set, we don't want this onLoadHandler
840         // load to get in to session history.
841         uint32_t parentBusy = BUSY_FLAGS_NONE;
842         uint32_t selfBusy = BUSY_FLAGS_NONE;
843         parentDS->GetBusyFlags(&parentBusy);
844         GetBusyFlags(&selfBusy);
845         if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
846           loadType = LOAD_NORMAL_REPLACE;
847           shEntry = nullptr;
848         }
849       }
850     }  // parentDS
851     else {
852       // This is the root docshell. If we got here while
853       // executing an onLoad Handler,this load will not go
854       // into session history.
855       bool inOnLoadHandler = false;
856       GetIsExecutingOnLoadHandler(&inOnLoadHandler);
857       if (inOnLoadHandler) {
858         loadType = LOAD_NORMAL_REPLACE;
859       }
860     }
861   }  // !shEntry
862 
863   if (shEntry) {
864 #ifdef DEBUG
865     MOZ_LOG(gDocShellLog, LogLevel::Debug,
866             ("nsDocShell[%p]: loading from session history", this));
867 #endif
868 
869     return LoadHistoryEntry(shEntry, loadType);
870   }
871 
872   // On history navigation via Back/Forward buttons, don't execute
873   // automatic JavaScript redirection such as |location.href = ...| or
874   // |window.open()|
875   //
876   // LOAD_NORMAL:        window.open(...) etc.
877   // LOAD_STOP_CONTENT:  location.href = ..., location.assign(...)
878   if ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) &&
879       ShouldBlockLoadingForBackButton()) {
880     return NS_OK;
881   }
882 
883   // Perform the load...
884 
885   // We need a principalToInherit.
886   //
887   // If principalIsExplicit is not set there are 4 possibilities:
888   // (1) If the system principal or an expanded principal was passed
889   //     in and we're a typeContent docshell, inherit the principal
890   //     from the current document instead.
891   // (2) In all other cases when the principal passed in is not null,
892   //     use that principal.
893   // (3) If the caller has allowed inheriting from the current document,
894   //     or if we're being called from system code (eg chrome JS or pure
895   //     C++) then inheritPrincipal should be true and InternalLoad will get
896   //     a principal from the current document. If none of these things are
897   //     true, then
898   // (4) we don't pass a principal into the channel, and a principal will be
899   //     created later from the channel's internal data.
900   //
901   // If principalIsExplicit *is* set, there are 4 possibilities
902   // (1) If the system principal or an expanded principal was passed in
903   //     and we're a typeContent docshell, return an error.
904   // (2) In all other cases when the principal passed in is not null,
905   //     use that principal.
906   // (3) If the caller has allowed inheriting from the current document,
907   //     then inheritPrincipal should be true and InternalLoad will get
908   //     a principal from the current document. If none of these things are
909   //     true, then
910   // (4) we dont' pass a principal into the channel, and a principal will be
911   //     created later from the channel's internal data.
912   nsCOMPtr<nsIPrincipal> principalToInherit = triggeringPrincipal;
913   if (principalToInherit && mItemType != typeChrome) {
914     if (nsContentUtils::IsSystemPrincipal(principalToInherit)) {
915       if (principalIsExplicit) {
916         return NS_ERROR_DOM_SECURITY_ERR;
917       }
918       principalToInherit = nullptr;
919       inheritPrincipal = true;
920     } else if (nsContentUtils::IsExpandedPrincipal(principalToInherit)) {
921       if (principalIsExplicit) {
922         return NS_ERROR_DOM_SECURITY_ERR;
923       }
924       // Don't inherit from the current page.  Just do the safe thing
925       // and pretend that we were loaded by a nullprincipal.
926       //
927       // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
928       // have origin attributes.
929       principalToInherit = NullPrincipal::CreateWithInheritedAttributes(this);
930       inheritPrincipal = false;
931     }
932   }
933   if (!principalToInherit && !inheritPrincipal && !principalIsExplicit) {
934     // See if there's system or chrome JS code running
935     inheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
936   }
937 
938   if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL) {
939     inheritPrincipal = false;
940     // If aFirstParty is true and the pref 'privacy.firstparty.isolate' is
941     // enabled, we will set firstPartyDomain on the origin attributes.
942     principalToInherit =
943         NullPrincipal::CreateWithInheritedAttributes(this, aFirstParty);
944   }
945 
946   // If the triggeringPrincipal is not passed explicitly, we first try to create
947   // a principal from the referrer, since the referrer URI reflects the web
948   // origin that triggered the load. If there is no referrer URI, we fall back
949   // to using the SystemPrincipal. It's safe to assume that no provided
950   // triggeringPrincipal and no referrer simulate a load that was triggered by
951   // the system. It's important to note that this block of code needs to appear
952   // *after* the block where we munge the principalToInherit, because otherwise
953   // we would never enter code blocks checking if the principalToInherit is null
954   // and we will end up with a wrong inheritPrincipal flag.
955   if (!triggeringPrincipal) {
956     if (referrer) {
957       nsresult rv = CreatePrincipalFromReferrer(
958           referrer, getter_AddRefs(triggeringPrincipal));
959       NS_ENSURE_SUCCESS(rv, rv);
960     } else {
961       triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
962     }
963   }
964 
965   uint32_t flags = 0;
966 
967   if (inheritPrincipal) {
968     flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
969   }
970 
971   if (!sendReferrer) {
972     flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
973   }
974 
975   if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
976     flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
977   }
978 
979   if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) {
980     flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
981   }
982 
983   if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) {
984     flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
985   }
986 
987   if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
988     flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
989   }
990 
991   if (isSrcdoc) {
992     flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
993   }
994 
995   if (forceAllowDataURI) {
996     flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
997   }
998 
999   if (originalFrameSrc) {
1000     flags |= INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
1001   }
1002 
1003   return InternalLoad(aURI, originalURI, resultPrincipalURI,
1004                       keepResultPrincipalURIIfSet, loadReplace,
1005                       isFromProcessingFrameAttributes, referrer, referrerPolicy,
1006                       triggeringPrincipal, principalToInherit, flags, target,
1007                       nullptr,       // No type hint
1008                       VoidString(),  // No forced download
1009                       postStream,
1010                       -1,  // XXXbaku
1011                       headersStream, loadType,
1012                       nullptr,  // No SHEntry
1013                       aFirstParty, srcdoc, sourceDocShell, baseURI,
1014                       nullptr,   // No nsIDocShell
1015                       nullptr);  // No nsIRequest
1016 }
1017 
1018 NS_IMETHODIMP
LoadStream(nsIInputStream * aStream,nsIURI * aURI,const nsACString & aContentType,const nsACString & aContentCharset,nsIDocShellLoadInfo * aLoadInfo)1019 nsDocShell::LoadStream(nsIInputStream* aStream, nsIURI* aURI,
1020                        const nsACString& aContentType,
1021                        const nsACString& aContentCharset,
1022                        nsIDocShellLoadInfo* aLoadInfo) {
1023   NS_ENSURE_ARG(aStream);
1024 
1025   mAllowKeywordFixup = false;
1026 
1027   // if the caller doesn't pass in a URI we need to create a dummy URI. necko
1028   // currently requires a URI in various places during the load. Some consumers
1029   // do as well.
1030   nsCOMPtr<nsIURI> uri = aURI;
1031   if (!uri) {
1032     // HACK ALERT
1033     nsresult rv = NS_OK;
1034     // Make sure that the URI spec "looks" like a protocol and path...
1035     // For now, just use a bogus protocol called "internal"
1036     rv = NS_MutateURI(NS_SIMPLEURIMUTATOR_CONTRACTID)
1037              .SetSpec(NS_LITERAL_CSTRING("internal:load-stream"))
1038              .Finalize(uri);
1039     if (NS_FAILED(rv)) {
1040       return rv;
1041     }
1042   }
1043 
1044   uint32_t loadType = LOAD_NORMAL;
1045   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
1046   if (aLoadInfo) {
1047     nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
1048     (void)aLoadInfo->GetLoadType(&lt);
1049     // Get the appropriate LoadType from nsIDocShellLoadInfo type
1050     loadType = ConvertDocShellInfoLoadTypeToLoadType(lt);
1051     aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
1052   }
1053 
1054   NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
1055 
1056   mLoadType = loadType;
1057 
1058   if (!triggeringPrincipal) {
1059     triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
1060   }
1061 
1062   // build up a channel for this stream.
1063   nsCOMPtr<nsIChannel> channel;
1064   nsCOMPtr<nsIInputStream> stream = aStream;
1065   nsresult rv = NS_NewInputStreamChannel(
1066       getter_AddRefs(channel), uri, stream.forget(), triggeringPrincipal,
1067       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1068       nsIContentPolicy::TYPE_OTHER, aContentType, aContentCharset);
1069   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
1070 
1071   nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
1072   NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
1073 
1074   NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false), NS_ERROR_FAILURE);
1075   return NS_OK;
1076 }
1077 
1078 NS_IMETHODIMP
CreateLoadInfo(nsIDocShellLoadInfo ** aLoadInfo)1079 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo** aLoadInfo) {
1080   nsDocShellLoadInfo* loadInfo = new nsDocShellLoadInfo();
1081   nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1082 
1083   localRef.forget(aLoadInfo);
1084   return NS_OK;
1085 }
1086 
1087 /*
1088  * Reset state to a new content model within the current document and the
1089  * document viewer. Called by the document before initiating an out of band
1090  * document.write().
1091  */
1092 NS_IMETHODIMP
PrepareForNewContentModel()1093 nsDocShell::PrepareForNewContentModel() {
1094   mEODForCurrentDocument = false;
1095   return NS_OK;
1096 }
1097 
1098 NS_IMETHODIMP
FirePageHideNotification(bool aIsUnload)1099 nsDocShell::FirePageHideNotification(bool aIsUnload) {
1100   FirePageHideNotificationInternal(aIsUnload, false);
1101   return NS_OK;
1102 }
1103 
FirePageHideNotificationInternal(bool aIsUnload,bool aSkipCheckingDynEntries)1104 void nsDocShell::FirePageHideNotificationInternal(
1105     bool aIsUnload, bool aSkipCheckingDynEntries) {
1106   if (mContentViewer && !mFiredUnloadEvent) {
1107     // Keep an explicit reference since calling PageHide could release
1108     // mContentViewer
1109     nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1110     mFiredUnloadEvent = true;
1111 
1112     if (mTiming) {
1113       mTiming->NotifyUnloadEventStart();
1114     }
1115 
1116     contentViewer->PageHide(aIsUnload);
1117 
1118     if (mTiming) {
1119       mTiming->NotifyUnloadEventEnd();
1120     }
1121 
1122     AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1123     uint32_t n = mChildList.Length();
1124     kids.SetCapacity(n);
1125     for (uint32_t i = 0; i < n; i++) {
1126       kids.AppendElement(do_QueryInterface(ChildAt(i)));
1127     }
1128 
1129     n = kids.Length();
1130     for (uint32_t i = 0; i < n; ++i) {
1131       RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1132       if (child) {
1133         // Skip checking dynamic subframe entries in our children.
1134         child->FirePageHideNotificationInternal(aIsUnload, true);
1135       }
1136     }
1137 
1138     // If the document is unloading, remove all dynamic subframe entries.
1139     if (aIsUnload && !aSkipCheckingDynEntries) {
1140       nsCOMPtr<nsISHistory> rootSH;
1141       GetRootSessionHistory(getter_AddRefs(rootSH));
1142       nsCOMPtr<nsISHistoryInternal> shPrivate = do_QueryInterface(rootSH);
1143       nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE));
1144       if (shPrivate && container) {
1145         int32_t index = -1;
1146         rootSH->GetIndex(&index);
1147         shPrivate->RemoveDynEntries(index, container);
1148       }
1149     }
1150 
1151     // Now make sure our editor, if any, is detached before we go
1152     // any farther.
1153     DetachEditorFromWindow();
1154   }
1155 }
1156 
DispatchToTabGroup(TaskCategory aCategory,already_AddRefed<nsIRunnable> && aRunnable)1157 nsresult nsDocShell::DispatchToTabGroup(
1158     TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
1159   // Hold the ref so we won't forget to release it.
1160   nsCOMPtr<nsIRunnable> runnable(aRunnable);
1161   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1162   if (!win) {
1163     // Window should only be unavailable after destroyed.
1164     MOZ_ASSERT(mIsBeingDestroyed);
1165     return NS_ERROR_FAILURE;
1166   }
1167 
1168   if (win->GetDocGroup()) {
1169     return win->GetDocGroup()->Dispatch(aCategory, runnable.forget());
1170   }
1171   RefPtr<mozilla::dom::TabGroup> tabGroup = win->TabGroup();
1172   return tabGroup->Dispatch(aCategory, runnable.forget());
1173 }
1174 
1175 NS_IMETHODIMP
DispatchLocationChangeEvent()1176 nsDocShell::DispatchLocationChangeEvent() {
1177   return DispatchToTabGroup(
1178       TaskCategory::Other,
1179       NewRunnableMethod("nsDocShell::FireDummyOnLocationChange", this,
1180                         &nsDocShell::FireDummyOnLocationChange));
1181 }
1182 
MaybeInitTiming()1183 bool nsDocShell::MaybeInitTiming() {
1184   if (mTiming && !mBlankTiming) {
1185     return false;
1186   }
1187 
1188   bool canBeReset = false;
1189 
1190   if (mScriptGlobal && mBlankTiming) {
1191     nsPIDOMWindowInner* innerWin =
1192         mScriptGlobal->AsOuter()->GetCurrentInnerWindow();
1193     if (innerWin && innerWin->GetPerformance()) {
1194       mTiming = innerWin->GetPerformance()->GetDOMTiming();
1195       mBlankTiming = false;
1196     }
1197   }
1198 
1199   if (!mTiming) {
1200     mTiming = new nsDOMNavigationTiming(this);
1201     canBeReset = true;
1202   }
1203 
1204   mTiming->NotifyNavigationStart(
1205       mIsActive ? nsDOMNavigationTiming::DocShellState::eActive
1206                 : nsDOMNavigationTiming::DocShellState::eInactive);
1207 
1208   return canBeReset;
1209 }
1210 
MaybeResetInitTiming(bool aReset)1211 void nsDocShell::MaybeResetInitTiming(bool aReset) {
1212   if (aReset) {
1213     mTiming = nullptr;
1214   }
1215 }
1216 
GetNavigationTiming() const1217 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
1218   return mTiming;
1219 }
1220 
1221 //
1222 // Bug 13871: Prevent frameset spoofing
1223 //
1224 // This routine answers: 'Is origin's document from same domain as
1225 // target's document?'
1226 //
1227 // file: uris are considered the same domain for the purpose of
1228 // frame navigation regardless of script accessibility (bug 420425)
1229 //
ValidateOrigin(nsIDocShellTreeItem * aOriginTreeItem,nsIDocShellTreeItem * aTargetTreeItem)1230 /* static */ bool nsDocShell::ValidateOrigin(
1231     nsIDocShellTreeItem* aOriginTreeItem,
1232     nsIDocShellTreeItem* aTargetTreeItem) {
1233   // We want to bypass this check for chrome callers, but only if there's
1234   // JS on the stack. System callers still need to do it.
1235   if (nsContentUtils::GetCurrentJSContext() &&
1236       nsContentUtils::IsCallerChrome()) {
1237     return true;
1238   }
1239 
1240   MOZ_ASSERT(aOriginTreeItem && aTargetTreeItem, "need two docshells");
1241 
1242   // Get origin document principal
1243   nsCOMPtr<nsIDocument> originDocument = aOriginTreeItem->GetDocument();
1244   NS_ENSURE_TRUE(originDocument, false);
1245 
1246   // Get target principal
1247   nsCOMPtr<nsIDocument> targetDocument = aTargetTreeItem->GetDocument();
1248   NS_ENSURE_TRUE(targetDocument, false);
1249 
1250   bool equal;
1251   nsresult rv = originDocument->NodePrincipal()->Equals(
1252       targetDocument->NodePrincipal(), &equal);
1253   if (NS_SUCCEEDED(rv) && equal) {
1254     return true;
1255   }
1256 
1257   // Not strictly equal, special case if both are file: uris
1258   bool originIsFile = false;
1259   bool targetIsFile = false;
1260   nsCOMPtr<nsIURI> originURI;
1261   nsCOMPtr<nsIURI> targetURI;
1262   nsCOMPtr<nsIURI> innerOriginURI;
1263   nsCOMPtr<nsIURI> innerTargetURI;
1264 
1265   rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1266   if (NS_SUCCEEDED(rv) && originURI) {
1267     innerOriginURI = NS_GetInnermostURI(originURI);
1268   }
1269 
1270   rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1271   if (NS_SUCCEEDED(rv) && targetURI) {
1272     innerTargetURI = NS_GetInnermostURI(targetURI);
1273   }
1274 
1275   return innerOriginURI && innerTargetURI &&
1276          NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1277          NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1278          originIsFile && targetIsFile;
1279 }
1280 
GetEldestPresContext(nsPresContext ** aPresContext)1281 nsresult nsDocShell::GetEldestPresContext(nsPresContext** aPresContext) {
1282   NS_ENSURE_ARG_POINTER(aPresContext);
1283   *aPresContext = nullptr;
1284 
1285   nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1286   while (viewer) {
1287     nsCOMPtr<nsIContentViewer> prevViewer;
1288     viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1289     if (!prevViewer) {
1290       return viewer->GetPresContext(aPresContext);
1291     }
1292     viewer = prevViewer;
1293   }
1294 
1295   return NS_OK;
1296 }
1297 
1298 NS_IMETHODIMP
GetPresContext(nsPresContext ** aPresContext)1299 nsDocShell::GetPresContext(nsPresContext** aPresContext) {
1300   NS_ENSURE_ARG_POINTER(aPresContext);
1301   *aPresContext = nullptr;
1302 
1303   if (!mContentViewer) {
1304     return NS_OK;
1305   }
1306 
1307   return mContentViewer->GetPresContext(aPresContext);
1308 }
1309 
NS_IMETHODIMP_(nsIPresShell *)1310 NS_IMETHODIMP_(nsIPresShell*)
1311 nsDocShell::GetPresShell() {
1312   RefPtr<nsPresContext> presContext;
1313   (void)GetPresContext(getter_AddRefs(presContext));
1314   return presContext ? presContext->GetPresShell() : nullptr;
1315 }
1316 
1317 NS_IMETHODIMP
GetEldestPresShell(nsIPresShell ** aPresShell)1318 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell) {
1319   nsresult rv = NS_OK;
1320 
1321   NS_ENSURE_ARG_POINTER(aPresShell);
1322   *aPresShell = nullptr;
1323 
1324   RefPtr<nsPresContext> presContext;
1325   (void)GetEldestPresContext(getter_AddRefs(presContext));
1326 
1327   if (presContext) {
1328     NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1329   }
1330 
1331   return rv;
1332 }
1333 
1334 NS_IMETHODIMP
GetContentViewer(nsIContentViewer ** aContentViewer)1335 nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer) {
1336   NS_ENSURE_ARG_POINTER(aContentViewer);
1337 
1338   *aContentViewer = mContentViewer;
1339   NS_IF_ADDREF(*aContentViewer);
1340   return NS_OK;
1341 }
1342 
1343 NS_IMETHODIMP
SetChromeEventHandler(nsIDOMEventTarget * aChromeEventHandler)1344 nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler) {
1345   mChromeEventHandler = do_QueryInterface(aChromeEventHandler);
1346 
1347   if (mScriptGlobal) {
1348     mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1349   }
1350 
1351   return NS_OK;
1352 }
1353 
1354 NS_IMETHODIMP
GetChromeEventHandler(nsIDOMEventTarget ** aChromeEventHandler)1355 nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler) {
1356   NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1357   nsCOMPtr<EventTarget> handler = mChromeEventHandler;
1358   handler.forget(aChromeEventHandler);
1359   return NS_OK;
1360 }
1361 
1362 NS_IMETHODIMP
SetCurrentURI(nsIURI * aURI)1363 nsDocShell::SetCurrentURI(nsIURI* aURI) {
1364   // Note that securityUI will set STATE_IS_INSECURE, even if
1365   // the scheme of |aURI| is "https".
1366   SetCurrentURI(aURI, nullptr, true, 0);
1367   return NS_OK;
1368 }
1369 
SetCurrentURI(nsIURI * aURI,nsIRequest * aRequest,bool aFireOnLocationChange,uint32_t aLocationFlags)1370 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1371                                bool aFireOnLocationChange,
1372                                uint32_t aLocationFlags) {
1373   MOZ_ASSERT(!mIsBeingDestroyed);
1374 
1375   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1376           ("DOCSHELL %p SetCurrentURI %s\n", this,
1377            aURI ? aURI->GetSpecOrDefault().get() : ""));
1378 
1379   // We don't want to send a location change when we're displaying an error
1380   // page, and we don't want to change our idea of "current URI" either
1381   if (mLoadType == LOAD_ERROR_PAGE) {
1382     return false;
1383   }
1384 
1385   mCurrentURI = NS_TryToMakeImmutable(aURI);
1386 
1387   if (!NS_IsAboutBlank(mCurrentURI)) {
1388     mHasLoadedNonBlankURI = true;
1389   }
1390 
1391   bool isRoot = false;      // Is this the root docshell
1392   bool isSubFrame = false;  // Is this a subframe navigation?
1393 
1394   nsCOMPtr<nsIDocShellTreeItem> root;
1395 
1396   GetSameTypeRootTreeItem(getter_AddRefs(root));
1397   if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
1398     // This is the root docshell
1399     isRoot = true;
1400   }
1401   if (mLSHE) {
1402     mLSHE->GetIsSubFrame(&isSubFrame);
1403   }
1404 
1405   if (!isSubFrame && !isRoot) {
1406     /*
1407      * We don't want to send OnLocationChange notifications when
1408      * a subframe is being loaded for the first time, while
1409      * visiting a frameset page
1410      */
1411     return false;
1412   }
1413 
1414   if (aFireOnLocationChange) {
1415     FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1416   }
1417   return !aFireOnLocationChange;
1418 }
1419 
1420 NS_IMETHODIMP
GetCharset(nsACString & aCharset)1421 nsDocShell::GetCharset(nsACString& aCharset) {
1422   aCharset.Truncate();
1423 
1424   nsIPresShell* presShell = GetPresShell();
1425   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1426   nsIDocument* doc = presShell->GetDocument();
1427   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1428   doc->GetDocumentCharacterSet()->Name(aCharset);
1429   return NS_OK;
1430 }
1431 
1432 NS_IMETHODIMP
GatherCharsetMenuTelemetry()1433 nsDocShell::GatherCharsetMenuTelemetry() {
1434   nsCOMPtr<nsIContentViewer> viewer;
1435   GetContentViewer(getter_AddRefs(viewer));
1436   if (!viewer) {
1437     return NS_OK;
1438   }
1439 
1440   nsIDocument* doc = viewer->GetDocument();
1441   if (!doc || doc->WillIgnoreCharsetOverride()) {
1442     return NS_OK;
1443   }
1444 
1445   Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
1446 
1447   bool isFileURL = false;
1448   nsIURI* url = doc->GetOriginalURI();
1449   if (url) {
1450     url->SchemeIs("file", &isFileURL);
1451   }
1452 
1453   int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1454   switch (charsetSource) {
1455     case kCharsetFromTopLevelDomain:
1456       // Unlabeled doc on a domain that we map to a fallback encoding
1457       Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7);
1458       break;
1459     case kCharsetFromFallback:
1460     case kCharsetFromDocTypeDefault:
1461     case kCharsetFromCache:
1462     case kCharsetFromParentFrame:
1463     case kCharsetFromHintPrevDoc:
1464       // Changing charset on an unlabeled doc.
1465       if (isFileURL) {
1466         Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0);
1467       } else {
1468         Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1);
1469       }
1470       break;
1471     case kCharsetFromAutoDetection:
1472       // Changing charset on unlabeled doc where chardet fired
1473       if (isFileURL) {
1474         Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2);
1475       } else {
1476         Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3);
1477       }
1478       break;
1479     case kCharsetFromMetaPrescan:
1480     case kCharsetFromMetaTag:
1481     case kCharsetFromChannel:
1482       // Changing charset on a doc that had a charset label.
1483       Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4);
1484       break;
1485     case kCharsetFromParentForced:
1486     case kCharsetFromUserForced:
1487       // Changing charset on a document that already had an override.
1488       Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5);
1489       break;
1490     case kCharsetFromIrreversibleAutoDetection:
1491     case kCharsetFromOtherComponent:
1492     case kCharsetFromByteOrderMark:
1493     case kCharsetUninitialized:
1494     default:
1495       // Bug. This isn't supposed to happen.
1496       Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6);
1497       break;
1498   }
1499   return NS_OK;
1500 }
1501 
1502 NS_IMETHODIMP
SetCharset(const nsACString & aCharset)1503 nsDocShell::SetCharset(const nsACString& aCharset) {
1504   // set the charset override
1505   return SetForcedCharset(aCharset);
1506 }
1507 
1508 NS_IMETHODIMP
SetForcedCharset(const nsACString & aCharset)1509 nsDocShell::SetForcedCharset(const nsACString& aCharset) {
1510   if (aCharset.IsEmpty()) {
1511     mForcedCharset = nullptr;
1512     return NS_OK;
1513   }
1514   const Encoding* encoding = Encoding::ForLabel(aCharset);
1515   if (!encoding) {
1516     // Reject unknown labels
1517     return NS_ERROR_INVALID_ARG;
1518   }
1519   if (!encoding->IsAsciiCompatible() && encoding != ISO_2022_JP_ENCODING) {
1520     // Reject XSS hazards
1521     return NS_ERROR_INVALID_ARG;
1522   }
1523   mForcedCharset = encoding;
1524   return NS_OK;
1525 }
1526 
1527 NS_IMETHODIMP
GetForcedCharset(nsACString & aResult)1528 nsDocShell::GetForcedCharset(nsACString& aResult) {
1529   if (mForcedCharset) {
1530     mForcedCharset->Name(aResult);
1531   } else {
1532     aResult.Truncate();
1533   }
1534   return NS_OK;
1535 }
1536 
SetParentCharset(const Encoding * & aCharset,int32_t aCharsetSource,nsIPrincipal * aPrincipal)1537 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1538                                   int32_t aCharsetSource,
1539                                   nsIPrincipal* aPrincipal) {
1540   mParentCharset = aCharset;
1541   mParentCharsetSource = aCharsetSource;
1542   mParentCharsetPrincipal = aPrincipal;
1543 }
1544 
GetParentCharset(const Encoding * & aCharset,int32_t * aCharsetSource,nsIPrincipal ** aPrincipal)1545 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1546                                   int32_t* aCharsetSource,
1547                                   nsIPrincipal** aPrincipal) {
1548   aCharset = mParentCharset;
1549   *aCharsetSource = mParentCharsetSource;
1550   NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1551 }
1552 
1553 NS_IMETHODIMP
GetChannelIsUnsafe(bool * aUnsafe)1554 nsDocShell::GetChannelIsUnsafe(bool* aUnsafe) {
1555   *aUnsafe = false;
1556 
1557   nsIChannel* channel = GetCurrentDocChannel();
1558   if (!channel) {
1559     return NS_OK;
1560   }
1561 
1562   nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
1563   if (!jarChannel) {
1564     return NS_OK;
1565   }
1566 
1567   return jarChannel->GetIsUnsafe(aUnsafe);
1568 }
1569 
1570 NS_IMETHODIMP
GetHasMixedActiveContentLoaded(bool * aHasMixedActiveContentLoaded)1571 nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded) {
1572   nsCOMPtr<nsIDocument> doc(GetDocument());
1573   *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
1574   return NS_OK;
1575 }
1576 
1577 NS_IMETHODIMP
GetHasMixedActiveContentBlocked(bool * aHasMixedActiveContentBlocked)1578 nsDocShell::GetHasMixedActiveContentBlocked(
1579     bool* aHasMixedActiveContentBlocked) {
1580   nsCOMPtr<nsIDocument> doc(GetDocument());
1581   *aHasMixedActiveContentBlocked =
1582       doc && doc->GetHasMixedActiveContentBlocked();
1583   return NS_OK;
1584 }
1585 
1586 NS_IMETHODIMP
GetHasMixedDisplayContentLoaded(bool * aHasMixedDisplayContentLoaded)1587 nsDocShell::GetHasMixedDisplayContentLoaded(
1588     bool* aHasMixedDisplayContentLoaded) {
1589   nsCOMPtr<nsIDocument> doc(GetDocument());
1590   *aHasMixedDisplayContentLoaded =
1591       doc && doc->GetHasMixedDisplayContentLoaded();
1592   return NS_OK;
1593 }
1594 
1595 NS_IMETHODIMP
GetHasMixedDisplayContentBlocked(bool * aHasMixedDisplayContentBlocked)1596 nsDocShell::GetHasMixedDisplayContentBlocked(
1597     bool* aHasMixedDisplayContentBlocked) {
1598   nsCOMPtr<nsIDocument> doc(GetDocument());
1599   *aHasMixedDisplayContentBlocked =
1600       doc && doc->GetHasMixedDisplayContentBlocked();
1601   return NS_OK;
1602 }
1603 
1604 NS_IMETHODIMP
GetHasTrackingContentBlocked(bool * aHasTrackingContentBlocked)1605 nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked) {
1606   nsCOMPtr<nsIDocument> doc(GetDocument());
1607   *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
1608   return NS_OK;
1609 }
1610 
1611 NS_IMETHODIMP
GetHasTrackingContentLoaded(bool * aHasTrackingContentLoaded)1612 nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded) {
1613   nsCOMPtr<nsIDocument> doc(GetDocument());
1614   *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
1615   return NS_OK;
1616 }
1617 
1618 NS_IMETHODIMP
GetAllowPlugins(bool * aAllowPlugins)1619 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
1620   NS_ENSURE_ARG_POINTER(aAllowPlugins);
1621 
1622   *aAllowPlugins = mAllowPlugins;
1623   if (!mAllowPlugins) {
1624     return NS_OK;
1625   }
1626 
1627   bool unsafe;
1628   *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1629   return NS_OK;
1630 }
1631 
1632 NS_IMETHODIMP
SetAllowPlugins(bool aAllowPlugins)1633 nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
1634   mAllowPlugins = aAllowPlugins;
1635   // XXX should enable or disable a plugin host
1636   return NS_OK;
1637 }
1638 
1639 NS_IMETHODIMP
GetAllowJavascript(bool * aAllowJavascript)1640 nsDocShell::GetAllowJavascript(bool* aAllowJavascript) {
1641   NS_ENSURE_ARG_POINTER(aAllowJavascript);
1642 
1643   *aAllowJavascript = mAllowJavascript;
1644   return NS_OK;
1645 }
1646 
1647 NS_IMETHODIMP
SetAllowJavascript(bool aAllowJavascript)1648 nsDocShell::SetAllowJavascript(bool aAllowJavascript) {
1649   mAllowJavascript = aAllowJavascript;
1650   RecomputeCanExecuteScripts();
1651   return NS_OK;
1652 }
1653 
1654 NS_IMETHODIMP
GetUsePrivateBrowsing(bool * aUsePrivateBrowsing)1655 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1656   NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1657   AssertOriginAttributesMatchPrivateBrowsing();
1658   *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
1659   return NS_OK;
1660 }
1661 
1662 NS_IMETHODIMP
SetUsePrivateBrowsing(bool aUsePrivateBrowsing)1663 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1664   nsContentUtils::ReportToConsoleNonLocalized(
1665       NS_LITERAL_STRING("Only internal code is allowed to set the "
1666                         "usePrivateBrowsing attribute"),
1667       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Internal API Used"),
1668       mContentViewer ? mContentViewer->GetDocument() : nullptr);
1669 
1670   if (!CanSetOriginAttributes()) {
1671     bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1672 
1673     return changed ? NS_ERROR_FAILURE : NS_OK;
1674   }
1675 
1676   return SetPrivateBrowsing(aUsePrivateBrowsing);
1677 }
1678 
1679 NS_IMETHODIMP
SetPrivateBrowsing(bool aUsePrivateBrowsing)1680 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1681   MOZ_ASSERT(!mIsBeingDestroyed);
1682 
1683   bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1684   if (changed) {
1685     mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0;
1686 
1687     if (mItemType != typeChrome) {
1688       mOriginAttributes.SyncAttributesWithPrivateBrowsing(aUsePrivateBrowsing);
1689     }
1690 
1691     if (mAffectPrivateSessionLifetime) {
1692       if (aUsePrivateBrowsing) {
1693         IncreasePrivateDocShellCount();
1694       } else {
1695         DecreasePrivateDocShellCount();
1696       }
1697     }
1698   }
1699 
1700   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
1701   while (iter.HasMore()) {
1702     nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
1703     if (shell) {
1704       shell->SetPrivateBrowsing(aUsePrivateBrowsing);
1705     }
1706   }
1707 
1708   if (changed) {
1709     nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1710     while (iter.HasMore()) {
1711       nsWeakPtr ref = iter.GetNext();
1712       nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1713       if (!obs) {
1714         mPrivacyObservers.RemoveElement(ref);
1715       } else {
1716         obs->PrivateModeChanged(aUsePrivateBrowsing);
1717       }
1718     }
1719   }
1720 
1721   AssertOriginAttributesMatchPrivateBrowsing();
1722   return NS_OK;
1723 }
1724 
1725 NS_IMETHODIMP
GetHasLoadedNonBlankURI(bool * aResult)1726 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1727   NS_ENSURE_ARG_POINTER(aResult);
1728 
1729   *aResult = mHasLoadedNonBlankURI;
1730   return NS_OK;
1731 }
1732 
1733 NS_IMETHODIMP
GetUseRemoteTabs(bool * aUseRemoteTabs)1734 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1735   NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1736 
1737   *aUseRemoteTabs = mUseRemoteTabs;
1738   return NS_OK;
1739 }
1740 
1741 NS_IMETHODIMP
SetRemoteTabs(bool aUseRemoteTabs)1742 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1743   if (aUseRemoteTabs) {
1744     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
1745                                        NS_LITERAL_CSTRING("1"));
1746   }
1747 
1748   mUseRemoteTabs = aUseRemoteTabs;
1749   return NS_OK;
1750 }
1751 
1752 NS_IMETHODIMP
SetAffectPrivateSessionLifetime(bool aAffectLifetime)1753 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) {
1754   MOZ_ASSERT(!mIsBeingDestroyed);
1755 
1756   bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
1757   if (change && UsePrivateBrowsing()) {
1758     AssertOriginAttributesMatchPrivateBrowsing();
1759     if (aAffectLifetime) {
1760       IncreasePrivateDocShellCount();
1761     } else {
1762       DecreasePrivateDocShellCount();
1763     }
1764   }
1765   mAffectPrivateSessionLifetime = aAffectLifetime;
1766 
1767   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
1768   while (iter.HasMore()) {
1769     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
1770     if (shell) {
1771       shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
1772     }
1773   }
1774   return NS_OK;
1775 }
1776 
1777 NS_IMETHODIMP
GetAffectPrivateSessionLifetime(bool * aAffectLifetime)1778 nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime) {
1779   *aAffectLifetime = mAffectPrivateSessionLifetime;
1780   return NS_OK;
1781 }
1782 
1783 NS_IMETHODIMP
AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver * aObserver)1784 nsDocShell::AddWeakPrivacyTransitionObserver(
1785     nsIPrivacyTransitionObserver* aObserver) {
1786   nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1787   if (!weakObs) {
1788     return NS_ERROR_NOT_AVAILABLE;
1789   }
1790   return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
1791 }
1792 
1793 NS_IMETHODIMP
AddWeakReflowObserver(nsIReflowObserver * aObserver)1794 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1795   nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1796   if (!weakObs) {
1797     return NS_ERROR_FAILURE;
1798   }
1799   return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
1800 }
1801 
1802 NS_IMETHODIMP
RemoveWeakReflowObserver(nsIReflowObserver * aObserver)1803 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1804   nsWeakPtr obs = do_GetWeakReference(aObserver);
1805   return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1806 }
1807 
1808 NS_IMETHODIMP
NotifyReflowObservers(bool aInterruptible,DOMHighResTimeStamp aStart,DOMHighResTimeStamp aEnd)1809 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1810                                   DOMHighResTimeStamp aStart,
1811                                   DOMHighResTimeStamp aEnd) {
1812   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1813   while (iter.HasMore()) {
1814     nsWeakPtr ref = iter.GetNext();
1815     nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1816     if (!obs) {
1817       mReflowObservers.RemoveElement(ref);
1818     } else if (aInterruptible) {
1819       obs->ReflowInterruptible(aStart, aEnd);
1820     } else {
1821       obs->Reflow(aStart, aEnd);
1822     }
1823   }
1824   return NS_OK;
1825 }
1826 
1827 NS_IMETHODIMP
GetAllowMetaRedirects(bool * aReturn)1828 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1829   NS_ENSURE_ARG_POINTER(aReturn);
1830 
1831   *aReturn = mAllowMetaRedirects;
1832   if (!mAllowMetaRedirects) {
1833     return NS_OK;
1834   }
1835 
1836   bool unsafe;
1837   *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
1838   return NS_OK;
1839 }
1840 
1841 NS_IMETHODIMP
SetAllowMetaRedirects(bool aValue)1842 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1843   mAllowMetaRedirects = aValue;
1844   return NS_OK;
1845 }
1846 
1847 NS_IMETHODIMP
GetAllowSubframes(bool * aAllowSubframes)1848 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1849   NS_ENSURE_ARG_POINTER(aAllowSubframes);
1850 
1851   *aAllowSubframes = mAllowSubframes;
1852   return NS_OK;
1853 }
1854 
1855 NS_IMETHODIMP
SetAllowSubframes(bool aAllowSubframes)1856 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1857   mAllowSubframes = aAllowSubframes;
1858   return NS_OK;
1859 }
1860 
1861 NS_IMETHODIMP
GetAllowImages(bool * aAllowImages)1862 nsDocShell::GetAllowImages(bool* aAllowImages) {
1863   NS_ENSURE_ARG_POINTER(aAllowImages);
1864 
1865   *aAllowImages = mAllowImages;
1866   return NS_OK;
1867 }
1868 
1869 NS_IMETHODIMP
SetAllowImages(bool aAllowImages)1870 nsDocShell::SetAllowImages(bool aAllowImages) {
1871   mAllowImages = aAllowImages;
1872   return NS_OK;
1873 }
1874 
1875 NS_IMETHODIMP
GetAllowMedia(bool * aAllowMedia)1876 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1877   *aAllowMedia = mAllowMedia;
1878   return NS_OK;
1879 }
1880 
1881 NS_IMETHODIMP
SetAllowMedia(bool aAllowMedia)1882 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1883   mAllowMedia = aAllowMedia;
1884 
1885   // Mute or unmute audio contexts attached to the inner window.
1886   if (mScriptGlobal) {
1887     if (nsPIDOMWindowInner* innerWin =
1888             mScriptGlobal->AsOuter()->GetCurrentInnerWindow()) {
1889       if (aAllowMedia) {
1890         innerWin->UnmuteAudioContexts();
1891       } else {
1892         innerWin->MuteAudioContexts();
1893       }
1894     }
1895   }
1896 
1897   return NS_OK;
1898 }
1899 
1900 NS_IMETHODIMP
GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)1901 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1902   *aAllowDNSPrefetch = mAllowDNSPrefetch;
1903   return NS_OK;
1904 }
1905 
1906 NS_IMETHODIMP
SetAllowDNSPrefetch(bool aAllowDNSPrefetch)1907 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
1908   mAllowDNSPrefetch = aAllowDNSPrefetch;
1909   return NS_OK;
1910 }
1911 
1912 NS_IMETHODIMP
GetAllowWindowControl(bool * aAllowWindowControl)1913 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
1914   *aAllowWindowControl = mAllowWindowControl;
1915   return NS_OK;
1916 }
1917 
1918 NS_IMETHODIMP
SetAllowWindowControl(bool aAllowWindowControl)1919 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
1920   mAllowWindowControl = aAllowWindowControl;
1921   return NS_OK;
1922 }
1923 
1924 NS_IMETHODIMP
GetAllowContentRetargeting(bool * aAllowContentRetargeting)1925 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
1926   *aAllowContentRetargeting = mAllowContentRetargeting;
1927   return NS_OK;
1928 }
1929 
1930 NS_IMETHODIMP
SetAllowContentRetargeting(bool aAllowContentRetargeting)1931 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
1932   mAllowContentRetargetingOnChildren = aAllowContentRetargeting;
1933   mAllowContentRetargeting = aAllowContentRetargeting;
1934   return NS_OK;
1935 }
1936 
1937 NS_IMETHODIMP
GetAllowContentRetargetingOnChildren(bool * aAllowContentRetargetingOnChildren)1938 nsDocShell::GetAllowContentRetargetingOnChildren(
1939     bool* aAllowContentRetargetingOnChildren) {
1940   *aAllowContentRetargetingOnChildren = mAllowContentRetargetingOnChildren;
1941   return NS_OK;
1942 }
1943 
1944 NS_IMETHODIMP
SetAllowContentRetargetingOnChildren(bool aAllowContentRetargetingOnChildren)1945 nsDocShell::SetAllowContentRetargetingOnChildren(
1946     bool aAllowContentRetargetingOnChildren) {
1947   mAllowContentRetargetingOnChildren = aAllowContentRetargetingOnChildren;
1948   return NS_OK;
1949 }
1950 
1951 NS_IMETHODIMP
GetInheritPrivateBrowsingId(bool * aInheritPrivateBrowsingId)1952 nsDocShell::GetInheritPrivateBrowsingId(bool* aInheritPrivateBrowsingId) {
1953   *aInheritPrivateBrowsingId = mInheritPrivateBrowsingId;
1954   return NS_OK;
1955 }
1956 
1957 NS_IMETHODIMP
SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId)1958 nsDocShell::SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId) {
1959   mInheritPrivateBrowsingId = aInheritPrivateBrowsingId;
1960   return NS_OK;
1961 }
1962 
1963 NS_IMETHODIMP
GetFullscreenAllowed(bool * aFullscreenAllowed)1964 nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed) {
1965   NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
1966 
1967   // Browsers and apps have their mFullscreenAllowed retrieved from their
1968   // corresponding iframe in their parent upon creation.
1969   if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
1970     *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
1971     return NS_OK;
1972   }
1973 
1974   // Assume false until we determine otherwise...
1975   *aFullscreenAllowed = false;
1976 
1977   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1978   if (!win) {
1979     return NS_OK;
1980   }
1981   if (nsCOMPtr<Element> frameElement = win->GetFrameElementInternal()) {
1982     if (frameElement->IsXULElement()) {
1983       if (frameElement->HasAttr(kNameSpaceID_None,
1984                                 nsGkAtoms::disablefullscreen)) {
1985         // Document inside this frame is explicitly disabled.
1986         return NS_OK;
1987       }
1988     } else {
1989       // We do not allow document inside any containing element other
1990       // than iframe to enter fullscreen.
1991       if (frameElement->IsHTMLElement(nsGkAtoms::iframe)) {
1992         // If any ancestor iframe does not have allowfullscreen attribute
1993         // set, then fullscreen is not allowed.
1994         if (!frameElement->HasAttr(kNameSpaceID_None,
1995                                    nsGkAtoms::allowfullscreen) &&
1996             !frameElement->HasAttr(kNameSpaceID_None,
1997                                    nsGkAtoms::mozallowfullscreen)) {
1998           return NS_OK;
1999         }
2000       } else if (frameElement->IsHTMLElement(nsGkAtoms::embed)) {
2001         // Respect allowfullscreen only if this is a rewritten YouTube embed.
2002         nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent =
2003             do_QueryInterface(frameElement);
2004         if (!objectLoadingContent) {
2005           return NS_OK;
2006         }
2007         nsObjectLoadingContent* olc =
2008             static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
2009         if (!olc->IsRewrittenYoutubeEmbed()) {
2010           return NS_OK;
2011         }
2012         // We don't have to check prefixed attributes because Flash does not
2013         // support them.
2014         if (!frameElement->HasAttr(kNameSpaceID_None,
2015                                    nsGkAtoms::allowfullscreen)) {
2016           return NS_OK;
2017         }
2018       } else {
2019         // neither iframe nor embed
2020         return NS_OK;
2021       }
2022     }
2023   }
2024 
2025   // If we have no parent then we're the root docshell; no ancestor of the
2026   // original docshell doesn't have a allowfullscreen attribute, so
2027   // report fullscreen as allowed.
2028   RefPtr<nsDocShell> parent = GetParentDocshell();
2029   if (!parent) {
2030     *aFullscreenAllowed = true;
2031     return NS_OK;
2032   }
2033 
2034   // Otherwise, we have a parent, continue the checking for
2035   // mozFullscreenAllowed in the parent docshell's ancestors.
2036   return parent->GetFullscreenAllowed(aFullscreenAllowed);
2037 }
2038 
2039 NS_IMETHODIMP
SetFullscreenAllowed(bool aFullscreenAllowed)2040 nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed) {
2041   if (!nsIDocShell::GetIsMozBrowser()) {
2042     // Only allow setting of fullscreenAllowed on content/process boundaries.
2043     // At non-boundaries the fullscreenAllowed attribute is calculated based on
2044     // whether all enclosing frames have the "mozFullscreenAllowed" attribute
2045     // set to "true". fullscreenAllowed is set at the process boundaries to
2046     // propagate the value of the parent's "mozFullscreenAllowed" attribute
2047     // across process boundaries.
2048     return NS_ERROR_UNEXPECTED;
2049   }
2050   mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
2051   return NS_OK;
2052 }
2053 
OrientationLock()2054 ScreenOrientationInternal nsDocShell::OrientationLock() {
2055   return mOrientationLock;
2056 }
2057 
SetOrientationLock(ScreenOrientationInternal aOrientationLock)2058 void nsDocShell::SetOrientationLock(
2059     ScreenOrientationInternal aOrientationLock) {
2060   mOrientationLock = aOrientationLock;
2061 }
2062 
2063 NS_IMETHODIMP
GetMayEnableCharacterEncodingMenu(bool * aMayEnableCharacterEncodingMenu)2064 nsDocShell::GetMayEnableCharacterEncodingMenu(
2065     bool* aMayEnableCharacterEncodingMenu) {
2066   *aMayEnableCharacterEncodingMenu = false;
2067   if (!mContentViewer) {
2068     return NS_OK;
2069   }
2070   nsIDocument* doc = mContentViewer->GetDocument();
2071   if (!doc) {
2072     return NS_OK;
2073   }
2074   if (doc->WillIgnoreCharsetOverride()) {
2075     return NS_OK;
2076   }
2077 
2078   *aMayEnableCharacterEncodingMenu = true;
2079   return NS_OK;
2080 }
2081 
2082 NS_IMETHODIMP
GetDocShellEnumerator(int32_t aItemType,int32_t aDirection,nsISimpleEnumerator ** aResult)2083 nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection,
2084                                   nsISimpleEnumerator** aResult) {
2085   NS_ENSURE_ARG_POINTER(aResult);
2086   *aResult = nullptr;
2087 
2088   RefPtr<nsDocShellEnumerator> docShellEnum;
2089   if (aDirection == ENUMERATE_FORWARDS) {
2090     docShellEnum = new nsDocShellForwardsEnumerator;
2091   } else {
2092     docShellEnum = new nsDocShellBackwardsEnumerator;
2093   }
2094 
2095   nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
2096   if (NS_FAILED(rv)) {
2097     return rv;
2098   }
2099 
2100   rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem*)this);
2101   if (NS_FAILED(rv)) {
2102     return rv;
2103   }
2104 
2105   rv = docShellEnum->First();
2106   if (NS_FAILED(rv)) {
2107     return rv;
2108   }
2109 
2110   rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator),
2111                                     (void**)aResult);
2112 
2113   return rv;
2114 }
2115 
2116 NS_IMETHODIMP
GetAppType(uint32_t * aAppType)2117 nsDocShell::GetAppType(uint32_t* aAppType) {
2118   *aAppType = mAppType;
2119   return NS_OK;
2120 }
2121 
2122 NS_IMETHODIMP
SetAppType(uint32_t aAppType)2123 nsDocShell::SetAppType(uint32_t aAppType) {
2124   mAppType = aAppType;
2125   return NS_OK;
2126 }
2127 
2128 NS_IMETHODIMP
GetAllowAuth(bool * aAllowAuth)2129 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2130   *aAllowAuth = mAllowAuth;
2131   return NS_OK;
2132 }
2133 
2134 NS_IMETHODIMP
SetAllowAuth(bool aAllowAuth)2135 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2136   mAllowAuth = aAllowAuth;
2137   return NS_OK;
2138 }
2139 
2140 NS_IMETHODIMP
GetZoom(float * aZoom)2141 nsDocShell::GetZoom(float* aZoom) {
2142   NS_ENSURE_ARG_POINTER(aZoom);
2143   *aZoom = 1.0f;
2144   return NS_OK;
2145 }
2146 
2147 NS_IMETHODIMP
SetZoom(float aZoom)2148 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2149 
2150 NS_IMETHODIMP
GetMarginWidth(int32_t * aWidth)2151 nsDocShell::GetMarginWidth(int32_t* aWidth) {
2152   NS_ENSURE_ARG_POINTER(aWidth);
2153 
2154   *aWidth = mMarginWidth;
2155   return NS_OK;
2156 }
2157 
2158 NS_IMETHODIMP
SetMarginWidth(int32_t aWidth)2159 nsDocShell::SetMarginWidth(int32_t aWidth) {
2160   mMarginWidth = aWidth;
2161   return NS_OK;
2162 }
2163 
2164 NS_IMETHODIMP
GetMarginHeight(int32_t * aHeight)2165 nsDocShell::GetMarginHeight(int32_t* aHeight) {
2166   NS_ENSURE_ARG_POINTER(aHeight);
2167 
2168   *aHeight = mMarginHeight;
2169   return NS_OK;
2170 }
2171 
2172 NS_IMETHODIMP
SetMarginHeight(int32_t aHeight)2173 nsDocShell::SetMarginHeight(int32_t aHeight) {
2174   mMarginHeight = aHeight;
2175   return NS_OK;
2176 }
2177 
2178 NS_IMETHODIMP
GetBusyFlags(uint32_t * aBusyFlags)2179 nsDocShell::GetBusyFlags(uint32_t* aBusyFlags) {
2180   NS_ENSURE_ARG_POINTER(aBusyFlags);
2181 
2182   *aBusyFlags = mBusyFlags;
2183   return NS_OK;
2184 }
2185 
2186 NS_IMETHODIMP
TabToTreeOwner(bool aForward,bool aForDocumentNavigation,bool * aTookFocus)2187 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
2188                            bool* aTookFocus) {
2189   NS_ENSURE_ARG_POINTER(aTookFocus);
2190 
2191   nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2192   if (chromeFocus) {
2193     if (aForward) {
2194       *aTookFocus =
2195           NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2196     } else {
2197       *aTookFocus =
2198           NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2199     }
2200   } else {
2201     *aTookFocus = false;
2202   }
2203 
2204   return NS_OK;
2205 }
2206 
2207 NS_IMETHODIMP
GetSecurityUI(nsISecureBrowserUI ** aSecurityUI)2208 nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI) {
2209   NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2210   return NS_OK;
2211 }
2212 
2213 NS_IMETHODIMP
SetSecurityUI(nsISecureBrowserUI * aSecurityUI)2214 nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI) {
2215   MOZ_ASSERT(!mIsBeingDestroyed);
2216 
2217   mSecurityUI = aSecurityUI;
2218   mSecurityUI->SetDocShell(this);
2219   return NS_OK;
2220 }
2221 
2222 NS_IMETHODIMP
GetLoadURIDelegate(nsILoadURIDelegate ** aLoadURIDelegate)2223 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2224   NS_IF_ADDREF(*aLoadURIDelegate = mLoadURIDelegate);
2225   return NS_OK;
2226 }
2227 
2228 NS_IMETHODIMP
SetLoadURIDelegate(nsILoadURIDelegate * aLoadURIDelegate)2229 nsDocShell::SetLoadURIDelegate(nsILoadURIDelegate* aLoadURIDelegate) {
2230   mLoadURIDelegate = aLoadURIDelegate;
2231   return NS_OK;
2232 }
2233 
2234 NS_IMETHODIMP
GetUseErrorPages(bool * aUseErrorPages)2235 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2236   *aUseErrorPages = UseErrorPages();
2237   return NS_OK;
2238 }
2239 
2240 NS_IMETHODIMP
SetUseErrorPages(bool aUseErrorPages)2241 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2242   // If mUseErrorPages is set explicitly, stop using sUseErrorPages.
2243   if (mObserveErrorPages) {
2244     mObserveErrorPages = false;
2245   }
2246   mUseErrorPages = aUseErrorPages;
2247   return NS_OK;
2248 }
2249 
2250 NS_IMETHODIMP
GetPreviousTransIndex(int32_t * aPreviousTransIndex)2251 nsDocShell::GetPreviousTransIndex(int32_t* aPreviousTransIndex) {
2252   *aPreviousTransIndex = mPreviousTransIndex;
2253   return NS_OK;
2254 }
2255 
2256 NS_IMETHODIMP
GetLoadedTransIndex(int32_t * aLoadedTransIndex)2257 nsDocShell::GetLoadedTransIndex(int32_t* aLoadedTransIndex) {
2258   *aLoadedTransIndex = mLoadedTransIndex;
2259   return NS_OK;
2260 }
2261 
2262 NS_IMETHODIMP
HistoryPurged(int32_t aNumEntries)2263 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2264   // These indices are used for fastback cache eviction, to determine
2265   // which session history entries are candidates for content viewer
2266   // eviction.  We need to adjust by the number of entries that we
2267   // just purged from history, so that we look at the right session history
2268   // entries during eviction.
2269   mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
2270   mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
2271 
2272   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2273   while (iter.HasMore()) {
2274     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2275     if (shell) {
2276       shell->HistoryPurged(aNumEntries);
2277     }
2278   }
2279 
2280   return NS_OK;
2281 }
2282 
HistoryTransactionRemoved(int32_t aIndex)2283 nsresult nsDocShell::HistoryTransactionRemoved(int32_t aIndex) {
2284   // These indices are used for fastback cache eviction, to determine
2285   // which session history entries are candidates for content viewer
2286   // eviction.  We need to adjust by the number of entries that we
2287   // just purged from history, so that we look at the right session history
2288   // entries during eviction.
2289   if (aIndex == mPreviousTransIndex) {
2290     mPreviousTransIndex = -1;
2291   } else if (aIndex < mPreviousTransIndex) {
2292     --mPreviousTransIndex;
2293   }
2294   if (mLoadedTransIndex == aIndex) {
2295     mLoadedTransIndex = 0;
2296   } else if (aIndex < mLoadedTransIndex) {
2297     --mLoadedTransIndex;
2298   }
2299 
2300   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2301   while (iter.HasMore()) {
2302     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2303     if (shell) {
2304       static_cast<nsDocShell*>(shell.get())->HistoryTransactionRemoved(aIndex);
2305     }
2306   }
2307 
2308   return NS_OK;
2309 }
2310 
2311 NS_IMETHODIMP
SetRecordProfileTimelineMarkers(bool aValue)2312 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) {
2313   bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
2314   if (currentValue == aValue) {
2315     return NS_OK;
2316   }
2317 
2318   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2319   if (!timelines) {
2320     return NS_OK;
2321   }
2322 
2323   if (aValue) {
2324     MOZ_ASSERT(!timelines->HasConsumer(this));
2325     timelines->AddConsumer(this);
2326     MOZ_ASSERT(timelines->HasConsumer(this));
2327     UseEntryScriptProfiling();
2328   } else {
2329     MOZ_ASSERT(timelines->HasConsumer(this));
2330     timelines->RemoveConsumer(this);
2331     MOZ_ASSERT(!timelines->HasConsumer(this));
2332     UnuseEntryScriptProfiling();
2333   }
2334 
2335   return NS_OK;
2336 }
2337 
2338 NS_IMETHODIMP
GetRecordProfileTimelineMarkers(bool * aValue)2339 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) {
2340   *aValue = !!mObserved;
2341   return NS_OK;
2342 }
2343 
PopProfileTimelineMarkers(JSContext * aCx,JS::MutableHandle<JS::Value> aOut)2344 nsresult nsDocShell::PopProfileTimelineMarkers(
2345     JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
2346   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2347   if (!timelines) {
2348     return NS_OK;
2349   }
2350 
2351   nsTArray<dom::ProfileTimelineMarker> store;
2352   SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
2353 
2354   timelines->PopMarkers(this, aCx, store);
2355 
2356   if (!ToJSValue(aCx, store, aOut)) {
2357     JS_ClearPendingException(aCx);
2358     return NS_ERROR_UNEXPECTED;
2359   }
2360 
2361   return NS_OK;
2362 }
2363 
Now(DOMHighResTimeStamp * aWhen)2364 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2365   *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2366   return NS_OK;
2367 }
2368 
2369 NS_IMETHODIMP
SetWindowDraggingAllowed(bool aValue)2370 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2371   RefPtr<nsDocShell> parent = GetParentDocshell();
2372   if (!aValue && mItemType == typeChrome && !parent) {
2373     // Window dragging is always allowed for top level
2374     // chrome docshells.
2375     return NS_ERROR_FAILURE;
2376   }
2377   mWindowDraggingAllowed = aValue;
2378   return NS_OK;
2379 }
2380 
2381 NS_IMETHODIMP
GetWindowDraggingAllowed(bool * aValue)2382 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2383   // window dragging regions in CSS (-moz-window-drag:drag)
2384   // can be slow. Default behavior is to only allow it for
2385   // chrome top level windows.
2386   RefPtr<nsDocShell> parent = GetParentDocshell();
2387   if (mItemType == typeChrome && !parent) {
2388     // Top level chrome window
2389     *aValue = true;
2390   } else {
2391     *aValue = mWindowDraggingAllowed;
2392   }
2393   return NS_OK;
2394 }
2395 
TopSessionStorageManager()2396 nsIDOMStorageManager* nsDocShell::TopSessionStorageManager() {
2397   nsresult rv;
2398 
2399   nsCOMPtr<nsIDocShellTreeItem> topItem;
2400   rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2401   if (NS_FAILED(rv)) {
2402     return nullptr;
2403   }
2404 
2405   if (!topItem) {
2406     return nullptr;
2407   }
2408 
2409   nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2410   if (topDocShell != this) {
2411     return topDocShell->TopSessionStorageManager();
2412   }
2413 
2414   if (!mSessionStorageManager) {
2415     mSessionStorageManager =
2416         do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
2417   }
2418 
2419   return mSessionStorageManager;
2420 }
2421 
2422 NS_IMETHODIMP
GetCurrentDocumentChannel(nsIChannel ** aResult)2423 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2424   NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2425   return NS_OK;
2426 }
2427 
GetCurrentDocChannel()2428 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2429   if (mContentViewer) {
2430     nsIDocument* doc = mContentViewer->GetDocument();
2431     if (doc) {
2432       return doc->GetChannel();
2433     }
2434   }
2435   return nullptr;
2436 }
2437 
2438 NS_IMETHODIMP
AddWeakScrollObserver(nsIScrollObserver * aObserver)2439 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2440   nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2441   if (!weakObs) {
2442     return NS_ERROR_FAILURE;
2443   }
2444   return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2445 }
2446 
2447 NS_IMETHODIMP
RemoveWeakScrollObserver(nsIScrollObserver * aObserver)2448 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2449   nsWeakPtr obs = do_GetWeakReference(aObserver);
2450   return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2451 }
2452 
NotifyAsyncPanZoomStarted()2453 void nsDocShell::NotifyAsyncPanZoomStarted() {
2454   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2455   while (iter.HasMore()) {
2456     nsWeakPtr ref = iter.GetNext();
2457     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2458     if (obs) {
2459       obs->AsyncPanZoomStarted();
2460     } else {
2461       mScrollObservers.RemoveElement(ref);
2462     }
2463   }
2464 }
2465 
NotifyAsyncPanZoomStopped()2466 void nsDocShell::NotifyAsyncPanZoomStopped() {
2467   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2468   while (iter.HasMore()) {
2469     nsWeakPtr ref = iter.GetNext();
2470     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2471     if (obs) {
2472       obs->AsyncPanZoomStopped();
2473     } else {
2474       mScrollObservers.RemoveElement(ref);
2475     }
2476   }
2477 }
2478 
2479 NS_IMETHODIMP
NotifyScrollObservers()2480 nsDocShell::NotifyScrollObservers() {
2481   nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2482   while (iter.HasMore()) {
2483     nsWeakPtr ref = iter.GetNext();
2484     nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2485     if (obs) {
2486       obs->ScrollPositionChanged();
2487     } else {
2488       mScrollObservers.RemoveElement(ref);
2489     }
2490   }
2491   return NS_OK;
2492 }
2493 
2494 //*****************************************************************************
2495 // nsDocShell::nsIDocShellTreeItem
2496 //*****************************************************************************
2497 
2498 NS_IMETHODIMP
GetName(nsAString & aName)2499 nsDocShell::GetName(nsAString& aName) {
2500   aName = mName;
2501   return NS_OK;
2502 }
2503 
2504 NS_IMETHODIMP
SetName(const nsAString & aName)2505 nsDocShell::SetName(const nsAString& aName) {
2506   mName = aName;
2507   return NS_OK;
2508 }
2509 
2510 NS_IMETHODIMP
NameEquals(const nsAString & aName,bool * aResult)2511 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2512   NS_ENSURE_ARG_POINTER(aResult);
2513   *aResult = mName.Equals(aName);
2514   return NS_OK;
2515 }
2516 
2517 NS_IMETHODIMP
GetCustomUserAgent(nsAString & aCustomUserAgent)2518 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2519   aCustomUserAgent = mCustomUserAgent;
2520   return NS_OK;
2521 }
2522 
2523 NS_IMETHODIMP
SetCustomUserAgent(const nsAString & aCustomUserAgent)2524 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2525   mCustomUserAgent = aCustomUserAgent;
2526   RefPtr<nsGlobalWindowInner> win =
2527       mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2528   if (win) {
2529     Navigator* navigator = win->Navigator();
2530     if (navigator) {
2531       navigator->ClearUserAgentCache();
2532     }
2533   }
2534 
2535   uint32_t childCount = mChildList.Length();
2536   for (uint32_t i = 0; i < childCount; ++i) {
2537     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
2538     if (childShell) {
2539       childShell->SetCustomUserAgent(aCustomUserAgent);
2540     }
2541   }
2542   return NS_OK;
2543 }
2544 
2545 NS_IMETHODIMP
GetTouchEventsOverride(uint32_t * aTouchEventsOverride)2546 nsDocShell::GetTouchEventsOverride(uint32_t* aTouchEventsOverride) {
2547   NS_ENSURE_ARG_POINTER(aTouchEventsOverride);
2548 
2549   *aTouchEventsOverride = mTouchEventsOverride;
2550   return NS_OK;
2551 }
2552 
2553 NS_IMETHODIMP
SetTouchEventsOverride(uint32_t aTouchEventsOverride)2554 nsDocShell::SetTouchEventsOverride(uint32_t aTouchEventsOverride) {
2555   if (!(aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE ||
2556         aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED ||
2557         aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED)) {
2558     return NS_ERROR_INVALID_ARG;
2559   }
2560 
2561   mTouchEventsOverride = aTouchEventsOverride;
2562 
2563   uint32_t childCount = mChildList.Length();
2564   for (uint32_t i = 0; i < childCount; ++i) {
2565     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
2566     if (childShell) {
2567       childShell->SetTouchEventsOverride(aTouchEventsOverride);
2568     }
2569   }
2570   return NS_OK;
2571 }
2572 
ItemType()2573 /* virtual */ int32_t nsDocShell::ItemType() { return mItemType; }
2574 
2575 NS_IMETHODIMP
GetItemType(int32_t * aItemType)2576 nsDocShell::GetItemType(int32_t* aItemType) {
2577   NS_ENSURE_ARG_POINTER(aItemType);
2578 
2579   *aItemType = ItemType();
2580   return NS_OK;
2581 }
2582 
2583 NS_IMETHODIMP
SetItemType(int32_t aItemType)2584 nsDocShell::SetItemType(int32_t aItemType) {
2585   NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
2586 
2587   // Only allow setting the type on root docshells.  Those would be the ones
2588   // that have the docloader service as mParent or have no mParent at all.
2589   nsCOMPtr<nsIDocumentLoader> docLoaderService =
2590       do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
2591   NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
2592 
2593   NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
2594 
2595   mItemType = aItemType;
2596 
2597   // disable auth prompting for anything but content
2598   mAllowAuth = mItemType == typeContent;
2599 
2600   RefPtr<nsPresContext> presContext = nullptr;
2601   GetPresContext(getter_AddRefs(presContext));
2602   if (presContext) {
2603     presContext->UpdateIsChrome();
2604   }
2605 
2606   return NS_OK;
2607 }
2608 
2609 NS_IMETHODIMP
GetParent(nsIDocShellTreeItem ** aParent)2610 nsDocShell::GetParent(nsIDocShellTreeItem** aParent) {
2611   if (!mParent) {
2612     *aParent = nullptr;
2613   } else {
2614     CallQueryInterface(mParent, aParent);
2615   }
2616   // Note that in the case when the parent is not an nsIDocShellTreeItem we
2617   // don't want to throw; we just want to return null.
2618   return NS_OK;
2619 }
2620 
GetParentDocshell()2621 already_AddRefed<nsDocShell> nsDocShell::GetParentDocshell() {
2622   nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2623   return docshell.forget().downcast<nsDocShell>();
2624 }
2625 
MaybeCreateInitialClientSource(nsIPrincipal * aPrincipal)2626 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2627   MOZ_ASSERT(!mIsBeingDestroyed);
2628 
2629   // If there is an existing document then there is no need to create
2630   // a client for a future initial about:blank document.
2631   if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
2632       mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
2633     MOZ_DIAGNOSTIC_ASSERT(mScriptGlobal->GetCurrentInnerWindowInternal()
2634                               ->GetClientInfo()
2635                               .isSome());
2636     MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2637     return;
2638   }
2639 
2640   // Don't recreate the initial client source.  We call this multiple times
2641   // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
2642   if (mInitialClientSource) {
2643     return;
2644   }
2645 
2646   // Don't pre-allocate the client when we are sandboxed.  The inherited
2647   // principal does not take sandboxing into account.
2648   // TODO: Refactor sandboxing principal code out so we can use it here.
2649   if (!aPrincipal && mSandboxFlags) {
2650     return;
2651   }
2652 
2653   nsIPrincipal* principal =
2654       aPrincipal ? aPrincipal : GetInheritedPrincipal(false);
2655 
2656   // Sometimes there is no principal available when we are called from
2657   // CreateAboutBlankContentViewer.  For example, sometimes the principal
2658   // is only extracted from the load context after the document is created
2659   // in nsDocument::ResetToURI().  Ideally we would do something similar
2660   // here, but for now lets just avoid the issue by not preallocating the
2661   // client.
2662   if (!principal) {
2663     return;
2664   }
2665 
2666   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2667   if (!win) {
2668     return;
2669   }
2670 
2671   mInitialClientSource = ClientManager::CreateSource(
2672       ClientType::Window, win->EventTargetFor(TaskCategory::Other), principal);
2673   MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2674 
2675   // Mark the initial client as execution ready, but owned by the docshell.
2676   // If the client is actually used this will cause ClientSource to force
2677   // the creation of the initial about:blank by calling
2678   // nsDocShell::GetDocument().
2679   mInitialClientSource->DocShellExecutionReady(this);
2680 
2681   // Next, check to see if the parent is controlled.
2682   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
2683   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2684   nsPIDOMWindowInner* parentInner =
2685       parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2686   if (!parentInner) {
2687     return;
2688   }
2689 
2690   nsCOMPtr<nsIURI> uri;
2691   MOZ_ALWAYS_SUCCEEDS(
2692       NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank")));
2693 
2694   // We're done if there is no parent controller or if this docshell
2695   // is not permitted to control for some reason.
2696   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2697   if (controller.isNothing() ||
2698       !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2699     return;
2700   }
2701 
2702   nsCOMPtr<nsIServiceWorkerManager> swm =
2703       mozilla::services::GetServiceWorkerManager();
2704   if (!swm) {
2705     return;
2706   }
2707 
2708   // If the parent is controlled then propagate that controller to the
2709   // initial about:blank client as well.  This will set the controller
2710   // in the ClientManagerService in the parent.
2711   //
2712   // Note: If the registration is missing from the SWM we avoid setting
2713   //       the controller on the client.  We can do this synchronously
2714   //       for now since SWM is in the child process.  In the future
2715   //       when SWM is in the parent process we will probably have to
2716   //       always set the initial client source and then somehow clear
2717   //       it if we find the registration is acutally gone.  Its also
2718   //       possible this race only occurs in cases where the resulting
2719   //       window is no longer exposed.  For example, in theory the SW
2720   //       should not go away if our parent window is controlled.
2721   if (!swm->StartControlling(mInitialClientSource->Info(), controller.ref())) {
2722     return;
2723   }
2724 
2725   // Also mark the ClientSource as controlled directly in case script
2726   // immediately accesses navigator.serviceWorker.controller.
2727   mInitialClientSource->SetController(controller.ref());
2728 }
2729 
GetInitialClientInfo() const2730 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2731   if (mInitialClientSource) {
2732     Maybe<ClientInfo> result;
2733     result.emplace(mInitialClientSource->Info());
2734     return Move(result);
2735   }
2736 
2737   nsGlobalWindowInner* innerWindow =
2738       mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2739   nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2740 
2741   if (!doc || !doc->IsInitialDocument()) {
2742     return Maybe<ClientInfo>();
2743   }
2744 
2745   return innerWindow->GetClientInfo();
2746 }
2747 
RecomputeCanExecuteScripts()2748 void nsDocShell::RecomputeCanExecuteScripts() {
2749   bool old = mCanExecuteScripts;
2750   RefPtr<nsDocShell> parent = GetParentDocshell();
2751 
2752   // If we have no tree owner, that means that we've been detached from the
2753   // docshell tree (this is distinct from having no parent dochshell, which
2754   // is the case for root docshells). It would be nice to simply disallow
2755   // script in detached docshells, but bug 986542 demonstrates that this
2756   // behavior breaks at least one website.
2757   //
2758   // So instead, we use our previous value, unless mAllowJavascript has been
2759   // explicitly set to false.
2760   if (!mTreeOwner) {
2761     mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
2762     // If scripting has been explicitly disabled on our docshell, we're done.
2763   } else if (!mAllowJavascript) {
2764     mCanExecuteScripts = false;
2765     // If we have a parent, inherit.
2766   } else if (parent) {
2767     mCanExecuteScripts = parent->mCanExecuteScripts;
2768     // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2769     // script. Allow.
2770   } else {
2771     mCanExecuteScripts = true;
2772   }
2773 
2774   // Inform our active DOM window.
2775   //
2776   // This will pass the outer, which will be in the scope of the active inner.
2777   if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
2778     xpc::Scriptability& scriptability =
2779         xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
2780     scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
2781   }
2782 
2783   // If our value has changed, our children might be affected. Recompute their
2784   // value as well.
2785   if (old != mCanExecuteScripts) {
2786     nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2787     while (iter.HasMore()) {
2788       static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
2789     }
2790   }
2791 }
2792 
SetDocLoaderParent(nsDocLoader * aParent)2793 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2794   bool wasFrame = IsFrame();
2795 #ifdef DEBUG
2796   bool wasPrivate = UsePrivateBrowsing();
2797 #endif
2798 
2799   nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2800   NS_ENSURE_SUCCESS(rv, rv);
2801 
2802   nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2803   if (wasFrame != IsFrame() && priorityGroup) {
2804     priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2805   }
2806 
2807   // Curse ambiguous nsISupports inheritance!
2808   nsISupports* parent = GetAsSupports(aParent);
2809 
2810   // If parent is another docshell, we inherit all their flags for
2811   // allowing plugins, scripting etc.
2812   bool value;
2813   nsString customUserAgent;
2814   nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2815   if (parentAsDocShell) {
2816     if (mAllowPlugins &&
2817         NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
2818       SetAllowPlugins(value);
2819     }
2820     if (mAllowJavascript &&
2821         NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
2822       SetAllowJavascript(value);
2823     }
2824     if (mAllowMetaRedirects &&
2825         NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2826       SetAllowMetaRedirects(value);
2827     }
2828     if (mAllowSubframes &&
2829         NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2830       SetAllowSubframes(value);
2831     }
2832     if (mAllowImages &&
2833         NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2834       SetAllowImages(value);
2835     }
2836     SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2837     if (mAllowWindowControl &&
2838         NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2839       SetAllowWindowControl(value);
2840     }
2841     SetAllowContentRetargeting(
2842         mAllowContentRetargeting &&
2843         parentAsDocShell->GetAllowContentRetargetingOnChildren());
2844     if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
2845       SetIsActive(value);
2846     }
2847     if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
2848         !customUserAgent.IsEmpty()) {
2849       SetCustomUserAgent(customUserAgent);
2850     }
2851     if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2852       value = false;
2853     }
2854     SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2855     if (mInheritPrivateBrowsingId) {
2856       value = parentAsDocShell->GetAffectPrivateSessionLifetime();
2857       SetAffectPrivateSessionLifetime(value);
2858     }
2859     uint32_t flags;
2860     if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
2861       SetDefaultLoadFlags(flags);
2862     }
2863     uint32_t touchEventsOverride;
2864     if (NS_SUCCEEDED(
2865             parentAsDocShell->GetTouchEventsOverride(&touchEventsOverride))) {
2866       SetTouchEventsOverride(touchEventsOverride);
2867     }
2868   }
2869 
2870   nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
2871   if (parentAsLoadContext && mInheritPrivateBrowsingId &&
2872       NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
2873     SetPrivateBrowsing(value);
2874   }
2875 
2876   nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2877   if (parentURIListener) {
2878     mContentListener->SetParentContentListener(parentURIListener);
2879   }
2880 
2881   // Our parent has changed. Recompute scriptability.
2882   RecomputeCanExecuteScripts();
2883 
2884   NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(),
2885                "Private browsing state changed while inheritance was disabled");
2886 
2887   return NS_OK;
2888 }
2889 
2890 NS_IMETHODIMP
GetSameTypeParent(nsIDocShellTreeItem ** aParent)2891 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent) {
2892   NS_ENSURE_ARG_POINTER(aParent);
2893   *aParent = nullptr;
2894 
2895   if (nsIDocShell::GetIsMozBrowser()) {
2896     return NS_OK;
2897   }
2898 
2899   nsCOMPtr<nsIDocShellTreeItem> parent =
2900       do_QueryInterface(GetAsSupports(mParent));
2901   if (!parent) {
2902     return NS_OK;
2903   }
2904 
2905   if (parent->ItemType() == mItemType) {
2906     parent.swap(*aParent);
2907   }
2908   return NS_OK;
2909 }
2910 
2911 NS_IMETHODIMP
GetSameTypeParentIgnoreBrowserBoundaries(nsIDocShell ** aParent)2912 nsDocShell::GetSameTypeParentIgnoreBrowserBoundaries(nsIDocShell** aParent) {
2913   NS_ENSURE_ARG_POINTER(aParent);
2914   *aParent = nullptr;
2915 
2916   nsCOMPtr<nsIDocShellTreeItem> parent =
2917       do_QueryInterface(GetAsSupports(mParent));
2918   if (!parent) {
2919     return NS_OK;
2920   }
2921 
2922   if (parent->ItemType() == mItemType) {
2923     nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
2924     parentDS.forget(aParent);
2925   }
2926   return NS_OK;
2927 }
2928 
2929 NS_IMETHODIMP
GetRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)2930 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2931   NS_ENSURE_ARG_POINTER(aRootTreeItem);
2932 
2933   RefPtr<nsDocShell> root = this;
2934   RefPtr<nsDocShell> parent = root->GetParentDocshell();
2935   while (parent) {
2936     root = parent;
2937     parent = root->GetParentDocshell();
2938   }
2939 
2940   root.forget(aRootTreeItem);
2941   return NS_OK;
2942 }
2943 
2944 NS_IMETHODIMP
GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem)2945 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2946   NS_ENSURE_ARG_POINTER(aRootTreeItem);
2947   *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2948 
2949   nsCOMPtr<nsIDocShellTreeItem> parent;
2950   NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
2951                     NS_ERROR_FAILURE);
2952   while (parent) {
2953     *aRootTreeItem = parent;
2954     NS_ENSURE_SUCCESS(
2955         (*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)),
2956         NS_ERROR_FAILURE);
2957   }
2958   NS_ADDREF(*aRootTreeItem);
2959   return NS_OK;
2960 }
2961 
2962 NS_IMETHODIMP
GetSameTypeRootTreeItemIgnoreBrowserBoundaries(nsIDocShell ** aRootTreeItem)2963 nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserBoundaries(
2964     nsIDocShell** aRootTreeItem) {
2965   NS_ENSURE_ARG_POINTER(aRootTreeItem);
2966   *aRootTreeItem = static_cast<nsIDocShell*>(this);
2967 
2968   nsCOMPtr<nsIDocShell> parent;
2969   NS_ENSURE_SUCCESS(
2970       GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent)),
2971       NS_ERROR_FAILURE);
2972   while (parent) {
2973     *aRootTreeItem = parent;
2974     NS_ENSURE_SUCCESS(
2975         (*aRootTreeItem)
2976             ->GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent)),
2977         NS_ERROR_FAILURE);
2978   }
2979   NS_ADDREF(*aRootTreeItem);
2980   return NS_OK;
2981 }
2982 
2983 /* static */
CanAccessItem(nsIDocShellTreeItem * aTargetItem,nsIDocShellTreeItem * aAccessingItem,bool aConsiderOpener)2984 bool nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
2985                                nsIDocShellTreeItem* aAccessingItem,
2986                                bool aConsiderOpener) {
2987   NS_PRECONDITION(aTargetItem, "Must have target item!");
2988 
2989   if (!gValidateOrigin || !aAccessingItem) {
2990     // Good to go
2991     return true;
2992   }
2993 
2994   // XXXbz should we care if aAccessingItem or the document therein is
2995   // chrome?  Should those get extra privileges?
2996 
2997   // For historical context, see:
2998   //
2999   // Bug 13871:  Prevent frameset spoofing
3000   // Bug 103638: Targets with same name in different windows open in wrong
3001   //             window with javascript
3002   // Bug 408052: Adopt "ancestor" frame navigation policy
3003 
3004   // Now do a security check.
3005   //
3006   // Disallow navigation if the two frames are not part of the same app, or if
3007   // they have different is-in-browser-element states.
3008   //
3009   // Allow navigation if
3010   //  1) aAccessingItem can script aTargetItem or one of its ancestors in
3011   //     the frame hierarchy or
3012   //  2) aTargetItem is a top-level frame and aAccessingItem is its descendant
3013   //  3) aTargetItem is a top-level frame and aAccessingItem can target
3014   //     its opener per rule (1) or (2).
3015 
3016   if (aTargetItem == aAccessingItem) {
3017     // A frame is allowed to navigate itself.
3018     return true;
3019   }
3020 
3021   nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
3022   nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
3023   if (!targetDS || !accessingDS) {
3024     // We must be able to convert both to nsIDocShell.
3025     return false;
3026   }
3027 
3028   if (targetDS->GetIsInIsolatedMozBrowserElement() !=
3029       accessingDS->GetIsInIsolatedMozBrowserElement()) {
3030     return false;
3031   }
3032 
3033   nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
3034   aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
3035   nsCOMPtr<nsIDocShell> accessingRootDS = do_QueryInterface(accessingRoot);
3036 
3037   nsCOMPtr<nsIDocShellTreeItem> targetRoot;
3038   aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
3039   nsCOMPtr<nsIDocShell> targetRootDS = do_QueryInterface(targetRoot);
3040 
3041   OriginAttributes targetOA =
3042       static_cast<nsDocShell*>(targetDS.get())->GetOriginAttributes();
3043   OriginAttributes accessingOA =
3044       static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes();
3045 
3046   // When the first party isolation is on, the top-level docShell may not have
3047   // the firstPartyDomain in its originAttributes, but its document will have
3048   // it. So we get the firstPartyDomain from the nodePrincipal of the document
3049   // before we compare the originAttributes.
3050   if (OriginAttributes::IsFirstPartyEnabled()) {
3051     if (aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent &&
3052         (accessingDS == accessingRootDS || accessingDS->GetIsMozBrowser())) {
3053       nsCOMPtr<nsIDocument> accessingDoc = aAccessingItem->GetDocument();
3054 
3055       if (accessingDoc) {
3056         nsCOMPtr<nsIPrincipal> accessingPrincipal =
3057             accessingDoc->NodePrincipal();
3058 
3059         accessingOA.mFirstPartyDomain =
3060             accessingPrincipal->OriginAttributesRef().mFirstPartyDomain;
3061       }
3062     }
3063 
3064     if (aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent &&
3065         (targetDS == targetRootDS || targetDS->GetIsMozBrowser())) {
3066       nsCOMPtr<nsIDocument> targetDoc = aAccessingItem->GetDocument();
3067 
3068       if (targetDoc) {
3069         nsCOMPtr<nsIPrincipal> targetPrincipal = targetDoc->NodePrincipal();
3070 
3071         targetOA.mFirstPartyDomain =
3072             targetPrincipal->OriginAttributesRef().mFirstPartyDomain;
3073       }
3074     }
3075   }
3076 
3077   if (targetOA != accessingOA) {
3078     return false;
3079   }
3080 
3081   // A private document can't access a non-private one, and vice versa.
3082   if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
3083       static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
3084     return false;
3085   }
3086 
3087   if (aTargetItem == accessingRoot) {
3088     // A frame can navigate its root.
3089     return true;
3090   }
3091 
3092   // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
3093   nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
3094   do {
3095     if (ValidateOrigin(aAccessingItem, target)) {
3096       return true;
3097     }
3098 
3099     nsCOMPtr<nsIDocShellTreeItem> parent;
3100     target->GetSameTypeParent(getter_AddRefs(parent));
3101     parent.swap(target);
3102   } while (target);
3103 
3104   if (aTargetItem != targetRoot) {
3105     // target is a subframe, not in accessor's frame hierarchy, and all its
3106     // ancestors have origins different from that of the accessor. Don't
3107     // allow access.
3108     return false;
3109   }
3110 
3111   if (!aConsiderOpener) {
3112     // All done here
3113     return false;
3114   }
3115 
3116   nsCOMPtr<nsPIDOMWindowOuter> targetWindow = aTargetItem->GetWindow();
3117   if (!targetWindow) {
3118     NS_ERROR("This should not happen, really");
3119     return false;
3120   }
3121 
3122   nsCOMPtr<mozIDOMWindowProxy> targetOpener = targetWindow->GetOpener();
3123   nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
3124   nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
3125 
3126   if (!openerItem) {
3127     return false;
3128   }
3129 
3130   return CanAccessItem(openerItem, aAccessingItem, false);
3131 }
3132 
ItemIsActive(nsIDocShellTreeItem * aItem)3133 static bool ItemIsActive(nsIDocShellTreeItem* aItem) {
3134   if (nsCOMPtr<nsPIDOMWindowOuter> window = aItem->GetWindow()) {
3135     auto* win = nsGlobalWindowOuter::Cast(window);
3136     if (!win->GetClosedOuter()) {
3137       return true;
3138     }
3139   }
3140 
3141   return false;
3142 }
3143 
3144 NS_IMETHODIMP
FindItemWithName(const nsAString & aName,nsIDocShellTreeItem * aRequestor,nsIDocShellTreeItem * aOriginalRequestor,bool aSkipTabGroup,nsIDocShellTreeItem ** aResult)3145 nsDocShell::FindItemWithName(const nsAString& aName,
3146                              nsIDocShellTreeItem* aRequestor,
3147                              nsIDocShellTreeItem* aOriginalRequestor,
3148                              bool aSkipTabGroup,
3149                              nsIDocShellTreeItem** aResult) {
3150   NS_ENSURE_ARG_POINTER(aResult);
3151 
3152   // If we don't find one, we return NS_OK and a null result
3153   *aResult = nullptr;
3154 
3155   if (aName.IsEmpty()) {
3156     return NS_OK;
3157   }
3158 
3159   if (aRequestor) {
3160     // If aRequestor is not null we don't need to check special names, so
3161     // just hand straight off to the search by actual name function.
3162     return DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3163                               aSkipTabGroup, aResult);
3164   } else {
3165     // This is the entry point into the target-finding algorithm.  Check
3166     // for special names.  This should only be done once, hence the check
3167     // for a null aRequestor.
3168 
3169     nsCOMPtr<nsIDocShellTreeItem> foundItem;
3170     if (aName.LowerCaseEqualsLiteral("_self")) {
3171       foundItem = this;
3172     } else if (aName.LowerCaseEqualsLiteral("_blank")) {
3173       // Just return null.  Caller must handle creating a new window with
3174       // a blank name himself.
3175       return NS_OK;
3176     } else if (aName.LowerCaseEqualsLiteral("_parent")) {
3177       GetSameTypeParent(getter_AddRefs(foundItem));
3178       if (!foundItem) {
3179         foundItem = this;
3180       }
3181     } else if (aName.LowerCaseEqualsLiteral("_top")) {
3182       GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
3183       NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
3184     } else {
3185       // Do the search for item by an actual name.
3186       DoFindItemWithName(aName, aRequestor, aOriginalRequestor, aSkipTabGroup,
3187                          getter_AddRefs(foundItem));
3188     }
3189 
3190     if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
3191       foundItem = nullptr;
3192     }
3193 
3194     // DoFindItemWithName only returns active items and we don't check if
3195     // the item is active for the special cases.
3196     if (foundItem) {
3197       foundItem.swap(*aResult);
3198     }
3199     return NS_OK;
3200   }
3201 }
3202 
AssertOriginAttributesMatchPrivateBrowsing()3203 void nsDocShell::AssertOriginAttributesMatchPrivateBrowsing() {
3204   // Chrome docshells must not have a private browsing OriginAttribute
3205   // Content docshells must maintain the equality:
3206   // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
3207   if (mItemType == typeChrome) {
3208     MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
3209   } else {
3210     MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
3211                           mPrivateBrowsingId);
3212   }
3213 }
3214 
DoFindItemWithName(const nsAString & aName,nsIDocShellTreeItem * aRequestor,nsIDocShellTreeItem * aOriginalRequestor,bool aSkipTabGroup,nsIDocShellTreeItem ** aResult)3215 nsresult nsDocShell::DoFindItemWithName(const nsAString& aName,
3216                                         nsIDocShellTreeItem* aRequestor,
3217                                         nsIDocShellTreeItem* aOriginalRequestor,
3218                                         bool aSkipTabGroup,
3219                                         nsIDocShellTreeItem** aResult) {
3220   // First we check our name.
3221   if (mName.Equals(aName) && ItemIsActive(this) &&
3222       CanAccessItem(this, aOriginalRequestor)) {
3223     NS_ADDREF(*aResult = this);
3224     return NS_OK;
3225   }
3226 
3227     // Second we check our children making sure not to ask a child if
3228     // it is the aRequestor.
3229 #ifdef DEBUG
3230   nsresult rv =
3231 #endif
3232       FindChildWithName(aName, true, true, aRequestor, aOriginalRequestor,
3233                         aResult);
3234   NS_ASSERTION(NS_SUCCEEDED(rv),
3235                "FindChildWithName should not be failing here.");
3236   if (*aResult) {
3237     return NS_OK;
3238   }
3239 
3240   // Third if we have a parent and it isn't the requestor then we
3241   // should ask it to do the search.  If it is the requestor we
3242   // should just stop here and let the parent do the rest.  If we
3243   // don't have a parent, then we should ask the
3244   // docShellTreeOwner to do the search.
3245   nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
3246       do_QueryInterface(GetAsSupports(mParent));
3247   if (parentAsTreeItem) {
3248     if (parentAsTreeItem == aRequestor) {
3249       return NS_OK;
3250     }
3251 
3252     // If we have a same-type parent, respecting browser and app boundaries.
3253     // NOTE: Could use GetSameTypeParent if the issues described in bug 1310344
3254     // are fixed.
3255     if (!GetIsMozBrowser() && parentAsTreeItem->ItemType() == mItemType) {
3256       return parentAsTreeItem->FindItemWithName(
3257           aName, static_cast<nsIDocShellTreeItem*>(this), aOriginalRequestor,
3258           /* aSkipTabGroup = */ false, aResult);
3259     }
3260   }
3261 
3262   // If we have a null parent or the parent is not of the same type, we need to
3263   // give up on finding it in our tree, and start looking in our TabGroup.
3264   nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
3265   if (window && !aSkipTabGroup) {
3266     RefPtr<mozilla::dom::TabGroup> tabGroup = window->TabGroup();
3267     tabGroup->FindItemWithName(aName, aRequestor, aOriginalRequestor, aResult);
3268   }
3269 
3270   return NS_OK;
3271 }
3272 
IsSandboxedFrom(nsIDocShell * aTargetDocShell)3273 bool nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell) {
3274   // If no target then not sandboxed.
3275   if (!aTargetDocShell) {
3276     return false;
3277   }
3278 
3279   // We cannot be sandboxed from ourselves.
3280   if (aTargetDocShell == this) {
3281     return false;
3282   }
3283 
3284   // Default the sandbox flags to our flags, so that if we can't retrieve the
3285   // active document, we will still enforce our own.
3286   uint32_t sandboxFlags = mSandboxFlags;
3287   if (mContentViewer) {
3288     nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
3289     if (doc) {
3290       sandboxFlags = doc->GetSandboxFlags();
3291     }
3292   }
3293 
3294   // If no flags, we are not sandboxed at all.
3295   if (!sandboxFlags) {
3296     return false;
3297   }
3298 
3299   // If aTargetDocShell has an ancestor, it is not top level.
3300   nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
3301   aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
3302   if (ancestorOfTarget) {
3303     do {
3304       // We are not sandboxed if we are an ancestor of target.
3305       if (ancestorOfTarget == this) {
3306         return false;
3307       }
3308       nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
3309       ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
3310       tempTreeItem.swap(ancestorOfTarget);
3311     } while (ancestorOfTarget);
3312 
3313     // Otherwise, we are sandboxed from aTargetDocShell.
3314     return true;
3315   }
3316 
3317   // aTargetDocShell is top level, are we the "one permitted sandboxed
3318   // navigator", i.e. did we open aTargetDocShell?
3319   nsCOMPtr<nsIDocShell> permittedNavigator;
3320   aTargetDocShell->GetOnePermittedSandboxedNavigator(
3321       getter_AddRefs(permittedNavigator));
3322   if (permittedNavigator == this) {
3323     return false;
3324   }
3325 
3326   // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
3327   // from our top.
3328   if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
3329     nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
3330     GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
3331     if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
3332       return false;
3333     }
3334   }
3335 
3336   // Otherwise, we are sandboxed from aTargetDocShell.
3337   return true;
3338 }
3339 
3340 NS_IMETHODIMP
GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)3341 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
3342   NS_ENSURE_ARG_POINTER(aTreeOwner);
3343 
3344   *aTreeOwner = mTreeOwner;
3345   NS_IF_ADDREF(*aTreeOwner);
3346   return NS_OK;
3347 }
3348 
3349 #ifdef DEBUG_DOCSHELL_FOCUS
PrintDocTree(nsIDocShellTreeItem * aParentNode,int aLevel)3350 static void PrintDocTree(nsIDocShellTreeItem* aParentNode, int aLevel) {
3351   for (int32_t i = 0; i < aLevel; i++) {
3352     printf("  ");
3353   }
3354 
3355   int32_t childWebshellCount;
3356   aParentNode->GetChildCount(&childWebshellCount);
3357   nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(aParentNode));
3358   int32_t type = aParentNode->ItemType();
3359   nsCOMPtr<nsIPresShell> presShell = parentAsDocShell->GetPresShell();
3360   RefPtr<nsPresContext> presContext;
3361   parentAsDocShell->GetPresContext(getter_AddRefs(presContext));
3362   nsIDocument* doc = presShell->GetDocument();
3363 
3364   nsCOMPtr<nsPIDOMWindowOuter> domwin(doc->GetWindow());
3365 
3366   nsCOMPtr<nsIWidget> widget;
3367   nsViewManager* vm = presShell->GetViewManager();
3368   if (vm) {
3369     vm->GetWidget(getter_AddRefs(widget));
3370   }
3371   dom::Element* rootElement = doc->GetRootElement();
3372 
3373   printf("DS %p  Ty %s  Doc %p DW %p EM %p CN %p\n",
3374          (void*)parentAsDocShell.get(),
3375          type == nsIDocShellTreeItem::typeChrome ? "Chr" : "Con", (void*)doc,
3376          (void*)domwin.get(), (void*)presContext->EventStateManager(),
3377          (void*)rootElement);
3378 
3379   if (childWebshellCount > 0) {
3380     for (int32_t i = 0; i < childWebshellCount; i++) {
3381       nsCOMPtr<nsIDocShellTreeItem> child;
3382       aParentNode->GetChildAt(i, getter_AddRefs(child));
3383       PrintDocTree(child, aLevel + 1);
3384     }
3385   }
3386 }
3387 
PrintDocTree(nsIDocShellTreeItem * aParentNode)3388 static void PrintDocTree(nsIDocShellTreeItem* aParentNode) {
3389   NS_ASSERTION(aParentNode, "Pointer is null!");
3390 
3391   nsCOMPtr<nsIDocShellTreeItem> parentItem;
3392   aParentNode->GetParent(getter_AddRefs(parentItem));
3393   while (parentItem) {
3394     nsCOMPtr<nsIDocShellTreeItem> tmp;
3395     parentItem->GetParent(getter_AddRefs(tmp));
3396     if (!tmp) {
3397       break;
3398     }
3399     parentItem = tmp;
3400   }
3401 
3402   if (!parentItem) {
3403     parentItem = aParentNode;
3404   }
3405 
3406   PrintDocTree(parentItem, 0);
3407 }
3408 #endif
3409 
3410 NS_IMETHODIMP
SetTreeOwner(nsIDocShellTreeOwner * aTreeOwner)3411 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
3412   if (mIsBeingDestroyed && aTreeOwner) {
3413     return NS_ERROR_FAILURE;
3414   }
3415 
3416 #ifdef DEBUG_DOCSHELL_FOCUS
3417   nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(aTreeOwner));
3418   if (item) {
3419     PrintDocTree(item);
3420   }
3421 #endif
3422 
3423   // Don't automatically set the progress based on the tree owner for frames
3424   if (!IsFrame()) {
3425     nsCOMPtr<nsIWebProgress> webProgress =
3426         do_QueryInterface(GetAsSupports(this));
3427 
3428     if (webProgress) {
3429       nsCOMPtr<nsIWebProgressListener> oldListener =
3430           do_QueryInterface(mTreeOwner);
3431       nsCOMPtr<nsIWebProgressListener> newListener =
3432           do_QueryInterface(aTreeOwner);
3433 
3434       if (oldListener) {
3435         webProgress->RemoveProgressListener(oldListener);
3436       }
3437 
3438       if (newListener) {
3439         webProgress->AddProgressListener(newListener,
3440                                          nsIWebProgress::NOTIFY_ALL);
3441       }
3442     }
3443   }
3444 
3445   mTreeOwner = aTreeOwner;  // Weak reference per API
3446 
3447   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3448   while (iter.HasMore()) {
3449     nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3450     NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3451 
3452     if (child->ItemType() == mItemType) {
3453       child->SetTreeOwner(aTreeOwner);
3454     }
3455   }
3456 
3457   // Our tree owner has changed. Recompute scriptability.
3458   //
3459   // Note that this is near-redundant with the recomputation in
3460   // SetDocLoaderParent(), but not so for the root DocShell, where the call to
3461   // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
3462   // and we never set another parent. Given that this is neither expensive nor
3463   // performance-critical, let's be safe and unconditionally recompute this
3464   // state whenever dependent state changes.
3465   RecomputeCanExecuteScripts();
3466 
3467   return NS_OK;
3468 }
3469 
3470 NS_IMETHODIMP
SetChildOffset(int32_t aChildOffset)3471 nsDocShell::SetChildOffset(int32_t aChildOffset) {
3472   mChildOffset = aChildOffset;
3473   return NS_OK;
3474 }
3475 
3476 NS_IMETHODIMP
GetChildOffset(int32_t * aChildOffset)3477 nsDocShell::GetChildOffset(int32_t* aChildOffset) {
3478   *aChildOffset = mChildOffset;
3479   return NS_OK;
3480 }
3481 
3482 NS_IMETHODIMP
GetHistoryID(nsID ** aID)3483 nsDocShell::GetHistoryID(nsID** aID) {
3484   *aID = mHistoryID.Clone();
3485   return NS_OK;
3486 }
3487 
HistoryID()3488 const nsID nsDocShell::HistoryID() { return mHistoryID; }
3489 
3490 NS_IMETHODIMP
GetIsInUnload(bool * aIsInUnload)3491 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
3492   *aIsInUnload = mFiredUnloadEvent;
3493   return NS_OK;
3494 }
3495 
3496 NS_IMETHODIMP
GetChildCount(int32_t * aChildCount)3497 nsDocShell::GetChildCount(int32_t* aChildCount) {
3498   NS_ENSURE_ARG_POINTER(aChildCount);
3499   *aChildCount = mChildList.Length();
3500   return NS_OK;
3501 }
3502 
3503 NS_IMETHODIMP
AddChild(nsIDocShellTreeItem * aChild)3504 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
3505   NS_ENSURE_ARG_POINTER(aChild);
3506 
3507   RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3508   NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3509 
3510   // Make sure we're not creating a loop in the docshell tree
3511   nsDocLoader* ancestor = this;
3512   do {
3513     if (childAsDocLoader == ancestor) {
3514       return NS_ERROR_ILLEGAL_VALUE;
3515     }
3516     ancestor = ancestor->GetParent();
3517   } while (ancestor);
3518 
3519   // Make sure to remove the child from its current parent.
3520   nsDocLoader* childsParent = childAsDocLoader->GetParent();
3521   if (childsParent) {
3522     nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
3523     NS_ENSURE_SUCCESS(rv, rv);
3524   }
3525 
3526   // Make sure to clear the treeowner in case this child is a different type
3527   // from us.
3528   aChild->SetTreeOwner(nullptr);
3529 
3530   nsresult res = AddChildLoader(childAsDocLoader);
3531   NS_ENSURE_SUCCESS(res, res);
3532   NS_ASSERTION(!mChildList.IsEmpty(),
3533                "child list must not be empty after a successful add");
3534 
3535   nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
3536   bool dynamic = false;
3537   childDocShell->GetCreatedDynamically(&dynamic);
3538   if (!dynamic) {
3539     nsCOMPtr<nsISHEntry> currentSH;
3540     bool oshe = false;
3541     GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
3542     if (currentSH) {
3543       currentSH->HasDynamicallyAddedChild(&dynamic);
3544     }
3545   }
3546   childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
3547 
3548   /* Set the child's global history if the parent has one */
3549   if (mUseGlobalHistory) {
3550     childDocShell->SetUseGlobalHistory(true);
3551   }
3552 
3553   if (aChild->ItemType() != mItemType) {
3554     return NS_OK;
3555   }
3556 
3557   aChild->SetTreeOwner(mTreeOwner);
3558 
3559   nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3560   if (!childAsDocShell) {
3561     return NS_OK;
3562   }
3563 
3564   // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
3565 
3566   // Now take this document's charset and set the child's parentCharset field
3567   // to it. We'll later use that field, in the loading process, for the
3568   // charset choosing algorithm.
3569   // If we fail, at any point, we just return NS_OK.
3570   // This code has some performance impact. But this will be reduced when
3571   // the current charset will finally be stored as an Atom, avoiding the
3572   // alias resolution extra look-up.
3573 
3574   // we are NOT going to propagate the charset is this Chrome's docshell
3575   if (mItemType == nsIDocShellTreeItem::typeChrome) {
3576     return NS_OK;
3577   }
3578 
3579   // get the parent's current charset
3580   if (!mContentViewer) {
3581     return NS_OK;
3582   }
3583   nsIDocument* doc = mContentViewer->GetDocument();
3584   if (!doc) {
3585     return NS_OK;
3586   }
3587 
3588   bool isWyciwyg = false;
3589 
3590   if (mCurrentURI) {
3591     // Check if the url is wyciwyg
3592     mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3593   }
3594 
3595   if (!isWyciwyg) {
3596     // If this docshell is loaded from a wyciwyg: URI, don't
3597     // advertise our charset since it does not in any way reflect
3598     // the actual source charset, which is what we're trying to
3599     // expose here.
3600 
3601     const Encoding* parentCS = doc->GetDocumentCharacterSet();
3602     int32_t charsetSource = doc->GetDocumentCharacterSetSource();
3603     // set the child's parentCharset
3604     childAsDocShell->SetParentCharset(parentCS, charsetSource,
3605                                       doc->NodePrincipal());
3606   }
3607 
3608   // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
3609   //        NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3610 
3611   return NS_OK;
3612 }
3613 
3614 NS_IMETHODIMP
RemoveChild(nsIDocShellTreeItem * aChild)3615 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
3616   NS_ENSURE_ARG_POINTER(aChild);
3617 
3618   RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3619   NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3620 
3621   nsresult rv = RemoveChildLoader(childAsDocLoader);
3622   NS_ENSURE_SUCCESS(rv, rv);
3623 
3624   aChild->SetTreeOwner(nullptr);
3625 
3626   return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3627 }
3628 
3629 NS_IMETHODIMP
GetChildAt(int32_t aIndex,nsIDocShellTreeItem ** aChild)3630 nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
3631   NS_ENSURE_ARG_POINTER(aChild);
3632 
3633 #ifdef DEBUG
3634   if (aIndex < 0) {
3635     NS_WARNING("Negative index passed to GetChildAt");
3636   } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3637     NS_WARNING("Too large an index passed to GetChildAt");
3638   }
3639 #endif
3640 
3641   nsIDocumentLoader* child = ChildAt(aIndex);
3642   NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3643 
3644   return CallQueryInterface(child, aChild);
3645 }
3646 
3647 NS_IMETHODIMP
FindChildWithName(const nsAString & aName,bool aRecurse,bool aSameType,nsIDocShellTreeItem * aRequestor,nsIDocShellTreeItem * aOriginalRequestor,nsIDocShellTreeItem ** aResult)3648 nsDocShell::FindChildWithName(const nsAString& aName, bool aRecurse,
3649                               bool aSameType, nsIDocShellTreeItem* aRequestor,
3650                               nsIDocShellTreeItem* aOriginalRequestor,
3651                               nsIDocShellTreeItem** aResult) {
3652   NS_ENSURE_ARG_POINTER(aResult);
3653 
3654   // if we don't find one, we return NS_OK and a null result
3655   *aResult = nullptr;
3656 
3657   if (aName.IsEmpty()) {
3658     return NS_OK;
3659   }
3660 
3661   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3662   while (iter.HasMore()) {
3663     nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3664     NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3665     int32_t childType = child->ItemType();
3666 
3667     if (aSameType && (childType != mItemType)) {
3668       continue;
3669     }
3670 
3671     bool childNameEquals = false;
3672     child->NameEquals(aName, &childNameEquals);
3673     if (childNameEquals && ItemIsActive(child) &&
3674         CanAccessItem(child, aOriginalRequestor)) {
3675       child.swap(*aResult);
3676       break;
3677     }
3678 
3679     // Only ask it to check children if it is same type
3680     if (childType != mItemType) {
3681       continue;
3682     }
3683 
3684     // Only ask the child if it isn't the requestor
3685     if (aRecurse && (aRequestor != child)) {
3686     // See if child contains the shell with the given name
3687 #ifdef DEBUG
3688       nsresult rv =
3689 #endif
3690           child->FindChildWithName(aName, true, aSameType,
3691                                    static_cast<nsIDocShellTreeItem*>(this),
3692                                    aOriginalRequestor, aResult);
3693       NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not fail here");
3694       if (*aResult) {
3695         // found it
3696         return NS_OK;
3697       }
3698     }
3699   }
3700   return NS_OK;
3701 }
3702 
3703 NS_IMETHODIMP
GetChildSHEntry(int32_t aChildOffset,nsISHEntry ** aResult)3704 nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry** aResult) {
3705   nsresult rv = NS_OK;
3706 
3707   NS_ENSURE_ARG_POINTER(aResult);
3708   *aResult = nullptr;
3709 
3710   // A nsISHEntry for a child is *only* available when the parent is in
3711   // the progress of loading a document too...
3712 
3713   if (mLSHE) {
3714     /* Before looking for the subframe's url, check
3715      * the expiration status of the parent. If the parent
3716      * has expired from cache, then subframes will not be
3717      * loaded from history in certain situations.
3718      */
3719     bool parentExpired = false;
3720     mLSHE->GetExpirationStatus(&parentExpired);
3721 
3722     /* Get the parent's Load Type so that it can be set on the child too.
3723      * By default give a loadHistory value
3724      */
3725     uint32_t loadType = nsIDocShellLoadInfo::loadHistory;
3726     mLSHE->GetLoadType(&loadType);
3727     // If the user did a shift-reload on this frameset page,
3728     // we don't want to load the subframes from history.
3729     if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
3730         loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
3731         loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
3732         loadType == nsIDocShellLoadInfo::loadRefresh) {
3733       return rv;
3734     }
3735 
3736     /* If the user pressed reload and the parent frame has expired
3737      *  from cache, we do not want to load the child frame from history.
3738      */
3739     if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
3740       // The parent has expired. Return null.
3741       *aResult = nullptr;
3742       return rv;
3743     }
3744 
3745     nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
3746     if (container) {
3747       // Get the child subframe from session history.
3748       rv = container->GetChildAt(aChildOffset, aResult);
3749       if (*aResult) {
3750         (*aResult)->SetLoadType(loadType);
3751       }
3752     }
3753   }
3754   return rv;
3755 }
3756 
3757 NS_IMETHODIMP
AddChildSHEntry(nsISHEntry * aCloneRef,nsISHEntry * aNewEntry,int32_t aChildOffset,uint32_t aLoadType,bool aCloneChildren)3758 nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
3759                             int32_t aChildOffset, uint32_t aLoadType,
3760                             bool aCloneChildren) {
3761   nsresult rv = NS_OK;
3762 
3763   if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
3764     /* You get here if you are currently building a
3765      * hierarchy ie.,you just visited a frameset page
3766      */
3767     nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
3768     if (container) {
3769       if (NS_FAILED(container->ReplaceChild(aNewEntry))) {
3770         rv = container->AddChild(aNewEntry, aChildOffset);
3771       }
3772     }
3773   } else if (!aCloneRef) {
3774     /* This is an initial load in some subframe.  Just append it if we can */
3775     nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
3776     if (container) {
3777       rv = container->AddChild(aNewEntry, aChildOffset);
3778     }
3779   } else {
3780     rv = AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset, aLoadType,
3781                                  aCloneChildren);
3782   }
3783   return rv;
3784 }
3785 
AddChildSHEntryInternal(nsISHEntry * aCloneRef,nsISHEntry * aNewEntry,int32_t aChildOffset,uint32_t aLoadType,bool aCloneChildren)3786 nsresult nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
3787                                              nsISHEntry* aNewEntry,
3788                                              int32_t aChildOffset,
3789                                              uint32_t aLoadType,
3790                                              bool aCloneChildren) {
3791   nsresult rv = NS_OK;
3792   if (mSessionHistory) {
3793     /* You are currently in the rootDocShell.
3794      * You will get here when a subframe has a new url
3795      * to load and you have walked up the tree all the
3796      * way to the top to clone the current SHEntry hierarchy
3797      * and replace the subframe where a new url was loaded with
3798      * a new entry.
3799      */
3800     int32_t index = -1;
3801     nsCOMPtr<nsISHEntry> currentHE;
3802     mSessionHistory->GetIndex(&index);
3803     if (index < 0) {
3804       return NS_ERROR_FAILURE;
3805     }
3806 
3807     rv = mSessionHistory->GetEntryAtIndex(index, false,
3808                                           getter_AddRefs(currentHE));
3809     NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
3810 
3811     nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
3812     if (currentEntry) {
3813       uint32_t cloneID = 0;
3814       nsCOMPtr<nsISHEntry> nextEntry;
3815       aCloneRef->GetID(&cloneID);
3816       rv = nsSHistory::CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
3817                                        aCloneChildren,
3818                                        getter_AddRefs(nextEntry));
3819 
3820       if (NS_SUCCEEDED(rv)) {
3821         nsCOMPtr<nsISHistoryInternal> shPrivate =
3822             do_QueryInterface(mSessionHistory);
3823         NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
3824         rv = shPrivate->AddEntry(nextEntry, true);
3825       }
3826     }
3827   } else {
3828     /* Just pass this along */
3829     nsCOMPtr<nsIDocShell> parent =
3830         do_QueryInterface(GetAsSupports(mParent), &rv);
3831     if (parent) {
3832       rv = static_cast<nsDocShell*>(parent.get())
3833                ->AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset,
3834                                          aLoadType, aCloneChildren);
3835     }
3836   }
3837   return rv;
3838 }
3839 
AddChildSHEntryToParent(nsISHEntry * aNewEntry,int32_t aChildOffset,bool aCloneChildren)3840 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
3841                                              int32_t aChildOffset,
3842                                              bool aCloneChildren) {
3843   /* You will get here when you are in a subframe and
3844    * a new url has been loaded on you.
3845    * The mOSHE in this subframe will be the previous url's
3846    * mOSHE. This mOSHE will be used as the identification
3847    * for this subframe in the  CloneAndReplace function.
3848    */
3849 
3850   // In this case, we will end up calling AddEntry, which increases the
3851   // current index by 1
3852   nsCOMPtr<nsISHistory> rootSH;
3853   GetRootSessionHistory(getter_AddRefs(rootSH));
3854   if (rootSH) {
3855     rootSH->GetIndex(&mPreviousTransIndex);
3856   }
3857 
3858   nsresult rv;
3859   nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
3860   if (parent) {
3861     rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
3862                                  aCloneChildren);
3863   }
3864 
3865   if (rootSH) {
3866     rootSH->GetIndex(&mLoadedTransIndex);
3867 #ifdef DEBUG_PAGE_CACHE
3868     printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
3869            mLoadedTransIndex);
3870 #endif
3871   }
3872 
3873   return rv;
3874 }
3875 
3876 NS_IMETHODIMP
SetUseGlobalHistory(bool aUseGlobalHistory)3877 nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory) {
3878   nsresult rv;
3879 
3880   mUseGlobalHistory = aUseGlobalHistory;
3881 
3882   if (!aUseGlobalHistory) {
3883     mGlobalHistory = nullptr;
3884     return NS_OK;
3885   }
3886 
3887   // No need to initialize mGlobalHistory if IHistory is available.
3888   nsCOMPtr<IHistory> history = services::GetHistoryService();
3889   if (history) {
3890     return NS_OK;
3891   }
3892 
3893   if (mGlobalHistory) {
3894     return NS_OK;
3895   }
3896 
3897   mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv);
3898   return rv;
3899 }
3900 
3901 NS_IMETHODIMP
GetUseGlobalHistory(bool * aUseGlobalHistory)3902 nsDocShell::GetUseGlobalHistory(bool* aUseGlobalHistory) {
3903   *aUseGlobalHistory = mUseGlobalHistory;
3904   return NS_OK;
3905 }
3906 
3907 NS_IMETHODIMP
RemoveFromSessionHistory()3908 nsDocShell::RemoveFromSessionHistory() {
3909   nsCOMPtr<nsISHistoryInternal> internalHistory;
3910   nsCOMPtr<nsISHistory> sessionHistory;
3911   nsCOMPtr<nsIDocShellTreeItem> root;
3912   GetSameTypeRootTreeItem(getter_AddRefs(root));
3913   if (root) {
3914     nsCOMPtr<nsIWebNavigation> rootAsWebnav = do_QueryInterface(root);
3915     if (rootAsWebnav) {
3916       rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
3917       internalHistory = do_QueryInterface(sessionHistory);
3918     }
3919   }
3920   if (!internalHistory) {
3921     return NS_OK;
3922   }
3923 
3924   int32_t index = 0;
3925   sessionHistory->GetIndex(&index);
3926   AutoTArray<nsID, 16> ids({mHistoryID});
3927   internalHistory->RemoveEntries(ids, index);
3928   return NS_OK;
3929 }
3930 
3931 NS_IMETHODIMP
SetCreatedDynamically(bool aDynamic)3932 nsDocShell::SetCreatedDynamically(bool aDynamic) {
3933   mDynamicallyCreated = aDynamic;
3934   return NS_OK;
3935 }
3936 
3937 NS_IMETHODIMP
GetCreatedDynamically(bool * aDynamic)3938 nsDocShell::GetCreatedDynamically(bool* aDynamic) {
3939   *aDynamic = mDynamicallyCreated;
3940   return NS_OK;
3941 }
3942 
3943 NS_IMETHODIMP
GetCurrentSHEntry(nsISHEntry ** aEntry,bool * aOSHE)3944 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3945   *aOSHE = false;
3946   *aEntry = nullptr;
3947   if (mLSHE) {
3948     NS_ADDREF(*aEntry = mLSHE);
3949   } else if (mOSHE) {
3950     NS_ADDREF(*aEntry = mOSHE);
3951     *aOSHE = true;
3952   }
3953   return NS_OK;
3954 }
3955 
GetScriptGlobalObject()3956 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3957   NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3958   return mScriptGlobal;
3959 }
3960 
GetDocument()3961 nsIDocument* nsDocShell::GetDocument() {
3962   NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
3963   return mContentViewer->GetDocument();
3964 }
3965 
GetWindow()3966 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3967   if (NS_FAILED(EnsureScriptEnvironment())) {
3968     return nullptr;
3969   }
3970   return mScriptGlobal->AsOuter();
3971 }
3972 
3973 NS_IMETHODIMP
SetDeviceSizeIsPageSize(bool aValue)3974 nsDocShell::SetDeviceSizeIsPageSize(bool aValue) {
3975   if (mDeviceSizeIsPageSize != aValue) {
3976     mDeviceSizeIsPageSize = aValue;
3977     RefPtr<nsPresContext> presContext;
3978     GetPresContext(getter_AddRefs(presContext));
3979     if (presContext) {
3980       presContext->MediaFeatureValuesChanged(
3981           {MediaFeatureChangeReason::DeviceSizeIsPageSizeChange});
3982     }
3983   }
3984   return NS_OK;
3985 }
3986 
3987 NS_IMETHODIMP
GetDeviceSizeIsPageSize(bool * aValue)3988 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) {
3989   *aValue = mDeviceSizeIsPageSize;
3990   return NS_OK;
3991 }
3992 
ClearFrameHistory(nsISHEntry * aEntry)3993 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3994   nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
3995   nsCOMPtr<nsISHistory> rootSH;
3996   GetRootSessionHistory(getter_AddRefs(rootSH));
3997   nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
3998   if (!history || !shcontainer) {
3999     return;
4000   }
4001 
4002   int32_t count = 0;
4003   shcontainer->GetChildCount(&count);
4004   AutoTArray<nsID, 16> ids;
4005   for (int32_t i = 0; i < count; ++i) {
4006     nsCOMPtr<nsISHEntry> child;
4007     shcontainer->GetChildAt(i, getter_AddRefs(child));
4008     if (child) {
4009       ids.AppendElement(child->DocshellID());
4010     }
4011   }
4012   int32_t index = 0;
4013   rootSH->GetIndex(&index);
4014   history->RemoveEntries(ids, index);
4015 }
4016 
4017 //-------------------------------------
4018 //-- Helper Method for Print discovery
4019 //-------------------------------------
IsPrintingOrPP(bool aDisplayErrorDialog)4020 bool nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog) {
4021   if (mIsPrintingOrPP && aDisplayErrorDialog) {
4022     DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
4023   }
4024 
4025   return mIsPrintingOrPP;
4026 }
4027 
IsNavigationAllowed(bool aDisplayPrintErrorDialog,bool aCheckIfUnloadFired)4028 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
4029                                      bool aCheckIfUnloadFired) {
4030   bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
4031                    (!aCheckIfUnloadFired || !mFiredUnloadEvent);
4032   if (!isAllowed) {
4033     return false;
4034   }
4035   if (!mContentViewer) {
4036     return true;
4037   }
4038   bool firingBeforeUnload;
4039   mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
4040   return !firingBeforeUnload;
4041 }
4042 
4043 //*****************************************************************************
4044 // nsDocShell::nsIWebNavigation
4045 //*****************************************************************************
4046 
4047 NS_IMETHODIMP
GetCanGoBack(bool * aCanGoBack)4048 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
4049   if (!IsNavigationAllowed(false)) {
4050     *aCanGoBack = false;
4051     return NS_OK;  // JS may not handle returning of an error code
4052   }
4053   nsresult rv;
4054   nsCOMPtr<nsISHistory> rootSH;
4055   rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4056   nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4057   NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4058   rv = webnav->GetCanGoBack(aCanGoBack);
4059   return rv;
4060 }
4061 
4062 NS_IMETHODIMP
GetCanGoForward(bool * aCanGoForward)4063 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
4064   if (!IsNavigationAllowed(false)) {
4065     *aCanGoForward = false;
4066     return NS_OK;  // JS may not handle returning of an error code
4067   }
4068   nsresult rv;
4069   nsCOMPtr<nsISHistory> rootSH;
4070   rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4071   nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4072   NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4073   rv = webnav->GetCanGoForward(aCanGoForward);
4074   return rv;
4075 }
4076 
4077 NS_IMETHODIMP
GoBack()4078 nsDocShell::GoBack() {
4079   if (!IsNavigationAllowed()) {
4080     return NS_OK;  // JS may not handle returning of an error code
4081   }
4082   nsresult rv;
4083   nsCOMPtr<nsISHistory> rootSH;
4084   rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4085   nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4086   NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4087   rv = webnav->GoBack();
4088   return rv;
4089 }
4090 
4091 NS_IMETHODIMP
GoForward()4092 nsDocShell::GoForward() {
4093   if (!IsNavigationAllowed()) {
4094     return NS_OK;  // JS may not handle returning of an error code
4095   }
4096   nsresult rv;
4097   nsCOMPtr<nsISHistory> rootSH;
4098   rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4099   nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4100   NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4101   rv = webnav->GoForward();
4102   return rv;
4103 }
4104 
4105 NS_IMETHODIMP
GotoIndex(int32_t aIndex)4106 nsDocShell::GotoIndex(int32_t aIndex) {
4107   if (!IsNavigationAllowed()) {
4108     return NS_OK;  // JS may not handle returning of an error code
4109   }
4110   nsresult rv;
4111   nsCOMPtr<nsISHistory> rootSH;
4112   rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4113   nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
4114   NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
4115   rv = webnav->GotoIndex(aIndex);
4116   return rv;
4117 }
4118 
4119 NS_IMETHODIMP
LoadURI(const char16_t * aURI,uint32_t aLoadFlags,nsIURI * aReferringURI,nsIInputStream * aPostStream,nsIInputStream * aHeaderStream,nsIPrincipal * aTriggeringPrincipal)4120 nsDocShell::LoadURI(const char16_t* aURI, uint32_t aLoadFlags,
4121                     nsIURI* aReferringURI, nsIInputStream* aPostStream,
4122                     nsIInputStream* aHeaderStream,
4123                     nsIPrincipal* aTriggeringPrincipal) {
4124   return LoadURIWithOptions(aURI, aLoadFlags, aReferringURI,
4125                             mozilla::net::RP_Unset, aPostStream, aHeaderStream,
4126                             nullptr, aTriggeringPrincipal);
4127 }
4128 
4129 NS_IMETHODIMP
LoadURIWithOptions(const char16_t * aURI,uint32_t aLoadFlags,nsIURI * aReferringURI,uint32_t aReferrerPolicy,nsIInputStream * aPostStream,nsIInputStream * aHeaderStream,nsIURI * aBaseURI,nsIPrincipal * aTriggeringPrincipal)4130 nsDocShell::LoadURIWithOptions(const char16_t* aURI, uint32_t aLoadFlags,
4131                                nsIURI* aReferringURI, uint32_t aReferrerPolicy,
4132                                nsIInputStream* aPostStream,
4133                                nsIInputStream* aHeaderStream, nsIURI* aBaseURI,
4134                                nsIPrincipal* aTriggeringPrincipal) {
4135   NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
4136 
4137   if (!IsNavigationAllowed()) {
4138     return NS_OK;  // JS may not handle returning of an error code
4139   }
4140   nsCOMPtr<nsIURI> uri;
4141   nsCOMPtr<nsIInputStream> postStream(aPostStream);
4142   nsresult rv = NS_OK;
4143 
4144   // Create a URI from our string; if that succeeds, we want to
4145   // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
4146   // flag.
4147 
4148   NS_ConvertUTF16toUTF8 uriString(aURI);
4149   // Cleanup the empty spaces that might be on each end.
4150   uriString.Trim(" ");
4151   // Eliminate embedded newlines, which single-line text fields now allow:
4152   uriString.StripCRLF();
4153   NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
4154 
4155   rv = NS_NewURI(getter_AddRefs(uri), uriString);
4156   if (uri) {
4157     aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
4158   }
4159 
4160   nsCOMPtr<nsIURIFixupInfo> fixupInfo;
4161   if (sURIFixup) {
4162     // Call the fixup object.  This will clobber the rv from NS_NewURI
4163     // above, but that's fine with us.  Note that we need to do this even
4164     // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
4165     // (things like view-source:mozilla.org for example).
4166     uint32_t fixupFlags = 0;
4167     if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4168       fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
4169     }
4170     if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
4171       fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
4172     }
4173     nsCOMPtr<nsIInputStream> fixupStream;
4174     rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
4175                                     getter_AddRefs(fixupStream),
4176                                     getter_AddRefs(fixupInfo));
4177 
4178     if (NS_SUCCEEDED(rv)) {
4179       fixupInfo->GetPreferredURI(getter_AddRefs(uri));
4180       fixupInfo->SetConsumer(GetAsSupports(this));
4181     }
4182 
4183     if (fixupStream) {
4184       // GetFixupURIInfo only returns a post data stream if it succeeded
4185       // and changed the URI, in which case we should override the
4186       // passed-in post data.
4187       postStream = fixupStream;
4188     }
4189 
4190     if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4191       nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4192       if (serv) {
4193         serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
4194       }
4195     }
4196   }
4197   // else no fixup service so just use the URI we created and see
4198   // what happens
4199 
4200   if (NS_ERROR_MALFORMED_URI == rv) {
4201     if (DisplayLoadError(rv, uri, aURI, nullptr) &&
4202         (aLoadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
4203       return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
4204     }
4205   }
4206 
4207   if (NS_FAILED(rv) || !uri) {
4208     return NS_ERROR_FAILURE;
4209   }
4210 
4211   PopupControlState popupState;
4212   if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
4213     popupState = openAllowed;
4214     aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
4215   } else {
4216     popupState = openOverridden;
4217   }
4218   nsAutoPopupStatePusher statePusher(popupState);
4219 
4220   bool forceAllowDataURI = aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
4221 
4222   // Don't pass certain flags that aren't needed and end up confusing
4223   // ConvertLoadTypeToDocShellInfoLoadType.  We do need to ensure that they are
4224   // passed to LoadURI though, since it uses them.
4225   uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
4226   aLoadFlags &= ~EXTRA_LOAD_FLAGS;
4227 
4228   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4229   rv = CreateLoadInfo(getter_AddRefs(loadInfo));
4230   if (NS_FAILED(rv)) {
4231     return rv;
4232   }
4233 
4234   /*
4235    * If the user "Disables Protection on This Page", we have to make sure to
4236    * remember the users decision when opening links in child tabs [Bug 906190]
4237    */
4238   uint32_t loadType;
4239   if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
4240     loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
4241   } else {
4242     loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
4243   }
4244 
4245   loadInfo->SetLoadType(ConvertLoadTypeToDocShellInfoLoadType(loadType));
4246   loadInfo->SetPostDataStream(postStream);
4247   loadInfo->SetReferrer(aReferringURI);
4248   loadInfo->SetReferrerPolicy(aReferrerPolicy);
4249   loadInfo->SetHeadersStream(aHeaderStream);
4250   loadInfo->SetBaseURI(aBaseURI);
4251   loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
4252   loadInfo->SetForceAllowDataURI(forceAllowDataURI);
4253 
4254   if (fixupInfo) {
4255     nsAutoString searchProvider, keyword;
4256     fixupInfo->GetKeywordProviderName(searchProvider);
4257     fixupInfo->GetKeywordAsSent(keyword);
4258     MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
4259   }
4260 
4261   rv = LoadURI(uri, loadInfo, extraFlags, true);
4262 
4263   // Save URI string in case it's needed later when
4264   // sending to search engine service in EndPageLoad()
4265   mOriginalUriString = uriString;
4266 
4267   return rv;
4268 }
4269 
4270 NS_IMETHODIMP
DisplayLoadError(nsresult aError,nsIURI * aURI,const char16_t * aURL,nsIChannel * aFailedChannel,bool * aDisplayedErrorPage)4271 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
4272                              const char16_t* aURL, nsIChannel* aFailedChannel,
4273                              bool* aDisplayedErrorPage) {
4274   *aDisplayedErrorPage = false;
4275   // Get prompt and string bundle servcies
4276   nsCOMPtr<nsIPrompt> prompter;
4277   nsCOMPtr<nsIStringBundle> stringBundle;
4278   GetPromptAndStringBundle(getter_AddRefs(prompter),
4279                            getter_AddRefs(stringBundle));
4280 
4281   NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
4282   NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
4283 
4284   const char* error = nullptr;
4285   // The key used to select the appropriate error message from the properties
4286   // file.
4287   const char* errorDescriptionID = nullptr;
4288   const uint32_t kMaxFormatStrArgs = 3;
4289   nsAutoString formatStrs[kMaxFormatStrArgs];
4290   uint32_t formatStrCount = 0;
4291   bool addHostPort = false;
4292   nsresult rv = NS_OK;
4293   nsAutoString messageStr;
4294   nsAutoCString cssClass;
4295   nsAutoCString errorPage;
4296 
4297   errorPage.AssignLiteral("neterror");
4298 
4299   // Turn the error code into a human readable error message.
4300   if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
4301     NS_ENSURE_ARG_POINTER(aURI);
4302 
4303     // Extract the schemes into a comma delimited list.
4304     nsAutoCString scheme;
4305     aURI->GetScheme(scheme);
4306     CopyASCIItoUTF16(scheme, formatStrs[0]);
4307     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
4308     while (nestedURI) {
4309       nsCOMPtr<nsIURI> tempURI;
4310       nsresult rv2;
4311       rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
4312       if (NS_SUCCEEDED(rv2) && tempURI) {
4313         tempURI->GetScheme(scheme);
4314         formatStrs[0].AppendLiteral(", ");
4315         AppendASCIItoUTF16(scheme, formatStrs[0]);
4316       }
4317       nestedURI = do_QueryInterface(tempURI);
4318     }
4319     formatStrCount = 1;
4320     error = "unknownProtocolFound";
4321   } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
4322     NS_ENSURE_ARG_POINTER(aURI);
4323     error = "fileNotFound";
4324   } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
4325     NS_ENSURE_ARG_POINTER(aURI);
4326     error = "fileAccessDenied";
4327   } else if (NS_ERROR_UNKNOWN_HOST == aError) {
4328     NS_ENSURE_ARG_POINTER(aURI);
4329     // Get the host
4330     nsAutoCString host;
4331     nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
4332     innermostURI->GetHost(host);
4333     CopyUTF8toUTF16(host, formatStrs[0]);
4334     formatStrCount = 1;
4335     errorDescriptionID = "dnsNotFound2";
4336     error = "dnsNotFound";
4337   } else if (NS_ERROR_CONNECTION_REFUSED == aError) {
4338     NS_ENSURE_ARG_POINTER(aURI);
4339     addHostPort = true;
4340     error = "connectionFailure";
4341   } else if (NS_ERROR_NET_INTERRUPT == aError) {
4342     NS_ENSURE_ARG_POINTER(aURI);
4343     addHostPort = true;
4344     error = "netInterrupt";
4345   } else if (NS_ERROR_NET_TIMEOUT == aError) {
4346     NS_ENSURE_ARG_POINTER(aURI);
4347     // Get the host
4348     nsAutoCString host;
4349     aURI->GetHost(host);
4350     CopyUTF8toUTF16(host, formatStrs[0]);
4351     formatStrCount = 1;
4352     error = "netTimeout";
4353   } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
4354              NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
4355     // CSP error
4356     cssClass.AssignLiteral("neterror");
4357     error = "cspBlocked";
4358   } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
4359     nsCOMPtr<nsINSSErrorsService> nsserr =
4360         do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
4361 
4362     uint32_t errorClass;
4363     if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
4364       errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
4365     }
4366 
4367     nsCOMPtr<nsISupports> securityInfo;
4368     nsCOMPtr<nsITransportSecurityInfo> tsi;
4369     if (aFailedChannel) {
4370       aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
4371     }
4372     tsi = do_QueryInterface(securityInfo);
4373     if (tsi) {
4374       uint32_t securityState;
4375       tsi->GetSecurityState(&securityState);
4376       if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
4377         error = "sslv3Used";
4378         addHostPort = true;
4379       } else if (securityState &
4380                  nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
4381         error = "weakCryptoUsed";
4382         addHostPort = true;
4383       } else {
4384         // Usually we should have aFailedChannel and get a detailed message
4385         tsi->GetErrorMessage(getter_Copies(messageStr));
4386       }
4387     } else {
4388       // No channel, let's obtain the generic error message
4389       if (nsserr) {
4390         nsserr->GetErrorMessage(aError, messageStr);
4391       }
4392     }
4393     if (!messageStr.IsEmpty()) {
4394       if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
4395         error = "nssBadCert";
4396 
4397         // If this is an HTTP Strict Transport Security host or a pinned host
4398         // and the certificate is bad, don't allow overrides (RFC 6797 section
4399         // 12.1, HPKP draft spec section 2.6).
4400         uint32_t flags =
4401             UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
4402         bool isStsHost = false;
4403         bool isPinnedHost = false;
4404         if (XRE_IsParentProcess()) {
4405           nsCOMPtr<nsISiteSecurityService> sss =
4406               do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
4407           NS_ENSURE_SUCCESS(rv, rv);
4408           rv =
4409               sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
4410                                mOriginAttributes, nullptr, nullptr, &isStsHost);
4411           NS_ENSURE_SUCCESS(rv, rv);
4412           rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
4413                                 flags, mOriginAttributes, nullptr, nullptr,
4414                                 &isPinnedHost);
4415           NS_ENSURE_SUCCESS(rv, rv);
4416         } else {
4417           mozilla::dom::ContentChild* cc =
4418               mozilla::dom::ContentChild::GetSingleton();
4419           mozilla::ipc::URIParams uri;
4420           SerializeURI(aURI, uri);
4421           cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
4422                               mOriginAttributes, &isStsHost);
4423           cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
4424                               mOriginAttributes, &isPinnedHost);
4425         }
4426 
4427         if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
4428                                  false)) {
4429           cssClass.AssignLiteral("expertBadCert");
4430         }
4431 
4432         // HSTS/pinning takes precedence over the expert bad cert pref. We
4433         // never want to show the "Add Exception" button for these sites.
4434         // In the future we should differentiate between an HSTS host and a
4435         // pinned host and display a more informative message to the user.
4436         if (isStsHost || isPinnedHost) {
4437           cssClass.AssignLiteral("badStsCert");
4438         }
4439 
4440         uint32_t bucketId;
4441         if (isStsHost) {
4442           // measuring STS separately allows us to measure click through
4443           // rates easily
4444           bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
4445         } else {
4446           bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
4447         }
4448 
4449         // See if an alternate cert error page is registered
4450         nsAutoCString alternateErrorPage;
4451         nsresult rv = Preferences::GetCString(
4452             "security.alternate_certificate_error_page", alternateErrorPage);
4453         if (NS_SUCCEEDED(rv)) {
4454           errorPage.Assign(alternateErrorPage);
4455         }
4456 
4457         if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) {
4458           Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
4459         }
4460 
4461       } else {
4462         error = "nssFailure2";
4463       }
4464     }
4465   } else if (NS_ERROR_PHISHING_URI == aError ||
4466              NS_ERROR_MALWARE_URI == aError ||
4467              NS_ERROR_UNWANTED_URI == aError ||
4468              NS_ERROR_HARMFUL_URI == aError) {
4469     nsAutoCString host;
4470     aURI->GetHost(host);
4471     CopyUTF8toUTF16(host, formatStrs[0]);
4472     formatStrCount = 1;
4473 
4474     // Malware and phishing detectors may want to use an alternate error
4475     // page, but if the pref's not set, we'll fall back on the standard page
4476     nsAutoCString alternateErrorPage;
4477     nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
4478                                           alternateErrorPage);
4479     if (NS_SUCCEEDED(rv)) {
4480       errorPage.Assign(alternateErrorPage);
4481     }
4482 
4483     uint32_t bucketId;
4484     bool sendTelemetry = false;
4485     if (NS_ERROR_PHISHING_URI == aError) {
4486       sendTelemetry = true;
4487       error = "deceptiveBlocked";
4488       bucketId = IsFrame()
4489                      ? IUrlClassifierUITelemetry::WARNING_PHISHING_PAGE_FRAME
4490                      : IUrlClassifierUITelemetry::WARNING_PHISHING_PAGE_TOP;
4491     } else if (NS_ERROR_MALWARE_URI == aError) {
4492       sendTelemetry = true;
4493       error = "malwareBlocked";
4494       bucketId = IsFrame()
4495                      ? IUrlClassifierUITelemetry::WARNING_MALWARE_PAGE_FRAME
4496                      : IUrlClassifierUITelemetry::WARNING_MALWARE_PAGE_TOP;
4497     } else if (NS_ERROR_UNWANTED_URI == aError) {
4498       sendTelemetry = true;
4499       error = "unwantedBlocked";
4500       bucketId = IsFrame()
4501                      ? IUrlClassifierUITelemetry::WARNING_UNWANTED_PAGE_FRAME
4502                      : IUrlClassifierUITelemetry::WARNING_UNWANTED_PAGE_TOP;
4503     } else if (NS_ERROR_HARMFUL_URI == aError) {
4504       sendTelemetry = true;
4505       error = "harmfulBlocked";
4506       bucketId = IsFrame()
4507                      ? IUrlClassifierUITelemetry::WARNING_HARMFUL_PAGE_FRAME
4508                      : IUrlClassifierUITelemetry::WARNING_HARMFUL_PAGE_TOP;
4509     }
4510 
4511     if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) {
4512       Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UI_EVENTS, bucketId);
4513     }
4514 
4515     cssClass.AssignLiteral("blacklist");
4516   } else if (NS_ERROR_CONTENT_CRASHED == aError) {
4517     errorPage.AssignLiteral("tabcrashed");
4518     error = "tabcrashed";
4519 
4520     nsCOMPtr<EventTarget> handler = mChromeEventHandler;
4521     if (handler) {
4522       nsCOMPtr<Element> element = do_QueryInterface(handler);
4523       element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
4524     }
4525 
4526     // DisplayLoadError requires a non-empty messageStr to proceed and call
4527     // LoadErrorPage. If the page doesn't have a title, we will use a blank
4528     // space which will be trimmed and thus treated as empty by the front-end.
4529     if (messageStr.IsEmpty()) {
4530       messageStr.AssignLiteral(u" ");
4531     }
4532   } else {
4533     // Errors requiring simple formatting
4534     switch (aError) {
4535       case NS_ERROR_MALFORMED_URI:
4536         // URI is malformed
4537         error = "malformedURI";
4538         errorDescriptionID = "malformedURI2";
4539         break;
4540       case NS_ERROR_REDIRECT_LOOP:
4541         // Doc failed to load because the server generated too many redirects
4542         error = "redirectLoop";
4543         break;
4544       case NS_ERROR_UNKNOWN_SOCKET_TYPE:
4545         // Doc failed to load because PSM is not installed
4546         error = "unknownSocketType";
4547         break;
4548       case NS_ERROR_NET_RESET:
4549         // Doc failed to load because the server kept reseting the connection
4550         // before we could read any data from it
4551         error = "netReset";
4552         break;
4553       case NS_ERROR_DOCUMENT_NOT_CACHED:
4554         // Doc failed to load because the cache does not contain a copy of
4555         // the document.
4556         error = "notCached";
4557         break;
4558       case NS_ERROR_OFFLINE:
4559         // Doc failed to load because we are offline.
4560         error = "netOffline";
4561         break;
4562       case NS_ERROR_DOCUMENT_IS_PRINTMODE:
4563         // Doc navigation attempted while Printing or Print Preview
4564         error = "isprinting";
4565         break;
4566       case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
4567         // Port blocked for security reasons
4568         addHostPort = true;
4569         error = "deniedPortAccess";
4570         break;
4571       case NS_ERROR_UNKNOWN_PROXY_HOST:
4572         // Proxy hostname could not be resolved.
4573         error = "proxyResolveFailure";
4574         break;
4575       case NS_ERROR_PROXY_CONNECTION_REFUSED:
4576         // Proxy connection was refused.
4577         error = "proxyConnectFailure";
4578         break;
4579       case NS_ERROR_INVALID_CONTENT_ENCODING:
4580         // Bad Content Encoding.
4581         error = "contentEncodingError";
4582         break;
4583       case NS_ERROR_REMOTE_XUL:
4584         error = "remoteXUL";
4585         break;
4586       case NS_ERROR_UNSAFE_CONTENT_TYPE:
4587         // Channel refused to load from an unrecognized content type.
4588         error = "unsafeContentType";
4589         break;
4590       case NS_ERROR_CORRUPTED_CONTENT:
4591         // Broken Content Detected. e.g. Content-MD5 check failure.
4592         error = "corruptedContentErrorv2";
4593         break;
4594       case NS_ERROR_INTERCEPTION_FAILED:
4595         // ServiceWorker intercepted request, but something went wrong.
4596         error = "corruptedContentErrorv2";
4597         break;
4598       case NS_ERROR_NET_INADEQUATE_SECURITY:
4599         // Server negotiated bad TLS for HTTP/2.
4600         error = "inadequateSecurityError";
4601         addHostPort = true;
4602         break;
4603       case NS_ERROR_BLOCKED_BY_POLICY:
4604         // Page blocked by policy
4605         error = "blockedByPolicy";
4606         break;
4607       default:
4608         break;
4609     }
4610   }
4611 
4612   // Test if the error should be displayed
4613   if (!error) {
4614     return NS_OK;
4615   }
4616 
4617   if (!errorDescriptionID) {
4618     errorDescriptionID = error;
4619   }
4620 
4621   // Test if the error needs to be formatted
4622   if (!messageStr.IsEmpty()) {
4623     // already obtained message
4624   } else {
4625     if (addHostPort) {
4626       // Build up the host:port string.
4627       nsAutoCString hostport;
4628       if (aURI) {
4629         aURI->GetHostPort(hostport);
4630       } else {
4631         hostport.Assign('?');
4632       }
4633       CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
4634     }
4635 
4636     nsAutoCString spec;
4637     rv = NS_ERROR_NOT_AVAILABLE;
4638     if (aURI) {
4639       // displaying "file://" is aesthetically unpleasing and could even be
4640       // confusing to the user
4641       bool isFileURI = false;
4642       rv = aURI->SchemeIs("file", &isFileURI);
4643       if (NS_SUCCEEDED(rv) && isFileURI) {
4644         aURI->GetPathQueryRef(spec);
4645       } else {
4646         aURI->GetSpec(spec);
4647       }
4648 
4649       nsCOMPtr<nsITextToSubURI> textToSubURI(
4650           do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
4651       if (NS_SUCCEEDED(rv)) {
4652         rv = textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"), spec,
4653                                             formatStrs[formatStrCount]);
4654       }
4655     } else {
4656       spec.Assign('?');
4657     }
4658     if (NS_FAILED(rv)) {
4659       CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
4660     }
4661     rv = NS_OK;
4662     ++formatStrCount;
4663 
4664     const char16_t* strs[kMaxFormatStrArgs];
4665     for (uint32_t i = 0; i < formatStrCount; i++) {
4666       strs[i] = formatStrs[i].get();
4667     }
4668     nsAutoString str;
4669     rv = stringBundle->FormatStringFromName(errorDescriptionID, strs,
4670                                             formatStrCount, str);
4671     NS_ENSURE_SUCCESS(rv, rv);
4672     messageStr.Assign(str.get());
4673   }
4674 
4675   // Display the error as a page or an alert prompt
4676   NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
4677 
4678   if (NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) {
4679     bool isSecureURI = false;
4680     rv = aURI->SchemeIs("https", &isSecureURI);
4681     if (NS_SUCCEEDED(rv) && isSecureURI) {
4682       // Maybe TLS intolerant. Treat this as an SSL error.
4683       error = "nssFailure2";
4684     }
4685   }
4686 
4687   if (UseErrorPages()) {
4688     // Display an error page
4689     nsresult loadedPage =
4690         LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
4691                       cssClass.get(), aFailedChannel);
4692     *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
4693   } else {
4694     // The prompter reqires that our private window has a document (or it
4695     // asserts). Satisfy that assertion now since GetDoc will force
4696     // creation of one if it hasn't already been created.
4697     if (mScriptGlobal) {
4698       Unused << mScriptGlobal->GetDoc();
4699     }
4700 
4701     // Display a message box
4702     prompter->Alert(nullptr, messageStr.get());
4703   }
4704 
4705   return NS_OK;
4706 }
4707 
4708 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
4709 
LoadErrorPage(nsIURI * aURI,const char16_t * aURL,const char * aErrorPage,const char * aErrorType,const char16_t * aDescription,const char * aCSSClass,nsIChannel * aFailedChannel)4710 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
4711                                    const char* aErrorPage,
4712                                    const char* aErrorType,
4713                                    const char16_t* aDescription,
4714                                    const char* aCSSClass,
4715                                    nsIChannel* aFailedChannel) {
4716   MOZ_ASSERT(!mIsBeingDestroyed);
4717 
4718 #if defined(DEBUG)
4719   if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
4720     nsAutoCString chanName;
4721     if (aFailedChannel) {
4722       aFailedChannel->GetName(chanName);
4723     } else {
4724       chanName.AssignLiteral("<no channel>");
4725     }
4726 
4727     MOZ_LOG(gDocShellLog, LogLevel::Debug,
4728             ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
4729              this, aURI ? aURI->GetSpecOrDefault().get() : "",
4730              NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
4731   }
4732 #endif
4733   mFailedChannel = aFailedChannel;
4734   mFailedURI = aURI;
4735   mFailedLoadType = mLoadType;
4736 
4737   if (mLSHE) {
4738     // Abandon mLSHE's BFCache entry and create a new one.  This way, if
4739     // we go back or forward to another SHEntry with the same doc
4740     // identifier, the error page won't persist.
4741     mLSHE->AbandonBFCacheEntry();
4742   }
4743 
4744   nsAutoCString url;
4745   if (aURI) {
4746     nsresult rv = aURI->GetSpec(url);
4747     NS_ENSURE_SUCCESS(rv, rv);
4748   } else if (aURL) {
4749     CopyUTF16toUTF8(aURL, url);
4750   } else {
4751     return NS_ERROR_INVALID_POINTER;
4752   }
4753 
4754     // Create a URL to pass all the error information through to the page.
4755 
4756 #undef SAFE_ESCAPE
4757 #define SAFE_ESCAPE(output, input, params)             \
4758   if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
4759     return NS_ERROR_OUT_OF_MEMORY;                     \
4760   }
4761 
4762   nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
4763   SAFE_ESCAPE(escapedUrl, url, url_Path);
4764   SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
4765   SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
4766               url_Path);
4767   if (aCSSClass) {
4768     nsCString cssClass(aCSSClass);
4769     SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
4770   }
4771   nsCString errorPageUrl("about:");
4772   errorPageUrl.AppendASCII(aErrorPage);
4773   errorPageUrl.AppendLiteral("?e=");
4774 
4775   errorPageUrl.AppendASCII(escapedError.get());
4776   errorPageUrl.AppendLiteral("&u=");
4777   errorPageUrl.AppendASCII(escapedUrl.get());
4778   if ((strcmp(aErrorPage, "blocked") == 0) &&
4779       Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
4780     errorPageUrl.AppendLiteral("&o=1");
4781   }
4782   if (!escapedCSSClass.IsEmpty()) {
4783     errorPageUrl.AppendLiteral("&s=");
4784     errorPageUrl.AppendASCII(escapedCSSClass.get());
4785   }
4786   errorPageUrl.AppendLiteral("&c=UTF-8");
4787 
4788   nsAutoCString frameType(FrameTypeToString(mFrameType));
4789   errorPageUrl.AppendLiteral("&f=");
4790   errorPageUrl.AppendASCII(frameType.get());
4791 
4792   nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
4793   int32_t cpsState;
4794   if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
4795       cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
4796     errorPageUrl.AppendLiteral("&captive=true");
4797   }
4798 
4799   // netError.xhtml's getDescription only handles the "d" parameter at the
4800   // end of the URL, so append it last.
4801   errorPageUrl.AppendLiteral("&d=");
4802   errorPageUrl.AppendASCII(escapedDescription.get());
4803 
4804   nsCOMPtr<nsIURI> errorPageURI;
4805   nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
4806   NS_ENSURE_SUCCESS(rv, rv);
4807 
4808   return InternalLoad(
4809       errorPageURI, nullptr, Nothing(), false, false, false, nullptr,
4810       mozilla::net::RP_Unset, nsContentUtils::GetSystemPrincipal(), nullptr,
4811       INTERNAL_LOAD_FLAGS_NONE, EmptyString(), nullptr, VoidString(), nullptr,
4812       -1, nullptr, LOAD_ERROR_PAGE, nullptr, true, VoidString(), this, nullptr,
4813       nullptr, nullptr);
4814 }
4815 
4816 NS_IMETHODIMP
Reload(uint32_t aReloadFlags)4817 nsDocShell::Reload(uint32_t aReloadFlags) {
4818   if (!IsNavigationAllowed()) {
4819     return NS_OK;  // JS may not handle returning of an error code
4820   }
4821   nsresult rv;
4822   NS_ASSERTION(((aReloadFlags & 0xf) == 0),
4823                "Reload command not updated to use load flags!");
4824   NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4825                "Don't pass these flags to Reload");
4826 
4827   uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4828   NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4829 
4830   // Send notifications to the HistoryListener if any, about the impending
4831   // reload
4832   nsCOMPtr<nsISHistory> rootSH;
4833   rv = GetRootSessionHistory(getter_AddRefs(rootSH));
4834   nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
4835   bool canReload = true;
4836   if (rootSH) {
4837     shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
4838   }
4839 
4840   if (!canReload) {
4841     return NS_OK;
4842   }
4843 
4844   /* If you change this part of code, make sure bug 45297 does not re-occur */
4845   if (mOSHE) {
4846     rv = LoadHistoryEntry(mOSHE, loadType);
4847   } else if (mLSHE) {  // In case a reload happened before the current load is
4848                        // done
4849     rv = LoadHistoryEntry(mLSHE, loadType);
4850   } else {
4851     nsCOMPtr<nsIDocument> doc(GetDocument());
4852 
4853     if (!doc) {
4854       return NS_OK;
4855     }
4856 
4857     // Do not inherit owner from document
4858     uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4859     nsAutoString srcdoc;
4860     nsCOMPtr<nsIURI> baseURI;
4861     nsCOMPtr<nsIURI> originalURI;
4862     nsCOMPtr<nsIURI> resultPrincipalURI;
4863     bool loadReplace = false;
4864 
4865     nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
4866     nsAutoString contentTypeHint;
4867     doc->GetContentType(contentTypeHint);
4868 
4869     if (doc->IsSrcdocDocument()) {
4870       doc->GetSrcdocData(srcdoc);
4871       flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4872       baseURI = doc->GetBaseURI();
4873     }
4874     nsCOMPtr<nsIChannel> chan = doc->GetChannel();
4875     if (chan) {
4876       uint32_t loadFlags;
4877       chan->GetLoadFlags(&loadFlags);
4878       loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4879       nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4880       if (httpChan) {
4881         httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4882       }
4883 
4884       nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
4885       if (loadInfo) {
4886         loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4887       }
4888     }
4889 
4890     MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal");
4891 
4892     // Stack variables to ensure changes to the member variables don't affect to
4893     // the call.
4894     nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4895     nsCOMPtr<nsIURI> referrerURI = mReferrerURI;
4896     uint32_t referrerPolicy = mReferrerPolicy;
4897 
4898     // Reload always rewrites result principal URI.
4899     Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4900     emplacedResultPrincipalURI.emplace(Move(resultPrincipalURI));
4901     rv = InternalLoad(currentURI, originalURI, emplacedResultPrincipalURI,
4902                       false, loadReplace,
4903                       false,  // IsFromProcessingFrameAttributes
4904                       referrerURI, referrerPolicy, triggeringPrincipal,
4905                       triggeringPrincipal, flags,
4906                       EmptyString(),  // No window target
4907                       NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
4908                       VoidString(),  // No forced download
4909                       nullptr,       // No post data
4910                       -1,            // No post data length
4911                       nullptr,       // No headers data
4912                       loadType,      // Load type
4913                       nullptr,       // No SHEntry
4914                       true,
4915                       srcdoc,  // srcdoc argument for iframe
4916                       this,    // For reloads we are the source
4917                       baseURI,
4918                       nullptr,   // No nsIDocShell
4919                       nullptr);  // No nsIRequest
4920   }
4921 
4922   return rv;
4923 }
4924 
4925 NS_IMETHODIMP
Stop(uint32_t aStopFlags)4926 nsDocShell::Stop(uint32_t aStopFlags) {
4927   // Revoke any pending event related to content viewer restoration
4928   mRestorePresentationEvent.Revoke();
4929 
4930   if (mLoadType == LOAD_ERROR_PAGE) {
4931     if (mLSHE) {
4932       // Since error page loads never unset mLSHE, do so now
4933       SetHistoryEntry(&mOSHE, mLSHE);
4934       SetHistoryEntry(&mLSHE, nullptr);
4935     }
4936 
4937     mFailedChannel = nullptr;
4938     mFailedURI = nullptr;
4939   }
4940 
4941   if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4942     // Stop the document loading
4943     if (mContentViewer) {
4944       nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4945       cv->Stop();
4946     }
4947   }
4948 
4949   if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4950     // Suspend any timers that were set for this loader.  We'll clear
4951     // them out for good in CreateContentViewer.
4952     if (mRefreshURIList) {
4953       SuspendRefreshURIs();
4954       mSavedRefreshURIList.swap(mRefreshURIList);
4955       mRefreshURIList = nullptr;
4956     }
4957 
4958     // XXXbz We could also pass |this| to nsIURILoader::Stop.  That will
4959     // just call Stop() on us as an nsIDocumentLoader... We need fewer
4960     // redundant apis!
4961     Stop();
4962   }
4963 
4964   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
4965   while (iter.HasMore()) {
4966     nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
4967     if (shellAsNav) {
4968       shellAsNav->Stop(aStopFlags);
4969     }
4970   }
4971 
4972   return NS_OK;
4973 }
4974 
4975 NS_IMETHODIMP
GetDocument(nsIDOMDocument ** aDocument)4976 nsDocShell::GetDocument(nsIDOMDocument** aDocument) {
4977   NS_ENSURE_ARG_POINTER(aDocument);
4978   NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4979 
4980   nsIDocument* doc = mContentViewer->GetDocument();
4981   if (!doc) {
4982     return NS_ERROR_NOT_AVAILABLE;
4983   }
4984 
4985   return CallQueryInterface(doc, aDocument);
4986 }
4987 
4988 NS_IMETHODIMP
GetCurrentURI(nsIURI ** aURI)4989 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4990   NS_ENSURE_ARG_POINTER(aURI);
4991 
4992   if (mCurrentURI) {
4993     return NS_EnsureSafeToReturn(mCurrentURI, aURI);
4994   }
4995 
4996   *aURI = nullptr;
4997   return NS_OK;
4998 }
4999 
5000 NS_IMETHODIMP
GetReferringURI(nsIURI ** aURI)5001 nsDocShell::GetReferringURI(nsIURI** aURI) {
5002   NS_ENSURE_ARG_POINTER(aURI);
5003 
5004   *aURI = mReferrerURI;
5005   NS_IF_ADDREF(*aURI);
5006 
5007   return NS_OK;
5008 }
5009 
5010 NS_IMETHODIMP
SetSessionHistory(nsISHistory * aSessionHistory)5011 nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory) {
5012   NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE);
5013   // make sure that we are the root docshell and
5014   // set a handle to root docshell in SH.
5015 
5016   MOZ_ASSERT(!mIsBeingDestroyed);
5017 
5018   nsCOMPtr<nsIDocShellTreeItem> root;
5019   /* Get the root docshell. If *this* is the root docshell
5020    * then save a handle to *this* in SH. SH needs it to do
5021    * traversions thro' its entries
5022    */
5023   GetSameTypeRootTreeItem(getter_AddRefs(root));
5024   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
5025   if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
5026     mSessionHistory = aSessionHistory;
5027     nsCOMPtr<nsISHistoryInternal> shPrivate =
5028         do_QueryInterface(mSessionHistory);
5029     NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
5030     shPrivate->SetRootDocShell(this);
5031     return NS_OK;
5032   }
5033   return NS_ERROR_FAILURE;
5034 }
5035 
5036 NS_IMETHODIMP
GetSessionHistory(nsISHistory ** aSessionHistory)5037 nsDocShell::GetSessionHistory(nsISHistory** aSessionHistory) {
5038   NS_ENSURE_ARG_POINTER(aSessionHistory);
5039   *aSessionHistory = mSessionHistory;
5040   NS_IF_ADDREF(*aSessionHistory);
5041   return NS_OK;
5042 }
5043 
5044 //*****************************************************************************
5045 // nsDocShell::nsIWebPageDescriptor
5046 //*****************************************************************************
5047 
5048 NS_IMETHODIMP
LoadPage(nsISupports * aPageDescriptor,uint32_t aDisplayType)5049 nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType) {
5050   nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
5051 
5052   // Currently, the opaque 'page descriptor' is an nsISHEntry...
5053   if (!shEntryIn) {
5054     return NS_ERROR_INVALID_POINTER;
5055   }
5056 
5057   // Now clone shEntryIn, since we might end up modifying it later on, and we
5058   // want a page descriptor to be reusable.
5059   nsCOMPtr<nsISHEntry> shEntry;
5060   nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
5061   NS_ENSURE_SUCCESS(rv, rv);
5062 
5063   // Give our cloned shEntry a new bfcache entry so this load is independent
5064   // of all other loads.  (This is important, in particular, for bugs 582795
5065   // and 585298.)
5066   rv = shEntry->AbandonBFCacheEntry();
5067   NS_ENSURE_SUCCESS(rv, rv);
5068 
5069   //
5070   // load the page as view-source
5071   //
5072   if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
5073     nsCOMPtr<nsIURI> oldUri, newUri;
5074     nsCString spec, newSpec;
5075 
5076     // Create a new view-source URI and replace the original.
5077     rv = shEntry->GetURI(getter_AddRefs(oldUri));
5078     if (NS_FAILED(rv)) {
5079       return rv;
5080     }
5081 
5082     oldUri->GetSpec(spec);
5083     newSpec.AppendLiteral("view-source:");
5084     newSpec.Append(spec);
5085 
5086     rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
5087     if (NS_FAILED(rv)) {
5088       return rv;
5089     }
5090     shEntry->SetURI(newUri);
5091     shEntry->SetOriginalURI(nullptr);
5092     shEntry->SetResultPrincipalURI(nullptr);
5093     // shEntry's current triggering principal is whoever loaded that page
5094     // initially. But now we're doing another load of the page, via an API that
5095     // is only exposed to system code.  The triggering principal for this load
5096     // should be the system principal.
5097     shEntry->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
5098   }
5099 
5100   rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
5101   return rv;
5102 }
5103 
5104 NS_IMETHODIMP
GetCurrentDescriptor(nsISupports ** aPageDescriptor)5105 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
5106   NS_PRECONDITION(aPageDescriptor, "Null out param?");
5107 
5108   *aPageDescriptor = nullptr;
5109 
5110   nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
5111   if (src) {
5112     nsCOMPtr<nsISHEntry> dest;
5113 
5114     nsresult rv = src->Clone(getter_AddRefs(dest));
5115     if (NS_FAILED(rv)) {
5116       return rv;
5117     }
5118 
5119     // null out inappropriate cloned attributes...
5120     dest->SetParent(nullptr);
5121     dest->SetIsSubFrame(false);
5122 
5123     return CallQueryInterface(dest, aPageDescriptor);
5124   }
5125 
5126   return NS_ERROR_NOT_AVAILABLE;
5127 }
5128 
5129 //*****************************************************************************
5130 // nsDocShell::nsIBaseWindow
5131 //*****************************************************************************
5132 
5133 NS_IMETHODIMP
InitWindow(nativeWindow aParentNativeWindow,nsIWidget * aParentWidget,int32_t aX,int32_t aY,int32_t aWidth,int32_t aHeight)5134 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
5135                        nsIWidget* aParentWidget, int32_t aX, int32_t aY,
5136                        int32_t aWidth, int32_t aHeight) {
5137   SetParentWidget(aParentWidget);
5138   SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
5139 
5140   return NS_OK;
5141 }
5142 
5143 NS_IMETHODIMP
Create()5144 nsDocShell::Create() {
5145   if (mCreated) {
5146     // We've already been created
5147     return NS_OK;
5148   }
5149 
5150   NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5151                "Unexpected item type in docshell");
5152 
5153   NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
5154   mCreated = true;
5155 
5156   if (gValidateOrigin == 0xffffffff) {
5157     // Check pref to see if we should prevent frameset spoofing
5158     gValidateOrigin =
5159         Preferences::GetBool("browser.frame.validate_origin", true);
5160   }
5161 
5162   // Should we use XUL error pages instead of alerts if possible?
5163   mUseErrorPages =
5164       Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
5165 
5166   if (!gAddedPreferencesVarCache) {
5167     Preferences::AddBoolVarCache(
5168         &sUseErrorPages, "browser.xul.error_pages.enabled", mUseErrorPages);
5169     gAddedPreferencesVarCache = true;
5170   }
5171 
5172   mDisableMetaRefreshWhenInactive =
5173       Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
5174                            mDisableMetaRefreshWhenInactive);
5175 
5176   mDeviceSizeIsPageSize = Preferences::GetBool(
5177       "docshell.device_size_is_page_size", mDeviceSizeIsPageSize);
5178 
5179   nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5180   if (serv) {
5181     const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
5182                                                : NS_CHROME_WEBNAVIGATION_CREATE;
5183     serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5184   }
5185 
5186   return NS_OK;
5187 }
5188 
5189 NS_IMETHODIMP
Destroy()5190 nsDocShell::Destroy() {
5191   // XXX: We allow this function to be called just once.  If you are going to
5192   // reset new variables in this function, please make sure the variables will
5193   // never be re-initialized.  Adding assertions to check |mIsBeingDestroyed|
5194   // in the setter functions for the variables would be enough.
5195   if (mIsBeingDestroyed) {
5196     return NS_ERROR_DOCSHELL_DYING;
5197   }
5198 
5199   NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5200                "Unexpected item type in docshell");
5201 
5202   AssertOriginAttributesMatchPrivateBrowsing();
5203 
5204   nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5205   if (serv) {
5206     const char* msg = mItemType == typeContent
5207                           ? NS_WEBNAVIGATION_DESTROY
5208                           : NS_CHROME_WEBNAVIGATION_DESTROY;
5209     serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5210   }
5211 
5212   mIsBeingDestroyed = true;
5213 
5214   // Brak the cycle with the initial client, if present.
5215   mInitialClientSource.reset();
5216 
5217   // Make sure we don't record profile timeline markers anymore
5218   SetRecordProfileTimelineMarkers(false);
5219 
5220   // Remove our pref observers
5221   if (mObserveErrorPages) {
5222     mObserveErrorPages = false;
5223   }
5224 
5225   // Make sure to blow away our mLoadingURI just in case.  No loads
5226   // from inside this pagehide.
5227   mLoadingURI = nullptr;
5228 
5229   // Fire unload event before we blow anything away.
5230   (void)FirePageHideNotification(true);
5231 
5232   // Clear pointers to any detached nsEditorData that's lying
5233   // around in shistory entries. Breaks cycle. See bug 430921.
5234   if (mOSHE) {
5235     mOSHE->SetEditorData(nullptr);
5236   }
5237   if (mLSHE) {
5238     mLSHE->SetEditorData(nullptr);
5239   }
5240 
5241   // Note: mContentListener can be null if Init() failed and we're being
5242   // called from the destructor.
5243   if (mContentListener) {
5244     mContentListener->DropDocShellReference();
5245     mContentListener->SetParentContentListener(nullptr);
5246     // Note that we do NOT set mContentListener to null here; that
5247     // way if someone tries to do a load in us after this point
5248     // the nsDSURIContentListener will block it.  All of which
5249     // means that we should do this before calling Stop(), of
5250     // course.
5251   }
5252 
5253   // Stop any URLs that are currently being loaded...
5254   Stop(nsIWebNavigation::STOP_ALL);
5255 
5256   mEditorData = nullptr;
5257 
5258   mTransferableHookData = nullptr;
5259 
5260   // Save the state of the current document, before destroying the window.
5261   // This is needed to capture the state of a frameset when the new document
5262   // causes the frameset to be destroyed...
5263   PersistLayoutHistoryState();
5264 
5265   // Remove this docshell from its parent's child list
5266   nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
5267       do_QueryInterface(GetAsSupports(mParent));
5268   if (docShellParentAsItem) {
5269     docShellParentAsItem->RemoveChild(this);
5270   }
5271 
5272   if (mContentViewer) {
5273     mContentViewer->Close(nullptr);
5274     mContentViewer->Destroy();
5275     mContentViewer = nullptr;
5276   }
5277 
5278   nsDocLoader::Destroy();
5279 
5280   mParentWidget = nullptr;
5281   mCurrentURI = nullptr;
5282 
5283   if (mScriptGlobal) {
5284     mScriptGlobal->DetachFromDocShell();
5285     mScriptGlobal = nullptr;
5286   }
5287 
5288   if (mSessionHistory) {
5289     // We want to destroy these content viewers now rather than
5290     // letting their destruction wait for the session history
5291     // entries to get garbage collected.  (Bug 488394)
5292     nsCOMPtr<nsISHistoryInternal> shPrivate =
5293         do_QueryInterface(mSessionHistory);
5294     if (shPrivate) {
5295       shPrivate->EvictAllContentViewers();
5296     }
5297     mSessionHistory = nullptr;
5298   }
5299 
5300   SetTreeOwner(nullptr);
5301 
5302   mChromeEventHandler = nullptr;
5303 
5304   mOnePermittedSandboxedNavigator = nullptr;
5305 
5306   // required to break ref cycle
5307   mSecurityUI = nullptr;
5308 
5309   // Cancel any timers that were set for this docshell; this is needed
5310   // to break the cycle between us and the timers.
5311   CancelRefreshURITimers();
5312 
5313   if (UsePrivateBrowsing()) {
5314     mPrivateBrowsingId = 0;
5315     mOriginAttributes.SyncAttributesWithPrivateBrowsing(false);
5316     if (mAffectPrivateSessionLifetime) {
5317       DecreasePrivateDocShellCount();
5318     }
5319   }
5320 
5321   return NS_OK;
5322 }
5323 
5324 NS_IMETHODIMP
GetUnscaledDevicePixelsPerCSSPixel(double * aScale)5325 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
5326   if (mParentWidget) {
5327     *aScale = mParentWidget->GetDefaultScale().scale;
5328     return NS_OK;
5329   }
5330 
5331   nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5332   if (ownerWindow) {
5333     return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
5334   }
5335 
5336   *aScale = 1.0;
5337   return NS_OK;
5338 }
5339 
5340 NS_IMETHODIMP
GetDevicePixelsPerDesktopPixel(double * aScale)5341 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
5342   if (mParentWidget) {
5343     *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
5344     return NS_OK;
5345   }
5346 
5347   nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5348   if (ownerWindow) {
5349     return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
5350   }
5351 
5352   *aScale = 1.0;
5353   return NS_OK;
5354 }
5355 
5356 NS_IMETHODIMP
SetPosition(int32_t aX,int32_t aY)5357 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
5358   mBounds.MoveTo(aX, aY);
5359 
5360   if (mContentViewer) {
5361     NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
5362   }
5363 
5364   return NS_OK;
5365 }
5366 
5367 NS_IMETHODIMP
SetPositionDesktopPix(int32_t aX,int32_t aY)5368 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
5369   nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5370   if (ownerWindow) {
5371     return ownerWindow->SetPositionDesktopPix(aX, aY);
5372   }
5373 
5374   double scale = 1.0;
5375   GetDevicePixelsPerDesktopPixel(&scale);
5376   return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
5377 }
5378 
5379 NS_IMETHODIMP
GetPosition(int32_t * aX,int32_t * aY)5380 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
5381   return GetPositionAndSize(aX, aY, nullptr, nullptr);
5382 }
5383 
5384 NS_IMETHODIMP
SetSize(int32_t aWidth,int32_t aHeight,bool aRepaint)5385 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
5386   int32_t x = 0, y = 0;
5387   GetPosition(&x, &y);
5388   return SetPositionAndSize(x, y, aWidth, aHeight,
5389                             aRepaint ? nsIBaseWindow::eRepaint : 0);
5390 }
5391 
5392 NS_IMETHODIMP
GetSize(int32_t * aWidth,int32_t * aHeight)5393 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
5394   return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
5395 }
5396 
5397 NS_IMETHODIMP
SetPositionAndSize(int32_t aX,int32_t aY,int32_t aWidth,int32_t aHeight,uint32_t aFlags)5398 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
5399                                int32_t aHeight, uint32_t aFlags) {
5400   mBounds.SetRect(aX, aY, aWidth, aHeight);
5401 
5402   // Hold strong ref, since SetBounds can make us null out mContentViewer
5403   nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
5404   if (viewer) {
5405     uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
5406                            ? nsIContentViewer::eDelayResize
5407                            : 0;
5408     // XXX Border figured in here or is that handled elsewhere?
5409     nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
5410     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
5411   }
5412 
5413   return NS_OK;
5414 }
5415 
5416 NS_IMETHODIMP
GetPositionAndSize(int32_t * aX,int32_t * aY,int32_t * aWidth,int32_t * aHeight)5417 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
5418                                int32_t* aHeight) {
5419   if (mParentWidget) {
5420     // ensure size is up-to-date if window has changed resolution
5421     LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
5422     SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
5423   }
5424 
5425   // We should really consider just getting this information from
5426   // our window instead of duplicating the storage and code...
5427   if (aWidth || aHeight) {
5428     // Caller wants to know our size; make sure to give them up to
5429     // date information.
5430     nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
5431     if (doc) {
5432       doc->FlushPendingNotifications(FlushType::Layout);
5433     }
5434   }
5435 
5436   DoGetPositionAndSize(aX, aY, aWidth, aHeight);
5437   return NS_OK;
5438 }
5439 
DoGetPositionAndSize(int32_t * aX,int32_t * aY,int32_t * aWidth,int32_t * aHeight)5440 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
5441                                       int32_t* aHeight) {
5442   if (aX) {
5443     *aX = mBounds.X();
5444   }
5445   if (aY) {
5446     *aY = mBounds.Y();
5447   }
5448   if (aWidth) {
5449     *aWidth = mBounds.Width();
5450   }
5451   if (aHeight) {
5452     *aHeight = mBounds.Height();
5453   }
5454 }
5455 
5456 NS_IMETHODIMP
Repaint(bool aForce)5457 nsDocShell::Repaint(bool aForce) {
5458   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5459   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5460 
5461   nsViewManager* viewManager = presShell->GetViewManager();
5462   NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
5463 
5464   viewManager->InvalidateAllViews();
5465   return NS_OK;
5466 }
5467 
5468 NS_IMETHODIMP
GetParentWidget(nsIWidget ** aParentWidget)5469 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
5470   NS_ENSURE_ARG_POINTER(aParentWidget);
5471 
5472   *aParentWidget = mParentWidget;
5473   NS_IF_ADDREF(*aParentWidget);
5474 
5475   return NS_OK;
5476 }
5477 
5478 NS_IMETHODIMP
SetParentWidget(nsIWidget * aParentWidget)5479 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
5480   MOZ_ASSERT(!mIsBeingDestroyed);
5481   mParentWidget = aParentWidget;
5482 
5483   return NS_OK;
5484 }
5485 
5486 NS_IMETHODIMP
GetParentNativeWindow(nativeWindow * aParentNativeWindow)5487 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
5488   NS_ENSURE_ARG_POINTER(aParentNativeWindow);
5489 
5490   if (mParentWidget) {
5491     *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
5492   } else {
5493     *aParentNativeWindow = nullptr;
5494   }
5495 
5496   return NS_OK;
5497 }
5498 
5499 NS_IMETHODIMP
SetParentNativeWindow(nativeWindow aParentNativeWindow)5500 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
5501   return NS_ERROR_NOT_IMPLEMENTED;
5502 }
5503 
5504 NS_IMETHODIMP
GetNativeHandle(nsAString & aNativeHandle)5505 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
5506   // the nativeHandle should be accessed from nsIXULWindow
5507   return NS_ERROR_NOT_IMPLEMENTED;
5508 }
5509 
5510 NS_IMETHODIMP
GetVisibility(bool * aVisibility)5511 nsDocShell::GetVisibility(bool* aVisibility) {
5512   NS_ENSURE_ARG_POINTER(aVisibility);
5513 
5514   *aVisibility = false;
5515 
5516   if (!mContentViewer) {
5517     return NS_OK;
5518   }
5519 
5520   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5521   if (!presShell) {
5522     return NS_OK;
5523   }
5524 
5525   // get the view manager
5526   nsViewManager* vm = presShell->GetViewManager();
5527   NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
5528 
5529   // get the root view
5530   nsView* view = vm->GetRootView();  // views are not ref counted
5531   NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
5532 
5533   // if our root view is hidden, we are not visible
5534   if (view->GetVisibility() == nsViewVisibility_kHide) {
5535     return NS_OK;
5536   }
5537 
5538   // otherwise, we must walk up the document and view trees checking
5539   // for a hidden view, unless we're an off screen browser, which
5540   // would make this test meaningless.
5541 
5542   RefPtr<nsDocShell> docShell = this;
5543   RefPtr<nsDocShell> parentItem = docShell->GetParentDocshell();
5544   while (parentItem) {
5545     presShell = docShell->GetPresShell();
5546 
5547     nsCOMPtr<nsIPresShell> pPresShell = parentItem->GetPresShell();
5548 
5549     // Null-check for crash in bug 267804
5550     if (!pPresShell) {
5551       NS_NOTREACHED("parent docshell has null pres shell");
5552       return NS_OK;
5553     }
5554 
5555     vm = presShell->GetViewManager();
5556     if (vm) {
5557       view = vm->GetRootView();
5558     }
5559 
5560     if (view) {
5561       view = view->GetParent();  // anonymous inner view
5562       if (view) {
5563         view = view->GetParent();  // subdocumentframe's view
5564       }
5565     }
5566 
5567     nsIFrame* frame = view ? view->GetFrame() : nullptr;
5568     bool isDocShellOffScreen = false;
5569     docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
5570     if (frame &&
5571         !frame->IsVisibleConsideringAncestors(
5572             nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
5573         !isDocShellOffScreen) {
5574       return NS_OK;
5575     }
5576 
5577     docShell = parentItem;
5578     parentItem = docShell->GetParentDocshell();
5579   }
5580 
5581   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5582   if (!treeOwnerAsWin) {
5583     *aVisibility = true;
5584     return NS_OK;
5585   }
5586 
5587   // Check with the tree owner as well to give embedders a chance to
5588   // expose visibility as well.
5589   return treeOwnerAsWin->GetVisibility(aVisibility);
5590 }
5591 
5592 NS_IMETHODIMP
SetIsOffScreenBrowser(bool aIsOffScreen)5593 nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen) {
5594   mIsOffScreenBrowser = aIsOffScreen;
5595   return NS_OK;
5596 }
5597 
5598 NS_IMETHODIMP
GetIsOffScreenBrowser(bool * aIsOffScreen)5599 nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen) {
5600   *aIsOffScreen = mIsOffScreenBrowser;
5601   return NS_OK;
5602 }
5603 
5604 NS_IMETHODIMP
SetIsActive(bool aIsActive)5605 nsDocShell::SetIsActive(bool aIsActive) {
5606   // We disallow setting active on chrome docshells.
5607   if (mItemType == nsIDocShellTreeItem::typeChrome) {
5608     return NS_ERROR_INVALID_ARG;
5609   }
5610 
5611   // Keep track ourselves.
5612   mIsActive = aIsActive;
5613 
5614   // Tell the PresShell about it.
5615   nsCOMPtr<nsIPresShell> pshell = GetPresShell();
5616   if (pshell) {
5617     pshell->SetIsActive(aIsActive);
5618   }
5619 
5620   // Tell the window about it
5621   if (mScriptGlobal) {
5622     mScriptGlobal->SetIsBackground(!aIsActive);
5623     if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
5624       // Update orientation when the top-level browsing context becomes active.
5625       if (aIsActive) {
5626         nsCOMPtr<nsIDocShellTreeItem> parent;
5627         GetSameTypeParent(getter_AddRefs(parent));
5628         if (!parent) {
5629           // We only care about the top-level browsing context.
5630           uint16_t orientation = OrientationLock();
5631           ScreenOrientation::UpdateActiveOrientationLock(orientation);
5632         }
5633       }
5634 
5635       doc->PostVisibilityUpdateEvent();
5636     }
5637   }
5638 
5639   // Tell the nsDOMNavigationTiming about it
5640   RefPtr<nsDOMNavigationTiming> timing = mTiming;
5641   if (!timing && mContentViewer) {
5642     nsIDocument* doc = mContentViewer->GetDocument();
5643     if (doc) {
5644       timing = doc->GetNavigationTiming();
5645     }
5646   }
5647   if (timing) {
5648     timing->NotifyDocShellStateChanged(
5649         aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
5650                   : nsDOMNavigationTiming::DocShellState::eInactive);
5651   }
5652 
5653   // Recursively tell all of our children, but don't tell <iframe mozbrowser>
5654   // children; they handle their state separately.
5655   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5656   while (iter.HasMore()) {
5657     nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5658     if (!docshell) {
5659       continue;
5660     }
5661 
5662     if (!docshell->GetIsMozBrowser()) {
5663       docshell->SetIsActive(aIsActive);
5664     }
5665   }
5666 
5667   // Restart or stop meta refresh timers if necessary
5668   if (mDisableMetaRefreshWhenInactive) {
5669     if (mIsActive) {
5670       ResumeRefreshURIs();
5671     } else {
5672       SuspendRefreshURIs();
5673     }
5674   }
5675 
5676   return NS_OK;
5677 }
5678 
5679 NS_IMETHODIMP
GetIsActive(bool * aIsActive)5680 nsDocShell::GetIsActive(bool* aIsActive) {
5681   *aIsActive = mIsActive;
5682   return NS_OK;
5683 }
5684 
5685 NS_IMETHODIMP
SetIsAppTab(bool aIsAppTab)5686 nsDocShell::SetIsAppTab(bool aIsAppTab) {
5687   mIsAppTab = aIsAppTab;
5688   return NS_OK;
5689 }
5690 
5691 NS_IMETHODIMP
GetIsAppTab(bool * aIsAppTab)5692 nsDocShell::GetIsAppTab(bool* aIsAppTab) {
5693   *aIsAppTab = mIsAppTab;
5694   return NS_OK;
5695 }
5696 
5697 NS_IMETHODIMP
SetSandboxFlags(uint32_t aSandboxFlags)5698 nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags) {
5699   mSandboxFlags = aSandboxFlags;
5700   return NS_OK;
5701 }
5702 
5703 NS_IMETHODIMP
GetSandboxFlags(uint32_t * aSandboxFlags)5704 nsDocShell::GetSandboxFlags(uint32_t* aSandboxFlags) {
5705   *aSandboxFlags = mSandboxFlags;
5706   return NS_OK;
5707 }
5708 
5709 NS_IMETHODIMP
SetOnePermittedSandboxedNavigator(nsIDocShell * aSandboxedNavigator)5710 nsDocShell::SetOnePermittedSandboxedNavigator(
5711     nsIDocShell* aSandboxedNavigator) {
5712   if (mOnePermittedSandboxedNavigator) {
5713     NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
5714     return NS_OK;
5715   }
5716 
5717   MOZ_ASSERT(!mIsBeingDestroyed);
5718 
5719   mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
5720   NS_ASSERTION(
5721       mOnePermittedSandboxedNavigator,
5722       "One Permitted Sandboxed Navigator must support weak references.");
5723 
5724   return NS_OK;
5725 }
5726 
5727 NS_IMETHODIMP
GetOnePermittedSandboxedNavigator(nsIDocShell ** aSandboxedNavigator)5728 nsDocShell::GetOnePermittedSandboxedNavigator(
5729     nsIDocShell** aSandboxedNavigator) {
5730   NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
5731   nsCOMPtr<nsIDocShell> permittedNavigator =
5732       do_QueryReferent(mOnePermittedSandboxedNavigator);
5733   permittedNavigator.forget(aSandboxedNavigator);
5734   return NS_OK;
5735 }
5736 
5737 NS_IMETHODIMP
SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)5738 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
5739   mDefaultLoadFlags = aDefaultLoadFlags;
5740 
5741   // Tell the load group to set these flags all requests in the group
5742   if (mLoadGroup) {
5743     mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
5744   } else {
5745     NS_WARNING(
5746         "nsDocShell::SetDefaultLoadFlags has no loadGroup to propagate the "
5747         "flags to");
5748   }
5749 
5750   // Recursively tell all of our children.  We *do not* skip
5751   // <iframe mozbrowser> children - if someone sticks custom flags in this
5752   // docShell then they too get the same flags.
5753   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5754   while (iter.HasMore()) {
5755     nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5756     if (!docshell) {
5757       continue;
5758     }
5759     docshell->SetDefaultLoadFlags(aDefaultLoadFlags);
5760   }
5761   return NS_OK;
5762 }
5763 
5764 NS_IMETHODIMP
GetDefaultLoadFlags(uint32_t * aDefaultLoadFlags)5765 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
5766   *aDefaultLoadFlags = mDefaultLoadFlags;
5767   return NS_OK;
5768 }
5769 
5770 NS_IMETHODIMP
SetMixedContentChannel(nsIChannel * aMixedContentChannel)5771 nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel) {
5772 #ifdef DEBUG
5773   // if the channel is non-null
5774   if (aMixedContentChannel) {
5775     // Get the root docshell.
5776     nsCOMPtr<nsIDocShellTreeItem> root;
5777     GetSameTypeRootTreeItem(getter_AddRefs(root));
5778     NS_WARNING_ASSERTION(root.get() == static_cast<nsIDocShellTreeItem*>(this),
5779                          "Setting mMixedContentChannel on a docshell that is "
5780                          "not the root docshell");
5781   }
5782 #endif
5783   mMixedContentChannel = aMixedContentChannel;
5784   return NS_OK;
5785 }
5786 
5787 NS_IMETHODIMP
GetFailedChannel(nsIChannel ** aFailedChannel)5788 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
5789   NS_ENSURE_ARG_POINTER(aFailedChannel);
5790   nsIDocument* doc = GetDocument();
5791   if (!doc) {
5792     *aFailedChannel = nullptr;
5793     return NS_OK;
5794   }
5795   NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
5796   return NS_OK;
5797 }
5798 
5799 NS_IMETHODIMP
GetMixedContentChannel(nsIChannel ** aMixedContentChannel)5800 nsDocShell::GetMixedContentChannel(nsIChannel** aMixedContentChannel) {
5801   NS_ENSURE_ARG_POINTER(aMixedContentChannel);
5802   NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
5803   return NS_OK;
5804 }
5805 
5806 NS_IMETHODIMP
GetAllowMixedContentAndConnectionData(bool * aRootHasSecureConnection,bool * aAllowMixedContent,bool * aIsRootDocShell)5807 nsDocShell::GetAllowMixedContentAndConnectionData(
5808     bool* aRootHasSecureConnection, bool* aAllowMixedContent,
5809     bool* aIsRootDocShell) {
5810   *aRootHasSecureConnection = true;
5811   *aAllowMixedContent = false;
5812   *aIsRootDocShell = false;
5813 
5814   nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
5815   GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
5816   NS_ASSERTION(
5817       sameTypeRoot,
5818       "No document shell root tree item from document shell tree item!");
5819   *aIsRootDocShell =
5820       sameTypeRoot.get() == static_cast<nsIDocShellTreeItem*>(this);
5821 
5822   // now get the document from sameTypeRoot
5823   nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
5824   if (rootDoc) {
5825     nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
5826 
5827     // For things with system principal (e.g. scratchpad) there is no uri
5828     // aRootHasSecureConnection should be false.
5829     nsCOMPtr<nsIURI> rootUri;
5830     if (nsContentUtils::IsSystemPrincipal(rootPrincipal) ||
5831         NS_FAILED(rootPrincipal->GetURI(getter_AddRefs(rootUri))) || !rootUri ||
5832         NS_FAILED(rootUri->SchemeIs("https", aRootHasSecureConnection))) {
5833       *aRootHasSecureConnection = false;
5834     }
5835 
5836     // Check the root doc's channel against the root docShell's
5837     // mMixedContentChannel to see if they are the same. If they are the same,
5838     // the user has overriden the block.
5839     nsCOMPtr<nsIDocShell> rootDocShell = do_QueryInterface(sameTypeRoot);
5840     nsCOMPtr<nsIChannel> mixedChannel;
5841     rootDocShell->GetMixedContentChannel(getter_AddRefs(mixedChannel));
5842     *aAllowMixedContent =
5843         mixedChannel && (mixedChannel == rootDoc->GetChannel());
5844   }
5845 
5846   return NS_OK;
5847 }
5848 
5849 NS_IMETHODIMP
SetVisibility(bool aVisibility)5850 nsDocShell::SetVisibility(bool aVisibility) {
5851   // Show()/Hide() may change mContentViewer.
5852   nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5853   if (!cv) {
5854     return NS_OK;
5855   }
5856   if (aVisibility) {
5857     cv->Show();
5858   } else {
5859     cv->Hide();
5860   }
5861 
5862   return NS_OK;
5863 }
5864 
5865 NS_IMETHODIMP
GetEnabled(bool * aEnabled)5866 nsDocShell::GetEnabled(bool* aEnabled) {
5867   NS_ENSURE_ARG_POINTER(aEnabled);
5868   *aEnabled = true;
5869   return NS_ERROR_NOT_IMPLEMENTED;
5870 }
5871 
5872 NS_IMETHODIMP
SetEnabled(bool aEnabled)5873 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
5874 
5875 NS_IMETHODIMP
SetFocus()5876 nsDocShell::SetFocus() { return NS_OK; }
5877 
5878 NS_IMETHODIMP
GetMainWidget(nsIWidget ** aMainWidget)5879 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
5880   // We don't create our own widget, so simply return the parent one.
5881   return GetParentWidget(aMainWidget);
5882 }
5883 
5884 NS_IMETHODIMP
GetTitle(nsAString & aTitle)5885 nsDocShell::GetTitle(nsAString& aTitle) {
5886   aTitle = mTitle;
5887   return NS_OK;
5888 }
5889 
5890 NS_IMETHODIMP
SetTitle(const nsAString & aTitle)5891 nsDocShell::SetTitle(const nsAString& aTitle) {
5892   // Store local title
5893   mTitle = aTitle;
5894 
5895   nsCOMPtr<nsIDocShellTreeItem> parent;
5896   GetSameTypeParent(getter_AddRefs(parent));
5897 
5898   // When title is set on the top object it should then be passed to the
5899   // tree owner.
5900   if (!parent) {
5901     nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5902     if (treeOwnerAsWin) {
5903       treeOwnerAsWin->SetTitle(aTitle);
5904     }
5905   }
5906 
5907   AssertOriginAttributesMatchPrivateBrowsing();
5908   if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
5909     UpdateGlobalHistoryTitle(mCurrentURI);
5910   }
5911 
5912   // Update SessionHistory with the document's title.
5913   if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
5914       mLoadType != LOAD_ERROR_PAGE) {
5915     mOSHE->SetTitle(mTitle);
5916   }
5917 
5918   return NS_OK;
5919 }
5920 
GetCurScrollPos(int32_t aScrollOrientation,int32_t * aCurPos)5921 nsresult nsDocShell::GetCurScrollPos(int32_t aScrollOrientation,
5922                                      int32_t* aCurPos) {
5923   NS_ENSURE_ARG_POINTER(aCurPos);
5924 
5925   nsIScrollableFrame* sf = GetRootScrollFrame();
5926   if (!sf) {
5927     return NS_ERROR_FAILURE;
5928   }
5929 
5930   nsPoint pt = sf->GetScrollPosition();
5931 
5932   switch (aScrollOrientation) {
5933     case ScrollOrientation_X:
5934       *aCurPos = pt.x;
5935       return NS_OK;
5936 
5937     case ScrollOrientation_Y:
5938       *aCurPos = pt.y;
5939       return NS_OK;
5940 
5941     default:
5942       NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5943   }
5944 }
5945 
SetCurScrollPosEx(int32_t aCurHorizontalPos,int32_t aCurVerticalPos)5946 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
5947                                        int32_t aCurVerticalPos) {
5948   nsIScrollableFrame* sf = GetRootScrollFrame();
5949   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5950 
5951   sf->ScrollTo(nsPoint(aCurHorizontalPos, aCurVerticalPos),
5952                nsIScrollableFrame::INSTANT);
5953   return NS_OK;
5954 }
5955 
5956 //*****************************************************************************
5957 // nsDocShell::nsIScrollable
5958 //*****************************************************************************
5959 
5960 NS_IMETHODIMP
GetDefaultScrollbarPreferences(int32_t aScrollOrientation,int32_t * aScrollbarPref)5961 nsDocShell::GetDefaultScrollbarPreferences(int32_t aScrollOrientation,
5962                                            int32_t* aScrollbarPref) {
5963   NS_ENSURE_ARG_POINTER(aScrollbarPref);
5964   switch (aScrollOrientation) {
5965     case ScrollOrientation_X:
5966       *aScrollbarPref = mDefaultScrollbarPref.x;
5967       return NS_OK;
5968 
5969     case ScrollOrientation_Y:
5970       *aScrollbarPref = mDefaultScrollbarPref.y;
5971       return NS_OK;
5972 
5973     default:
5974       NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5975   }
5976   return NS_ERROR_FAILURE;
5977 }
5978 
5979 NS_IMETHODIMP
SetDefaultScrollbarPreferences(int32_t aScrollOrientation,int32_t aScrollbarPref)5980 nsDocShell::SetDefaultScrollbarPreferences(int32_t aScrollOrientation,
5981                                            int32_t aScrollbarPref) {
5982   switch (aScrollOrientation) {
5983     case ScrollOrientation_X:
5984       mDefaultScrollbarPref.x = aScrollbarPref;
5985       return NS_OK;
5986 
5987     case ScrollOrientation_Y:
5988       mDefaultScrollbarPref.y = aScrollbarPref;
5989       return NS_OK;
5990 
5991     default:
5992       NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5993   }
5994   return NS_ERROR_FAILURE;
5995 }
5996 
5997 NS_IMETHODIMP
GetScrollbarVisibility(bool * aVerticalVisible,bool * aHorizontalVisible)5998 nsDocShell::GetScrollbarVisibility(bool* aVerticalVisible,
5999                                    bool* aHorizontalVisible) {
6000   nsIScrollableFrame* sf = GetRootScrollFrame();
6001   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6002 
6003   uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
6004   if (aVerticalVisible) {
6005     *aVerticalVisible =
6006         (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
6007   }
6008   if (aHorizontalVisible) {
6009     *aHorizontalVisible =
6010         (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
6011   }
6012 
6013   return NS_OK;
6014 }
6015 
6016 //*****************************************************************************
6017 // nsDocShell::nsITextScroll
6018 //*****************************************************************************
6019 
6020 NS_IMETHODIMP
ScrollByLines(int32_t aNumLines)6021 nsDocShell::ScrollByLines(int32_t aNumLines) {
6022   nsIScrollableFrame* sf = GetRootScrollFrame();
6023   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6024 
6025   sf->ScrollBy(nsIntPoint(0, aNumLines), nsIScrollableFrame::LINES,
6026                nsIScrollableFrame::SMOOTH);
6027   return NS_OK;
6028 }
6029 
6030 NS_IMETHODIMP
ScrollByPages(int32_t aNumPages)6031 nsDocShell::ScrollByPages(int32_t aNumPages) {
6032   nsIScrollableFrame* sf = GetRootScrollFrame();
6033   NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6034 
6035   sf->ScrollBy(nsIntPoint(0, aNumPages), nsIScrollableFrame::PAGES,
6036                nsIScrollableFrame::SMOOTH);
6037   return NS_OK;
6038 }
6039 
6040 //*****************************************************************************
6041 // nsDocShell::nsIRefreshURI
6042 //*****************************************************************************
6043 
6044 NS_IMETHODIMP
RefreshURI(nsIURI * aURI,nsIPrincipal * aPrincipal,int32_t aDelay,bool aRepeat,bool aMetaRefresh)6045 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDelay,
6046                        bool aRepeat, bool aMetaRefresh) {
6047   MOZ_ASSERT(!mIsBeingDestroyed);
6048 
6049   NS_ENSURE_ARG(aURI);
6050 
6051   /* Check if Meta refresh/redirects are permitted. Some
6052    * embedded applications may not want to do this.
6053    * Must do this before sending out NOTIFY_REFRESH events
6054    * because listeners may have side effects (e.g. displaying a
6055    * button to manually trigger the refresh later).
6056    */
6057   bool allowRedirects = true;
6058   GetAllowMetaRedirects(&allowRedirects);
6059   if (!allowRedirects) {
6060     return NS_OK;
6061   }
6062 
6063   // If any web progress listeners are listening for NOTIFY_REFRESH events,
6064   // give them a chance to block this refresh.
6065   bool sameURI;
6066   nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
6067   if (NS_FAILED(rv)) {
6068     sameURI = false;
6069   }
6070   if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
6071     return NS_OK;
6072   }
6073 
6074   nsCOMPtr<nsITimerCallback> refreshTimer =
6075       new nsRefreshTimer(this, aURI, aPrincipal, aDelay, aRepeat, aMetaRefresh);
6076 
6077   uint32_t busyFlags = 0;
6078   GetBusyFlags(&busyFlags);
6079 
6080   if (!mRefreshURIList) {
6081     mRefreshURIList = nsArray::Create();
6082   }
6083 
6084   if (busyFlags & BUSY_FLAGS_BUSY ||
6085       (!mIsActive && mDisableMetaRefreshWhenInactive)) {
6086     // We don't  want to create the timer right now. Instead queue up the
6087     // request and trigger the timer in EndPageLoad() or whenever we become
6088     // active.
6089     mRefreshURIList->AppendElement(refreshTimer);
6090   } else {
6091     // There is no page loading going on right now.  Create the
6092     // timer and fire it right away.
6093     nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
6094     NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
6095 
6096     nsCOMPtr<nsITimer> timer;
6097     MOZ_TRY_VAR(timer,
6098                 NS_NewTimerWithCallback(
6099                     refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT,
6100                     win->TabGroup()->EventTargetFor(TaskCategory::Network)));
6101 
6102     mRefreshURIList->AppendElement(timer);  // owning timer ref
6103   }
6104   return NS_OK;
6105 }
6106 
ForceRefreshURIFromTimer(nsIURI * aURI,nsIPrincipal * aPrincipal,int32_t aDelay,bool aMetaRefresh,nsITimer * aTimer)6107 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
6108                                               nsIPrincipal* aPrincipal,
6109                                               int32_t aDelay, bool aMetaRefresh,
6110                                               nsITimer* aTimer) {
6111   NS_PRECONDITION(aTimer, "Must have a timer here");
6112 
6113   // Remove aTimer from mRefreshURIList if needed
6114   if (mRefreshURIList) {
6115     uint32_t n = 0;
6116     mRefreshURIList->GetLength(&n);
6117 
6118     for (uint32_t i = 0; i < n; ++i) {
6119       nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6120       if (timer == aTimer) {
6121         mRefreshURIList->RemoveElementAt(i);
6122         break;
6123       }
6124     }
6125   }
6126 
6127   return ForceRefreshURI(aURI, aPrincipal, aDelay, aMetaRefresh);
6128 }
6129 
6130 NS_IMETHODIMP
ForceRefreshURI(nsIURI * aURI,nsIPrincipal * aPrincipal,int32_t aDelay,bool aMetaRefresh)6131 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
6132                             int32_t aDelay, bool aMetaRefresh) {
6133   NS_ENSURE_ARG(aURI);
6134 
6135   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
6136   CreateLoadInfo(getter_AddRefs(loadInfo));
6137   NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
6138 
6139   /* We do need to pass in a referrer, but we don't want it to
6140    * be sent to the server.
6141    */
6142   loadInfo->SetSendReferrer(false);
6143 
6144   /* for most refreshes the current URI is an appropriate
6145    * internal referrer
6146    */
6147   loadInfo->SetReferrer(mCurrentURI);
6148 
6149   loadInfo->SetOriginalURI(mCurrentURI);
6150   loadInfo->SetResultPrincipalURI(aURI);
6151   loadInfo->SetResultPrincipalURIIsSome(true);
6152   loadInfo->SetKeepResultPrincipalURIIfSet(true);
6153 
6154   // Set the triggering pricipal to aPrincipal if available, or current
6155   // document's principal otherwise.
6156   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
6157   if (!principal) {
6158     nsCOMPtr<nsIDocument> doc = GetDocument();
6159     if (!doc) {
6160       return NS_ERROR_FAILURE;
6161     }
6162     principal = doc->NodePrincipal();
6163   }
6164   loadInfo->SetTriggeringPrincipal(principal);
6165   loadInfo->SetPrincipalIsExplicit(true);
6166 
6167   /* Check if this META refresh causes a redirection
6168    * to another site.
6169    */
6170   bool equalUri = false;
6171   nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
6172   if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
6173       aDelay <= REFRESH_REDIRECT_TIMER) {
6174     /* It is a META refresh based redirection within the threshold time
6175      * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
6176      * Pass a REPLACE flag to LoadURI().
6177      */
6178     loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
6179 
6180     /* for redirects we mimic HTTP, which passes the
6181      *  original referrer
6182      */
6183     nsCOMPtr<nsIURI> internalReferrer;
6184     GetReferringURI(getter_AddRefs(internalReferrer));
6185     if (internalReferrer) {
6186       loadInfo->SetReferrer(internalReferrer);
6187     }
6188   } else {
6189     loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
6190   }
6191 
6192   /*
6193    * LoadURI(...) will cancel all refresh timers... This causes the
6194    * Timer and its refreshData instance to be released...
6195    */
6196   LoadURI(aURI, loadInfo,
6197           nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, true);
6198 
6199   return NS_OK;
6200 }
6201 
SetupRefreshURIFromHeader(nsIURI * aBaseURI,nsIPrincipal * aPrincipal,const nsACString & aHeader)6202 nsresult nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
6203                                                nsIPrincipal* aPrincipal,
6204                                                const nsACString& aHeader) {
6205   // Refresh headers are parsed with the following format in mind
6206   // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
6207   // By the time we are here, the following is true:
6208   // header = "REFRESH"
6209   // content = "5; URL=http://uri" // note the URL attribute is
6210   // optional, if it is absent, the currently loaded url is used.
6211   // Also note that the seconds and URL separator can be either
6212   // a ';' or a ','. The ',' separator should be illegal but CNN
6213   // is using it.
6214   //
6215   // We need to handle the following strings, where
6216   //  - X is a set of digits
6217   //  - URI is either a relative or absolute URI
6218   //
6219   // Note that URI should start with "url=" but we allow omission
6220   //
6221   // "" || ";" || ","
6222   //  empty string. use the currently loaded URI
6223   //  and refresh immediately.
6224   // "X" || "X;" || "X,"
6225   //  Refresh the currently loaded URI in X seconds.
6226   // "X; URI" || "X, URI"
6227   //  Refresh using URI as the destination in X seconds.
6228   // "URI" || "; URI" || ", URI"
6229   //  Refresh immediately using URI as the destination.
6230   //
6231   // Currently, anything immediately following the URI, if
6232   // separated by any char in the set "'\"\t\r\n " will be
6233   // ignored. So "10; url=go.html ; foo=bar" will work,
6234   // and so will "10; url='go.html'; foo=bar". However,
6235   // "10; url=go.html; foo=bar" will result in the uri
6236   // "go.html;" since ';' and ',' are valid uri characters.
6237   //
6238   // Note that we need to remove any tokens wrapping the URI.
6239   // These tokens currently include spaces, double and single
6240   // quotes.
6241 
6242   // when done, seconds is 0 or the given number of seconds
6243   //            uriAttrib is empty or the URI specified
6244   MOZ_ASSERT(aPrincipal);
6245 
6246   nsAutoCString uriAttrib;
6247   int32_t seconds = 0;
6248   bool specifiesSeconds = false;
6249 
6250   nsACString::const_iterator iter, tokenStart, doneIterating;
6251 
6252   aHeader.BeginReading(iter);
6253   aHeader.EndReading(doneIterating);
6254 
6255   // skip leading whitespace
6256   while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6257     ++iter;
6258   }
6259 
6260   tokenStart = iter;
6261 
6262   // skip leading + and -
6263   if (iter != doneIterating && (*iter == '-' || *iter == '+')) {
6264     ++iter;
6265   }
6266 
6267   // parse number
6268   while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
6269     seconds = seconds * 10 + (*iter - '0');
6270     specifiesSeconds = true;
6271     ++iter;
6272   }
6273 
6274   if (iter != doneIterating) {
6275     // if we started with a '-', number is negative
6276     if (*tokenStart == '-') {
6277       seconds = -seconds;
6278     }
6279 
6280     // skip to next ';' or ','
6281     nsACString::const_iterator iterAfterDigit = iter;
6282     while (iter != doneIterating && !(*iter == ';' || *iter == ',')) {
6283       if (specifiesSeconds) {
6284         // Non-whitespace characters here mean that the string is
6285         // malformed but tolerate sites that specify a decimal point,
6286         // even though meta refresh only works on whole seconds.
6287         if (iter == iterAfterDigit && !nsCRT::IsAsciiSpace(*iter) &&
6288             *iter != '.') {
6289           // The characters between the seconds and the next
6290           // section are just garbage!
6291           //   e.g. content="2a0z+,URL=http://www.mozilla.org/"
6292           // Just ignore this redirect.
6293           return NS_ERROR_FAILURE;
6294         } else if (nsCRT::IsAsciiSpace(*iter)) {
6295           // We've had at least one whitespace so tolerate the mistake
6296           // and drop through.
6297           // e.g. content="10 foo"
6298           ++iter;
6299           break;
6300         }
6301       }
6302       ++iter;
6303     }
6304 
6305     // skip any remaining whitespace
6306     while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6307       ++iter;
6308     }
6309 
6310     // skip ';' or ','
6311     if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
6312       ++iter;
6313     }
6314 
6315     // skip whitespace
6316     while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6317       ++iter;
6318     }
6319   }
6320 
6321   // possible start of URI
6322   tokenStart = iter;
6323 
6324   // skip "url = " to real start of URI
6325   if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
6326     ++iter;
6327     if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
6328       ++iter;
6329       if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
6330         ++iter;
6331 
6332         // skip whitespace
6333         while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6334           ++iter;
6335         }
6336 
6337         if (iter != doneIterating && *iter == '=') {
6338           ++iter;
6339 
6340           // skip whitespace
6341           while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6342             ++iter;
6343           }
6344 
6345           // found real start of URI
6346           tokenStart = iter;
6347         }
6348       }
6349     }
6350   }
6351 
6352   // skip a leading '"' or '\''.
6353 
6354   bool isQuotedURI = false;
6355   if (tokenStart != doneIterating &&
6356       (*tokenStart == '"' || *tokenStart == '\'')) {
6357     isQuotedURI = true;
6358     ++tokenStart;
6359   }
6360 
6361   // set iter to start of URI
6362   iter = tokenStart;
6363 
6364   // tokenStart here points to the beginning of URI
6365 
6366   // grab the rest of the URI
6367   while (iter != doneIterating) {
6368     if (isQuotedURI && (*iter == '"' || *iter == '\'')) {
6369       break;
6370     }
6371     ++iter;
6372   }
6373 
6374   // move iter one back if the last character is a '"' or '\''
6375   if (iter != tokenStart && isQuotedURI) {
6376     --iter;
6377     if (!(*iter == '"' || *iter == '\'')) {
6378       ++iter;
6379     }
6380   }
6381 
6382   // URI is whatever's contained from tokenStart to iter.
6383   // note: if tokenStart == doneIterating, so is iter.
6384 
6385   nsresult rv = NS_OK;
6386 
6387   nsCOMPtr<nsIURI> uri;
6388   bool specifiesURI = false;
6389   if (tokenStart == iter) {
6390     uri = aBaseURI;
6391   } else {
6392     uriAttrib = Substring(tokenStart, iter);
6393     // NS_NewURI takes care of any whitespace surrounding the URL
6394     rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
6395     specifiesURI = true;
6396   }
6397 
6398   // No URI or seconds were specified
6399   if (!specifiesSeconds && !specifiesURI) {
6400     // Do nothing because the alternative is to spin around in a refresh
6401     // loop forever!
6402     return NS_ERROR_FAILURE;
6403   }
6404 
6405   if (NS_SUCCEEDED(rv)) {
6406     nsCOMPtr<nsIScriptSecurityManager> securityManager(
6407         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
6408     if (NS_SUCCEEDED(rv)) {
6409       rv = securityManager->CheckLoadURIWithPrincipal(
6410           aPrincipal, uri,
6411           nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
6412 
6413       if (NS_SUCCEEDED(rv)) {
6414         bool isjs = true;
6415         rv = NS_URIChainHasFlags(
6416             uri, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
6417         NS_ENSURE_SUCCESS(rv, rv);
6418 
6419         if (isjs) {
6420           return NS_ERROR_FAILURE;
6421         }
6422       }
6423 
6424       if (NS_SUCCEEDED(rv)) {
6425         // Since we can't travel back in time yet, just pretend
6426         // negative numbers do nothing at all.
6427         if (seconds < 0) {
6428           return NS_ERROR_FAILURE;
6429         }
6430 
6431         rv = RefreshURI(uri, aPrincipal, seconds * 1000, false, true);
6432       }
6433     }
6434   }
6435   return rv;
6436 }
6437 
6438 NS_IMETHODIMP
SetupRefreshURI(nsIChannel * aChannel)6439 nsDocShell::SetupRefreshURI(nsIChannel* aChannel) {
6440   nsresult rv;
6441   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
6442   if (NS_SUCCEEDED(rv)) {
6443     nsAutoCString refreshHeader;
6444     rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
6445                                         refreshHeader);
6446 
6447     if (!refreshHeader.IsEmpty()) {
6448       nsCOMPtr<nsIScriptSecurityManager> secMan =
6449           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6450       NS_ENSURE_SUCCESS(rv, rv);
6451 
6452       nsCOMPtr<nsIPrincipal> principal;
6453       rv = secMan->GetChannelResultPrincipal(aChannel,
6454                                              getter_AddRefs(principal));
6455       NS_ENSURE_SUCCESS(rv, rv);
6456 
6457       SetupReferrerFromChannel(aChannel);
6458       rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
6459       if (NS_SUCCEEDED(rv)) {
6460         return NS_REFRESHURI_HEADER_FOUND;
6461       }
6462     }
6463   }
6464   return rv;
6465 }
6466 
DoCancelRefreshURITimers(nsIMutableArray * aTimerList)6467 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
6468   if (!aTimerList) {
6469     return;
6470   }
6471 
6472   uint32_t n = 0;
6473   aTimerList->GetLength(&n);
6474 
6475   while (n) {
6476     nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
6477 
6478     aTimerList->RemoveElementAt(n);  // bye bye owning timer ref
6479 
6480     if (timer) {
6481       timer->Cancel();
6482     }
6483   }
6484 }
6485 
6486 NS_IMETHODIMP
CancelRefreshURITimers()6487 nsDocShell::CancelRefreshURITimers() {
6488   DoCancelRefreshURITimers(mRefreshURIList);
6489   DoCancelRefreshURITimers(mSavedRefreshURIList);
6490   mRefreshURIList = nullptr;
6491   mSavedRefreshURIList = nullptr;
6492 
6493   return NS_OK;
6494 }
6495 
6496 NS_IMETHODIMP
GetRefreshPending(bool * aResult)6497 nsDocShell::GetRefreshPending(bool* aResult) {
6498   if (!mRefreshURIList) {
6499     *aResult = false;
6500     return NS_OK;
6501   }
6502 
6503   uint32_t count;
6504   nsresult rv = mRefreshURIList->GetLength(&count);
6505   if (NS_SUCCEEDED(rv)) {
6506     *aResult = (count != 0);
6507   }
6508   return rv;
6509 }
6510 
6511 NS_IMETHODIMP
SuspendRefreshURIs()6512 nsDocShell::SuspendRefreshURIs() {
6513   if (mRefreshURIList) {
6514     uint32_t n = 0;
6515     mRefreshURIList->GetLength(&n);
6516 
6517     for (uint32_t i = 0; i < n; ++i) {
6518       nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6519       if (!timer) {
6520         continue;  // this must be a nsRefreshURI already
6521       }
6522 
6523       // Replace this timer object with a nsRefreshTimer object.
6524       nsCOMPtr<nsITimerCallback> callback;
6525       timer->GetCallback(getter_AddRefs(callback));
6526 
6527       timer->Cancel();
6528 
6529       nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
6530       NS_ASSERTION(
6531           rt,
6532           "RefreshURIList timer callbacks should only be RefreshTimer objects");
6533 
6534       mRefreshURIList->ReplaceElementAt(rt, i);
6535     }
6536   }
6537 
6538   // Suspend refresh URIs for our child shells as well.
6539   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6540   while (iter.HasMore()) {
6541     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6542     if (shell) {
6543       shell->SuspendRefreshURIs();
6544     }
6545   }
6546 
6547   return NS_OK;
6548 }
6549 
6550 NS_IMETHODIMP
ResumeRefreshURIs()6551 nsDocShell::ResumeRefreshURIs() {
6552   RefreshURIFromQueue();
6553 
6554   // Resume refresh URIs for our child shells as well.
6555   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6556   while (iter.HasMore()) {
6557     nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6558     if (shell) {
6559       shell->ResumeRefreshURIs();
6560     }
6561   }
6562 
6563   return NS_OK;
6564 }
6565 
RefreshURIFromQueue()6566 nsresult nsDocShell::RefreshURIFromQueue() {
6567   if (!mRefreshURIList) {
6568     return NS_OK;
6569   }
6570   uint32_t n = 0;
6571   mRefreshURIList->GetLength(&n);
6572 
6573   while (n) {
6574     nsCOMPtr<nsITimerCallback> refreshInfo =
6575         do_QueryElementAt(mRefreshURIList, --n);
6576 
6577     if (refreshInfo) {
6578       // This is the nsRefreshTimer object, waiting to be
6579       // setup in a timer object and fired.
6580       // Create the timer and  trigger it.
6581       uint32_t delay = static_cast<nsRefreshTimer*>(
6582                            static_cast<nsITimerCallback*>(refreshInfo))
6583                            ->GetDelay();
6584       nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
6585       if (win) {
6586         nsCOMPtr<nsITimer> timer;
6587         NS_NewTimerWithCallback(
6588             getter_AddRefs(timer), refreshInfo, delay, nsITimer::TYPE_ONE_SHOT,
6589             win->TabGroup()->EventTargetFor(TaskCategory::Network));
6590 
6591         if (timer) {
6592           // Replace the nsRefreshTimer element in the queue with
6593           // its corresponding timer object, so that in case another
6594           // load comes through before the timer can go off, the timer will
6595           // get cancelled in CancelRefreshURITimer()
6596           mRefreshURIList->ReplaceElementAt(timer, n);
6597         }
6598       }
6599     }
6600   }
6601 
6602   return NS_OK;
6603 }
6604 
Embed(nsIContentViewer * aContentViewer,const char * aCommand,nsISupports * aExtraInfo)6605 nsresult nsDocShell::Embed(nsIContentViewer* aContentViewer,
6606                            const char* aCommand, nsISupports* aExtraInfo) {
6607   // Save the LayoutHistoryState of the previous document, before
6608   // setting up new document
6609   PersistLayoutHistoryState();
6610 
6611   nsresult rv = SetupNewViewer(aContentViewer);
6612   NS_ENSURE_SUCCESS(rv, rv);
6613 
6614   // If we are loading a wyciwyg url from history, change the base URI for
6615   // the document to the original http url that created the document.write().
6616   // This makes sure that all relative urls in a document.written page loaded
6617   // via history work properly.
6618   if (mCurrentURI &&
6619       (mLoadType & LOAD_CMD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL ||
6620        mLoadType == LOAD_RELOAD_CHARSET_CHANGE ||
6621        mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
6622        mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)) {
6623     bool isWyciwyg = false;
6624     // Check if the url is wyciwyg
6625     rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
6626     if (isWyciwyg && NS_SUCCEEDED(rv)) {
6627       SetBaseUrlForWyciwyg(aContentViewer);
6628     }
6629   }
6630   // XXX What if SetupNewViewer fails?
6631   if (mLSHE) {
6632     // Restore the editing state, if it's stored in session history.
6633     if (mLSHE->HasDetachedEditor()) {
6634       ReattachEditorToWindow(mLSHE);
6635     }
6636     // Set history.state
6637     SetDocCurrentStateObj(mLSHE);
6638 
6639     SetHistoryEntry(&mOSHE, mLSHE);
6640   }
6641 
6642   bool updateHistory = true;
6643 
6644   // Determine if this type of load should update history
6645   switch (mLoadType) {
6646     case LOAD_NORMAL_REPLACE:
6647     case LOAD_STOP_CONTENT_AND_REPLACE:
6648     case LOAD_RELOAD_BYPASS_CACHE:
6649     case LOAD_RELOAD_BYPASS_PROXY:
6650     case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
6651     case LOAD_REPLACE_BYPASS_CACHE:
6652       updateHistory = false;
6653       break;
6654     default:
6655       break;
6656   }
6657 
6658   if (!updateHistory) {
6659     SetLayoutHistoryState(nullptr);
6660   }
6661 
6662   return NS_OK;
6663 }
6664 
6665 //*****************************************************************************
6666 // nsDocShell::nsIWebProgressListener
6667 //*****************************************************************************
6668 
6669 NS_IMETHODIMP
OnProgressChange(nsIWebProgress * aProgress,nsIRequest * aRequest,int32_t aCurSelfProgress,int32_t aMaxSelfProgress,int32_t aCurTotalProgress,int32_t aMaxTotalProgress)6670 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
6671                              int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
6672                              int32_t aCurTotalProgress,
6673                              int32_t aMaxTotalProgress) {
6674   return NS_OK;
6675 }
6676 
6677 NS_IMETHODIMP
OnStateChange(nsIWebProgress * aProgress,nsIRequest * aRequest,uint32_t aStateFlags,nsresult aStatus)6678 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
6679                           uint32_t aStateFlags, nsresult aStatus) {
6680   if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
6681     // Save timing statistics.
6682     nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6683     nsCOMPtr<nsIURI> uri;
6684     channel->GetURI(getter_AddRefs(uri));
6685     nsAutoCString aURI;
6686     uri->GetAsciiSpec(aURI);
6687 
6688     nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
6689     nsCOMPtr<nsIWebProgress> webProgress =
6690         do_QueryInterface(GetAsSupports(this));
6691 
6692     // We don't update navigation timing for wyciwyg channels
6693     if (this == aProgress && !wcwgChannel) {
6694       mozilla::Unused << MaybeInitTiming();
6695       mTiming->NotifyFetchStart(uri,
6696                                 ConvertLoadTypeToNavigationType(mLoadType));
6697     }
6698 
6699     // Was the wyciwyg document loaded on this docshell?
6700     if (wcwgChannel && !mLSHE && (mItemType == typeContent) &&
6701         aProgress == webProgress.get()) {
6702       bool equalUri = true;
6703       // Store the wyciwyg url in session history, only if it is
6704       // being loaded fresh for the first time. We don't want
6705       // multiple entries for successive loads
6706       if (mCurrentURI && NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
6707           !equalUri) {
6708         nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
6709         GetSameTypeParent(getter_AddRefs(parentAsItem));
6710         nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
6711         bool inOnLoadHandler = false;
6712         if (parentDS) {
6713           parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
6714         }
6715         if (inOnLoadHandler) {
6716           // We're handling parent's load event listener, which causes
6717           // document.write in a subdocument.
6718           // Need to clear the session history for all child
6719           // docshells so that we can handle them like they would
6720           // all be added dynamically.
6721           nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
6722           if (parent) {
6723             bool oshe = false;
6724             nsCOMPtr<nsISHEntry> entry;
6725             parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
6726             static_cast<nsDocShell*>(parent.get())->ClearFrameHistory(entry);
6727           }
6728         }
6729 
6730         // This is a document.write(). Get the made-up url
6731         // from the channel and store it in session history.
6732         // Pass false for aCloneChildren, since we're creating
6733         // a new DOM here.
6734         AddToSessionHistory(uri, wcwgChannel, nullptr, nullptr, false,
6735                             getter_AddRefs(mLSHE));
6736         SetCurrentURI(uri, aRequest, true, 0);
6737         // Save history state of the previous page
6738         PersistLayoutHistoryState();
6739         // We'll never get an Embed() for this load, so just go ahead
6740         // and SetHistoryEntry now.
6741         SetHistoryEntry(&mOSHE, mLSHE);
6742       }
6743     }
6744     // Page has begun to load
6745     mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
6746 
6747     if ((aStateFlags & STATE_RESTORING) == 0) {
6748       // Show the progress cursor if the pref is set
6749       if (nsContentUtils::UseActivityCursor()) {
6750         nsCOMPtr<nsIWidget> mainWidget;
6751         GetMainWidget(getter_AddRefs(mainWidget));
6752         if (mainWidget) {
6753           mainWidget->SetCursor(eCursor_spinning);
6754         }
6755       }
6756     }
6757   } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
6758     // Page is loading
6759     mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
6760   } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
6761     // Page has finished loading
6762     mBusyFlags = BUSY_FLAGS_NONE;
6763 
6764     // Hide the progress cursor if the pref is set
6765     if (nsContentUtils::UseActivityCursor()) {
6766       nsCOMPtr<nsIWidget> mainWidget;
6767       GetMainWidget(getter_AddRefs(mainWidget));
6768       if (mainWidget) {
6769         mainWidget->SetCursor(eCursor_standard);
6770       }
6771     }
6772   }
6773   if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
6774     nsCOMPtr<nsIWebProgress> webProgress =
6775         do_QueryInterface(GetAsSupports(this));
6776     // Is the document stop notification for this document?
6777     if (aProgress == webProgress.get()) {
6778       nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6779       EndPageLoad(aProgress, channel, aStatus);
6780     }
6781   }
6782   // note that redirect state changes will go through here as well, but it
6783   // is better to handle those in OnRedirectStateChange where more
6784   // information is available.
6785   return NS_OK;
6786 }
6787 
6788 NS_IMETHODIMP
OnLocationChange(nsIWebProgress * aProgress,nsIRequest * aRequest,nsIURI * aURI,uint32_t aFlags)6789 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
6790                              nsIURI* aURI, uint32_t aFlags) {
6791   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6792   return NS_OK;
6793 }
6794 
OnRedirectStateChange(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aRedirectFlags,uint32_t aStateFlags)6795 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
6796                                        nsIChannel* aNewChannel,
6797                                        uint32_t aRedirectFlags,
6798                                        uint32_t aStateFlags) {
6799   NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
6800                "Calling OnRedirectStateChange when there is no redirect");
6801 
6802   // If mixed content is allowed for the old channel, we forward
6803   // the permission to the new channel if it has the same origin
6804   // as the old one.
6805   if (mMixedContentChannel && mMixedContentChannel == aOldChannel) {
6806     nsresult rv =
6807         nsContentUtils::CheckSameOrigin(mMixedContentChannel, aNewChannel);
6808     if (NS_SUCCEEDED(rv)) {
6809       SetMixedContentChannel(aNewChannel);  // Same origin: forward permission.
6810     } else {
6811       SetMixedContentChannel(
6812           nullptr);  // Different origin: clear mMixedContentChannel.
6813     }
6814   }
6815 
6816   if (!(aStateFlags & STATE_IS_DOCUMENT)) {
6817     return;  // not a toplevel document
6818   }
6819 
6820   nsCOMPtr<nsIURI> oldURI, newURI;
6821   aOldChannel->GetURI(getter_AddRefs(oldURI));
6822   aNewChannel->GetURI(getter_AddRefs(newURI));
6823   if (!oldURI || !newURI) {
6824     return;
6825   }
6826 
6827   // Below a URI visit is saved (see AddURIVisit method doc).
6828   // The visit chain looks something like:
6829   //   ...
6830   //   Site N - 1
6831   //                =>  Site N
6832   //   (redirect to =>) Site N + 1 (we are here!)
6833 
6834   // Get N - 1 and transition type
6835   nsCOMPtr<nsIURI> previousURI;
6836   uint32_t previousFlags = 0;
6837   ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
6838 
6839   if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
6840       ChannelIsPost(aOldChannel)) {
6841     // 1. Internal redirects are ignored because they are specific to the
6842     //    channel implementation.
6843     // 2. POSTs are not saved by global history.
6844     //
6845     // Regardless, we need to propagate the previous visit to the new
6846     // channel.
6847     SaveLastVisit(aNewChannel, previousURI, previousFlags);
6848   } else {
6849     nsCOMPtr<nsIURI> referrer;
6850     // Treat referrer as null if there is an error getting it.
6851     (void)NS_GetReferrerFromChannel(aOldChannel, getter_AddRefs(referrer));
6852 
6853     // Get the HTTP response code, if available.
6854     uint32_t responseStatus = 0;
6855     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
6856     if (httpChannel) {
6857       Unused << httpChannel->GetResponseStatus(&responseStatus);
6858     }
6859 
6860     // Add visit N -1 => N
6861     AddURIVisit(oldURI, referrer, previousURI, previousFlags, responseStatus);
6862 
6863     // Since N + 1 could be the final destination, we will not save N => N + 1
6864     // here.  OnNewURI will do that, so we will cache it.
6865     SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
6866   }
6867 
6868   // check if the new load should go through the application cache.
6869   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
6870       do_QueryInterface(aNewChannel);
6871   if (appCacheChannel) {
6872     if (GeckoProcessType_Default != XRE_GetProcessType()) {
6873       // Permission will be checked in the parent process.
6874       appCacheChannel->SetChooseApplicationCache(true);
6875     } else {
6876       nsCOMPtr<nsIScriptSecurityManager> secMan =
6877           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
6878 
6879       if (secMan) {
6880         nsCOMPtr<nsIPrincipal> principal;
6881         secMan->GetDocShellCodebasePrincipal(newURI, this,
6882                                              getter_AddRefs(principal));
6883         appCacheChannel->SetChooseApplicationCache(
6884             NS_ShouldCheckAppCache(principal));
6885       }
6886     }
6887   }
6888 
6889   if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
6890       mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
6891     mLoadType = LOAD_NORMAL_REPLACE;
6892     SetHistoryEntry(&mLSHE, nullptr);
6893   }
6894 }
6895 
6896 NS_IMETHODIMP
OnStatusChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,nsresult aStatus,const char16_t * aMessage)6897 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
6898                            nsresult aStatus, const char16_t* aMessage) {
6899   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6900   return NS_OK;
6901 }
6902 
6903 NS_IMETHODIMP
OnSecurityChange(nsIWebProgress * aWebProgress,nsIRequest * aRequest,uint32_t aState)6904 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
6905                              uint32_t aState) {
6906   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
6907   return NS_OK;
6908 }
6909 
EndPageLoad(nsIWebProgress * aProgress,nsIChannel * aChannel,nsresult aStatus)6910 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6911                                  nsIChannel* aChannel, nsresult aStatus) {
6912   if (!aChannel) {
6913     return NS_ERROR_NULL_POINTER;
6914   }
6915 
6916   // Make sure to discard the initial client if we never created the initial
6917   // about:blank document.  Do this before possibly returning from the method
6918   // due to an error.
6919   mInitialClientSource.reset();
6920 
6921   nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6922   if (reporter) {
6923     nsCOMPtr<nsILoadGroup> loadGroup;
6924     aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6925     if (loadGroup) {
6926       reporter->FlushConsoleReports(loadGroup);
6927     } else {
6928       reporter->FlushConsoleReports(GetDocument());
6929     }
6930   }
6931 
6932   nsCOMPtr<nsIURI> url;
6933   nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6934   if (NS_FAILED(rv)) {
6935     return rv;
6936   }
6937 
6938   nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6939   if (timingChannel) {
6940     TimeStamp channelCreationTime;
6941     rv = timingChannel->GetChannelCreation(&channelCreationTime);
6942     if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6943       Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
6944                                      channelCreationTime);
6945       nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
6946           do_QueryInterface(mLoadGroup);
6947       if (internalLoadGroup) {
6948         internalLoadGroup->OnEndPageLoad(aChannel);
6949       }
6950     }
6951   }
6952 
6953   // Timing is picked up by the window, we don't need it anymore
6954   mTiming = nullptr;
6955 
6956   // clean up reload state for meta charset
6957   if (eCharsetReloadRequested == mCharsetReloadState) {
6958     mCharsetReloadState = eCharsetReloadStopOrigional;
6959   } else {
6960     mCharsetReloadState = eCharsetReloadInit;
6961   }
6962 
6963   // Save a pointer to the currently-loading history entry.
6964   // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6965   // entry further down in this method.
6966   nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6967   mozilla::Unused << loadingSHE;  // XXX: Not sure if we need this anymore
6968 
6969   //
6970   // one of many safeguards that prevent death and destruction if
6971   // someone is so very very rude as to bring this window down
6972   // during this load handler.
6973   //
6974   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6975 
6976   // Notify the ContentViewer that the Document has finished loading.  This
6977   // will cause any OnLoad(...) and PopState(...) handlers to fire.
6978   if (!mEODForCurrentDocument && mContentViewer) {
6979     mIsExecutingOnLoadHandler = true;
6980     mContentViewer->LoadComplete(aStatus);
6981     mIsExecutingOnLoadHandler = false;
6982 
6983     mEODForCurrentDocument = true;
6984 
6985     // If all documents have completed their loading
6986     // favor native event dispatch priorities
6987     // over performance
6988     if (--gNumberOfDocumentsLoading == 0) {
6989       // Hint to use normal native event dispatch priorities
6990       FavorPerformanceHint(false);
6991     }
6992   }
6993   /* Check if the httpChannel has any cache-control related response headers,
6994    * like no-store, no-cache. If so, update SHEntry so that
6995    * when a user goes back/forward to this page, we appropriately do
6996    * form value restoration or load from server.
6997    */
6998   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6999   if (!httpChannel) {
7000     // HttpChannel could be hiding underneath a Multipart channel.
7001     GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
7002   }
7003 
7004   if (httpChannel) {
7005     // figure out if SH should be saving layout state.
7006     bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
7007     if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
7008         (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
7009       mLSHE->SetSaveLayoutStateFlag(false);
7010     }
7011   }
7012 
7013   // Clear mLSHE after calling the onLoadHandlers. This way, if the
7014   // onLoadHandler tries to load something different in
7015   // itself or one of its children, we can deal with it appropriately.
7016   if (mLSHE) {
7017     mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
7018 
7019     // Clear the mLSHE reference to indicate document loading is done one
7020     // way or another.
7021     SetHistoryEntry(&mLSHE, nullptr);
7022   }
7023   // if there's a refresh header in the channel, this method
7024   // will set it up for us.
7025   if (mIsActive || !mDisableMetaRefreshWhenInactive) RefreshURIFromQueue();
7026 
7027   // Test whether this is the top frame or a subframe
7028   bool isTopFrame = true;
7029   nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
7030   rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
7031   if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
7032     isTopFrame = false;
7033   }
7034 
7035   //
7036   // If the page load failed, then deal with the error condition...
7037   // Errors are handled as follows:
7038   //   1. Check to see if it's a file not found error or bad content
7039   //      encoding error.
7040   //   2. Send the URI to a keyword server (if enabled)
7041   //   3. If the error was DNS failure, then add www and .com to the URI
7042   //      (if appropriate).
7043   //   4. Throw an error dialog box...
7044   //
7045   if (url && NS_FAILED(aStatus)) {
7046     if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
7047         aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
7048         aStatus == NS_ERROR_CORRUPTED_CONTENT ||
7049         aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
7050       DisplayLoadError(aStatus, url, nullptr, aChannel);
7051       return NS_OK;
7052     }
7053 
7054     // Handle iframe document not loading error because source was
7055     // a tracking URL. We make a note of this iframe node by including
7056     // it in a dedicated array of blocked tracking nodes under its parent
7057     // document. (document of parent window of blocked document)
7058     if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
7059       // frameElement is our nsIContent to be annotated
7060       nsCOMPtr<nsIDOMElement> frameElement;
7061       nsPIDOMWindowOuter* thisWindow = GetWindow();
7062       if (!thisWindow) {
7063         return NS_OK;
7064       }
7065 
7066       frameElement = thisWindow->GetFrameElement();
7067       if (!frameElement) {
7068         return NS_OK;
7069       }
7070 
7071       // Parent window
7072       nsCOMPtr<nsIDocShellTreeItem> parentItem;
7073       GetSameTypeParent(getter_AddRefs(parentItem));
7074       if (!parentItem) {
7075         return NS_OK;
7076       }
7077 
7078       nsCOMPtr<nsIDocument> parentDoc;
7079       parentDoc = parentItem->GetDocument();
7080       if (!parentDoc) {
7081         return NS_OK;
7082       }
7083 
7084       nsCOMPtr<nsIContent> cont = do_QueryInterface(frameElement);
7085       parentDoc->AddBlockedTrackingNode(cont);
7086 
7087       return NS_OK;
7088     }
7089 
7090     if (sURIFixup) {
7091       //
7092       // Try and make an alternative URI from the old one
7093       //
7094       nsCOMPtr<nsIURI> newURI;
7095       nsCOMPtr<nsIInputStream> newPostData;
7096 
7097       nsAutoCString oldSpec;
7098       url->GetSpec(oldSpec);
7099 
7100       //
7101       // First try keyword fixup
7102       //
7103       nsAutoString keywordProviderName, keywordAsSent;
7104       if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
7105         bool keywordsEnabled = Preferences::GetBool("keyword.enabled", false);
7106 
7107         nsAutoCString host;
7108         url->GetHost(host);
7109 
7110         nsAutoCString scheme;
7111         url->GetScheme(scheme);
7112 
7113         int32_t dotLoc = host.FindChar('.');
7114 
7115         // we should only perform a keyword search under the following
7116         // conditions:
7117         // (0) Pref keyword.enabled is true
7118         // (1) the url scheme is http (or https)
7119         // (2) the url does not have a protocol scheme
7120         // If we don't enforce such a policy, then we end up doing
7121         // keyword searchs on urls we don't intend like imap, file,
7122         // mailbox, etc. This could lead to a security problem where we
7123         // send data to the keyword server that we shouldn't be.
7124         // Someone needs to clean up keywords in general so we can
7125         // determine on a per url basis if we want keywords
7126         // enabled...this is just a bandaid...
7127         if (keywordsEnabled && !scheme.IsEmpty() &&
7128             (scheme.Find("http") != 0)) {
7129           keywordsEnabled = false;
7130         }
7131 
7132         if (keywordsEnabled && (kNotFound == dotLoc)) {
7133           nsCOMPtr<nsIURIFixupInfo> info;
7134           // only send non-qualified hosts to the keyword server
7135           if (!mOriginalUriString.IsEmpty()) {
7136             sURIFixup->KeywordToURI(mOriginalUriString,
7137                                     getter_AddRefs(newPostData),
7138                                     getter_AddRefs(info));
7139           } else {
7140             //
7141             // If this string was passed through nsStandardURL by
7142             // chance, then it may have been converted from UTF-8 to
7143             // ACE, which would result in a completely bogus keyword
7144             // query.  Here we try to recover the original Unicode
7145             // value, but this is not 100% correct since the value may
7146             // have been normalized per the IDN normalization rules.
7147             //
7148             // Since we don't have access to the exact original string
7149             // that was entered by the user, this will just have to do.
7150             bool isACE;
7151             nsAutoCString utf8Host;
7152             nsCOMPtr<nsIIDNService> idnSrv =
7153                 do_GetService(NS_IDNSERVICE_CONTRACTID);
7154             if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
7155                 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
7156               sURIFixup->KeywordToURI(utf8Host, getter_AddRefs(newPostData),
7157                                       getter_AddRefs(info));
7158             } else {
7159               sURIFixup->KeywordToURI(host, getter_AddRefs(newPostData),
7160                                       getter_AddRefs(info));
7161             }
7162           }
7163 
7164           info->GetPreferredURI(getter_AddRefs(newURI));
7165           if (newURI) {
7166             info->GetKeywordAsSent(keywordAsSent);
7167             info->GetKeywordProviderName(keywordProviderName);
7168           }
7169         }  // end keywordsEnabled
7170       }
7171 
7172       //
7173       // Now try change the address, e.g. turn http://foo into
7174       // http://www.foo.com
7175       //
7176       if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
7177         bool doCreateAlternate = true;
7178 
7179         // Skip fixup for anything except a normal document load
7180         // operation on the topframe.
7181 
7182         if (mLoadType != LOAD_NORMAL || !isTopFrame) {
7183           doCreateAlternate = false;
7184         } else {
7185           // Test if keyword lookup produced a new URI or not
7186           if (newURI) {
7187             bool sameURI = false;
7188             url->Equals(newURI, &sameURI);
7189             if (!sameURI) {
7190               // Keyword lookup made a new URI so no need to try
7191               // an alternate one.
7192               doCreateAlternate = false;
7193             }
7194           }
7195 
7196           if (doCreateAlternate) {
7197             // Skip doing this if our channel was redirected, because we
7198             // shouldn't be guessing things about the post-redirect URI.
7199             nsLoadFlags loadFlags = 0;
7200             if (NS_FAILED(aChannel->GetLoadFlags(&loadFlags)) ||
7201                 (loadFlags & nsIChannel::LOAD_REPLACE)) {
7202               doCreateAlternate = false;
7203             }
7204           }
7205         }
7206         if (doCreateAlternate) {
7207           newURI = nullptr;
7208           newPostData = nullptr;
7209           keywordProviderName.Truncate();
7210           keywordAsSent.Truncate();
7211           sURIFixup->CreateFixupURI(
7212               oldSpec, nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
7213               getter_AddRefs(newPostData), getter_AddRefs(newURI));
7214         }
7215       }
7216 
7217       // Did we make a new URI that is different to the old one? If so
7218       // load it.
7219       //
7220       if (newURI) {
7221         // Make sure the new URI is different from the old one,
7222         // otherwise there's little point trying to load it again.
7223         bool sameURI = false;
7224         url->Equals(newURI, &sameURI);
7225         if (!sameURI) {
7226           nsAutoCString newSpec;
7227           newURI->GetSpec(newSpec);
7228           NS_ConvertUTF8toUTF16 newSpecW(newSpec);
7229 
7230           // This notification is meant for Firefox Health Report so it
7231           // can increment counts from the search engine
7232           MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
7233 
7234           nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
7235           nsCOMPtr<nsIPrincipal> triggeringPrincipal =
7236               loadInfo ? loadInfo->TriggeringPrincipal()
7237                        : nsContentUtils::GetSystemPrincipal();
7238           return LoadURI(newSpecW.get(),        // URI string
7239                          LOAD_FLAGS_NONE,       // Load flags
7240                          nullptr,               // Referring URI
7241                          newPostData,           // Post data stream
7242                          nullptr,               // Headers stream
7243                          triggeringPrincipal);  // TriggeringPrincipal
7244         }
7245       }
7246     }
7247 
7248     // Well, fixup didn't work :-(
7249     // It is time to throw an error dialog box, and be done with it...
7250 
7251     // Errors to be shown only on top-level frames
7252     if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
7253          aStatus == NS_ERROR_CONNECTION_REFUSED ||
7254          aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
7255          aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
7256          aStatus == NS_ERROR_BLOCKED_BY_POLICY) &&
7257         (isTopFrame || UseErrorPages())) {
7258       DisplayLoadError(aStatus, url, nullptr, aChannel);
7259     } else if (aStatus == NS_ERROR_NET_TIMEOUT ||
7260                aStatus == NS_ERROR_REDIRECT_LOOP ||
7261                aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
7262                aStatus == NS_ERROR_NET_INTERRUPT ||
7263                aStatus == NS_ERROR_NET_RESET || aStatus == NS_ERROR_OFFLINE ||
7264                aStatus == NS_ERROR_MALWARE_URI ||
7265                aStatus == NS_ERROR_PHISHING_URI ||
7266                aStatus == NS_ERROR_UNWANTED_URI ||
7267                aStatus == NS_ERROR_HARMFUL_URI ||
7268                aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
7269                aStatus == NS_ERROR_REMOTE_XUL ||
7270                aStatus == NS_ERROR_INTERCEPTION_FAILED ||
7271                aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
7272                NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
7273       // Errors to be shown for any frame
7274       DisplayLoadError(aStatus, url, nullptr, aChannel);
7275     } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
7276       // Non-caching channels will simply return NS_ERROR_OFFLINE.
7277       // Caching channels would have to look at their flags to work
7278       // out which error to return. Or we can fix up the error here.
7279       if (!(mLoadType & LOAD_CMD_HISTORY)) {
7280         aStatus = NS_ERROR_OFFLINE;
7281       }
7282       DisplayLoadError(aStatus, url, nullptr, aChannel);
7283     }
7284   } else if (url && NS_SUCCEEDED(aStatus)) {
7285     // If we have a host
7286     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
7287     if (loadInfo) {
7288       mozilla::net::PredictorLearnRedirect(url, aChannel,
7289                                            loadInfo->GetOriginAttributes());
7290     }
7291   }
7292 
7293   return NS_OK;
7294 }
7295 
7296 //*****************************************************************************
7297 // nsDocShell: Content Viewer Management
7298 //*****************************************************************************
7299 
EnsureContentViewer()7300 nsresult nsDocShell::EnsureContentViewer() {
7301   if (mContentViewer) {
7302     return NS_OK;
7303   }
7304   if (mIsBeingDestroyed) {
7305     return NS_ERROR_FAILURE;
7306   }
7307 
7308   nsCOMPtr<nsIURI> baseURI;
7309   nsIPrincipal* principal = GetInheritedPrincipal(false);
7310   nsCOMPtr<nsIDocShellTreeItem> parentItem;
7311   GetSameTypeParent(getter_AddRefs(parentItem));
7312   if (parentItem) {
7313     if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
7314       nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
7315       if (parentElement) {
7316         baseURI = parentElement->GetBaseURI();
7317       }
7318     }
7319   }
7320 
7321   nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
7322 
7323   NS_ENSURE_STATE(mContentViewer);
7324 
7325   if (NS_SUCCEEDED(rv)) {
7326     nsCOMPtr<nsIDocument> doc(GetDocument());
7327     NS_ASSERTION(doc,
7328                  "Should have doc if CreateAboutBlankContentViewer "
7329                  "succeeded!");
7330 
7331     doc->SetIsInitialDocument(true);
7332 
7333     // Documents created using EnsureContentViewer may be transient
7334     // placeholders created by framescripts before content has a chance to
7335     // load. In some cases, window.open(..., "noopener") will create such a
7336     // document (in a new TabGroup) and then synchronously tear it down, firing
7337     // a "pagehide" event. Doing so violates our assertions about
7338     // DocGroups. It's easier to silence the assertion here than to avoid
7339     // creating the extra document.
7340     doc->IgnoreDocGroupMismatches();
7341   }
7342 
7343   return rv;
7344 }
7345 
CreateAboutBlankContentViewer(nsIPrincipal * aPrincipal,nsIURI * aBaseURI,bool aTryToSaveOldPresentation,bool aCheckPermitUnload)7346 nsresult nsDocShell::CreateAboutBlankContentViewer(
7347     nsIPrincipal* aPrincipal, nsIURI* aBaseURI, bool aTryToSaveOldPresentation,
7348     bool aCheckPermitUnload) {
7349   nsCOMPtr<nsIDocument> blankDoc;
7350   nsCOMPtr<nsIContentViewer> viewer;
7351   nsresult rv = NS_ERROR_FAILURE;
7352 
7353   /* mCreatingDocument should never be true at this point. However, it's
7354      a theoretical possibility. We want to know about it and make it stop,
7355      and this sounds like a job for an assertion. */
7356   NS_ASSERTION(!mCreatingDocument,
7357                "infinite(?) loop creating document averted");
7358   if (mCreatingDocument) {
7359     return NS_ERROR_FAILURE;
7360   }
7361 
7362   // mContentViewer->PermitUnload may release |this| docshell.
7363   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7364 
7365   AutoRestore<bool> creatingDocument(mCreatingDocument);
7366   mCreatingDocument = true;
7367 
7368   if (aPrincipal && !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
7369       mItemType != typeChrome) {
7370     MOZ_ASSERT(aPrincipal->OriginAttributesRef() == mOriginAttributes);
7371   }
7372 
7373   // Make sure timing is created.  But first record whether we had it
7374   // already, so we don't clobber the timing for an in-progress load.
7375   bool hadTiming = mTiming;
7376   bool toBeReset = MaybeInitTiming();
7377   if (mContentViewer) {
7378     if (aCheckPermitUnload) {
7379       // We've got a content viewer already. Make sure the user
7380       // permits us to discard the current document and replace it
7381       // with about:blank. And also ensure we fire the unload events
7382       // in the current document.
7383 
7384       // Unload gets fired first for
7385       // document loaded from the session history.
7386       mTiming->NotifyBeforeUnload();
7387 
7388       bool okToUnload;
7389       rv = mContentViewer->PermitUnload(&okToUnload);
7390 
7391       if (NS_SUCCEEDED(rv) && !okToUnload) {
7392         // The user chose not to unload the page, interrupt the load.
7393         MaybeResetInitTiming(toBeReset);
7394         return NS_ERROR_FAILURE;
7395       }
7396       if (mTiming) {
7397         mTiming->NotifyUnloadAccepted(mCurrentURI);
7398       }
7399     }
7400 
7401     mSavingOldViewer = aTryToSaveOldPresentation &&
7402                        CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
7403 
7404     // Make sure to blow away our mLoadingURI just in case.  No loads
7405     // from inside this pagehide.
7406     mLoadingURI = nullptr;
7407 
7408     // Stop any in-progress loading, so that we don't accidentally trigger any
7409     // PageShow notifications from Embed() interrupting our loading below.
7410     Stop();
7411 
7412     // Notify the current document that it is about to be unloaded!!
7413     //
7414     // It is important to fire the unload() notification *before* any state
7415     // is changed within the DocShell - otherwise, javascript will get the
7416     // wrong information :-(
7417     //
7418     (void)FirePageHideNotification(!mSavingOldViewer);
7419     // pagehide notification might destroy this docshell.
7420     if (mIsBeingDestroyed) {
7421       return NS_ERROR_DOCSHELL_DYING;
7422     }
7423   }
7424 
7425   // Now make sure we don't think we're in the middle of firing unload after
7426   // this point.  This will make us fire unload when the about:blank document
7427   // unloads... but that's ok, more or less.  Would be nice if it fired load
7428   // too, of course.
7429   mFiredUnloadEvent = false;
7430 
7431   nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
7432       nsContentUtils::FindInternalContentViewer(
7433           NS_LITERAL_CSTRING("text/html"));
7434 
7435   if (docFactory) {
7436     nsCOMPtr<nsIPrincipal> principal;
7437     if (mSandboxFlags & SANDBOXED_ORIGIN) {
7438       if (aPrincipal) {
7439         principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
7440       } else {
7441         principal = NullPrincipal::CreateWithInheritedAttributes(this);
7442       }
7443     } else {
7444       principal = aPrincipal;
7445     }
7446 
7447     MaybeCreateInitialClientSource(principal);
7448 
7449     // generate (about:blank) document to load
7450     blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal, this);
7451     if (blankDoc) {
7452       // Hack: set the base URI manually, since this document never
7453       // got Reset() with a channel.
7454       blankDoc->SetBaseURI(aBaseURI);
7455 
7456       // Copy our sandbox flags to the document. These are immutable
7457       // after being set here.
7458       blankDoc->SetSandboxFlags(mSandboxFlags);
7459 
7460       // create a content viewer for us and the new document
7461       docFactory->CreateInstanceForDocument(
7462           NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
7463           getter_AddRefs(viewer));
7464 
7465       // hook 'em up
7466       if (viewer) {
7467         viewer->SetContainer(this);
7468         rv = Embed(viewer, "", 0);
7469         NS_ENSURE_SUCCESS(rv, rv);
7470 
7471         SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
7472         rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
7473       }
7474     }
7475   }
7476 
7477   // The transient about:blank viewer doesn't have a session history entry.
7478   SetHistoryEntry(&mOSHE, nullptr);
7479 
7480   // Clear out our mTiming like we would in EndPageLoad, if we didn't
7481   // have one before entering this function.
7482   if (!hadTiming) {
7483     mTiming = nullptr;
7484     mBlankTiming = true;
7485   }
7486 
7487   return rv;
7488 }
7489 
7490 NS_IMETHODIMP
CreateAboutBlankContentViewer(nsIPrincipal * aPrincipal)7491 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) {
7492   return CreateAboutBlankContentViewer(aPrincipal, nullptr);
7493 }
7494 
7495 NS_IMETHODIMP
ForceCreateAboutBlankContentViewer(nsIPrincipal * aPrincipal)7496 nsDocShell::ForceCreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) {
7497   return CreateAboutBlankContentViewer(aPrincipal, nullptr, true, false);
7498 }
7499 
CanSavePresentation(uint32_t aLoadType,nsIRequest * aNewRequest,nsIDocument * aNewDocument)7500 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
7501                                      nsIRequest* aNewRequest,
7502                                      nsIDocument* aNewDocument) {
7503   if (!mOSHE) {
7504     return false;  // no entry to save into
7505   }
7506 
7507   nsCOMPtr<nsIContentViewer> viewer;
7508   mOSHE->GetContentViewer(getter_AddRefs(viewer));
7509   if (viewer) {
7510     NS_WARNING("mOSHE already has a content viewer!");
7511     return false;
7512   }
7513 
7514   // Only save presentation for "normal" loads and link loads.  Anything else
7515   // probably wants to refetch the page, so caching the old presentation
7516   // would be incorrect.
7517   if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
7518       aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
7519       aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
7520       aLoadType != LOAD_ERROR_PAGE) {
7521     return false;
7522   }
7523 
7524   // If the session history entry has the saveLayoutState flag set to false,
7525   // then we should not cache the presentation.
7526   bool canSaveState;
7527   mOSHE->GetSaveLayoutStateFlag(&canSaveState);
7528   if (!canSaveState) {
7529     return false;
7530   }
7531 
7532   // If the document is not done loading, don't cache it.
7533   if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
7534     return false;
7535   }
7536 
7537   if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
7538     return false;
7539   }
7540 
7541   // Avoid doing the work of saving the presentation state in the case where
7542   // the content viewer cache is disabled.
7543   if (nsSHistory::GetMaxTotalViewers() == 0) {
7544     return false;
7545   }
7546 
7547   // Don't cache the content viewer if we're in a subframe.
7548   nsCOMPtr<nsIDocShellTreeItem> root;
7549   GetSameTypeParent(getter_AddRefs(root));
7550   if (root && root != this) {
7551     return false;  // this is a subframe load
7552   }
7553 
7554   // If the document does not want its presentation cached, then don't.
7555   nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc();
7556   return doc && doc->CanSavePresentation(aNewRequest);
7557 }
7558 
ReattachEditorToWindow(nsISHEntry * aSHEntry)7559 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
7560   MOZ_ASSERT(!mIsBeingDestroyed);
7561 
7562   NS_ASSERTION(!mEditorData,
7563                "Why reattach an editor when we already have one?");
7564   NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
7565                "Reattaching when there's not a detached editor.");
7566 
7567   if (mEditorData || !aSHEntry) {
7568     return;
7569   }
7570 
7571   mEditorData = aSHEntry->ForgetEditorData();
7572   if (mEditorData) {
7573 #ifdef DEBUG
7574     nsresult rv =
7575 #endif
7576         mEditorData->ReattachToWindow(this);
7577     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7578   }
7579 }
7580 
DetachEditorFromWindow()7581 void nsDocShell::DetachEditorFromWindow() {
7582   if (!mEditorData || mEditorData->WaitingForLoad()) {
7583     // If there's nothing to detach, or if the editor data is actually set
7584     // up for the _new_ page that's coming in, don't detach.
7585     return;
7586   }
7587 
7588   NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7589                "Detaching editor when it's already detached.");
7590 
7591   nsresult res = mEditorData->DetachFromWindow();
7592   NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7593 
7594   if (NS_SUCCEEDED(res)) {
7595     // Make mOSHE hold the owning ref to the editor data.
7596     if (mOSHE) {
7597       MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
7598                  "We should not set the editor data again once after we "
7599                  "detached the editor data during destroying this docshell");
7600       mOSHE->SetEditorData(mEditorData.forget());
7601     } else {
7602       mEditorData = nullptr;
7603     }
7604   }
7605 
7606 #ifdef DEBUG
7607   {
7608     bool isEditable;
7609     GetEditable(&isEditable);
7610     NS_ASSERTION(!isEditable,
7611                  "Window is still editable after detaching editor.");
7612   }
7613 #endif  // DEBUG
7614 }
7615 
CaptureState()7616 nsresult nsDocShell::CaptureState() {
7617   if (!mOSHE || mOSHE == mLSHE) {
7618     // No entry to save into, or we're replacing the existing entry.
7619     return NS_ERROR_FAILURE;
7620   }
7621 
7622   if (!mScriptGlobal) {
7623     return NS_ERROR_FAILURE;
7624   }
7625 
7626   nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7627   NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7628 
7629 #ifdef DEBUG_PAGE_CACHE
7630   nsCOMPtr<nsIURI> uri;
7631   mOSHE->GetURI(getter_AddRefs(uri));
7632   nsAutoCString spec;
7633   if (uri) {
7634     uri->GetSpec(spec);
7635   }
7636   printf("Saving presentation into session history\n");
7637   printf("  SH URI: %s\n", spec.get());
7638 #endif
7639 
7640   nsresult rv = mOSHE->SetWindowState(windowState);
7641   NS_ENSURE_SUCCESS(rv, rv);
7642 
7643   // Suspend refresh URIs and save off the timer queue
7644   rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7645   NS_ENSURE_SUCCESS(rv, rv);
7646 
7647   // Capture the current content viewer bounds.
7648   if (mContentViewer) {
7649     nsIntRect bounds;
7650     mContentViewer->GetBounds(bounds);
7651     rv = mOSHE->SetViewerBounds(bounds);
7652     NS_ENSURE_SUCCESS(rv, rv);
7653   }
7654 
7655   // Capture the docshell hierarchy.
7656   mOSHE->ClearChildShells();
7657 
7658   uint32_t childCount = mChildList.Length();
7659   for (uint32_t i = 0; i < childCount; ++i) {
7660     nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7661     NS_ASSERTION(childShell, "null child shell");
7662 
7663     mOSHE->AddChildShell(childShell);
7664   }
7665 
7666   return NS_OK;
7667 }
7668 
7669 NS_IMETHODIMP
Run()7670 nsDocShell::RestorePresentationEvent::Run() {
7671   if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7672     NS_WARNING("RestoreFromHistory failed");
7673   }
7674   return NS_OK;
7675 }
7676 
7677 NS_IMETHODIMP
BeginRestore(nsIContentViewer * aContentViewer,bool aTop)7678 nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop) {
7679   nsresult rv;
7680   if (!aContentViewer) {
7681     rv = EnsureContentViewer();
7682     NS_ENSURE_SUCCESS(rv, rv);
7683 
7684     aContentViewer = mContentViewer;
7685   }
7686 
7687   // Dispatch events for restoring the presentation.  We try to simulate
7688   // the progress notifications loading the document would cause, so we add
7689   // the document's channel to the loadgroup to initiate stateChange
7690   // notifications.
7691 
7692   nsCOMPtr<nsIDocument> doc = aContentViewer->GetDocument();
7693   if (doc) {
7694     nsIChannel* channel = doc->GetChannel();
7695     if (channel) {
7696       mEODForCurrentDocument = false;
7697       mIsRestoringDocument = true;
7698       mLoadGroup->AddRequest(channel, nullptr);
7699       mIsRestoringDocument = false;
7700     }
7701   }
7702 
7703   if (!aTop) {
7704     // This point corresponds to us having gotten OnStartRequest or
7705     // STATE_START, so do the same thing that CreateContentViewer does at
7706     // this point to ensure that unload/pagehide events for this document
7707     // will fire when it's unloaded again.
7708     mFiredUnloadEvent = false;
7709 
7710     // For non-top frames, there is no notion of making sure that the
7711     // previous document is in the domwindow when STATE_START notifications
7712     // happen.  We can just call BeginRestore for all of the child shells
7713     // now.
7714     rv = BeginRestoreChildren();
7715     NS_ENSURE_SUCCESS(rv, rv);
7716   }
7717 
7718   return NS_OK;
7719 }
7720 
BeginRestoreChildren()7721 nsresult nsDocShell::BeginRestoreChildren() {
7722   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7723   while (iter.HasMore()) {
7724     nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7725     if (child) {
7726       nsresult rv = child->BeginRestore(nullptr, false);
7727       NS_ENSURE_SUCCESS(rv, rv);
7728     }
7729   }
7730   return NS_OK;
7731 }
7732 
7733 NS_IMETHODIMP
FinishRestore()7734 nsDocShell::FinishRestore() {
7735   // First we call finishRestore() on our children.  In the simulated load,
7736   // all of the child frames finish loading before the main document.
7737 
7738   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7739   while (iter.HasMore()) {
7740     nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7741     if (child) {
7742       child->FinishRestore();
7743     }
7744   }
7745 
7746   if (mOSHE && mOSHE->HasDetachedEditor()) {
7747     ReattachEditorToWindow(mOSHE);
7748   }
7749 
7750   nsCOMPtr<nsIDocument> doc = GetDocument();
7751   if (doc) {
7752     // Finally, we remove the request from the loadgroup.  This will
7753     // cause onStateChange(STATE_STOP) to fire, which will fire the
7754     // pageshow event to the chrome.
7755 
7756     nsIChannel* channel = doc->GetChannel();
7757     if (channel) {
7758       mIsRestoringDocument = true;
7759       mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7760       mIsRestoringDocument = false;
7761     }
7762   }
7763 
7764   return NS_OK;
7765 }
7766 
7767 NS_IMETHODIMP
GetRestoringDocument(bool * aRestoring)7768 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7769   *aRestoring = mIsRestoringDocument;
7770   return NS_OK;
7771 }
7772 
RestorePresentation(nsISHEntry * aSHEntry,bool * aRestoring)7773 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7774                                          bool* aRestoring) {
7775   MOZ_ASSERT(!mIsBeingDestroyed);
7776 
7777   NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7778                "RestorePresentation should only be called for history loads");
7779 
7780   nsCOMPtr<nsIContentViewer> viewer;
7781   aSHEntry->GetContentViewer(getter_AddRefs(viewer));
7782 
7783 #ifdef DEBUG_PAGE_CACHE
7784   nsCOMPtr<nsIURI> uri;
7785   aSHEntry->GetURI(getter_AddRefs(uri));
7786 
7787   nsAutoCString spec;
7788   if (uri) {
7789     uri->GetSpec(spec);
7790   }
7791 #endif
7792 
7793   *aRestoring = false;
7794 
7795   if (!viewer) {
7796 #ifdef DEBUG_PAGE_CACHE
7797     printf("no saved presentation for uri: %s\n", spec.get());
7798 #endif
7799     return NS_OK;
7800   }
7801 
7802   // We need to make sure the content viewer's container is this docshell.
7803   // In subframe navigation, it's possible for the docshell that the
7804   // content viewer was originally loaded into to be replaced with a
7805   // different one.  We don't currently support restoring the presentation
7806   // in that case.
7807 
7808   nsCOMPtr<nsIDocShell> container;
7809   viewer->GetContainer(getter_AddRefs(container));
7810   if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7811 #ifdef DEBUG_PAGE_CACHE
7812     printf("No valid container, clearing presentation\n");
7813 #endif
7814     aSHEntry->SetContentViewer(nullptr);
7815     return NS_ERROR_FAILURE;
7816   }
7817 
7818   NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7819 
7820 #ifdef DEBUG_PAGE_CACHE
7821   printf("restoring presentation from session history: %s\n", spec.get());
7822 #endif
7823 
7824   SetHistoryEntry(&mLSHE, aSHEntry);
7825 
7826   // Post an event that will remove the request after we've returned
7827   // to the event loop.  This mimics the way it is called by nsIChannel
7828   // implementations.
7829 
7830   // Revoke any pending restore (just in case)
7831   NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7832                "should only have one RestorePresentationEvent");
7833   mRestorePresentationEvent.Revoke();
7834 
7835   RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7836   nsresult rv = DispatchToTabGroup(
7837       TaskCategory::Other, RefPtr<RestorePresentationEvent>(evt).forget());
7838   if (NS_SUCCEEDED(rv)) {
7839     mRestorePresentationEvent = evt.get();
7840     // The rest of the restore processing will happen on our event
7841     // callback.
7842     *aRestoring = true;
7843   }
7844 
7845   return rv;
7846 }
7847 
7848 namespace {
7849 class MOZ_STACK_CLASS PresentationEventForgetter {
7850  public:
PresentationEventForgetter(nsRevocableEventPtr<nsDocShell::RestorePresentationEvent> & aRestorePresentationEvent)7851   explicit PresentationEventForgetter(
7852       nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7853           aRestorePresentationEvent)
7854       : mRestorePresentationEvent(aRestorePresentationEvent),
7855         mEvent(aRestorePresentationEvent.get()) {}
7856 
~PresentationEventForgetter()7857   ~PresentationEventForgetter() { Forget(); }
7858 
Forget()7859   void Forget() {
7860     if (mRestorePresentationEvent.get() == mEvent) {
7861       mRestorePresentationEvent.Forget();
7862       mEvent = nullptr;
7863     }
7864   }
7865 
7866  private:
7867   nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7868       mRestorePresentationEvent;
7869   RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7870 };
7871 
7872 }  // namespace
7873 
SandboxFlagsImplyCookies(const uint32_t & aSandboxFlags)7874 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7875   return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7876 }
7877 
RestoreFromHistory()7878 nsresult nsDocShell::RestoreFromHistory() {
7879   MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7880   PresentationEventForgetter forgetter(mRestorePresentationEvent);
7881 
7882   // This section of code follows the same ordering as CreateContentViewer.
7883   if (!mLSHE) {
7884     return NS_ERROR_FAILURE;
7885   }
7886 
7887   nsCOMPtr<nsIContentViewer> viewer;
7888   mLSHE->GetContentViewer(getter_AddRefs(viewer));
7889   if (!viewer) {
7890     return NS_ERROR_FAILURE;
7891   }
7892 
7893   if (mSavingOldViewer) {
7894     // We determined that it was safe to cache the document presentation
7895     // at the time we initiated the new load.  We need to check whether
7896     // it's still safe to do so, since there may have been DOM mutations
7897     // or new requests initiated.
7898     nsCOMPtr<nsIDocument> doc = viewer->GetDocument();
7899     nsIRequest* request = nullptr;
7900     if (doc) {
7901       request = doc->GetChannel();
7902     }
7903     mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
7904   }
7905 
7906   nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
7907   nsCOMPtr<nsIContentViewer> newCv(viewer);
7908   int32_t minFontSize = 0;
7909   float textZoom = 1.0f;
7910   float pageZoom = 1.0f;
7911   float overrideDPPX = 0.0f;
7912 
7913   bool styleDisabled = false;
7914   if (oldCv && newCv) {
7915     oldCv->GetMinFontSize(&minFontSize);
7916     oldCv->GetTextZoom(&textZoom);
7917     oldCv->GetFullZoom(&pageZoom);
7918     oldCv->GetOverrideDPPX(&overrideDPPX);
7919     oldCv->GetAuthorStyleDisabled(&styleDisabled);
7920   }
7921 
7922   // Protect against mLSHE going away via a load triggered from
7923   // pagehide or unload.
7924   nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7925 
7926   // Make sure to blow away our mLoadingURI just in case.  No loads
7927   // from inside this pagehide.
7928   mLoadingURI = nullptr;
7929 
7930   // Notify the old content viewer that it's being hidden.
7931   FirePageHideNotification(!mSavingOldViewer);
7932   // pagehide notification might destroy this docshell.
7933   if (mIsBeingDestroyed) {
7934     return NS_ERROR_DOCSHELL_DYING;
7935   }
7936 
7937   // If mLSHE was changed as a result of the pagehide event, then
7938   // something else was loaded.  Don't finish restoring.
7939   if (mLSHE != origLSHE) {
7940     return NS_OK;
7941   }
7942 
7943   // Add the request to our load group.  We do this before swapping out
7944   // the content viewers so that consumers of STATE_START can access
7945   // the old document.  We only deal with the toplevel load at this time --
7946   // to be consistent with normal document loading, subframes cannot start
7947   // loading until after data arrives, which is after STATE_START completes.
7948 
7949   RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7950       mRestorePresentationEvent.get();
7951   Stop();
7952   // Make sure we're still restoring the same presentation.
7953   // If we aren't, docshell is in process doing another load already.
7954   NS_ENSURE_STATE(currentPresentationRestoration ==
7955                   mRestorePresentationEvent.get());
7956   BeginRestore(viewer, true);
7957   NS_ENSURE_STATE(currentPresentationRestoration ==
7958                   mRestorePresentationEvent.get());
7959   forgetter.Forget();
7960 
7961   // Set mFiredUnloadEvent = false so that the unload handler for the
7962   // *new* document will fire.
7963   mFiredUnloadEvent = false;
7964 
7965   mURIResultedInDocument = true;
7966   nsCOMPtr<nsISHistory> rootSH;
7967   GetRootSessionHistory(getter_AddRefs(rootSH));
7968   if (rootSH) {
7969     nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
7970     rootSH->GetIndex(&mPreviousTransIndex);
7971     hist->UpdateIndex();
7972     rootSH->GetIndex(&mLoadedTransIndex);
7973 #ifdef DEBUG_PAGE_CACHE
7974     printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
7975            mLoadedTransIndex);
7976 #endif
7977   }
7978 
7979   // Rather than call Embed(), we will retrieve the viewer from the session
7980   // history entry and swap it in.
7981   // XXX can we refactor this so that we can just call Embed()?
7982   PersistLayoutHistoryState();
7983   nsresult rv;
7984   if (mContentViewer) {
7985     if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7986       if (mOSHE) {
7987         mOSHE->SyncPresentationState();
7988       }
7989       mSavingOldViewer = false;
7990     }
7991   }
7992 
7993   mSavedRefreshURIList = nullptr;
7994 
7995   // In cases where we use a transient about:blank viewer between loads,
7996   // we never show the transient viewer, so _its_ previous viewer is never
7997   // unhooked from the view hierarchy.  Destroy any such previous viewer now,
7998   // before we grab the root view sibling, so that we don't grab a view
7999   // that's about to go away.
8000 
8001   if (mContentViewer) {
8002     nsCOMPtr<nsIContentViewer> previousViewer;
8003     mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
8004     if (previousViewer) {
8005       mContentViewer->SetPreviousViewer(nullptr);
8006       previousViewer->Destroy();
8007     }
8008   }
8009 
8010   // Save off the root view's parent and sibling so that we can insert the
8011   // new content viewer's root view at the same position.  Also save the
8012   // bounds of the root view's widget.
8013 
8014   nsView* rootViewSibling = nullptr;
8015   nsView* rootViewParent = nullptr;
8016   nsIntRect newBounds(0, 0, 0, 0);
8017 
8018   nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
8019   if (oldPresShell) {
8020     nsViewManager* vm = oldPresShell->GetViewManager();
8021     if (vm) {
8022       nsView* oldRootView = vm->GetRootView();
8023 
8024       if (oldRootView) {
8025         rootViewSibling = oldRootView->GetNextSibling();
8026         rootViewParent = oldRootView->GetParent();
8027 
8028         mContentViewer->GetBounds(newBounds);
8029       }
8030     }
8031   }
8032 
8033   nsCOMPtr<nsIContent> container;
8034   nsCOMPtr<nsIDocument> sibling;
8035   if (rootViewParent && rootViewParent->GetParent()) {
8036     nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
8037     container = frame ? frame->GetContent() : nullptr;
8038   }
8039   if (rootViewSibling) {
8040     nsIFrame* frame = rootViewSibling->GetFrame();
8041     sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
8042   }
8043 
8044   // Transfer ownership to mContentViewer.  By ensuring that either the
8045   // docshell or the session history, but not both, have references to the
8046   // content viewer, we prevent the viewer from being torn down after
8047   // Destroy() is called.
8048 
8049   if (mContentViewer) {
8050     mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8051     viewer->SetPreviousViewer(mContentViewer);
8052   }
8053   if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8054     // We don't plan to save a viewer in mOSHE; tell it to drop
8055     // any other state it's holding.
8056     mOSHE->SyncPresentationState();
8057   }
8058 
8059   // Order the mContentViewer setup just like Embed does.
8060   mContentViewer = nullptr;
8061 
8062   // Now that we're about to switch documents, forget all of our children.
8063   // Note that we cached them as needed up in CaptureState above.
8064   DestroyChildren();
8065 
8066   mContentViewer.swap(viewer);
8067 
8068   // Grab all of the related presentation from the SHEntry now.
8069   // Clearing the viewer from the SHEntry will clear all of this state.
8070   nsCOMPtr<nsISupports> windowState;
8071   mLSHE->GetWindowState(getter_AddRefs(windowState));
8072   mLSHE->SetWindowState(nullptr);
8073 
8074   bool sticky;
8075   mLSHE->GetSticky(&sticky);
8076 
8077   nsCOMPtr<nsIDocument> document = mContentViewer->GetDocument();
8078 
8079   nsCOMArray<nsIDocShellTreeItem> childShells;
8080   int32_t i = 0;
8081   nsCOMPtr<nsIDocShellTreeItem> child;
8082   while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
8083          child) {
8084     childShells.AppendObject(child);
8085   }
8086 
8087   // get the previous content viewer size
8088   nsIntRect oldBounds(0, 0, 0, 0);
8089   mLSHE->GetViewerBounds(oldBounds);
8090 
8091   // Restore the refresh URI list.  The refresh timers will be restarted
8092   // when EndPageLoad() is called.
8093   nsCOMPtr<nsIMutableArray> refreshURIList;
8094   mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
8095 
8096   // Reattach to the window object.
8097   mIsRestoringDocument = true;  // for MediaDocument::BecomeInteractive
8098   rv = mContentViewer->Open(windowState, mLSHE);
8099   mIsRestoringDocument = false;
8100 
8101   // Hack to keep nsDocShellEditorData alive across the
8102   // SetContentViewer(nullptr) call below.
8103   nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
8104 
8105   // Now remove it from the cached presentation.
8106   mLSHE->SetContentViewer(nullptr);
8107   mEODForCurrentDocument = false;
8108 
8109   mLSHE->SetEditorData(data.forget());
8110 
8111 #ifdef DEBUG
8112   {
8113     nsCOMPtr<nsIMutableArray> refreshURIs;
8114     mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
8115     nsCOMPtr<nsIDocShellTreeItem> childShell;
8116     mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
8117     NS_ASSERTION(!refreshURIs && !childShell,
8118                  "SHEntry should have cleared presentation state");
8119   }
8120 #endif
8121 
8122   // Restore the sticky state of the viewer.  The viewer has set this state
8123   // on the history entry in Destroy() just before marking itself non-sticky,
8124   // to avoid teardown of the presentation.
8125   mContentViewer->SetSticky(sticky);
8126 
8127   NS_ENSURE_SUCCESS(rv, rv);
8128 
8129   // mLSHE is now our currently-loaded document.
8130   SetHistoryEntry(&mOSHE, mLSHE);
8131 
8132   // XXX special wyciwyg handling in Embed()?
8133 
8134   // We aren't going to restore any items from the LayoutHistoryState,
8135   // but we don't want them to stay around in case the page is reloaded.
8136   SetLayoutHistoryState(nullptr);
8137 
8138   // This is the end of our Embed() replacement
8139 
8140   mSavingOldViewer = false;
8141   mEODForCurrentDocument = false;
8142 
8143   // Tell the event loop to favor plevents over user events, see comments
8144   // in CreateContentViewer.
8145   if (++gNumberOfDocumentsLoading == 1) {
8146     FavorPerformanceHint(true);
8147   }
8148 
8149   if (oldCv && newCv) {
8150     newCv->SetMinFontSize(minFontSize);
8151     newCv->SetTextZoom(textZoom);
8152     newCv->SetFullZoom(pageZoom);
8153     newCv->SetOverrideDPPX(overrideDPPX);
8154     newCv->SetAuthorStyleDisabled(styleDisabled);
8155   }
8156 
8157   if (document) {
8158     RefPtr<nsDocShell> parent = GetParentDocshell();
8159     if (parent) {
8160       nsCOMPtr<nsIDocument> d = parent->GetDocument();
8161       if (d) {
8162         if (d->EventHandlingSuppressed()) {
8163           document->SuppressEventHandling(d->EventHandlingSuppressed());
8164         }
8165       }
8166     }
8167 
8168     // Use the uri from the mLSHE we had when we entered this function
8169     // (which need not match the document's URI if anchors are involved),
8170     // since that's the history entry we're loading.  Note that if we use
8171     // origLSHE we don't have to worry about whether the entry in question
8172     // is still mLSHE or whether it's now mOSHE.
8173     nsCOMPtr<nsIURI> uri;
8174     origLSHE->GetURI(getter_AddRefs(uri));
8175     SetCurrentURI(uri, document->GetChannel(), true, 0);
8176   }
8177 
8178   // This is the end of our CreateContentViewer() replacement.
8179   // Now we simulate a load.  First, we restore the state of the javascript
8180   // window object.
8181   nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
8182   NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
8183 
8184   // Now, dispatch a title change event which would happen as the
8185   // <head> is parsed.
8186   document->NotifyPossibleTitleChange(false);
8187 
8188   // Now we simulate appending child docshells for subframes.
8189   for (i = 0; i < childShells.Count(); ++i) {
8190     nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
8191     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
8192 
8193     // Make sure to not clobber the state of the child.  Since AddChild
8194     // always clobbers it, save it off first.
8195     bool allowPlugins;
8196     childShell->GetAllowPlugins(&allowPlugins);
8197 
8198     bool allowJavascript;
8199     childShell->GetAllowJavascript(&allowJavascript);
8200 
8201     bool allowRedirects;
8202     childShell->GetAllowMetaRedirects(&allowRedirects);
8203 
8204     bool allowSubframes;
8205     childShell->GetAllowSubframes(&allowSubframes);
8206 
8207     bool allowImages;
8208     childShell->GetAllowImages(&allowImages);
8209 
8210     bool allowMedia = childShell->GetAllowMedia();
8211 
8212     bool allowDNSPrefetch;
8213     childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
8214 
8215     bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
8216     bool allowContentRetargetingOnChildren =
8217         childShell->GetAllowContentRetargetingOnChildren();
8218 
8219     uint32_t defaultLoadFlags;
8220     childShell->GetDefaultLoadFlags(&defaultLoadFlags);
8221 
8222     // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
8223     // the child inherits our state. Among other things, this means that the
8224     // child inherits our mIsActive mPrivateBrowsingId, which is what we want.
8225     AddChild(childItem);
8226 
8227     childShell->SetAllowPlugins(allowPlugins);
8228     childShell->SetAllowJavascript(allowJavascript);
8229     childShell->SetAllowMetaRedirects(allowRedirects);
8230     childShell->SetAllowSubframes(allowSubframes);
8231     childShell->SetAllowImages(allowImages);
8232     childShell->SetAllowMedia(allowMedia);
8233     childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
8234     childShell->SetAllowContentRetargeting(allowContentRetargeting);
8235     childShell->SetAllowContentRetargetingOnChildren(
8236         allowContentRetargetingOnChildren);
8237     childShell->SetDefaultLoadFlags(defaultLoadFlags);
8238 
8239     rv = childShell->BeginRestore(nullptr, false);
8240     NS_ENSURE_SUCCESS(rv, rv);
8241   }
8242 
8243   // Make sure to restore the window state after adding the child shells back
8244   // to the tree.  This is necessary for Thaw() and Resume() to propagate
8245   // properly.
8246   rv = privWin->RestoreWindowState(windowState);
8247   NS_ENSURE_SUCCESS(rv, rv);
8248 
8249   nsCOMPtr<nsIPresShell> shell = GetPresShell();
8250 
8251   // We may be displayed on a different monitor (or in a different
8252   // HiDPI mode) than when we got into the history list.  So we need
8253   // to check if this has happened. See bug 838239.
8254 
8255   // Because the prescontext normally handles resolution changes via
8256   // a runnable (see nsPresContext::UIResolutionChanged), its device
8257   // context won't be -immediately- updated as a result of calling
8258   // shell->BackingScaleFactorChanged().
8259 
8260   // But we depend on that device context when adjusting the view size
8261   // via mContentViewer->SetBounds(newBounds) below. So we need to
8262   // explicitly tell it to check for changed resolution here.
8263   if (shell && shell->GetPresContext()->DeviceContext()->CheckDPIChange()) {
8264     shell->BackingScaleFactorChanged();
8265   }
8266 
8267   nsViewManager* newVM = shell ? shell->GetViewManager() : nullptr;
8268   nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
8269 
8270   // Insert the new root view at the correct location in the view tree.
8271   if (container) {
8272     nsSubDocumentFrame* subDocFrame =
8273         do_QueryFrame(container->GetPrimaryFrame());
8274     rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
8275   } else {
8276     rootViewParent = nullptr;
8277   }
8278   if (sibling && sibling->GetShell() && sibling->GetShell()->GetViewManager()) {
8279     rootViewSibling = sibling->GetShell()->GetViewManager()->GetRootView();
8280   } else {
8281     rootViewSibling = nullptr;
8282   }
8283   if (rootViewParent && newRootView &&
8284       newRootView->GetParent() != rootViewParent) {
8285     nsViewManager* parentVM = rootViewParent->GetViewManager();
8286     if (parentVM) {
8287       // InsertChild(parent, child, sib, true) inserts the child after
8288       // sib in content order, which is before sib in view order. BUT
8289       // when sib is null it inserts at the end of the the document
8290       // order, i.e., first in view order.  But when oldRootSibling is
8291       // null, the old root as at the end of the view list --- last in
8292       // content order --- and we want to call InsertChild(parent, child,
8293       // nullptr, false) in that case.
8294       parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
8295                             rootViewSibling ? true : false);
8296 
8297       NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
8298                    "error in InsertChild");
8299     }
8300   }
8301 
8302   nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
8303 
8304   // If parent is suspended, increase suspension count.
8305   // This can't be done as early as event suppression since this
8306   // depends on docshell tree.
8307   privWinInner->SyncStateFromParentWindow();
8308 
8309   // Now that all of the child docshells have been put into place, we can
8310   // restart the timers for the window and all of the child frames.
8311   privWinInner->Resume();
8312 
8313   // Restore the refresh URI list.  The refresh timers will be restarted
8314   // when EndPageLoad() is called.
8315   mRefreshURIList = refreshURIList;
8316 
8317   // Meta-refresh timers have been restarted for this shell, but not
8318   // for our children.  Walk the child shells and restart their timers.
8319   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
8320   while (iter.HasMore()) {
8321     nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
8322     if (child) {
8323       child->ResumeRefreshURIs();
8324     }
8325   }
8326 
8327   // Make sure this presentation is the same size as the previous
8328   // presentation.  If this is not the same size we showed it at last time,
8329   // then we need to resize the widget.
8330 
8331   // XXXbryner   This interacts poorly with Firefox's infobar.  If the old
8332   // presentation had the infobar visible, then we will resize the new
8333   // presentation to that smaller size.  However, firing the locationchanged
8334   // event will hide the infobar, which will immediately resize the window
8335   // back to the larger size.  A future optimization might be to restore
8336   // the presentation at the "wrong" size, then fire the locationchanged
8337   // event and check whether the docshell's new size is the same as the
8338   // cached viewer size (skipping the resize if they are equal).
8339 
8340   if (newRootView) {
8341     if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
8342 #ifdef DEBUG_PAGE_CACHE
8343       printf("resize widget(%d, %d, %d, %d)\n", newBounds.x, newBounds.y,
8344              newBounds.width, newBounds.height);
8345 #endif
8346       mContentViewer->SetBounds(newBounds);
8347     } else {
8348       nsIScrollableFrame* rootScrollFrame =
8349           shell->GetRootScrollFrameAsScrollable();
8350       if (rootScrollFrame) {
8351         rootScrollFrame->PostScrolledAreaEventForCurrentArea();
8352       }
8353     }
8354   }
8355 
8356   // The FinishRestore call below can kill these, null them out so we don't
8357   // have invalid pointer lying around.
8358   newRootView = rootViewSibling = rootViewParent = nullptr;
8359   newVM = nullptr;
8360 
8361   // Simulate the completion of the load.
8362   nsDocShell::FinishRestore();
8363 
8364   // Restart plugins, and paint the content.
8365   if (shell) {
8366     shell->Thaw();
8367   }
8368 
8369   return privWin->FireDelayedDOMEvents();
8370 }
8371 
CreateContentViewer(const nsACString & aContentType,nsIRequest * aRequest,nsIStreamListener ** aContentHandler)8372 nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
8373                                          nsIRequest* aRequest,
8374                                          nsIStreamListener** aContentHandler) {
8375   *aContentHandler = nullptr;
8376 
8377   if (!mTreeOwner || mIsBeingDestroyed) {
8378     // If we don't have a tree owner, then we're in the process of being
8379     // destroyed. Rather than continue trying to load something, just give up.
8380     return NS_ERROR_DOCSHELL_DYING;
8381   }
8382 
8383   // Can we check the content type of the current content viewer
8384   // and reuse it without destroying it and re-creating it?
8385 
8386   NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
8387 
8388   // Instantiate the content viewer object
8389   nsCOMPtr<nsIContentViewer> viewer;
8390   nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
8391                                     aContentHandler, getter_AddRefs(viewer));
8392 
8393   if (NS_FAILED(rv)) {
8394     return rv;
8395   }
8396 
8397   // Notify the current document that it is about to be unloaded!!
8398   //
8399   // It is important to fire the unload() notification *before* any state
8400   // is changed within the DocShell - otherwise, javascript will get the
8401   // wrong information :-(
8402   //
8403 
8404   if (mSavingOldViewer) {
8405     // We determined that it was safe to cache the document presentation
8406     // at the time we initiated the new load.  We need to check whether
8407     // it's still safe to do so, since there may have been DOM mutations
8408     // or new requests initiated.
8409     nsCOMPtr<nsIDocument> doc = viewer->GetDocument();
8410     mSavingOldViewer = CanSavePresentation(mLoadType, aRequest, doc);
8411   }
8412 
8413   NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
8414 
8415   nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8416   if (aOpenedChannel) {
8417     aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
8418   }
8419   FirePageHideNotification(!mSavingOldViewer);
8420   if (mIsBeingDestroyed) {
8421     // Force to stop the newly created orphaned viewer.
8422     viewer->Stop();
8423     return NS_ERROR_DOCSHELL_DYING;
8424   }
8425   mLoadingURI = nullptr;
8426 
8427   // Set mFiredUnloadEvent = false so that the unload handler for the
8428   // *new* document will fire.
8429   mFiredUnloadEvent = false;
8430 
8431   // we've created a new document so go ahead and call
8432   // OnLoadingSite(), but don't fire OnLocationChange()
8433   // notifications before we've called Embed(). See bug 284993.
8434   mURIResultedInDocument = true;
8435   bool errorOnLocationChangeNeeded = false;
8436   nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
8437   nsCOMPtr<nsIURI> failedURI;
8438 
8439   if (mLoadType == LOAD_ERROR_PAGE) {
8440     // We need to set the SH entry and our current URI here and not
8441     // at the moment we load the page. We want the same behavior
8442     // of Stop() as for a normal page load. See bug 514232 for details.
8443 
8444     // Revert mLoadType to load type to state the page load failed,
8445     // following function calls need it.
8446     mLoadType = mFailedLoadType;
8447 
8448     nsIDocument* doc = viewer->GetDocument();
8449     if (doc) {
8450       doc->SetFailedChannel(failedChannel);
8451     }
8452 
8453     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
8454     if (failedChannel) {
8455       // Make sure we have a URI to set currentURI.
8456       NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
8457     } else {
8458       // if there is no failed channel we have to explicitly provide
8459       // a triggeringPrincipal for the history entry.
8460       triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
8461     }
8462 
8463     if (!failedURI) {
8464       failedURI = mFailedURI;
8465     }
8466     if (!failedURI) {
8467       // We need a URI object to store a session history entry, so make up a URI
8468       NS_NewURI(getter_AddRefs(failedURI), "about:blank");
8469     }
8470 
8471     // When we don't have failedURI, something wrong will happen. See
8472     // bug 291876.
8473     MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
8474 
8475     mFailedChannel = nullptr;
8476     mFailedURI = nullptr;
8477 
8478     // Create an shistory entry for the old load.
8479     if (failedURI) {
8480       errorOnLocationChangeNeeded =
8481           OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
8482                    mLoadType, false, false, false);
8483     }
8484 
8485     // Be sure to have a correct mLSHE, it may have been cleared by
8486     // EndPageLoad. See bug 302115.
8487     if (mSessionHistory && !mLSHE) {
8488       int32_t idx;
8489       mSessionHistory->GetRequestedIndex(&idx);
8490       if (idx == -1) {
8491         mSessionHistory->GetIndex(&idx);
8492       }
8493       mSessionHistory->GetEntryAtIndex(idx, false, getter_AddRefs(mLSHE));
8494     }
8495 
8496     mLoadType = LOAD_ERROR_PAGE;
8497   }
8498 
8499   bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
8500 
8501   // let's try resetting the load group if we need to...
8502   nsCOMPtr<nsILoadGroup> currentLoadGroup;
8503   NS_ENSURE_SUCCESS(
8504       aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
8505       NS_ERROR_FAILURE);
8506 
8507   if (currentLoadGroup != mLoadGroup) {
8508     nsLoadFlags loadFlags = 0;
8509 
8510     // Cancel any URIs that are currently loading...
8511     // XXX: Need to do this eventually      Stop();
8512     //
8513     // Retarget the document to this loadgroup...
8514     //
8515     /* First attach the channel to the right loadgroup
8516      * and then remove from the old loadgroup. This
8517      * puts the notifications in the right order and
8518      * we don't null-out mLSHE in OnStateChange() for
8519      * all redirected urls
8520      */
8521     aOpenedChannel->SetLoadGroup(mLoadGroup);
8522 
8523     // Mark the channel as being a document URI...
8524     aOpenedChannel->GetLoadFlags(&loadFlags);
8525     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
8526     if (SandboxFlagsImplyCookies(mSandboxFlags)) {
8527       loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
8528     }
8529 
8530     aOpenedChannel->SetLoadFlags(loadFlags);
8531 
8532     mLoadGroup->AddRequest(aRequest, nullptr);
8533     if (currentLoadGroup) {
8534       currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
8535     }
8536 
8537     // Update the notification callbacks, so that progress and
8538     // status information are sent to the right docshell...
8539     aOpenedChannel->SetNotificationCallbacks(this);
8540   }
8541 
8542   NS_ENSURE_SUCCESS(Embed(viewer, "", nullptr), NS_ERROR_FAILURE);
8543 
8544   mSavedRefreshURIList = nullptr;
8545   mSavingOldViewer = false;
8546   mEODForCurrentDocument = false;
8547 
8548   // if this document is part of a multipart document,
8549   // the ID can be used to distinguish it from the other parts.
8550   nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
8551   if (multiPartChannel) {
8552     nsCOMPtr<nsIPresShell> shell = GetPresShell();
8553     if (NS_SUCCEEDED(rv) && shell) {
8554       nsIDocument* doc = shell->GetDocument();
8555       if (doc) {
8556         uint32_t partID;
8557         multiPartChannel->GetPartID(&partID);
8558         doc->SetPartID(partID);
8559       }
8560     }
8561   }
8562 
8563   // Give hint to native plevent dispatch mechanism. If a document
8564   // is loading the native plevent dispatch mechanism should favor
8565   // performance over normal native event dispatch priorities.
8566   if (++gNumberOfDocumentsLoading == 1) {
8567     // Hint to favor performance for the plevent notification mechanism.
8568     // We want the pages to load as fast as possible even if its means
8569     // native messages might be starved.
8570     FavorPerformanceHint(true);
8571   }
8572 
8573   if (errorOnLocationChangeNeeded) {
8574     FireOnLocationChange(this, failedChannel, failedURI,
8575                          LOCATION_CHANGE_ERROR_PAGE);
8576   } else if (onLocationChangeNeeded) {
8577     FireOnLocationChange(this, aRequest, mCurrentURI, 0);
8578   }
8579 
8580   return NS_OK;
8581 }
8582 
NewContentViewerObj(const nsACString & aContentType,nsIRequest * aRequest,nsILoadGroup * aLoadGroup,nsIStreamListener ** aContentHandler,nsIContentViewer ** aViewer)8583 nsresult nsDocShell::NewContentViewerObj(const nsACString& aContentType,
8584                                          nsIRequest* aRequest,
8585                                          nsILoadGroup* aLoadGroup,
8586                                          nsIStreamListener** aContentHandler,
8587                                          nsIContentViewer** aViewer) {
8588   nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8589 
8590   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8591       nsContentUtils::FindInternalContentViewer(aContentType);
8592   if (!docLoaderFactory) {
8593     return NS_ERROR_FAILURE;
8594   }
8595 
8596   // Now create an instance of the content viewer nsLayoutDLF makes the
8597   // determination if it should be a "view-source" instead of "view"
8598   nsresult rv = docLoaderFactory->CreateInstance(
8599       "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
8600       aContentHandler, aViewer);
8601   NS_ENSURE_SUCCESS(rv, rv);
8602 
8603   (*aViewer)->SetContainer(this);
8604   return NS_OK;
8605 }
8606 
SetupNewViewer(nsIContentViewer * aNewViewer)8607 nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer) {
8608   MOZ_ASSERT(!mIsBeingDestroyed);
8609 
8610   //
8611   // Copy content viewer state from previous or parent content viewer.
8612   //
8613   // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8614   //
8615   // Do NOT to maintain a reference to the old content viewer outside
8616   // of this "copying" block, or it will not be destroyed until the end of
8617   // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8618   //
8619   // In this block of code, if we get an error result, we return it
8620   // but if we get a null pointer, that's perfectly legal for parent
8621   // and parentContentViewer.
8622   //
8623 
8624   int32_t x = 0;
8625   int32_t y = 0;
8626   int32_t cx = 0;
8627   int32_t cy = 0;
8628 
8629   // This will get the size from the current content viewer or from the
8630   // Init settings
8631   DoGetPositionAndSize(&x, &y, &cx, &cy);
8632 
8633   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8634   NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
8635                     NS_ERROR_FAILURE);
8636   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8637 
8638   const Encoding* forceCharset = nullptr;
8639   const Encoding* hintCharset = nullptr;
8640   int32_t hintCharsetSource;
8641   int32_t minFontSize;
8642   float textZoom;
8643   float pageZoom;
8644   float overrideDPPX;
8645   bool styleDisabled;
8646   // |newMUDV| also serves as a flag to set the data from the above vars
8647   nsCOMPtr<nsIContentViewer> newCv;
8648 
8649   if (mContentViewer || parent) {
8650     nsCOMPtr<nsIContentViewer> oldCv;
8651     if (mContentViewer) {
8652       // Get any interesting state from old content viewer
8653       // XXX: it would be far better to just reuse the document viewer ,
8654       //      since we know we're just displaying the same document as before
8655       oldCv = mContentViewer;
8656 
8657       // Tell the old content viewer to hibernate in session history when
8658       // it is destroyed.
8659 
8660       if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8661         if (mOSHE) {
8662           mOSHE->SyncPresentationState();
8663         }
8664         mSavingOldViewer = false;
8665       }
8666     } else {
8667       // No old content viewer, so get state from parent's content viewer
8668       parent->GetContentViewer(getter_AddRefs(oldCv));
8669     }
8670 
8671     if (oldCv) {
8672       newCv = aNewViewer;
8673       if (newCv) {
8674         forceCharset = oldCv->GetForceCharset();
8675         hintCharset = oldCv->GetHintCharset();
8676         NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSetSource(&hintCharsetSource),
8677                           NS_ERROR_FAILURE);
8678         NS_ENSURE_SUCCESS(oldCv->GetMinFontSize(&minFontSize),
8679                           NS_ERROR_FAILURE);
8680         NS_ENSURE_SUCCESS(oldCv->GetTextZoom(&textZoom), NS_ERROR_FAILURE);
8681         NS_ENSURE_SUCCESS(oldCv->GetFullZoom(&pageZoom), NS_ERROR_FAILURE);
8682         NS_ENSURE_SUCCESS(oldCv->GetOverrideDPPX(&overrideDPPX),
8683                           NS_ERROR_FAILURE);
8684         NS_ENSURE_SUCCESS(oldCv->GetAuthorStyleDisabled(&styleDisabled),
8685                           NS_ERROR_FAILURE);
8686       }
8687     }
8688   }
8689 
8690   nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8691   bool isActive = false;
8692   // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8693   nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
8694   if (contentViewer) {
8695     // Stop any activity that may be happening in the old document before
8696     // releasing it...
8697     contentViewer->Stop();
8698 
8699     // Try to extract the canvas background color from the old
8700     // presentation shell, so we can use it for the next document.
8701     nsCOMPtr<nsIPresShell> shell;
8702     contentViewer->GetPresShell(getter_AddRefs(shell));
8703 
8704     if (shell) {
8705       bgcolor = shell->GetCanvasBackground();
8706       isActive = shell->IsActive();
8707     }
8708 
8709     contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8710     aNewViewer->SetPreviousViewer(contentViewer);
8711   }
8712   if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8713     // We don't plan to save a viewer in mOSHE; tell it to drop
8714     // any other state it's holding.
8715     mOSHE->SyncPresentationState();
8716   }
8717 
8718   mContentViewer = nullptr;
8719 
8720   // Now that we're about to switch documents, forget all of our children.
8721   // Note that we cached them as needed up in CaptureState above.
8722   DestroyChildren();
8723 
8724   mContentViewer = aNewViewer;
8725 
8726   nsCOMPtr<nsIWidget> widget;
8727   NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8728 
8729   nsIntRect bounds(x, y, cx, cy);
8730 
8731   mContentViewer->SetNavigationTiming(mTiming);
8732 
8733   if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
8734     mContentViewer = nullptr;
8735     NS_WARNING("ContentViewer Initialization failed");
8736     return NS_ERROR_FAILURE;
8737   }
8738 
8739   // If we have old state to copy, set the old state onto the new content
8740   // viewer
8741   if (newCv) {
8742     newCv->SetForceCharset(forceCharset);
8743     newCv->SetHintCharset(hintCharset);
8744     NS_ENSURE_SUCCESS(newCv->SetHintCharacterSetSource(hintCharsetSource),
8745                       NS_ERROR_FAILURE);
8746     NS_ENSURE_SUCCESS(newCv->SetMinFontSize(minFontSize), NS_ERROR_FAILURE);
8747     NS_ENSURE_SUCCESS(newCv->SetTextZoom(textZoom), NS_ERROR_FAILURE);
8748     NS_ENSURE_SUCCESS(newCv->SetFullZoom(pageZoom), NS_ERROR_FAILURE);
8749     NS_ENSURE_SUCCESS(newCv->SetOverrideDPPX(overrideDPPX), NS_ERROR_FAILURE);
8750     NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
8751                       NS_ERROR_FAILURE);
8752   }
8753 
8754   // Stuff the bgcolor from the old pres shell into the new
8755   // pres shell. This improves page load continuity.
8756   nsCOMPtr<nsIPresShell> shell;
8757   mContentViewer->GetPresShell(getter_AddRefs(shell));
8758 
8759   if (shell) {
8760     shell->SetCanvasBackground(bgcolor);
8761     if (isActive) {
8762       shell->SetIsActive(isActive);
8763     }
8764   }
8765 
8766   // XXX: It looks like the LayoutState gets restored again in Embed()
8767   //      right after the call to SetupNewViewer(...)
8768 
8769   // We don't show the mContentViewer yet, since we want to draw the old page
8770   // until we have enough of the new page to show.  Just return with the new
8771   // viewer still set to hidden.
8772 
8773   return NS_OK;
8774 }
8775 
SetDocCurrentStateObj(nsISHEntry * aShEntry)8776 nsresult nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry) {
8777   NS_ENSURE_STATE(mContentViewer);
8778   nsCOMPtr<nsIDocument> document = GetDocument();
8779   NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8780 
8781   nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8782   if (aShEntry) {
8783     nsresult rv = aShEntry->GetStateData(getter_AddRefs(scContainer));
8784     NS_ENSURE_SUCCESS(rv, rv);
8785 
8786     // If aShEntry is null, just set the document's state object to null.
8787   }
8788 
8789   // It's OK for scContainer too be null here; that just means there's no
8790   // state data associated with this history entry.
8791   document->SetStateObject(scContainer);
8792 
8793   return NS_OK;
8794 }
8795 
CheckLoadingPermissions()8796 nsresult nsDocShell::CheckLoadingPermissions() {
8797   // This method checks whether the caller may load content into
8798   // this docshell. Even though we've done our best to hide windows
8799   // from code that doesn't have the right to access them, it's
8800   // still possible for an evil site to open a window and access
8801   // frames in the new window through window.frames[] (which is
8802   // allAccess for historic reasons), so we still need to do this
8803   // check on load.
8804   nsresult rv = NS_OK;
8805 
8806   if (!gValidateOrigin || !IsFrame()) {
8807     // Origin validation was turned off, or we're not a frame.
8808     // Permit all loads.
8809 
8810     return rv;
8811   }
8812 
8813   // Note - The check for a current JSContext here isn't necessarily sensical.
8814   // It's just designed to preserve the old semantics during a mass-conversion
8815   // patch.
8816   if (!nsContentUtils::GetCurrentJSContext()) {
8817     return NS_OK;
8818   }
8819 
8820   // Check if the caller is from the same origin as this docshell,
8821   // or any of its ancestors.
8822   nsCOMPtr<nsIDocShellTreeItem> item(this);
8823   do {
8824     nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
8825     nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8826 
8827     nsIPrincipal* p;
8828     if (!sop || !(p = sop->GetPrincipal())) {
8829       return NS_ERROR_UNEXPECTED;
8830     }
8831 
8832     if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8833       // Same origin, permit load
8834       return NS_OK;
8835     }
8836 
8837     nsCOMPtr<nsIDocShellTreeItem> tmp;
8838     item->GetSameTypeParent(getter_AddRefs(tmp));
8839     item.swap(tmp);
8840   } while (item);
8841 
8842   return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8843 }
8844 
8845 //*****************************************************************************
8846 // nsDocShell: Site Loading
8847 //*****************************************************************************
8848 
CopyFavicon(nsIURI * aOldURI,nsIURI * aNewURI,nsIPrincipal * aLoadingPrincipal,bool aInPrivateBrowsing)8849 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8850                              nsIPrincipal* aLoadingPrincipal,
8851                              bool aInPrivateBrowsing) {
8852   if (XRE_IsContentProcess()) {
8853     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8854     if (contentChild) {
8855       mozilla::ipc::URIParams oldURI, newURI;
8856       SerializeURI(aOldURI, oldURI);
8857       SerializeURI(aNewURI, newURI);
8858       contentChild->SendCopyFavicon(oldURI, newURI,
8859                                     IPC::Principal(aLoadingPrincipal),
8860                                     aInPrivateBrowsing);
8861     }
8862     return;
8863   }
8864 
8865 #ifdef MOZ_PLACES
8866   nsCOMPtr<mozIAsyncFavicons> favSvc =
8867       do_GetService("@mozilla.org/browser/favicon-service;1");
8868   if (favSvc) {
8869     favSvc->CopyFavicons(aOldURI, aNewURI,
8870                          aInPrivateBrowsing
8871                              ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8872                              : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8873                          nullptr);
8874   }
8875 #endif
8876 }
8877 
8878 class InternalLoadEvent : public Runnable {
8879  public:
InternalLoadEvent(nsDocShell * aDocShell,nsIURI * aURI,nsIURI * aOriginalURI,Maybe<nsCOMPtr<nsIURI>> const & aResultPrincipalURI,bool aKeepResultPrincipalURIIfSet,bool aLoadReplace,bool aIsFromProcessingFrameAttributes,nsIURI * aReferrer,uint32_t aReferrerPolicy,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,uint32_t aFlags,const char * aTypeHint,nsIInputStream * aPostData,int64_t aPostDataLength,nsIInputStream * aHeadersData,uint32_t aLoadType,nsISHEntry * aSHEntry,bool aFirstParty,const nsAString & aSrcdoc,nsIDocShell * aSourceDocShell,nsIURI * aBaseURI)8880   InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI, nsIURI* aOriginalURI,
8881                     Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
8882                     bool aKeepResultPrincipalURIIfSet, bool aLoadReplace,
8883                     bool aIsFromProcessingFrameAttributes, nsIURI* aReferrer,
8884                     uint32_t aReferrerPolicy,
8885                     nsIPrincipal* aTriggeringPrincipal,
8886                     nsIPrincipal* aPrincipalToInherit, uint32_t aFlags,
8887                     const char* aTypeHint, nsIInputStream* aPostData,
8888                     int64_t aPostDataLength, nsIInputStream* aHeadersData,
8889                     uint32_t aLoadType, nsISHEntry* aSHEntry, bool aFirstParty,
8890                     const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell,
8891                     nsIURI* aBaseURI)
8892       : mozilla::Runnable("InternalLoadEvent"),
8893         mSrcdoc(aSrcdoc),
8894         mDocShell(aDocShell),
8895         mURI(aURI),
8896         mOriginalURI(aOriginalURI),
8897         mResultPrincipalURI(aResultPrincipalURI),
8898         mKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet),
8899         mLoadReplace(aLoadReplace),
8900         mIsFromProcessingFrameAttributes(aIsFromProcessingFrameAttributes),
8901         mReferrer(aReferrer),
8902         mReferrerPolicy(aReferrerPolicy),
8903         mTriggeringPrincipal(aTriggeringPrincipal),
8904         mPrincipalToInherit(aPrincipalToInherit),
8905         mPostData(aPostData),
8906         mPostDataLength(aPostDataLength),
8907         mHeadersData(aHeadersData),
8908         mSHEntry(aSHEntry),
8909         mFlags(aFlags),
8910         mLoadType(aLoadType),
8911         mFirstParty(aFirstParty),
8912         mSourceDocShell(aSourceDocShell),
8913         mBaseURI(aBaseURI) {
8914     // Make sure to keep null things null as needed
8915     if (aTypeHint) {
8916       mTypeHint = aTypeHint;
8917     } else {
8918       mTypeHint.SetIsVoid(true);
8919     }
8920   }
8921 
8922   NS_IMETHOD
Run()8923   Run() override {
8924     return mDocShell->InternalLoad(
8925         mURI, mOriginalURI, mResultPrincipalURI, mKeepResultPrincipalURIIfSet,
8926         mLoadReplace, mIsFromProcessingFrameAttributes, mReferrer,
8927         mReferrerPolicy, mTriggeringPrincipal, mPrincipalToInherit, mFlags,
8928         EmptyString(), mTypeHint.IsVoid() ? nullptr : mTypeHint.get(),
8929         VoidString(), mPostData, mPostDataLength, mHeadersData, mLoadType,
8930         mSHEntry, mFirstParty, mSrcdoc, mSourceDocShell, mBaseURI, nullptr,
8931         nullptr);
8932   }
8933 
8934  private:
8935   nsCString mTypeHint;
8936   nsString mSrcdoc;
8937 
8938   RefPtr<nsDocShell> mDocShell;
8939   nsCOMPtr<nsIURI> mURI;
8940   nsCOMPtr<nsIURI> mOriginalURI;
8941   Maybe<nsCOMPtr<nsIURI>> mResultPrincipalURI;
8942   bool mKeepResultPrincipalURIIfSet;
8943   bool mLoadReplace;
8944   bool mIsFromProcessingFrameAttributes;
8945   nsCOMPtr<nsIURI> mReferrer;
8946   uint32_t mReferrerPolicy;
8947   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
8948   nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
8949   nsCOMPtr<nsIInputStream> mPostData;
8950   int64_t mPostDataLength;
8951   nsCOMPtr<nsIInputStream> mHeadersData;
8952   nsCOMPtr<nsISHEntry> mSHEntry;
8953   uint32_t mFlags;
8954   uint32_t mLoadType;
8955   bool mFirstParty;
8956   nsCOMPtr<nsIDocShell> mSourceDocShell;
8957   nsCOMPtr<nsIURI> mBaseURI;
8958 };
8959 
8960 /**
8961  * Returns true if we started an asynchronous load (i.e., from the network), but
8962  * the document we're loading there hasn't yet become this docshell's active
8963  * document.
8964  *
8965  * When JustStartedNetworkLoad is true, you should be careful about modifying
8966  * mLoadType and mLSHE.  These are both set when the asynchronous load first
8967  * starts, and the load expects that, when it eventually runs InternalLoad,
8968  * mLoadType and mLSHE will have their original values.
8969  */
JustStartedNetworkLoad()8970 bool nsDocShell::JustStartedNetworkLoad() {
8971   return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8972 }
8973 
CreatePrincipalFromReferrer(nsIURI * aReferrer,nsIPrincipal ** aResult)8974 nsresult nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
8975                                                  nsIPrincipal** aResult) {
8976   nsCOMPtr<nsIPrincipal> prin =
8977       BasePrincipal::CreateCodebasePrincipal(aReferrer, mOriginAttributes);
8978   prin.forget(aResult);
8979 
8980   return *aResult ? NS_OK : NS_ERROR_FAILURE;
8981 }
8982 
8983 NS_IMETHODIMP
InternalLoad(nsIURI * aURI,nsIURI * aOriginalURI,Maybe<nsCOMPtr<nsIURI>> const & aResultPrincipalURI,bool aKeepResultPrincipalURIIfSet,bool aLoadReplace,bool aIsFromProcessingFrameAttributes,nsIURI * aReferrer,uint32_t aReferrerPolicy,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,uint32_t aFlags,const nsAString & aWindowTarget,const char * aTypeHint,const nsAString & aFileName,nsIInputStream * aPostData,int64_t aPostDataLength,nsIInputStream * aHeadersData,uint32_t aLoadType,nsISHEntry * aSHEntry,bool aFirstParty,const nsAString & aSrcdoc,nsIDocShell * aSourceDocShell,nsIURI * aBaseURI,nsIDocShell ** aDocShell,nsIRequest ** aRequest)8984 nsDocShell::InternalLoad(nsIURI* aURI, nsIURI* aOriginalURI,
8985                          Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
8986                          bool aKeepResultPrincipalURIIfSet, bool aLoadReplace,
8987                          bool aIsFromProcessingFrameAttributes,
8988                          nsIURI* aReferrer, uint32_t aReferrerPolicy,
8989                          nsIPrincipal* aTriggeringPrincipal,
8990                          nsIPrincipal* aPrincipalToInherit, uint32_t aFlags,
8991                          const nsAString& aWindowTarget, const char* aTypeHint,
8992                          const nsAString& aFileName, nsIInputStream* aPostData,
8993                          int64_t aPostDataLength, nsIInputStream* aHeadersData,
8994                          uint32_t aLoadType, nsISHEntry* aSHEntry,
8995                          bool aFirstParty, const nsAString& aSrcdoc,
8996                          nsIDocShell* aSourceDocShell, nsIURI* aBaseURI,
8997                          nsIDocShell** aDocShell, nsIRequest** aRequest) {
8998   MOZ_ASSERT(aTriggeringPrincipal, "need a valid TriggeringPrincipal");
8999 
9000   nsresult rv = NS_OK;
9001   mOriginalUriString.Truncate();
9002 
9003   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9004           ("DOCSHELL %p InternalLoad %s\n", this,
9005            aURI ? aURI->GetSpecOrDefault().get() : ""));
9006   // Initialize aDocShell/aRequest
9007   if (aDocShell) {
9008     *aDocShell = nullptr;
9009   }
9010   if (aRequest) {
9011     *aRequest = nullptr;
9012   }
9013 
9014   if (!aURI) {
9015     return NS_ERROR_NULL_POINTER;
9016   }
9017 
9018   NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
9019 
9020   NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
9021 
9022   rv = EnsureScriptEnvironment();
9023   if (NS_FAILED(rv)) {
9024     return rv;
9025   }
9026 
9027   // wyciwyg urls can only be loaded through history. Any normal load of
9028   // wyciwyg through docshell is  illegal. Disallow such loads.
9029   if (aLoadType & LOAD_CMD_NORMAL) {
9030     bool isWyciwyg = false;
9031     rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
9032     if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv)) {
9033       return NS_ERROR_FAILURE;
9034     }
9035   }
9036 
9037   bool isJavaScript = false;
9038   if (NS_FAILED(aURI->SchemeIs("javascript", &isJavaScript))) {
9039     isJavaScript = false;
9040   }
9041 
9042   bool isTargetTopLevelDocShell = false;
9043   nsCOMPtr<nsIDocShell> targetDocShell;
9044   if (!aWindowTarget.IsEmpty()) {
9045     // Locate the target DocShell.
9046     nsCOMPtr<nsIDocShellTreeItem> targetItem;
9047     // Only _self, _parent, and _top are supported in noopener case.  But we
9048     // have to be careful to not apply that to the noreferrer case.  See bug
9049     // 1358469.
9050     bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
9051                             (aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
9052     if (allowNamedTarget || aWindowTarget.LowerCaseEqualsLiteral("_self") ||
9053         aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
9054         aWindowTarget.LowerCaseEqualsLiteral("_top")) {
9055       rv = FindItemWithName(aWindowTarget, nullptr, this, false,
9056                             getter_AddRefs(targetItem));
9057       NS_ENSURE_SUCCESS(rv, rv);
9058     }
9059 
9060     targetDocShell = do_QueryInterface(targetItem);
9061     if (targetDocShell) {
9062       // If the targetDocShell and the rootDocShell are the same, then the
9063       // targetDocShell is the top level document and hence we should
9064       // consider this TYPE_DOCUMENT
9065       //
9066       // For example:
9067       // 1. target="_top"
9068       // 2. target="_parent", where this docshell is in the 2nd level of
9069       //    docshell tree.
9070       nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
9071       targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
9072       NS_ASSERTION(sameTypeRoot,
9073                    "No document shell root tree item from targetDocShell!");
9074       nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
9075       NS_ASSERTION(rootShell,
9076                    "No root docshell from document shell root tree item.");
9077       isTargetTopLevelDocShell = targetDocShell == rootShell;
9078     } else {
9079       // If the targetDocShell doesn't exist, then this is a new docShell
9080       // and we should consider this a TYPE_DOCUMENT load
9081       //
9082       // For example, when target="_blank"
9083       isTargetTopLevelDocShell = true;
9084     }
9085   }
9086 
9087   // The contentType will be INTERNAL_(I)FRAME if:
9088   // 1. This docshell is for iframe.
9089   // 2. AND aWindowTarget is not a new window, nor a top-level window.
9090   //
9091   // This variable will be used when we call NS_CheckContentLoadPolicy, and
9092   // later when we call DoURILoad.
9093   uint32_t contentType;
9094   if (IsFrame() && !isTargetTopLevelDocShell) {
9095     nsCOMPtr<Element> requestingElement =
9096         mScriptGlobal->AsOuter()->GetFrameElementInternal();
9097     if (requestingElement) {
9098       contentType = requestingElement->IsHTMLElement(nsGkAtoms::iframe)
9099                         ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
9100                         : nsIContentPolicy::TYPE_INTERNAL_FRAME;
9101     } else {
9102       // If we have lost our frame element by now, just assume we're
9103       // an iframe since that's more common.
9104       contentType = nsIContentPolicy::TYPE_INTERNAL_IFRAME;
9105     }
9106   } else {
9107     contentType = nsIContentPolicy::TYPE_DOCUMENT;
9108     isTargetTopLevelDocShell = true;
9109   }
9110 
9111   // If there's no targetDocShell, that means we are about to create a new
9112   // window (or aWindowTarget is empty). Perform a content policy check before
9113   // creating the window. Please note for all other docshell loads
9114   // content policy checks are performed within the contentSecurityManager
9115   // when the channel is about to be openend.
9116   if (!targetDocShell && !aWindowTarget.IsEmpty()) {
9117     MOZ_ASSERT(contentType == nsIContentPolicy::TYPE_DOCUMENT,
9118                "opening a new window requires type to be TYPE_DOCUMENT");
9119 
9120     nsISupports* requestingContext = nullptr;
9121     if (XRE_IsContentProcess()) {
9122       // In e10s the child process doesn't have access to the element that
9123       // contains the browsing context (because that element is in the chrome
9124       // process). So we just pass mScriptGlobal.
9125       requestingContext = ToSupports(mScriptGlobal);
9126     } else {
9127       // This is for loading non-e10s tabs and toplevel windows of various
9128       // sorts.
9129       // For the toplevel window cases, requestingElement will be null.
9130       nsCOMPtr<Element> requestingElement =
9131           mScriptGlobal->AsOuter()->GetFrameElementInternal();
9132       requestingContext = requestingElement;
9133     }
9134 
9135     // Since Content Policy checks are performed within docShell as well as
9136     // the ContentSecurityManager we need a reliable way to let certain
9137     // nsIContentPolicy consumers ignore duplicate calls. Let's use the 'extra'
9138     // argument to pass a specific identifier.
9139     nsCOMPtr<nsISupportsString> extraStr =
9140         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
9141     NS_ENSURE_SUCCESS(rv, rv);
9142     NS_NAMED_LITERAL_STRING(msg, "conPolCheckFromDocShell");
9143     rv = extraStr->SetData(msg);
9144     NS_ENSURE_SUCCESS(rv, rv);
9145 
9146     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
9147     rv = NS_CheckContentLoadPolicy(contentType, aURI,
9148                                    // This is a top-level load, so the loading
9149                                    // principal is null.
9150                                    nullptr, aTriggeringPrincipal,
9151                                    requestingContext,
9152                                    EmptyCString(),  // mime guess
9153                                    extraStr,        // extra
9154                                    &shouldLoad);
9155 
9156     if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
9157       if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
9158         return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
9159       }
9160 
9161       return NS_ERROR_CONTENT_BLOCKED;
9162     }
9163   }
9164 
9165   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
9166   //
9167   // Get a principal from the current document if necessary.  Note that we only
9168   // do this for URIs that inherit a security context and local file URIs;
9169   // in particular we do NOT do this for about:blank.  This way, random
9170   // about:blank loads that have no principal (which basically means they were
9171   // done by someone from chrome manually messing with our nsIWebNavigation
9172   // or by C++ setting document.location) don't get a funky principal.  If
9173   // callers want something interesting to happen with the about:blank
9174   // principal in this case, they should pass aPrincipalToInherit in.
9175   //
9176   {
9177     bool inherits;
9178     // One more twist: Don't inherit the principal for external loads.
9179     if (aLoadType != LOAD_NORMAL_EXTERNAL && !principalToInherit &&
9180         (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
9181         NS_SUCCEEDED(
9182             nsContentUtils::URIInheritsSecurityContext(aURI, &inherits)) &&
9183         inherits) {
9184       principalToInherit = GetInheritedPrincipal(true);
9185     }
9186   }
9187 
9188   // Don't allow loads that would inherit our security context
9189   // if this document came from an unsafe channel.
9190   {
9191     bool willInherit;
9192     // This condition needs to match the one in
9193     // nsContentUtils::ChannelShouldInheritPrincipal.
9194     // Except we reverse the rv check to be safe in case
9195     // nsContentUtils::URIInheritsSecurityContext fails here and
9196     // succeeds there.
9197     rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
9198     if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
9199       nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
9200       do {
9201         nsCOMPtr<nsIDocShell> itemDocShell = do_QueryInterface(treeItem);
9202         bool isUnsafe;
9203         if (itemDocShell &&
9204             NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
9205             isUnsafe) {
9206           return NS_ERROR_DOM_SECURITY_ERR;
9207         }
9208 
9209         nsCOMPtr<nsIDocShellTreeItem> parent;
9210         treeItem->GetSameTypeParent(getter_AddRefs(parent));
9211         parent.swap(treeItem);
9212       } while (treeItem);
9213     }
9214   }
9215 
9216   nsIDocument* doc = mContentViewer ? mContentViewer->GetDocument() : nullptr;
9217 
9218   const bool isDocumentAuxSandboxed =
9219       doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
9220 
9221   if (aURI && mLoadURIDelegate &&
9222       (!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
9223     // Dispatch only load requests for the current or a new window to the
9224     // delegate, e.g., to allow for GeckoView apps to handle the load event
9225     // outside of Gecko.
9226     const int where = (aWindowTarget.IsEmpty() || targetDocShell)
9227                           ? nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
9228                           : nsIBrowserDOMWindow::OPEN_NEWWINDOW;
9229 
9230     if (where == nsIBrowserDOMWindow::OPEN_NEWWINDOW &&
9231         isDocumentAuxSandboxed) {
9232       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9233     }
9234 
9235     bool loadURIHandled = false;
9236     rv = mLoadURIDelegate->LoadURI(aURI, where, aFlags, aTriggeringPrincipal,
9237                                    &loadURIHandled);
9238     if (NS_SUCCEEDED(rv) && loadURIHandled) {
9239       // The request has been handled, nothing to do here.
9240       return NS_OK;
9241     }
9242   }
9243 
9244   //
9245   // Resolve the window target before going any further...
9246   // If the load has been targeted to another DocShell, then transfer the
9247   // load to it...
9248   //
9249   if (!aWindowTarget.IsEmpty()) {
9250     // We've already done our owner-inheriting.  Mask out that bit, so we
9251     // don't try inheriting an owner from the target window if we came up
9252     // with a null owner above.
9253     aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
9254 
9255     bool isNewWindow = false;
9256     if (!targetDocShell) {
9257       // If the docshell's document is sandboxed, only open a new window
9258       // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
9259       // (i.e. if allow-popups is specified)
9260       NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
9261       if (isDocumentAuxSandboxed) {
9262         return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9263       }
9264 
9265       nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
9266       NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
9267 
9268       nsCOMPtr<nsPIDOMWindowOuter> newWin;
9269       nsAutoCString spec;
9270       if (aURI) {
9271         aURI->GetSpec(spec);
9272       }
9273       // If we are a noopener load, we just hand the whole thing over to our
9274       // window.
9275       if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
9276         // Various asserts that we know to hold because NO_OPENER loads can only
9277         // happen for links.
9278         MOZ_ASSERT(!aLoadReplace);
9279         MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal);
9280         MOZ_ASSERT(aFlags == INTERNAL_LOAD_FLAGS_NO_OPENER ||
9281                    aFlags == (INTERNAL_LOAD_FLAGS_NO_OPENER |
9282                               INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
9283         MOZ_ASSERT(!aPostData);
9284         MOZ_ASSERT(!aHeadersData);
9285         // If OnLinkClickSync was invoked inside the onload handler, the load
9286         // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
9287         // LOAD_LINK.
9288         MOZ_ASSERT(aLoadType == LOAD_LINK || aLoadType == LOAD_NORMAL_REPLACE);
9289         MOZ_ASSERT(!aSHEntry);
9290         MOZ_ASSERT(aFirstParty);  // Windowwatcher will assume this.
9291 
9292         nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
9293         rv = CreateLoadInfo(getter_AddRefs(loadInfo));
9294         if (NS_FAILED(rv)) {
9295           return rv;
9296         }
9297 
9298         // Set up our loadinfo so it will do the load as much like we would have
9299         // as possible.
9300         loadInfo->SetReferrer(aReferrer);
9301         loadInfo->SetReferrerPolicy(aReferrerPolicy);
9302         loadInfo->SetSendReferrer(
9303             !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
9304         loadInfo->SetOriginalURI(aOriginalURI);
9305         SetMaybeResultPrincipalURI(loadInfo, aResultPrincipalURI);
9306         loadInfo->SetKeepResultPrincipalURIIfSet(aKeepResultPrincipalURIIfSet);
9307         loadInfo->SetLoadReplace(aLoadReplace);
9308         loadInfo->SetIsFromProcessingFrameAttributes(
9309             aIsFromProcessingFrameAttributes);
9310         loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
9311         loadInfo->SetInheritPrincipal(aFlags &
9312                                       INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
9313         // Explicit principal because we do not want any guesses as to what the
9314         // principal to inherit is: it should be aTriggeringPrincipal.
9315         loadInfo->SetPrincipalIsExplicit(true);
9316         loadInfo->SetLoadType(ConvertLoadTypeToDocShellInfoLoadType(LOAD_LINK));
9317         loadInfo->SetForceAllowDataURI(
9318             aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
9319 
9320         rv = win->Open(NS_ConvertUTF8toUTF16(spec),
9321                        aWindowTarget,  // window name
9322                        EmptyString(),  // Features
9323                        loadInfo,
9324                        true,  // aForceNoOpener
9325                        getter_AddRefs(newWin));
9326         MOZ_ASSERT(!newWin);
9327         return rv;
9328       }
9329 
9330       rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
9331                                aWindowTarget,  // window name
9332                                EmptyString(),  // Features
9333                                getter_AddRefs(newWin));
9334 
9335       // In some cases the Open call doesn't actually result in a new
9336       // window being opened.  We can detect these cases by examining the
9337       // document in |newWin|, if any.
9338       nsCOMPtr<nsPIDOMWindowOuter> piNewWin = do_QueryInterface(newWin);
9339       if (piNewWin) {
9340         nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
9341         if (!newDoc || newDoc->IsInitialDocument()) {
9342           isNewWindow = true;
9343           aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
9344         }
9345       }
9346 
9347       nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
9348       targetDocShell = do_QueryInterface(webNav);
9349     }
9350 
9351     //
9352     // Transfer the load to the target DocShell...  Pass nullptr as the
9353     // window target name from to prevent recursive retargeting!
9354     //
9355     if (NS_SUCCEEDED(rv) && targetDocShell) {
9356       rv = targetDocShell->InternalLoad(
9357           aURI, aOriginalURI, aResultPrincipalURI, aKeepResultPrincipalURIIfSet,
9358           aLoadReplace, aIsFromProcessingFrameAttributes, aReferrer,
9359           aReferrerPolicy, aTriggeringPrincipal, principalToInherit, aFlags,
9360           EmptyString(),  // No window target
9361           aTypeHint,
9362           VoidString(),  // No forced download
9363           aPostData, aPostDataLength, aHeadersData, aLoadType, aSHEntry,
9364           aFirstParty, aSrcdoc, aSourceDocShell, aBaseURI, aDocShell, aRequest);
9365       if (rv == NS_ERROR_NO_CONTENT) {
9366         // XXXbz except we never reach this code!
9367         if (isNewWindow) {
9368           //
9369           // At this point, a new window has been created, but the
9370           // URI did not have any data associated with it...
9371           //
9372           // So, the best we can do, is to tear down the new window
9373           // that was just created!
9374           //
9375           if (nsCOMPtr<nsPIDOMWindowOuter> domWin =
9376                   targetDocShell->GetWindow()) {
9377             domWin->Close();
9378           }
9379         }
9380         //
9381         // NS_ERROR_NO_CONTENT should not be returned to the
9382         // caller... This is an internal error code indicating that
9383         // the URI had no data associated with it - probably a
9384         // helper-app style protocol (ie. mailto://)
9385         //
9386         rv = NS_OK;
9387       } else if (isNewWindow) {
9388         // XXX: Once new windows are created hidden, the new
9389         //      window will need to be made visible...  For now,
9390         //      do nothing.
9391       }
9392 
9393       if (NS_SUCCEEDED(rv)) {
9394         // Switch to target tab if we're currently focused window.
9395         // Take loadDivertedInBackground into account so the behavior would be
9396         // the same as how the tab first opened.
9397         bool isTargetActive = false;
9398         targetDocShell->GetIsActive(&isTargetActive);
9399         nsCOMPtr<nsPIDOMWindowOuter> domWin = targetDocShell->GetWindow();
9400         if (mIsActive && !isTargetActive && domWin &&
9401             !Preferences::GetBool("browser.tabs.loadDivertedInBackground",
9402                                   false)) {
9403           if (NS_FAILED(nsContentUtils::DispatchFocusChromeEvent(domWin))) {
9404             return NS_ERROR_FAILURE;
9405           }
9406         }
9407       }
9408     }
9409 
9410     // Else we ran out of memory, or were a popup and got blocked,
9411     // or something.
9412 
9413     return rv;
9414   }
9415 
9416   //
9417   // Load is being targetted at this docshell so return an error if the
9418   // docshell is in the process of being destroyed.
9419   //
9420   if (mIsBeingDestroyed) {
9421     return NS_ERROR_FAILURE;
9422   }
9423 
9424   NS_ENSURE_STATE(!HasUnloadedParent());
9425 
9426   rv = CheckLoadingPermissions();
9427   if (NS_FAILED(rv)) {
9428     return rv;
9429   }
9430 
9431   if (mFiredUnloadEvent) {
9432     if (IsOKToLoadURI(aURI)) {
9433       NS_PRECONDITION(aWindowTarget.IsEmpty(),
9434                       "Shouldn't have a window target here!");
9435 
9436       // If this is a replace load, make whatever load triggered
9437       // the unload event also a replace load, so we don't
9438       // create extra history entries.
9439       if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9440         mLoadType = LOAD_NORMAL_REPLACE;
9441       }
9442 
9443       // Do this asynchronously
9444       nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(
9445           this, aURI, aOriginalURI, aResultPrincipalURI,
9446           aKeepResultPrincipalURIIfSet, aLoadReplace,
9447           aIsFromProcessingFrameAttributes, aReferrer, aReferrerPolicy,
9448           aTriggeringPrincipal, principalToInherit, aFlags, aTypeHint,
9449           aPostData, aPostDataLength, aHeadersData, aLoadType, aSHEntry,
9450           aFirstParty, aSrcdoc, aSourceDocShell, aBaseURI);
9451       return DispatchToTabGroup(TaskCategory::Other, ev.forget());
9452     }
9453 
9454     // Just ignore this load attempt
9455     return NS_OK;
9456   }
9457 
9458   // If a source docshell has been passed, check to see if we are sandboxed
9459   // from it as the result of an iframe or CSP sandbox.
9460   if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
9461     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9462   }
9463 
9464   // If this docshell is owned by a frameloader, make sure to cancel
9465   // possible frameloader initialization before loading a new page.
9466   nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
9467   if (parent) {
9468     nsCOMPtr<nsIDocument> doc = parent->GetDocument();
9469     if (doc) {
9470       doc->TryCancelFrameLoaderInitialization(this);
9471     }
9472   }
9473 
9474   bool loadFromExternal = false;
9475 
9476   // Before going any further vet loads initiated by external programs.
9477   if (aLoadType == LOAD_NORMAL_EXTERNAL) {
9478     loadFromExternal = true;
9479     // Disallow external chrome: loads targetted at content windows
9480     bool isChrome = false;
9481     if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
9482       NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9483       return NS_ERROR_FAILURE;
9484     }
9485 
9486     // clear the decks to prevent context bleed-through (bug 298255)
9487     rv = CreateAboutBlankContentViewer(nullptr, nullptr);
9488     if (NS_FAILED(rv)) {
9489       return NS_ERROR_FAILURE;
9490     }
9491 
9492     // reset loadType so we don't have to add lots of tests for
9493     // LOAD_NORMAL_EXTERNAL after this point
9494     aLoadType = LOAD_NORMAL;
9495   }
9496 
9497   mAllowKeywordFixup =
9498       (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
9499   mURIResultedInDocument = false;  // reset the clock...
9500 
9501   if (aLoadType == LOAD_NORMAL || aLoadType == LOAD_STOP_CONTENT ||
9502       LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
9503       aLoadType == LOAD_HISTORY || aLoadType == LOAD_LINK) {
9504     nsCOMPtr<nsIURI> currentURI = mCurrentURI;
9505 
9506     nsAutoCString curHash, newHash;
9507     bool curURIHasRef = false, newURIHasRef = false;
9508 
9509     nsresult rvURINew = aURI->GetRef(newHash);
9510     if (NS_SUCCEEDED(rvURINew)) {
9511       rvURINew = aURI->GetHasRef(&newURIHasRef);
9512     }
9513 
9514     bool sameExceptHashes = false;
9515     if (currentURI && NS_SUCCEEDED(rvURINew)) {
9516       nsresult rvURIOld = currentURI->GetRef(curHash);
9517       if (NS_SUCCEEDED(rvURIOld)) {
9518         rvURIOld = currentURI->GetHasRef(&curURIHasRef);
9519       }
9520       if (NS_SUCCEEDED(rvURIOld)) {
9521         if (NS_FAILED(currentURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
9522           sameExceptHashes = false;
9523         }
9524       }
9525     }
9526 
9527     if (!sameExceptHashes && sURIFixup && currentURI &&
9528         NS_SUCCEEDED(rvURINew)) {
9529       // Maybe aURI came from the exposable form of currentURI?
9530       nsCOMPtr<nsIURI> currentExposableURI;
9531       rv = sURIFixup->CreateExposableURI(currentURI,
9532                                          getter_AddRefs(currentExposableURI));
9533       NS_ENSURE_SUCCESS(rv, rv);
9534       nsresult rvURIOld = currentExposableURI->GetRef(curHash);
9535       if (NS_SUCCEEDED(rvURIOld)) {
9536         rvURIOld = currentExposableURI->GetHasRef(&curURIHasRef);
9537       }
9538       if (NS_SUCCEEDED(rvURIOld)) {
9539         if (NS_FAILED(currentExposableURI->EqualsExceptRef(
9540                 aURI, &sameExceptHashes))) {
9541           sameExceptHashes = false;
9542         }
9543       }
9544     }
9545 
9546     bool historyNavBetweenSameDoc = false;
9547     if (mOSHE && aSHEntry) {
9548       // We're doing a history load.
9549 
9550       mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
9551 
9552 #ifdef DEBUG
9553       if (historyNavBetweenSameDoc) {
9554         nsCOMPtr<nsIInputStream> currentPostData;
9555         mOSHE->GetPostData(getter_AddRefs(currentPostData));
9556         NS_ASSERTION(currentPostData == aPostData,
9557                      "Different POST data for entries for the same page?");
9558       }
9559 #endif
9560     }
9561 
9562     // A short-circuited load happens when we navigate between two SHEntries
9563     // for the same document.  We do a short-circuited load under two
9564     // circumstances.  Either
9565     //
9566     //  a) we're navigating between two different SHEntries which share a
9567     //     document, or
9568     //
9569     //  b) we're navigating to a new shentry whose URI differs from the
9570     //     current URI only in its hash, the new hash is non-empty, and
9571     //     we're not doing a POST.
9572     //
9573     // The restriction tha the SHEntries in (a) must be different ensures
9574     // that history.go(0) and the like trigger full refreshes, rather than
9575     // short-circuited loads.
9576     bool doShortCircuitedLoad =
9577         (historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
9578         (!aSHEntry && !aPostData && sameExceptHashes && newURIHasRef);
9579 
9580     if (doShortCircuitedLoad) {
9581       // Save the position of the scrollers.
9582       nscoord cx = 0, cy = 0;
9583       GetCurScrollPos(ScrollOrientation_X, &cx);
9584       GetCurScrollPos(ScrollOrientation_Y, &cy);
9585 
9586       // Reset mLoadType to its original value once we exit this block,
9587       // because this short-circuited load might have started after a
9588       // normal, network load, and we don't want to clobber its load type.
9589       // See bug 737307.
9590       AutoRestore<uint32_t> loadTypeResetter(mLoadType);
9591 
9592       // If a non-short-circuit load (i.e., a network load) is pending,
9593       // make this a replacement load, so that we don't add a SHEntry here
9594       // and the network load goes into the SHEntry it expects to.
9595       if (JustStartedNetworkLoad() && (aLoadType & LOAD_CMD_NORMAL)) {
9596         mLoadType = LOAD_NORMAL_REPLACE;
9597       } else {
9598         mLoadType = aLoadType;
9599       }
9600 
9601       mURIResultedInDocument = true;
9602 
9603       nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
9604 
9605       /* we need to assign mLSHE to aSHEntry right here, so that on History
9606        * loads, SetCurrentURI() called from OnNewURI() will send proper
9607        * onLocationChange() notifications to the browser to update
9608        * back/forward buttons.
9609        */
9610       SetHistoryEntry(&mLSHE, aSHEntry);
9611 
9612       // Set the doc's URI according to the new history entry's URI.
9613       nsCOMPtr<nsIDocument> doc = GetDocument();
9614       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
9615       doc->SetDocumentURI(aURI);
9616 
9617       /* This is a anchor traversal with in the same page.
9618        * call OnNewURI() so that, this traversal will be
9619        * recorded in session and global history.
9620        */
9621       nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal,
9622           newURIPrincipalToInherit;
9623       if (mOSHE) {
9624         mOSHE->GetTriggeringPrincipal(
9625             getter_AddRefs(newURITriggeringPrincipal));
9626         mOSHE->GetPrincipalToInherit(getter_AddRefs(newURIPrincipalToInherit));
9627       } else {
9628         newURITriggeringPrincipal = aTriggeringPrincipal;
9629         newURIPrincipalToInherit = doc->NodePrincipal();
9630       }
9631       // Pass true for aCloneSHChildren, since we're not
9632       // changing documents here, so all of our subframes are
9633       // still relevant to the new session history entry.
9634       //
9635       // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
9636       // flag on firing onLocationChange(...).
9637       // Anyway, aCloneSHChildren param is simply reflecting
9638       // doShortCircuitedLoad in this scope.
9639       OnNewURI(aURI, nullptr, newURITriggeringPrincipal,
9640                newURIPrincipalToInherit, mLoadType, true, true, true);
9641 
9642       nsCOMPtr<nsIInputStream> postData;
9643       nsCOMPtr<nsISupports> cacheKey;
9644 
9645       bool scrollRestorationIsManual = false;
9646       if (mOSHE) {
9647         /* save current position of scroller(s) (bug 59774) */
9648         mOSHE->SetScrollPosition(cx, cy);
9649         mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
9650         // Get the postdata and page ident from the current page, if
9651         // the new load is being done via normal means.  Note that
9652         // "normal means" can be checked for just by checking for
9653         // LOAD_CMD_NORMAL, given the loadType and allowScroll check
9654         // above -- it filters out some LOAD_CMD_NORMAL cases that we
9655         // wouldn't want here.
9656         if (aLoadType & LOAD_CMD_NORMAL) {
9657           mOSHE->GetPostData(getter_AddRefs(postData));
9658           mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
9659 
9660           // Link our new SHEntry to the old SHEntry's back/forward
9661           // cache data, since the two SHEntries correspond to the
9662           // same document.
9663           if (mLSHE) {
9664             if (!aSHEntry) {
9665               // If we're not doing a history load, scroll restoration
9666               // should be inherited from the previous session history entry.
9667               mLSHE->SetScrollRestorationIsManual(scrollRestorationIsManual);
9668             }
9669             mLSHE->AdoptBFCacheEntry(mOSHE);
9670           }
9671         }
9672       }
9673 
9674       // If we're doing a history load, use its scroll restoration state.
9675       if (aSHEntry) {
9676         aSHEntry->GetScrollRestorationIsManual(&scrollRestorationIsManual);
9677       }
9678 
9679       /* Assign mOSHE to mLSHE. This will either be a new entry created
9680        * by OnNewURI() for normal loads or aSHEntry for history loads.
9681        */
9682       if (mLSHE) {
9683         SetHistoryEntry(&mOSHE, mLSHE);
9684         // Save the postData obtained from the previous page
9685         // in to the session history entry created for the
9686         // anchor page, so that any history load of the anchor
9687         // page will restore the appropriate postData.
9688         if (postData) {
9689           mOSHE->SetPostData(postData);
9690         }
9691 
9692         // Make sure we won't just repost without hitting the
9693         // cache first
9694         if (cacheKey) {
9695           mOSHE->SetCacheKey(cacheKey);
9696         }
9697       }
9698 
9699       /* Restore the original LSHE if we were loading something
9700        * while short-circuited load was initiated.
9701        */
9702       SetHistoryEntry(&mLSHE, oldLSHE);
9703       /* Set the title for the SH entry for this target url. so that
9704        * SH menus in go/back/forward buttons won't be empty for this.
9705        */
9706       if (mSessionHistory) {
9707         int32_t index = -1;
9708         mSessionHistory->GetIndex(&index);
9709         nsCOMPtr<nsISHEntry> shEntry;
9710         mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(shEntry));
9711         NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
9712         shEntry->SetTitle(mTitle);
9713       }
9714 
9715       /* Set the title for the Global History entry for this anchor url.
9716        */
9717       UpdateGlobalHistoryTitle(aURI);
9718 
9719       SetDocCurrentStateObj(mOSHE);
9720 
9721       // Inform the favicon service that the favicon for oldURI also
9722       // applies to aURI.
9723       CopyFavicon(currentURI, aURI, doc->NodePrincipal(), UsePrivateBrowsing());
9724 
9725       RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9726       RefPtr<nsGlobalWindowInner> win =
9727           scriptGlobal ? scriptGlobal->GetCurrentInnerWindowInternal()
9728                        : nullptr;
9729 
9730       // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9731       // the function decides whether a scroll is appropriate based on the
9732       // arguments it receives.  But even if we don't end up scrolling,
9733       // ScrollToAnchor performs other important tasks, such as informing
9734       // the presShell that we have a new hash.  See bug 680257.
9735       rv = ScrollToAnchor(curURIHasRef, newURIHasRef, newHash, aLoadType);
9736       NS_ENSURE_SUCCESS(rv, rv);
9737 
9738       /* restore previous position of scroller(s), if we're moving
9739        * back in history (bug 59774)
9740        */
9741       nscoord bx = 0;
9742       nscoord by = 0;
9743       bool needsScrollPosUpdate = false;
9744       if (mOSHE &&
9745           (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) &&
9746           !scrollRestorationIsManual) {
9747         needsScrollPosUpdate = true;
9748         mOSHE->GetScrollPosition(&bx, &by);
9749       }
9750 
9751       // Dispatch the popstate and hashchange events, as appropriate.
9752       //
9753       // The event dispatch below can cause us to re-enter script and
9754       // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9755       // reference to avoid null derefs. See bug 914521.
9756       if (win) {
9757         // Fire a hashchange event URIs differ, and only in their hashes.
9758         bool doHashchange = sameExceptHashes && (curURIHasRef != newURIHasRef ||
9759                                                  !curHash.Equals(newHash));
9760 
9761         if (historyNavBetweenSameDoc || doHashchange) {
9762           win->DispatchSyncPopState();
9763         }
9764 
9765         if (needsScrollPosUpdate && win->AsInner()->HasActiveDocument()) {
9766           SetCurScrollPosEx(bx, by);
9767         }
9768 
9769         if (doHashchange) {
9770           // Note that currentURI hasn't changed because it's on the
9771           // stack, so we can just use it directly as the old URI.
9772           win->DispatchAsyncHashchange(currentURI, aURI);
9773         }
9774       }
9775 
9776       return NS_OK;
9777     }
9778   }
9779 
9780   // mContentViewer->PermitUnload can destroy |this| docShell, which
9781   // causes the next call of CanSavePresentation to crash.
9782   // Hold onto |this| until we return, to prevent a crash from happening.
9783   // (bug#331040)
9784   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9785 
9786   // Don't init timing for javascript:, since it generally doesn't
9787   // actually start a load or anything.  If it does, we'll init
9788   // timing then, from OnStateChange.
9789 
9790   // XXXbz mTiming should know what channel it's for, so we don't
9791   // need this hackery.
9792   bool toBeReset = false;
9793   if (!isJavaScript) {
9794     toBeReset = MaybeInitTiming();
9795   }
9796   bool timeBeforeUnload = aFileName.IsVoid();
9797   if (mTiming && timeBeforeUnload) {
9798     mTiming->NotifyBeforeUnload();
9799   }
9800   // Check if the page doesn't want to be unloaded. The javascript:
9801   // protocol handler deals with this for javascript: URLs.
9802   if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
9803     bool okToUnload;
9804     rv = mContentViewer->PermitUnload(&okToUnload);
9805 
9806     if (NS_SUCCEEDED(rv) && !okToUnload) {
9807       // The user chose not to unload the page, interrupt the
9808       // load.
9809       MaybeResetInitTiming(toBeReset);
9810       return NS_OK;
9811     }
9812   }
9813 
9814   if (mTiming && timeBeforeUnload) {
9815     mTiming->NotifyUnloadAccepted(mCurrentURI);
9816   }
9817 
9818   // Check if the webbrowser chrome wants the load to proceed; this can be
9819   // used to cancel attempts to load URIs in the wrong process.
9820   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
9821   if (browserChrome3) {
9822     bool shouldLoad;
9823     rv = browserChrome3->ShouldLoadURI(this, aURI, aReferrer, !!aPostData,
9824                                        aTriggeringPrincipal, &shouldLoad);
9825     if (NS_SUCCEEDED(rv) && !shouldLoad) {
9826       return NS_OK;
9827     }
9828   }
9829 
9830   // Whenever a top-level browsing context is navigated, the user agent MUST
9831   // lock the orientation of the document to the document's default
9832   // orientation. We don't explicitly check for a top-level browsing context
9833   // here because orientation is only set on top-level browsing contexts.
9834   if (OrientationLock() != eScreenOrientation_None) {
9835 #ifdef DEBUG
9836     nsCOMPtr<nsIDocShellTreeItem> parent;
9837     GetSameTypeParent(getter_AddRefs(parent));
9838     MOZ_ASSERT(!parent);
9839 #endif
9840     SetOrientationLock(eScreenOrientation_None);
9841     if (mIsActive) {
9842       ScreenOrientation::UpdateActiveOrientationLock(eScreenOrientation_None);
9843     }
9844   }
9845 
9846   // Check for saving the presentation here, before calling Stop().
9847   // This is necessary so that we can catch any pending requests.
9848   // Since the new request has not been created yet, we pass null for the
9849   // new request parameter.
9850   // Also pass nullptr for the document, since it doesn't affect the return
9851   // value for our purposes here.
9852   bool savePresentation = CanSavePresentation(aLoadType, nullptr, nullptr);
9853 
9854   // Don't stop current network activity for javascript: URL's since
9855   // they might not result in any data, and thus nothing should be
9856   // stopped in those cases. In the case where they do result in
9857   // data, the javascript: URL channel takes care of stopping
9858   // current network activity.
9859   if (!isJavaScript && aFileName.IsVoid()) {
9860     // Stop any current network activity.
9861     // Also stop content if this is a zombie doc. otherwise
9862     // the onload will be delayed by other loads initiated in the
9863     // background by the first document that
9864     // didn't fully load before the next load was initiated.
9865     // If not a zombie, don't stop content until data
9866     // starts arriving from the new URI...
9867 
9868     nsCOMPtr<nsIContentViewer> zombieViewer;
9869     if (mContentViewer) {
9870       mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
9871     }
9872 
9873     if (zombieViewer ||
9874         LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
9875       rv = Stop(nsIWebNavigation::STOP_ALL);
9876     } else {
9877       rv = Stop(nsIWebNavigation::STOP_NETWORK);
9878     }
9879 
9880     if (NS_FAILED(rv)) {
9881       return rv;
9882     }
9883   }
9884 
9885   mLoadType = aLoadType;
9886 
9887   // mLSHE should be assigned to aSHEntry, only after Stop() has
9888   // been called. But when loading an error page, do not clear the
9889   // mLSHE for the real page.
9890   if (mLoadType != LOAD_ERROR_PAGE) {
9891     SetHistoryEntry(&mLSHE, aSHEntry);
9892     if (aSHEntry) {
9893       // We're making history navigation or a reload. Make sure our history ID
9894       // points to the same ID as SHEntry's docshell ID.
9895       mHistoryID = aSHEntry->DocshellID();
9896     }
9897   }
9898 
9899   mSavingOldViewer = savePresentation;
9900 
9901   // If we have a saved content viewer in history, restore and show it now.
9902   if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
9903     // It's possible that the previous viewer of mContentViewer is the
9904     // viewer that will end up in aSHEntry when it gets closed.  If that's
9905     // the case, we need to go ahead and force it into its shentry so we
9906     // can restore it.
9907     if (mContentViewer) {
9908       nsCOMPtr<nsIContentViewer> prevViewer;
9909       mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
9910       if (prevViewer) {
9911 #ifdef DEBUG
9912         nsCOMPtr<nsIContentViewer> prevPrevViewer;
9913         prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
9914         NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9915 #endif
9916         nsCOMPtr<nsISHEntry> viewerEntry;
9917         prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9918         if (viewerEntry == aSHEntry) {
9919           // Make sure this viewer ends up in the right place
9920           mContentViewer->SetPreviousViewer(nullptr);
9921           prevViewer->Destroy();
9922         }
9923       }
9924     }
9925     nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9926     bool restoring;
9927     rv = RestorePresentation(aSHEntry, &restoring);
9928     if (restoring) {
9929       return rv;
9930     }
9931 
9932     // We failed to restore the presentation, so clean up.
9933     // Both the old and new history entries could potentially be in
9934     // an inconsistent state.
9935     if (NS_FAILED(rv)) {
9936       if (oldEntry) {
9937         oldEntry->SyncPresentationState();
9938       }
9939 
9940       aSHEntry->SyncPresentationState();
9941     }
9942   }
9943 
9944   nsAutoString srcdoc;
9945   if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) {
9946     srcdoc = aSrcdoc;
9947   } else {
9948     srcdoc = VoidString();
9949   }
9950 
9951   bool isTopLevelDoc = mItemType == typeContent &&
9952                        (isTargetTopLevelDocShell || GetIsMozBrowser());
9953 
9954   OriginAttributes attrs = GetOriginAttributes();
9955   attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
9956 
9957   net::PredictorLearn(aURI, nullptr, nsINetworkPredictor::LEARN_LOAD_TOPLEVEL,
9958                       attrs);
9959   net::PredictorPredict(aURI, nullptr, nsINetworkPredictor::PREDICT_LOAD, attrs,
9960                         nullptr);
9961 
9962   nsCOMPtr<nsIRequest> req;
9963   rv = DoURILoad(
9964       aURI, aOriginalURI, aResultPrincipalURI, aKeepResultPrincipalURIIfSet,
9965       aLoadReplace, aIsFromProcessingFrameAttributes, loadFromExternal,
9966       (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI),
9967       (aFlags & INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC), aReferrer,
9968       !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), aReferrerPolicy,
9969       aTriggeringPrincipal, principalToInherit, aTypeHint, aFileName, aPostData,
9970       aPostDataLength, aHeadersData, aFirstParty, aDocShell,
9971       getter_AddRefs(req), (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
9972       (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
9973       (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0, srcdoc, aBaseURI,
9974       contentType);
9975   if (req && aRequest) {
9976     NS_ADDREF(*aRequest = req);
9977   }
9978 
9979   if (NS_FAILED(rv)) {
9980     nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9981     if (DisplayLoadError(rv, aURI, nullptr, chan) &&
9982         (aFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
9983       return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9984     }
9985   }
9986 
9987   return rv;
9988 }
9989 
GetInheritedPrincipal(bool aConsiderCurrentDocument)9990 nsIPrincipal* nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument) {
9991   nsCOMPtr<nsIDocument> document;
9992   bool inheritedFromCurrent = false;
9993 
9994   if (aConsiderCurrentDocument && mContentViewer) {
9995     document = mContentViewer->GetDocument();
9996     inheritedFromCurrent = true;
9997   }
9998 
9999   if (!document) {
10000     nsCOMPtr<nsIDocShellTreeItem> parentItem;
10001     GetSameTypeParent(getter_AddRefs(parentItem));
10002     if (parentItem) {
10003       document = parentItem->GetDocument();
10004     }
10005   }
10006 
10007   if (!document) {
10008     if (!aConsiderCurrentDocument) {
10009       return nullptr;
10010     }
10011 
10012     // Make sure we end up with _something_ as the principal no matter
10013     // what.If this fails, we'll just get a null docViewer and bail.
10014     EnsureContentViewer();
10015     if (!mContentViewer) {
10016       return nullptr;
10017     }
10018     document = mContentViewer->GetDocument();
10019   }
10020 
10021   //-- Get the document's principal
10022   if (document) {
10023     nsIPrincipal* docPrincipal = document->NodePrincipal();
10024 
10025     // Don't allow loads in typeContent docShells to inherit the system
10026     // principal from existing documents.
10027     if (inheritedFromCurrent && mItemType == typeContent &&
10028         nsContentUtils::IsSystemPrincipal(docPrincipal)) {
10029       return nullptr;
10030     }
10031 
10032     return docPrincipal;
10033   }
10034 
10035   return nullptr;
10036 }
10037 
10038 // CSPs upgrade-insecure-requests directive applies to same origin top level
10039 // navigations. Using the SOP would return false for the case when an https
10040 // page triggers and http page to load, even though that http page would be
10041 // upgraded to https later. Hence we have to use that custom function instead
10042 // of simply calling aTriggeringPrincipal->Equals(aResultPrincipal).
IsConsideredSameOriginForUIR(nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aResultPrincipal)10043 static bool IsConsideredSameOriginForUIR(nsIPrincipal* aTriggeringPrincipal,
10044                                          nsIPrincipal* aResultPrincipal) {
10045   MOZ_ASSERT(aTriggeringPrincipal);
10046   MOZ_ASSERT(aResultPrincipal);
10047 
10048   // we only have to make sure that the following truth table holds:
10049   // aTriggeringPrincipal         | aResultPrincipal             | Result
10050   // ----------------------------------------------------------------
10051   // http://example.com/foo.html  | http://example.com/bar.html  | true
10052   // https://example.com/foo.html | https://example.com/bar.html | true
10053   // https://example.com/foo.html | http://example.com/bar.html  | true
10054   if (aTriggeringPrincipal->Equals(aResultPrincipal)) {
10055     return true;
10056   }
10057 
10058   if (!aResultPrincipal->GetIsCodebasePrincipal()) {
10059     return false;
10060   }
10061 
10062   nsCOMPtr<nsIURI> resultURI;
10063   nsresult rv = aResultPrincipal->GetURI(getter_AddRefs(resultURI));
10064   NS_ENSURE_SUCCESS(rv, false);
10065 
10066   nsAutoCString resultScheme;
10067   rv = resultURI->GetScheme(resultScheme);
10068   NS_ENSURE_SUCCESS(rv, false);
10069   if (!resultScheme.EqualsLiteral("http")) {
10070     return false;
10071   }
10072 
10073   nsAutoCString tmpResultSpec;
10074   rv = resultURI->GetSpec(tmpResultSpec);
10075   NS_ENSURE_SUCCESS(rv, false);
10076   // replace http with https
10077   tmpResultSpec.ReplaceLiteral(0, 4, "https");
10078 
10079   nsCOMPtr<nsIURI> tmpResultURI;
10080   rv = NS_NewURI(getter_AddRefs(tmpResultURI), tmpResultSpec);
10081   NS_ENSURE_SUCCESS(rv, false);
10082 
10083   mozilla::OriginAttributes tmpOA =
10084       BasePrincipal::Cast(aResultPrincipal)->OriginAttributesRef();
10085 
10086   nsCOMPtr<nsIPrincipal> tmpResultPrincipal =
10087       BasePrincipal::CreateCodebasePrincipal(tmpResultURI, tmpOA);
10088 
10089   return aTriggeringPrincipal->Equals(tmpResultPrincipal);
10090 }
10091 
DoURILoad(nsIURI * aURI,nsIURI * aOriginalURI,Maybe<nsCOMPtr<nsIURI>> const & aResultPrincipalURI,bool aKeepResultPrincipalURIIfSet,bool aLoadReplace,bool aIsFromProcessingFrameAttributes,bool aLoadFromExternal,bool aForceAllowDataURI,bool aOriginalFrameSrc,nsIURI * aReferrerURI,bool aSendReferrer,uint32_t aReferrerPolicy,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,const char * aTypeHint,const nsAString & aFileName,nsIInputStream * aPostData,int64_t aPostDataLength,nsIInputStream * aHeadersData,bool aFirstParty,nsIDocShell ** aDocShell,nsIRequest ** aRequest,bool aIsNewWindowTarget,bool aBypassClassifier,bool aForceAllowCookies,const nsAString & aSrcdoc,nsIURI * aBaseURI,nsContentPolicyType aContentPolicyType)10092 nsresult nsDocShell::DoURILoad(
10093     nsIURI* aURI, nsIURI* aOriginalURI,
10094     Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
10095     bool aKeepResultPrincipalURIIfSet, bool aLoadReplace,
10096     bool aIsFromProcessingFrameAttributes, bool aLoadFromExternal,
10097     bool aForceAllowDataURI, bool aOriginalFrameSrc, nsIURI* aReferrerURI,
10098     bool aSendReferrer, uint32_t aReferrerPolicy,
10099     nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
10100     const char* aTypeHint, const nsAString& aFileName,
10101     nsIInputStream* aPostData, int64_t aPostDataLength,
10102     nsIInputStream* aHeadersData, bool aFirstParty, nsIDocShell** aDocShell,
10103     nsIRequest** aRequest, bool aIsNewWindowTarget, bool aBypassClassifier,
10104     bool aForceAllowCookies, const nsAString& aSrcdoc, nsIURI* aBaseURI,
10105     nsContentPolicyType aContentPolicyType) {
10106   // Double-check that we're still around to load this URI.
10107   if (mIsBeingDestroyed) {
10108     // Return NS_OK despite not doing anything to avoid throwing exceptions from
10109     // nsLocation::SetHref if the unload handler of the existing page tears us
10110     // down.
10111     return NS_OK;
10112   }
10113 
10114   nsresult rv;
10115   nsCOMPtr<nsIURILoader> uriLoader =
10116       do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
10117   if (NS_FAILED(rv)) {
10118     return rv;
10119   }
10120 
10121   if (IsFrame()) {
10122     MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10123                    aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10124                "DoURILoad thinks this is a frame and InternalLoad does not");
10125 
10126     // Only allow view-source scheme in top-level docshells. view-source is
10127     // the only scheme to which this applies at the moment due to potential
10128     // timing attacks to read data from cross-origin iframes. If this widens
10129     // we should add a protocol flag for whether the scheme is allowed in
10130     // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10131     nsCOMPtr<nsIURI> tempURI = aURI;
10132     nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10133     while (nestedURI) {
10134       // view-source should always be an nsINestedURI, loop and check the
10135       // scheme on this and all inner URIs that are also nested URIs.
10136       bool isViewSource = false;
10137       rv = tempURI->SchemeIs("view-source", &isViewSource);
10138       if (NS_FAILED(rv) || isViewSource) {
10139         return NS_ERROR_UNKNOWN_PROTOCOL;
10140       }
10141       nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10142       nestedURI = do_QueryInterface(tempURI);
10143     }
10144   } else {
10145     MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10146                "DoURILoad thinks this is a document and InternalLoad does not");
10147   }
10148 
10149   // open a channel for the url
10150   nsCOMPtr<nsIChannel> channel;
10151 
10152   bool isSrcdoc = !aSrcdoc.IsVoid();
10153 
10154   // There are two cases we care about:
10155   // * Top-level load: In this case, loadingNode is null, but loadingWindow
10156   //   is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10157   // * Subframe load: loadingWindow is null, but loadingNode is the frame
10158   //   element for the load. loadingPrincipal is the NodePrincipal of the frame
10159   //   element.
10160   nsCOMPtr<nsINode> loadingNode;
10161   nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10162   nsCOMPtr<nsIPrincipal> loadingPrincipal;
10163   nsCOMPtr<nsISupports> topLevelLoadingContext;
10164 
10165   if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10166     loadingNode = nullptr;
10167     loadingPrincipal = nullptr;
10168     loadingWindow = mScriptGlobal->AsOuter();
10169     if (XRE_IsContentProcess()) {
10170       // In e10s the child process doesn't have access to the element that
10171       // contains the browsing context (because that element is in the chrome
10172       // process).
10173       nsCOMPtr<nsITabChild> tabChild = GetTabChild();
10174       topLevelLoadingContext = ToSupports(tabChild);
10175     } else {
10176       // This is for loading non-e10s tabs and toplevel windows of various
10177       // sorts.
10178       // For the toplevel window cases, requestingElement will be null.
10179       nsCOMPtr<Element> requestingElement =
10180           loadingWindow->GetFrameElementInternal();
10181       topLevelLoadingContext = requestingElement;
10182     }
10183   } else {
10184     loadingWindow = nullptr;
10185     loadingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
10186     if (loadingNode) {
10187       // If we have a loading node, then use that as our loadingPrincipal.
10188       loadingPrincipal = loadingNode->NodePrincipal();
10189 #ifdef DEBUG
10190       // Get the docshell type for requestingElement.
10191       nsCOMPtr<nsIDocument> requestingDoc = loadingNode->OwnerDoc();
10192       nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10193       // requestingElement docshell type = current docshell type.
10194       MOZ_ASSERT(
10195           mItemType == elementDocShell->ItemType(),
10196           "subframes should have the same docshell type as their parent");
10197 #endif
10198     } else {
10199       // If this isn't a top-level load and mScriptGlobal's frame element is
10200       // null, then the element got removed from the DOM while we were trying
10201       // to load this resource. This docshell is scheduled for destruction
10202       // already, so bail out here.
10203       return NS_OK;
10204     }
10205   }
10206 
10207   // Getting the right triggeringPrincipal needs to be updated and is only
10208   // ready for use once bug 1182569 landed. Until then, we cannot rely on
10209   // the triggeringPrincipal for TYPE_DOCUMENT loads.
10210   MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
10211 
10212   bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN;
10213 
10214   // We want to inherit aPrincipalToInherit when:
10215   // 1. ChannelShouldInheritPrincipal returns true.
10216   // 2. aURI is not data: URI, or data: URI is not configured as unique opaque
10217   //    origin.
10218   bool inheritAttrs = false, inheritPrincipal = false;
10219 
10220   if (aPrincipalToInherit) {
10221     inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10222         aPrincipalToInherit, aURI,
10223         true,  // aInheritForAboutBlank
10224         isSrcdoc);
10225 
10226     bool isData;
10227     bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() &&
10228                              NS_SUCCEEDED(aURI->SchemeIs("data", &isData)) &&
10229                              isData;
10230     inheritPrincipal = inheritAttrs && !isURIUniqueOrigin;
10231   }
10232 
10233   nsLoadFlags loadFlags = mDefaultLoadFlags;
10234   nsSecurityFlags securityFlags =
10235       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
10236 
10237   if (aFirstParty) {
10238     // tag first party URL loads
10239     loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
10240   }
10241 
10242   if (mLoadType == LOAD_ERROR_PAGE) {
10243     // Error pages are LOAD_BACKGROUND
10244     loadFlags |= nsIChannel::LOAD_BACKGROUND;
10245     securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10246   }
10247 
10248   if (inheritPrincipal) {
10249     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10250   }
10251   if (isSandBoxed) {
10252     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
10253   }
10254 
10255   RefPtr<LoadInfo> loadInfo =
10256       (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10257           ? new LoadInfo(loadingWindow, aTriggeringPrincipal,
10258                          topLevelLoadingContext, securityFlags)
10259           : new LoadInfo(loadingPrincipal, aTriggeringPrincipal, loadingNode,
10260                          securityFlags, aContentPolicyType);
10261 
10262   if (aPrincipalToInherit) {
10263     loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
10264   }
10265   loadInfo->SetLoadTriggeredFromExternal(aLoadFromExternal);
10266   loadInfo->SetForceAllowDataURI(aForceAllowDataURI);
10267   loadInfo->SetOriginalFrameSrcLoad(aOriginalFrameSrc);
10268 
10269   // We have to do this in case our OriginAttributes are different from the
10270   // OriginAttributes of the parent document. Or in case there isn't a
10271   // parent document.
10272   bool isTopLevelDoc = mItemType == typeContent &&
10273                        (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
10274                         GetIsMozBrowser());
10275 
10276   OriginAttributes attrs;
10277 
10278   // Inherit origin attributes from aPrincipalToInherit if inheritAttrs is true.
10279   // Otherwise we just use the origin attributes from docshell.
10280   if (inheritAttrs) {
10281     MOZ_ASSERT(aPrincipalToInherit, "We should have aPrincipalToInherit here.");
10282     attrs = aPrincipalToInherit->OriginAttributesRef();
10283     // If firstPartyIsolation is not enabled, then PrincipalToInherit should
10284     // have the same origin attributes with docshell.
10285     MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
10286                   attrs == GetOriginAttributes());
10287   } else {
10288     attrs = GetOriginAttributes();
10289     attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
10290   }
10291 
10292   rv = loadInfo->SetOriginAttributes(attrs);
10293   if (NS_WARN_IF(NS_FAILED(rv))) {
10294     return rv;
10295   }
10296 
10297   // Document loads should set the reload flag on the channel so that it
10298   // can be exposed on the service worker FetchEvent.
10299   rv = loadInfo->SetIsDocshellReload(mLoadType & LOAD_CMD_RELOAD);
10300   NS_ENSURE_SUCCESS(rv, rv);
10301 
10302   if (aIsFromProcessingFrameAttributes) {
10303     loadInfo->SetIsFromProcessingFrameAttributes();
10304   }
10305 
10306   if (!isSrcdoc) {
10307     rv = NS_NewChannelInternal(getter_AddRefs(channel), aURI, loadInfo,
10308                                nullptr,  // PerformanceStorage
10309                                nullptr,  // loadGroup
10310                                static_cast<nsIInterfaceRequestor*>(this),
10311                                loadFlags);
10312 
10313     if (NS_FAILED(rv)) {
10314       if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
10315         // This is a uri with a protocol scheme we don't know how
10316         // to handle.  Embedders might still be interested in
10317         // handling the load, though, so we fire a notification
10318         // before throwing the load away.
10319         bool abort = false;
10320         nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
10321         if (NS_SUCCEEDED(rv2) && abort) {
10322           // Hey, they're handling the load for us!  How convenient!
10323           return NS_OK;
10324         }
10325       }
10326       return rv;
10327     }
10328 
10329     if (aBaseURI) {
10330       nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
10331       if (vsc) {
10332         rv = vsc->SetBaseURI(aBaseURI);
10333         MOZ_ASSERT(NS_SUCCEEDED(rv));
10334       }
10335     }
10336   } else {
10337     nsAutoCString scheme;
10338     rv = aURI->GetScheme(scheme);
10339     NS_ENSURE_SUCCESS(rv, rv);
10340     bool isViewSource;
10341     aURI->SchemeIs("view-source", &isViewSource);
10342 
10343     if (isViewSource) {
10344       nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
10345       NS_ENSURE_TRUE(vsh, NS_ERROR_FAILURE);
10346 
10347       rv = vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, loadInfo,
10348                                  getter_AddRefs(channel));
10349     } else {
10350       rv = NS_NewInputStreamChannelInternal(
10351           getter_AddRefs(channel), aURI, aSrcdoc,
10352           NS_LITERAL_CSTRING("text/html"), loadInfo, true);
10353       NS_ENSURE_SUCCESS(rv, rv);
10354       nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
10355       MOZ_ASSERT(isc);
10356       isc->SetBaseURI(aBaseURI);
10357     }
10358   }
10359 
10360   // Navigational requests that are same origin need to be upgraded in case
10361   // upgrade-insecure-requests is present. Please note that in that case
10362   // the triggeringPrincipal is holding the CSP that potentially
10363   // holds upgrade-insecure-requests.
10364   nsCOMPtr<nsIContentSecurityPolicy> csp;
10365   aTriggeringPrincipal->GetCsp(getter_AddRefs(csp));
10366   if (csp) {
10367     bool upgradeInsecureRequests = false;
10368     csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10369     if (upgradeInsecureRequests) {
10370       // only upgrade if the navigation is same origin
10371       nsCOMPtr<nsIPrincipal> resultPrincipal;
10372       rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10373           channel, getter_AddRefs(resultPrincipal));
10374       NS_ENSURE_SUCCESS(rv, rv);
10375       if (IsConsideredSameOriginForUIR(aTriggeringPrincipal, resultPrincipal)) {
10376         static_cast<mozilla::LoadInfo*>(loadInfo.get())
10377             ->SetUpgradeInsecureRequests();
10378       }
10379     }
10380   }
10381 
10382   nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
10383       do_QueryInterface(channel);
10384   if (appCacheChannel) {
10385     // Any document load should not inherit application cache.
10386     appCacheChannel->SetInheritApplicationCache(false);
10387 
10388     // Loads with the correct permissions should check for a matching
10389     // application cache.
10390     if (GeckoProcessType_Default != XRE_GetProcessType()) {
10391       // Permission will be checked in the parent process
10392       appCacheChannel->SetChooseApplicationCache(true);
10393     } else {
10394       nsCOMPtr<nsIScriptSecurityManager> secMan =
10395           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10396 
10397       if (secMan) {
10398         nsCOMPtr<nsIPrincipal> principal;
10399         secMan->GetDocShellCodebasePrincipal(aURI, this,
10400                                              getter_AddRefs(principal));
10401         appCacheChannel->SetChooseApplicationCache(
10402             NS_ShouldCheckAppCache(principal));
10403       }
10404     }
10405   }
10406 
10407   // Make sure to give the caller a channel if we managed to create one
10408   // This is important for correct error page/session history interaction
10409   if (aRequest) {
10410     NS_ADDREF(*aRequest = channel);
10411   }
10412 
10413   if (aOriginalURI) {
10414     channel->SetOriginalURI(aOriginalURI);
10415     // The LOAD_REPLACE flag and its handling here will be removed as part
10416     // of bug 1319110.  For now preserve its restoration here to not break
10417     // any code expecting it being set specially on redirected channels.
10418     // If the flag has originally been set to change result of
10419     // NS_GetFinalChannelURI it won't have any effect and also won't cause
10420     // any harm.
10421     if (aLoadReplace) {
10422       uint32_t loadFlags;
10423       channel->GetLoadFlags(&loadFlags);
10424       NS_ENSURE_SUCCESS(rv, rv);
10425       channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10426     }
10427   } else {
10428     channel->SetOriginalURI(aURI);
10429   }
10430 
10431   nsCOMPtr<nsIURI> rpURI;
10432   loadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10433   if (aResultPrincipalURI && (!aKeepResultPrincipalURIIfSet || !rpURI)) {
10434     // Unconditionally override, we want the replay to be equal to what has
10435     // been captured.
10436     loadInfo->SetResultPrincipalURI(aResultPrincipalURI.ref());
10437   }
10438 
10439   if (aTypeHint && *aTypeHint) {
10440     channel->SetContentType(nsDependentCString(aTypeHint));
10441     mContentTypeHint = aTypeHint;
10442   } else {
10443     mContentTypeHint.Truncate();
10444   }
10445 
10446   if (!aFileName.IsVoid()) {
10447     rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
10448     NS_ENSURE_SUCCESS(rv, rv);
10449     if (!aFileName.IsEmpty()) {
10450       rv = channel->SetContentDispositionFilename(aFileName);
10451       NS_ENSURE_SUCCESS(rv, rv);
10452     }
10453   }
10454 
10455   if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
10456       mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
10457     rv = SetMixedContentChannel(channel);
10458     NS_ENSURE_SUCCESS(rv, rv);
10459   } else if (mMixedContentChannel) {
10460     /*
10461      * If the user "Disables Protection on This Page", we call
10462      * SetMixedContentChannel for the first time, otherwise
10463      * mMixedContentChannel is still null.
10464      * Later, if the new channel passes a same orign check, we remember the
10465      * users decision by calling SetMixedContentChannel using the new channel.
10466      * This way, the user does not have to click the disable protection button
10467      * over and over for browsing the same site.
10468      */
10469     rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel);
10470     if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) {
10471       SetMixedContentChannel(nullptr);
10472     }
10473   }
10474 
10475   // hack
10476   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
10477   nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
10478       do_QueryInterface(channel));
10479   if (httpChannelInternal) {
10480     if (aForceAllowCookies) {
10481       rv = httpChannelInternal->SetThirdPartyFlags(
10482           nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
10483       MOZ_ASSERT(NS_SUCCEEDED(rv));
10484     }
10485     if (aFirstParty) {
10486       rv = httpChannelInternal->SetDocumentURI(aURI);
10487       MOZ_ASSERT(NS_SUCCEEDED(rv));
10488     } else {
10489       rv = httpChannelInternal->SetDocumentURI(aReferrerURI);
10490       MOZ_ASSERT(NS_SUCCEEDED(rv));
10491     }
10492     rv = httpChannelInternal->SetRedirectMode(
10493         nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
10494     MOZ_ASSERT(NS_SUCCEEDED(rv));
10495   }
10496 
10497   nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
10498   if (props) {
10499     // save true referrer for those who need it (e.g. xpinstall whitelisting)
10500     // Currently only http and ftp channels support this.
10501     props->SetPropertyAsInterface(
10502         NS_LITERAL_STRING("docshell.internalReferrer"), aReferrerURI);
10503   }
10504 
10505   nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
10506   /* Get the cache Key from SH */
10507   nsCOMPtr<nsISupports> cacheKey;
10508   if (cacheChannel) {
10509     if (mLSHE) {
10510       mLSHE->GetCacheKey(getter_AddRefs(cacheKey));
10511     } else if (mOSHE) {  // for reload cases
10512       mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
10513     }
10514   }
10515 
10516   // figure out if we need to set the post data stream on the channel...
10517   if (aPostData) {
10518     nsCOMPtr<nsIFormPOSTActionChannel> postChannel(do_QueryInterface(channel));
10519     if (postChannel) {
10520       // XXX it's a bit of a hack to rewind the postdata stream here but
10521       // it has to be done in case the post data is being reused multiple
10522       // times.
10523       nsCOMPtr<nsISeekableStream> postDataSeekable =
10524           do_QueryInterface(aPostData);
10525       if (postDataSeekable) {
10526         rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
10527         NS_ENSURE_SUCCESS(rv, rv);
10528       }
10529 
10530       // we really need to have a content type associated with this stream!!
10531       postChannel->SetUploadStream(aPostData, EmptyCString(), aPostDataLength);
10532     }
10533 
10534     /* If there is a valid postdata *and* it is a History Load,
10535      * set up the cache key on the channel, to retrieve the
10536      * data *only* from the cache. If it is a normal reload, the
10537      * cache is free to go to the server for updated postdata.
10538      */
10539     if (cacheChannel && cacheKey) {
10540       if (mLoadType == LOAD_HISTORY ||
10541           mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10542         cacheChannel->SetCacheKey(cacheKey);
10543         uint32_t loadFlags;
10544         if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
10545           channel->SetLoadFlags(loadFlags |
10546                                 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
10547         }
10548       } else if (mLoadType == LOAD_RELOAD_NORMAL) {
10549         cacheChannel->SetCacheKey(cacheKey);
10550       }
10551     }
10552   } else {
10553     /* If there is no postdata, set the cache key on the channel, and
10554      * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10555      * will be free to get it from net if it is not found in cache.
10556      * New cache may use it creatively on CGI pages with GET
10557      * method and even on those that say "no-cache"
10558      */
10559     if (mLoadType == LOAD_HISTORY || mLoadType == LOAD_RELOAD_NORMAL ||
10560         mLoadType == LOAD_RELOAD_CHARSET_CHANGE ||
10561         mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
10562         mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
10563       if (cacheChannel && cacheKey) {
10564         cacheChannel->SetCacheKey(cacheKey);
10565       }
10566     }
10567   }
10568 
10569   if (httpChannel) {
10570     if (aHeadersData) {
10571       rv = AddHeadersToChannel(aHeadersData, httpChannel);
10572     }
10573     // Set the referrer explicitly
10574     if (aReferrerURI && aSendReferrer) {
10575       // Referrer is currenly only set for link clicks here.
10576       rv = httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
10577       MOZ_ASSERT(NS_SUCCEEDED(rv));
10578     }
10579   }
10580 
10581   nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
10582   if (scriptChannel) {
10583     // Allow execution against our context if the principals match
10584     scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10585   }
10586 
10587   if (aIsNewWindowTarget) {
10588     nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
10589     if (props) {
10590       props->SetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
10591                                true);
10592     }
10593   }
10594 
10595   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
10596   if (timedChannel) {
10597     timedChannel->SetTimingEnabled(true);
10598 
10599     nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10600     if (IsFrame() && win) {
10601       nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
10602       if (frameElement) {
10603         timedChannel->SetInitiatorType(frameElement->LocalName());
10604       }
10605     }
10606   }
10607 
10608   // Mark the http channel as UrgentStart for top level document loading
10609   // in active tab.
10610   if (mIsActive || (mLoadType & (LOAD_CMD_NORMAL | LOAD_CMD_HISTORY))) {
10611     if (httpChannel && isTopLevelDoc) {
10612       nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
10613       if (cos) {
10614         cos->AddClassFlags(nsIClassOfService::UrgentStart);
10615       }
10616     }
10617   }
10618 
10619   rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
10620 
10621   //
10622   // If the channel load failed, we failed and nsIWebProgress just ain't
10623   // gonna happen.
10624   //
10625   if (NS_SUCCEEDED(rv)) {
10626     if (aDocShell) {
10627       *aDocShell = this;
10628       NS_ADDREF(*aDocShell);
10629     }
10630   }
10631 
10632   return rv;
10633 }
10634 
AppendSegmentToString(nsIInputStream * aIn,void * aClosure,const char * aFromRawSegment,uint32_t aToOffset,uint32_t aCount,uint32_t * aWriteCount)10635 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10636                                       const char* aFromRawSegment,
10637                                       uint32_t aToOffset, uint32_t aCount,
10638                                       uint32_t* aWriteCount) {
10639   // aFromSegment now contains aCount bytes of data.
10640 
10641   nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10642   buf->Append(aFromRawSegment, aCount);
10643 
10644   // Indicate that we have consumed all of aFromSegment
10645   *aWriteCount = aCount;
10646   return NS_OK;
10647 }
10648 
AddHeadersToChannel(nsIInputStream * aHeadersData,nsIChannel * aGenericChannel)10649 nsresult nsDocShell::AddHeadersToChannel(nsIInputStream* aHeadersData,
10650                                          nsIChannel* aGenericChannel) {
10651   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10652   NS_ENSURE_STATE(httpChannel);
10653 
10654   uint32_t numRead;
10655   nsAutoCString headersString;
10656   nsresult rv = aHeadersData->ReadSegments(
10657       AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10658   NS_ENSURE_SUCCESS(rv, rv);
10659 
10660   // used during the manipulation of the String from the InputStream
10661   nsAutoCString headerName;
10662   nsAutoCString headerValue;
10663   int32_t crlf;
10664   int32_t colon;
10665 
10666   //
10667   // Iterate over the headersString: for each "\r\n" delimited chunk,
10668   // add the value as a header to the nsIHttpChannel
10669   //
10670 
10671   static const char kWhitespace[] = "\b\t\r\n ";
10672   while (true) {
10673     crlf = headersString.Find("\r\n");
10674     if (crlf == kNotFound) {
10675       return NS_OK;
10676     }
10677 
10678     const nsACString& oneHeader = StringHead(headersString, crlf);
10679 
10680     colon = oneHeader.FindChar(':');
10681     if (colon == kNotFound) {
10682       return NS_ERROR_UNEXPECTED;
10683     }
10684 
10685     headerName = StringHead(oneHeader, colon);
10686     headerValue = Substring(oneHeader, colon + 1);
10687 
10688     headerName.Trim(kWhitespace);
10689     headerValue.Trim(kWhitespace);
10690 
10691     headersString.Cut(0, crlf + 2);
10692 
10693     //
10694     // FINALLY: we can set the header!
10695     //
10696 
10697     rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10698     NS_ENSURE_SUCCESS(rv, rv);
10699   }
10700 
10701   NS_NOTREACHED("oops");
10702   return NS_ERROR_UNEXPECTED;
10703 }
10704 
DoChannelLoad(nsIChannel * aChannel,nsIURILoader * aURILoader,bool aBypassClassifier)10705 nsresult nsDocShell::DoChannelLoad(nsIChannel* aChannel,
10706                                    nsIURILoader* aURILoader,
10707                                    bool aBypassClassifier) {
10708   nsresult rv;
10709   // Mark the channel as being a document URI and allow content sniffing...
10710   nsLoadFlags loadFlags = 0;
10711   (void)aChannel->GetLoadFlags(&loadFlags);
10712   loadFlags |=
10713       nsIChannel::LOAD_DOCUMENT_URI | nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
10714 
10715   if (SandboxFlagsImplyCookies(mSandboxFlags)) {
10716     loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
10717   }
10718   // Load attributes depend on load type...
10719   switch (mLoadType) {
10720     case LOAD_HISTORY: {
10721       // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
10722       // push/replaceState (bug 669671).
10723       bool uriModified = false;
10724       if (mLSHE) {
10725         mLSHE->GetURIWasModified(&uriModified);
10726       }
10727 
10728       if (!uriModified) {
10729         loadFlags |= nsIRequest::VALIDATE_NEVER;
10730       }
10731       break;
10732     }
10733 
10734     case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
10735     case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
10736       loadFlags |=
10737           nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::LOAD_FRESH_CONNECTION;
10738       MOZ_FALLTHROUGH;
10739 
10740     case LOAD_RELOAD_CHARSET_CHANGE: {
10741       // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we only
10742       // want to force cache load for this channel, not the whole loadGroup.
10743       nsCOMPtr<nsICacheInfoChannel> cachingChannel =
10744           do_QueryInterface(aChannel);
10745       if (cachingChannel) {
10746         cachingChannel->SetAllowStaleCacheContent(true);
10747       }
10748       break;
10749     }
10750 
10751     case LOAD_RELOAD_NORMAL:
10752     case LOAD_REFRESH:
10753       loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10754       break;
10755 
10756     case LOAD_NORMAL_BYPASS_CACHE:
10757     case LOAD_NORMAL_BYPASS_PROXY:
10758     case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
10759     case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
10760     case LOAD_RELOAD_BYPASS_CACHE:
10761     case LOAD_RELOAD_BYPASS_PROXY:
10762     case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
10763     case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
10764     case LOAD_REPLACE_BYPASS_CACHE:
10765       loadFlags |=
10766           nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::LOAD_FRESH_CONNECTION;
10767       break;
10768 
10769     case LOAD_NORMAL:
10770     case LOAD_LINK:
10771       // Set cache checking flags
10772       switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
10773         case 0:
10774           loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
10775           break;
10776         case 1:
10777           loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10778           break;
10779         case 2:
10780           loadFlags |= nsIRequest::VALIDATE_NEVER;
10781           break;
10782       }
10783       break;
10784   }
10785 
10786   if (!aBypassClassifier) {
10787     loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
10788   }
10789 
10790   // If the user pressed shift-reload, then do not allow ServiceWorker
10791   // interception to occur. See step 12.1 of the SW HandleFetch algorithm.
10792   if (IsForceReloadType(mLoadType)) {
10793     loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
10794   }
10795 
10796   (void)aChannel->SetLoadFlags(loadFlags);
10797 
10798   uint32_t openFlags = 0;
10799   if (mLoadType == LOAD_LINK) {
10800     openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10801   }
10802   if (!mAllowContentRetargeting) {
10803     openFlags |= nsIURILoader::DONT_RETARGET;
10804   }
10805 
10806   // If anything fails here, make sure to clear our initial ClientSource.
10807   auto cleanupInitialClient =
10808       MakeScopeExit([&] { mInitialClientSource.reset(); });
10809 
10810   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10811   NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10812 
10813   MaybeCreateInitialClientSource();
10814 
10815   // Since we are loading a document we need to make sure the proper reserved
10816   // and initial client data is stored on the nsILoadInfo.  The
10817   // ClientChannelHelper does this and ensures that it is propagated properly
10818   // on redirects.  We pass no reserved client here so that the helper will
10819   // create the reserved ClientSource if necessary.
10820   Maybe<ClientInfo> noReservedClient;
10821   rv = AddClientChannelHelper(aChannel, Move(noReservedClient),
10822                               GetInitialClientInfo(),
10823                               win->EventTargetFor(TaskCategory::Other));
10824   NS_ENSURE_SUCCESS(rv, rv);
10825 
10826   rv = aURILoader->OpenURI(aChannel, openFlags, this);
10827   NS_ENSURE_SUCCESS(rv, rv);
10828 
10829   // We're about to load a new page and it may take time before necko
10830   // gives back any data, so main thread might have a chance to process a
10831   // collector slice
10832   nsJSContext::MaybeRunNextCollectorSlice(this, JS::gcreason::DOCSHELL);
10833 
10834   // Success.  Keep the initial ClientSource if it exists.
10835   cleanupInitialClient.release();
10836 
10837   return NS_OK;
10838 }
10839 
ScrollToAnchor(bool aCurHasRef,bool aNewHasRef,nsACString & aNewHash,uint32_t aLoadType)10840 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10841                                     nsACString& aNewHash, uint32_t aLoadType) {
10842   if (!mCurrentURI) {
10843     return NS_OK;
10844   }
10845 
10846   nsCOMPtr<nsIPresShell> shell = GetPresShell();
10847   if (!shell) {
10848     // If we failed to get the shell, or if there is no shell,
10849     // nothing left to do here.
10850     return NS_OK;
10851   }
10852 
10853   nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
10854   if (rootScroll) {
10855     rootScroll->ClearDidHistoryRestore();
10856   }
10857 
10858   // If we have no new anchor, we do not want to scroll, unless there is a
10859   // current anchor and we are doing a history load.  So return if we have no
10860   // new anchor, and there is no current anchor or the load is not a history
10861   // load.
10862   if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10863     return NS_OK;
10864   }
10865 
10866   // Both the new and current URIs refer to the same page. We can now
10867   // browse to the hash stored in the new URI.
10868 
10869   if (!aNewHash.IsEmpty()) {
10870     // anchor is there, but if it's a load from history,
10871     // we don't have any anchor jumping to do
10872     bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10873 
10874     // We assume that the bytes are in UTF-8, as it says in the
10875     // spec:
10876     // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
10877 
10878     // We try the UTF-8 string first, and then try the document's
10879     // charset (see below).  If the string is not UTF-8,
10880     // conversion will fail and give us an empty Unicode string.
10881     // In that case, we should just fall through to using the
10882     // page's charset.
10883     nsresult rv = NS_ERROR_FAILURE;
10884     NS_ConvertUTF8toUTF16 uStr(aNewHash);
10885     if (!uStr.IsEmpty()) {
10886       rv = shell->GoToAnchor(uStr, scroll, nsIPresShell::SCROLL_SMOOTH_AUTO);
10887     }
10888 
10889     if (NS_FAILED(rv)) {
10890       char* str = ToNewCString(aNewHash);
10891       if (!str) {
10892         return NS_ERROR_OUT_OF_MEMORY;
10893       }
10894       nsUnescape(str);
10895       NS_ConvertUTF8toUTF16 utf16Str(str);
10896       if (!utf16Str.IsEmpty()) {
10897         rv = shell->GoToAnchor(utf16Str, scroll,
10898                                nsIPresShell::SCROLL_SMOOTH_AUTO);
10899       }
10900       free(str);
10901     }
10902 
10903     // Above will fail if the anchor name is not UTF-8.  Need to
10904     // convert from document charset to unicode.
10905     if (NS_FAILED(rv)) {
10906       // Get a document charset
10907       NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
10908       nsIDocument* doc = mContentViewer->GetDocument();
10909       NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
10910       nsAutoCString charset;
10911       doc->GetDocumentCharacterSet()->Name(charset);
10912 
10913       nsCOMPtr<nsITextToSubURI> textToSubURI =
10914           do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10915       NS_ENSURE_SUCCESS(rv, rv);
10916 
10917       // Unescape and convert to unicode
10918       nsAutoString uStr;
10919 
10920       rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
10921       NS_ENSURE_SUCCESS(rv, rv);
10922 
10923       // Ignore return value of GoToAnchor, since it will return an error
10924       // if there is no such anchor in the document, which is actually a
10925       // success condition for us (we want to update the session history
10926       // with the new URI no matter whether we actually scrolled
10927       // somewhere).
10928       //
10929       // When aNewHash contains "%00", unescaped string may be empty.
10930       // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10931       shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
10932                         nsIPresShell::SCROLL_SMOOTH_AUTO);
10933     }
10934   } else {
10935     // Tell the shell it's at an anchor, without scrolling.
10936     shell->GoToAnchor(EmptyString(), false);
10937 
10938     // An empty anchor was found, but if it's a load from history,
10939     // we don't have to jump to the top of the page. Scrollbar
10940     // position will be restored by the caller, based on positions
10941     // stored in session history.
10942     if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
10943       return NS_OK;
10944     }
10945     // An empty anchor. Scroll to the top of the page.  Ignore the
10946     // return value; failure to scroll here (e.g. if there is no
10947     // root scrollframe) is not grounds for canceling the load!
10948     SetCurScrollPosEx(0, 0);
10949   }
10950 
10951   return NS_OK;
10952 }
10953 
SetupReferrerFromChannel(nsIChannel * aChannel)10954 void nsDocShell::SetupReferrerFromChannel(nsIChannel* aChannel) {
10955   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10956   if (httpChannel) {
10957     nsCOMPtr<nsIURI> referrer;
10958     nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
10959     if (NS_SUCCEEDED(rv)) {
10960       SetReferrerURI(referrer);
10961     }
10962     uint32_t referrerPolicy;
10963     rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
10964     if (NS_SUCCEEDED(rv)) {
10965       SetReferrerPolicy(referrerPolicy);
10966     }
10967   }
10968 }
10969 
OnNewURI(nsIURI * aURI,nsIChannel * aChannel,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,uint32_t aLoadType,bool aFireOnLocationChange,bool aAddToGlobalHistory,bool aCloneSHChildren)10970 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10971                           nsIPrincipal* aTriggeringPrincipal,
10972                           nsIPrincipal* aPrincipalToInherit, uint32_t aLoadType,
10973                           bool aFireOnLocationChange, bool aAddToGlobalHistory,
10974                           bool aCloneSHChildren) {
10975   NS_PRECONDITION(aURI, "uri is null");
10976   NS_PRECONDITION(!aChannel || !aTriggeringPrincipal,
10977                   "Shouldn't have both set");
10978 
10979   MOZ_ASSERT(!aPrincipalToInherit ||
10980              (aPrincipalToInherit && aTriggeringPrincipal));
10981 
10982 #if defined(DEBUG)
10983   if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10984     nsAutoCString chanName;
10985     if (aChannel) {
10986       aChannel->GetName(chanName);
10987     } else {
10988       chanName.AssignLiteral("<no channel>");
10989     }
10990 
10991     MOZ_LOG(gDocShellLog, LogLevel::Debug,
10992             ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10993              aURI->GetSpecOrDefault().get(), chanName.get(), aLoadType));
10994   }
10995 #endif
10996 
10997   bool equalUri = false;
10998 
10999   // Get the post data and the HTTP response code from the channel.
11000   uint32_t responseStatus = 0;
11001   nsCOMPtr<nsIInputStream> inputStream;
11002   if (aChannel) {
11003     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11004 
11005     // Check if the HTTPChannel is hiding under a multiPartChannel
11006     if (!httpChannel) {
11007       GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11008     }
11009 
11010     if (httpChannel) {
11011       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11012       if (uploadChannel) {
11013         uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11014       }
11015 
11016       // If the response status indicates an error, unlink this session
11017       // history entry from any entries sharing its document.
11018       nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
11019       if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
11020         mLSHE->AbandonBFCacheEntry();
11021       }
11022     }
11023   }
11024 
11025   // Determine if this type of load should update history.
11026   bool updateGHistory =
11027       !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
11028         aLoadType & LOAD_CMD_HISTORY);
11029 
11030   // We don't update session history on reload unless we're loading
11031   // an iframe in shift-reload case.
11032   bool updateSHistory =
11033       updateGHistory && (!(aLoadType & LOAD_CMD_RELOAD) ||
11034                          (IsForceReloadType(aLoadType) && IsFrame()));
11035 
11036   // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
11037   // current frame or in the root docshell.
11038   nsCOMPtr<nsISHistory> rootSH = mSessionHistory;
11039   if (!rootSH) {
11040     // Get the handle to SH from the root docshell
11041     GetRootSessionHistory(getter_AddRefs(rootSH));
11042     if (!rootSH) {
11043       updateSHistory = false;
11044       updateGHistory = false;  // XXX Why global history too?
11045     }
11046   }
11047 
11048   // Check if the url to be loaded is the same as the one already loaded.
11049   if (mCurrentURI) {
11050     aURI->Equals(mCurrentURI, &equalUri);
11051   }
11052 
11053 #ifdef DEBUG
11054   bool shAvailable = (rootSH != nullptr);
11055 
11056   // XXX This log message is almost useless because |updateSHistory|
11057   //     and |updateGHistory| are not correct at this point.
11058 
11059   MOZ_LOG(gDocShellLog, LogLevel::Debug,
11060           ("  shAvailable=%i updateSHistory=%i updateGHistory=%i"
11061            " equalURI=%i\n",
11062            shAvailable, updateSHistory, updateGHistory, equalUri));
11063 
11064   if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
11065     // XXX mCurrentURI can be changed from any caller regardless what actual
11066     // loaded document is, so testing mCurrentURI isn't really a reliable way.
11067     // Session restore is one example which changes current URI in order to
11068     // show address before loading. See bug 1301399.
11069     NS_ASSERTION(NS_IsAboutBlank(mCurrentURI),
11070                  "no SHEntry for a non-transient viewer?");
11071   }
11072 #endif
11073 
11074   /* If the url to be loaded is the same as the one already there,
11075    * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
11076    * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
11077    * AddToSessionHistory() won't mess with the current SHEntry and
11078    * if this page has any frame children, it also will be handled
11079    * properly. see bug 83684
11080    *
11081    * NB: If mOSHE is null but we have a current URI, then it means
11082    * that we must be at the transient about:blank content viewer
11083    * (asserted above) and we should let the normal load continue,
11084    * since there's nothing to replace.
11085    *
11086    * XXX Hopefully changing the loadType at this time will not hurt
11087    *  anywhere. The other way to take care of sequentially repeating
11088    *  frameset pages is to add new methods to nsIDocShellTreeItem.
11089    * Hopefully I don't have to do that.
11090    */
11091   if (equalUri && mOSHE &&
11092       (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
11093        mLoadType == LOAD_STOP_CONTENT) &&
11094       !inputStream) {
11095     mLoadType = LOAD_NORMAL_REPLACE;
11096   }
11097 
11098   // If this is a refresh to the currently loaded url, we don't
11099   // have to update session or global history.
11100   if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
11101     SetHistoryEntry(&mLSHE, mOSHE);
11102   }
11103 
11104   /* If the user pressed shift-reload, cache will create a new cache key
11105    * for the page. Save the new cacheKey in Session History.
11106    * see bug 90098
11107    */
11108   if (aChannel && IsForceReloadType(aLoadType)) {
11109     MOZ_ASSERT(!updateSHistory || IsFrame(),
11110                "We shouldn't be updating session history for forced"
11111                " reloads unless we're in a newly created iframe!");
11112 
11113     nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
11114     nsCOMPtr<nsISupports> cacheKey;
11115     // Get the Cache Key and store it in SH.
11116     if (cacheChannel) {
11117       cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
11118     }
11119     // If we already have a loading history entry, store the new cache key
11120     // in it.  Otherwise, since we're doing a reload and won't be updating
11121     // our history entry, store the cache key in our current history entry.
11122     if (mLSHE) {
11123       mLSHE->SetCacheKey(cacheKey);
11124     } else if (mOSHE) {
11125       mOSHE->SetCacheKey(cacheKey);
11126     }
11127 
11128     // Since we're force-reloading, clear all the sub frame history.
11129     ClearFrameHistory(mLSHE);
11130     ClearFrameHistory(mOSHE);
11131   }
11132 
11133   // Clear subframe history on refresh.
11134   // XXX: history.go(0) won't go this path as aLoadType is LOAD_HISTORY in this
11135   // case. One should re-validate after bug 1331865 fixed.
11136   if (aLoadType == LOAD_REFRESH) {
11137     ClearFrameHistory(mLSHE);
11138     ClearFrameHistory(mOSHE);
11139   }
11140 
11141   if (updateSHistory) {
11142     // Update session history if necessary...
11143     if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11144       /* This is  a fresh page getting loaded for the first time
11145        *.Create a Entry for it and add it to SH, if this is the
11146        * rootDocShell
11147        */
11148       (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11149                                 aPrincipalToInherit, aCloneSHChildren,
11150                                 getter_AddRefs(mLSHE));
11151     }
11152   } else if (mSessionHistory && mLSHE && mURIResultedInDocument) {
11153     // Even if we don't add anything to SHistory, ensure the current index
11154     // points to the same SHEntry as our mLSHE.
11155     int32_t index = 0;
11156     mSessionHistory->GetRequestedIndex(&index);
11157     if (index == -1) {
11158       mSessionHistory->GetIndex(&index);
11159     }
11160     nsCOMPtr<nsISHEntry> currentSH;
11161     mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(currentSH));
11162     if (currentSH != mLSHE) {
11163       nsCOMPtr<nsISHistoryInternal> shPrivate =
11164           do_QueryInterface(mSessionHistory);
11165       shPrivate->ReplaceEntry(index, mLSHE);
11166     }
11167   }
11168 
11169   // If this is a POST request, we do not want to include this in global
11170   // history.
11171   if (updateGHistory && aAddToGlobalHistory && !ChannelIsPost(aChannel)) {
11172     nsCOMPtr<nsIURI> previousURI;
11173     uint32_t previousFlags = 0;
11174 
11175     if (aLoadType & LOAD_CMD_RELOAD) {
11176       // On a reload request, we don't set redirecting flags.
11177       previousURI = aURI;
11178     } else {
11179       ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11180     }
11181 
11182     // Note: We don't use |referrer| when our global history is
11183     //       based on IHistory.
11184     nsCOMPtr<nsIURI> referrer;
11185     // Treat referrer as null if there is an error getting it.
11186     (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
11187 
11188     AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
11189   }
11190 
11191   // If this was a history load or a refresh, or it was a history load but
11192   // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11193   // in session history.
11194   if (rootSH && ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11195                  mLoadType == LOAD_NORMAL_REPLACE)) {
11196     nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
11197     if (shInternal) {
11198       rootSH->GetIndex(&mPreviousTransIndex);
11199       shInternal->UpdateIndex();
11200       rootSH->GetIndex(&mLoadedTransIndex);
11201 #ifdef DEBUG_PAGE_CACHE
11202       printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
11203              mLoadedTransIndex);
11204 #endif
11205     }
11206   }
11207 
11208   // aCloneSHChildren exactly means "we are not loading a new document".
11209   uint32_t locationFlags =
11210       aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11211 
11212   bool onLocationChangeNeeded =
11213       SetCurrentURI(aURI, aChannel, aFireOnLocationChange, locationFlags);
11214   // Make sure to store the referrer from the channel, if any
11215   SetupReferrerFromChannel(aChannel);
11216   return onLocationChangeNeeded;
11217 }
11218 
OnLoadingSite(nsIChannel * aChannel,bool aFireOnLocationChange,bool aAddToGlobalHistory)11219 bool nsDocShell::OnLoadingSite(nsIChannel* aChannel, bool aFireOnLocationChange,
11220                                bool aAddToGlobalHistory) {
11221   nsCOMPtr<nsIURI> uri;
11222   // If this a redirect, use the final url (uri)
11223   // else use the original url
11224   //
11225   // Note that this should match what documents do (see nsDocument::Reset).
11226   NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
11227   NS_ENSURE_TRUE(uri, false);
11228 
11229   // Pass false for aCloneSHChildren, since we're loading a new page here.
11230   return OnNewURI(uri, aChannel, nullptr, nullptr, mLoadType,
11231                   aFireOnLocationChange, aAddToGlobalHistory, false);
11232 }
11233 
SetReferrerURI(nsIURI * aURI)11234 void nsDocShell::SetReferrerURI(nsIURI* aURI) {
11235   mReferrerURI = aURI;  // This assigment addrefs
11236 }
11237 
SetReferrerPolicy(uint32_t aReferrerPolicy)11238 void nsDocShell::SetReferrerPolicy(uint32_t aReferrerPolicy) {
11239   mReferrerPolicy = aReferrerPolicy;
11240 }
11241 
11242 //*****************************************************************************
11243 // nsDocShell: Session History
11244 //*****************************************************************************
11245 
11246 NS_IMETHODIMP
AddState(JS::Handle<JS::Value> aData,const nsAString & aTitle,const nsAString & aURL,bool aReplace,JSContext * aCx)11247 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11248                      const nsAString& aURL, bool aReplace, JSContext* aCx) {
11249   // Implements History.pushState and History.replaceState
11250 
11251   // Here's what we do, roughly in the order specified by HTML5:
11252   // 1. Serialize aData using structured clone.
11253   // 2. If the third argument is present,
11254   //     a. Resolve the url, relative to the first script's base URL
11255   //     b. If (a) fails, raise a SECURITY_ERR
11256   //     c. Compare the resulting absolute URL to the document's address.  If
11257   //        any part of the URLs difer other than the <path>, <query>, and
11258   //        <fragment> components, raise a SECURITY_ERR and abort.
11259   // 3. If !aReplace:
11260   //     Remove from the session history all entries after the current entry,
11261   //     as we would after a regular navigation, and save the current
11262   //     entry's scroll position (bug 590573).
11263   // 4. As apropriate, either add a state object entry to the session history
11264   //    after the current entry with the following properties, or modify the
11265   //    current session history entry to set
11266   //      a. cloned data as the state object,
11267   //      b. if the third argument was present, the absolute URL found in
11268   //         step 2
11269   //    Also clear the new history entry's POST data (see bug 580069).
11270   // 5. If aReplace is false (i.e. we're doing a pushState instead of a
11271   //    replaceState), notify bfcache that we've navigated to a new page.
11272   // 6. If the third argument is present, set the document's current address
11273   //    to the absolute URL found in step 2.
11274   //
11275   // It's important that this function not run arbitrary scripts after step 1
11276   // and before completing step 5.  For example, if a script called
11277   // history.back() before we completed step 5, bfcache might destroy an
11278   // active content viewer.  Since EvictOutOfRangeContentViewers at the end of
11279   // step 5 might run script, we can't just put a script blocker around the
11280   // critical section.
11281   //
11282   // Note that we completely ignore the aTitle parameter.
11283 
11284   nsresult rv;
11285 
11286   // Don't clobber the load type of an existing network load.
11287   AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11288 
11289   // pushState effectively becomes replaceState when we've started a network
11290   // load but haven't adopted its document yet.  This mirrors what we do with
11291   // changes to the hash at this stage of the game.
11292   if (JustStartedNetworkLoad()) {
11293     aReplace = true;
11294   }
11295 
11296   nsCOMPtr<nsIDocument> document = GetDocument();
11297   NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11298 
11299   // Step 1: Serialize aData using structured clone.
11300   nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11301 
11302   // scContainer->Init might cause arbitrary JS to run, and this code might
11303   // navigate the page we're on, potentially to a different origin! (bug
11304   // 634834)  To protect against this, we abort if our principal changes due
11305   // to the InitFromJSVal() call.
11306   {
11307     nsCOMPtr<nsIDocument> origDocument = GetDocument();
11308     if (!origDocument) {
11309       return NS_ERROR_DOM_SECURITY_ERR;
11310     }
11311     nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11312 
11313     scContainer = new nsStructuredCloneContainer();
11314     rv = scContainer->InitFromJSVal(aData, aCx);
11315     NS_ENSURE_SUCCESS(rv, rv);
11316 
11317     nsCOMPtr<nsIDocument> newDocument = GetDocument();
11318     if (!newDocument) {
11319       return NS_ERROR_DOM_SECURITY_ERR;
11320     }
11321     nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11322 
11323     bool principalsEqual = false;
11324     origPrincipal->Equals(newPrincipal, &principalsEqual);
11325     NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11326   }
11327 
11328   // Check that the state object isn't too long.
11329   // Default max length: 640k bytes.
11330   int32_t maxStateObjSize =
11331       Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
11332   if (maxStateObjSize < 0) {
11333     maxStateObjSize = 0;
11334   }
11335 
11336   uint64_t scSize;
11337   rv = scContainer->GetSerializedNBytes(&scSize);
11338   NS_ENSURE_SUCCESS(rv, rv);
11339 
11340   NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11341 
11342   // Step 2: Resolve aURL
11343   bool equalURIs = true;
11344   nsCOMPtr<nsIURI> currentURI;
11345   if (sURIFixup && mCurrentURI) {
11346     rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(currentURI));
11347     NS_ENSURE_SUCCESS(rv, rv);
11348   } else {
11349     currentURI = mCurrentURI;
11350   }
11351   nsCOMPtr<nsIURI> oldURI = currentURI;
11352   nsCOMPtr<nsIURI> newURI;
11353   if (aURL.Length() == 0) {
11354     newURI = currentURI;
11355   } else {
11356     // 2a: Resolve aURL relative to mURI
11357 
11358     nsIURI* docBaseURI = document->GetDocBaseURI();
11359     if (!docBaseURI) {
11360       return NS_ERROR_FAILURE;
11361     }
11362 
11363     nsAutoCString spec;
11364     docBaseURI->GetSpec(spec);
11365 
11366     rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11367                    document->GetDocumentCharacterSet(), docBaseURI);
11368 
11369     // 2b: If 2a fails, raise a SECURITY_ERR
11370     if (NS_FAILED(rv)) {
11371       return NS_ERROR_DOM_SECURITY_ERR;
11372     }
11373 
11374     // 2c: Same-origin check.
11375     if (!nsContentUtils::URIIsLocalFile(newURI)) {
11376       // In addition to checking that the security manager says that
11377       // the new URI has the same origin as our current URI, we also
11378       // check that the two URIs have the same userpass. (The
11379       // security manager says that |http://foo.com| and
11380       // |http://me@foo.com| have the same origin.)  currentURI
11381       // won't contain the password part of the userpass, so this
11382       // means that it's never valid to specify a password in a
11383       // pushState or replaceState URI.
11384 
11385       nsCOMPtr<nsIScriptSecurityManager> secMan =
11386           do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11387       NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11388 
11389       // It's very important that we check that newURI is of the same
11390       // origin as currentURI, not docBaseURI, because a page can
11391       // set docBaseURI arbitrarily to any domain.
11392       nsAutoCString currentUserPass, newUserPass;
11393       NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11394                         NS_ERROR_FAILURE);
11395       NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11396       if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true)) ||
11397           !currentUserPass.Equals(newUserPass)) {
11398         return NS_ERROR_DOM_SECURITY_ERR;
11399       }
11400     } else {
11401       // It's a file:// URI
11402       nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
11403           do_QueryInterface(document);
11404 
11405       if (!docScriptObj) {
11406         return NS_ERROR_DOM_SECURITY_ERR;
11407       }
11408 
11409       nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
11410 
11411       if (!principal ||
11412           NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
11413         return NS_ERROR_DOM_SECURITY_ERR;
11414       }
11415     }
11416 
11417     if (currentURI) {
11418       currentURI->Equals(newURI, &equalURIs);
11419     } else {
11420       equalURIs = false;
11421     }
11422 
11423   }  // end of same-origin check
11424 
11425   // Step 3: Create a new entry in the session history. This will erase
11426   // all SHEntries after the new entry and make this entry the current
11427   // one.  This operation may modify mOSHE, which we need later, so we
11428   // keep a reference here.
11429   NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
11430   nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11431 
11432   mLoadType = LOAD_PUSHSTATE;
11433 
11434   nsCOMPtr<nsISHEntry> newSHEntry;
11435   if (!aReplace) {
11436     // Save the current scroll position (bug 590573).
11437     nscoord cx = 0, cy = 0;
11438     GetCurScrollPos(ScrollOrientation_X, &cx);
11439     GetCurScrollPos(ScrollOrientation_Y, &cy);
11440     mOSHE->SetScrollPosition(cx, cy);
11441 
11442     bool scrollRestorationIsManual = false;
11443     mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
11444 
11445     // Since we're not changing which page we have loaded, pass
11446     // true for aCloneChildren.
11447     rv = AddToSessionHistory(newURI, nullptr,
11448                              document->NodePrincipal(),  // triggeringPrincipal
11449                              nullptr, true, getter_AddRefs(newSHEntry));
11450     NS_ENSURE_SUCCESS(rv, rv);
11451 
11452     NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11453 
11454     // Session history entries created by pushState inherit scroll restoration
11455     // mode from the current entry.
11456     newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11457 
11458     // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11459     // two entries correspond to the same document.
11460     NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), NS_ERROR_FAILURE);
11461 
11462     // Set the new SHEntry's title (bug 655273).
11463     nsString title;
11464     mOSHE->GetTitle(getter_Copies(title));
11465     newSHEntry->SetTitle(title);
11466 
11467     // AddToSessionHistory may not modify mOSHE.  In case it doesn't,
11468     // we'll just set mOSHE here.
11469     mOSHE = newSHEntry;
11470 
11471   } else {
11472     newSHEntry = mOSHE;
11473     newSHEntry->SetURI(newURI);
11474     newSHEntry->SetOriginalURI(newURI);
11475     newSHEntry->SetLoadReplace(false);
11476   }
11477 
11478   // Step 4: Modify new/original session history entry and clear its POST
11479   // data, if there is any.
11480   newSHEntry->SetStateData(scContainer);
11481   newSHEntry->SetPostData(nullptr);
11482 
11483   // If this push/replaceState changed the document's current URI and the new
11484   // URI differs from the old URI in more than the hash, or if the old
11485   // SHEntry's URI was modified in this way by a push/replaceState call
11486   // set URIWasModified to true for the current SHEntry (bug 669671).
11487   bool sameExceptHashes = true, oldURIWasModified = false;
11488   newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
11489   oldOSHE->GetURIWasModified(&oldURIWasModified);
11490   newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
11491 
11492   // Step 5: If aReplace is false, indicating that we're doing a pushState
11493   // rather than a replaceState, notify bfcache that we've added a page to
11494   // the history so it can evict content viewers if appropriate. Otherwise
11495   // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
11496   // was replaced.
11497   nsCOMPtr<nsISHistory> rootSH;
11498   GetRootSessionHistory(getter_AddRefs(rootSH));
11499   NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
11500 
11501   nsCOMPtr<nsISHistoryInternal> internalSH = do_QueryInterface(rootSH);
11502   NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
11503 
11504   if (!aReplace) {
11505     int32_t curIndex = -1;
11506     rv = rootSH->GetIndex(&curIndex);
11507     if (NS_SUCCEEDED(rv) && curIndex > -1) {
11508       internalSH->EvictOutOfRangeContentViewers(curIndex);
11509     }
11510   } else {
11511     nsCOMPtr<nsISHEntry> rootSHEntry = nsSHistory::GetRootSHEntry(newSHEntry);
11512 
11513     int32_t index = -1;
11514     rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
11515     if (NS_SUCCEEDED(rv) && index > -1) {
11516       internalSH->ReplaceEntry(index, rootSHEntry);
11517     }
11518   }
11519 
11520   // Step 6: If the document's URI changed, update document's URI and update
11521   // global history.
11522   //
11523   // We need to call FireOnLocationChange so that the browser's address bar
11524   // gets updated and the back button is enabled, but we only need to
11525   // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11526   // since SetCurrentURI will call FireOnLocationChange for us.
11527   //
11528   // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11529   // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11530   // notification is allowed only when we know docshell is not loading a new
11531   // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11532   // FireOnLocationChange(...) breaks security UI.
11533   if (!equalURIs) {
11534     document->SetDocumentURI(newURI);
11535     // We can't trust SetCurrentURI to do always fire locationchange events
11536     // when we expect it to, so we hack around that by doing it ourselves...
11537     SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
11538     if (mLoadType != LOAD_ERROR_PAGE) {
11539       FireDummyOnLocationChange();
11540     }
11541 
11542     AddURIVisit(newURI, oldURI, oldURI, 0);
11543 
11544     // AddURIVisit doesn't set the title for the new URI in global history,
11545     // so do that here.
11546     UpdateGlobalHistoryTitle(newURI);
11547 
11548     // Inform the favicon service that our old favicon applies to this new
11549     // URI.
11550     CopyFavicon(oldURI, newURI, document->NodePrincipal(),
11551                 UsePrivateBrowsing());
11552   } else {
11553     FireDummyOnLocationChange();
11554   }
11555   document->SetStateObject(scContainer);
11556 
11557   return NS_OK;
11558 }
11559 
11560 NS_IMETHODIMP
GetCurrentScrollRestorationIsManual(bool * aIsManual)11561 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11562   *aIsManual = false;
11563   if (mOSHE) {
11564     mOSHE->GetScrollRestorationIsManual(aIsManual);
11565   }
11566 
11567   return NS_OK;
11568 }
11569 
11570 NS_IMETHODIMP
SetCurrentScrollRestorationIsManual(bool aIsManual)11571 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11572   if (mOSHE) {
11573     mOSHE->SetScrollRestorationIsManual(aIsManual);
11574   }
11575 
11576   return NS_OK;
11577 }
11578 
ShouldAddToSessionHistory(nsIURI * aURI,nsIChannel * aChannel)11579 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11580   // I believe none of the about: urls should go in the history. But then
11581   // that could just be me... If the intent is only deny about:blank then we
11582   // should just do a spec compare, rather than two gets of the scheme and
11583   // then the path.  -Gagan
11584   nsresult rv;
11585   nsAutoCString buf;
11586 
11587   rv = aURI->GetScheme(buf);
11588   if (NS_FAILED(rv)) {
11589     return false;
11590   }
11591 
11592   if (buf.EqualsLiteral("about")) {
11593     rv = aURI->GetPathQueryRef(buf);
11594     if (NS_FAILED(rv)) {
11595       return false;
11596     }
11597 
11598     if (buf.EqualsLiteral("blank")) {
11599       return false;
11600     }
11601     // We only want to add about:newtab if it's not privileged:
11602     if (buf.EqualsLiteral("newtab")) {
11603       NS_ENSURE_TRUE(aChannel, false);
11604       nsCOMPtr<nsIPrincipal> resultPrincipal;
11605       rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11606           aChannel, getter_AddRefs(resultPrincipal));
11607       NS_ENSURE_SUCCESS(rv, false);
11608       return !nsContentUtils::IsSystemPrincipal(resultPrincipal);
11609     }
11610   }
11611 
11612   return true;
11613 }
11614 
AddToSessionHistory(nsIURI * aURI,nsIChannel * aChannel,nsIPrincipal * aTriggeringPrincipal,nsIPrincipal * aPrincipalToInherit,bool aCloneChildren,nsISHEntry ** aNewEntry)11615 nsresult nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
11616                                          nsIPrincipal* aTriggeringPrincipal,
11617                                          nsIPrincipal* aPrincipalToInherit,
11618                                          bool aCloneChildren,
11619                                          nsISHEntry** aNewEntry) {
11620   NS_PRECONDITION(aURI, "uri is null");
11621   NS_PRECONDITION(!aChannel || !aTriggeringPrincipal,
11622                   "Shouldn't have both set");
11623 
11624 #if defined(DEBUG)
11625   if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11626     nsAutoCString chanName;
11627     if (aChannel) {
11628       aChannel->GetName(chanName);
11629     } else {
11630       chanName.AssignLiteral("<no channel>");
11631     }
11632 
11633     MOZ_LOG(gDocShellLog, LogLevel::Debug,
11634             ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11635              aURI->GetSpecOrDefault().get(), chanName.get()));
11636   }
11637 #endif
11638 
11639   nsresult rv = NS_OK;
11640   nsCOMPtr<nsISHEntry> entry;
11641 
11642   // Get a handle to the root docshell
11643   nsCOMPtr<nsIDocShellTreeItem> root;
11644   GetSameTypeRootTreeItem(getter_AddRefs(root));
11645   /*
11646    * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11647    * the existing SH entry in the page and replace the url and
11648    * other vitalities.
11649    */
11650   if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11651       root != static_cast<nsIDocShellTreeItem*>(this)) {
11652     // This is a subframe
11653     entry = mOSHE;
11654     nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
11655     if (shContainer) {
11656       int32_t childCount = 0;
11657       shContainer->GetChildCount(&childCount);
11658       // Remove all children of this entry
11659       for (int32_t i = childCount - 1; i >= 0; i--) {
11660         nsCOMPtr<nsISHEntry> child;
11661         shContainer->GetChildAt(i, getter_AddRefs(child));
11662         shContainer->RemoveChild(child);
11663       }
11664       entry->AbandonBFCacheEntry();
11665     }
11666   }
11667 
11668   // Create a new entry if necessary.
11669   if (!entry) {
11670     entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
11671 
11672     if (!entry) {
11673       return NS_ERROR_OUT_OF_MEMORY;
11674     }
11675   }
11676 
11677   // Get the post data & referrer
11678   nsCOMPtr<nsIInputStream> inputStream;
11679   nsCOMPtr<nsIURI> originalURI;
11680   nsCOMPtr<nsIURI> resultPrincipalURI;
11681   bool loadReplace = false;
11682   nsCOMPtr<nsIURI> referrerURI;
11683   uint32_t referrerPolicy = mozilla::net::RP_Unset;
11684   nsCOMPtr<nsISupports> cacheKey;
11685   nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11686   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11687   bool expired = false;
11688   bool discardLayoutState = false;
11689   nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11690   if (aChannel) {
11691     cacheChannel = do_QueryInterface(aChannel);
11692 
11693     /* If there is a caching channel, get the Cache Key and store it
11694      * in SH.
11695      */
11696     if (cacheChannel) {
11697       cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
11698     }
11699     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11700 
11701     // Check if the httpChannel is hiding under a multipartChannel
11702     if (!httpChannel) {
11703       GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11704     }
11705     if (httpChannel) {
11706       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11707       if (uploadChannel) {
11708         uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11709       }
11710       httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11711       uint32_t loadFlags;
11712       aChannel->GetLoadFlags(&loadFlags);
11713       loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11714       rv = httpChannel->GetReferrer(getter_AddRefs(referrerURI));
11715       MOZ_ASSERT(NS_SUCCEEDED(rv));
11716       rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
11717       MOZ_ASSERT(NS_SUCCEEDED(rv));
11718 
11719       discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11720     }
11721 
11722     nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
11723     if (loadInfo) {
11724       if (!triggeringPrincipal) {
11725         triggeringPrincipal = loadInfo->TriggeringPrincipal();
11726       }
11727 
11728       loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11729 
11730       // For now keep storing just the principal in the SHEntry.
11731       if (!principalToInherit) {
11732         if (loadInfo->GetLoadingSandboxed()) {
11733           if (loadInfo->LoadingPrincipal()) {
11734             principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11735                 loadInfo->LoadingPrincipal());
11736           } else {
11737             // get the OriginAttributes
11738             OriginAttributes attrs;
11739             loadInfo->GetOriginAttributes(&attrs);
11740             principalToInherit = NullPrincipal::Create(attrs);
11741           }
11742         } else {
11743           principalToInherit = loadInfo->PrincipalToInherit();
11744         }
11745       }
11746     }
11747   }
11748 
11749   // Title is set in nsDocShell::SetTitle()
11750   entry->Create(aURI,                 // uri
11751                 EmptyString(),        // Title
11752                 inputStream,          // Post data stream
11753                 nullptr,              // LayoutHistory state
11754                 cacheKey,             // CacheKey
11755                 mContentTypeHint,     // Content-type
11756                 triggeringPrincipal,  // Channel or provided principal
11757                 principalToInherit, mHistoryID, mDynamicallyCreated);
11758 
11759   entry->SetOriginalURI(originalURI);
11760   entry->SetResultPrincipalURI(resultPrincipalURI);
11761   entry->SetLoadReplace(loadReplace);
11762   entry->SetReferrerURI(referrerURI);
11763   entry->SetReferrerPolicy(referrerPolicy);
11764   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11765   if (inStrmChan) {
11766     bool isSrcdocChannel;
11767     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11768     if (isSrcdocChannel) {
11769       nsAutoString srcdoc;
11770       inStrmChan->GetSrcdocData(srcdoc);
11771       entry->SetSrcdocData(srcdoc);
11772       nsCOMPtr<nsIURI> baseURI;
11773       inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11774       entry->SetBaseURI(baseURI);
11775     }
11776   }
11777   /* If cache got a 'no-store', ask SH not to store
11778    * HistoryLayoutState. By default, SH will set this
11779    * flag to true and save HistoryLayoutState.
11780    */
11781   if (discardLayoutState) {
11782     entry->SetSaveLayoutStateFlag(false);
11783   }
11784   if (cacheChannel) {
11785     // Check if the page has expired from cache
11786     uint32_t expTime = 0;
11787     cacheChannel->GetCacheTokenExpirationTime(&expTime);
11788     uint32_t now = PRTimeToSeconds(PR_Now());
11789     if (expTime <= now) {
11790       expired = true;
11791     }
11792   }
11793   if (expired) {
11794     entry->SetExpirationStatus(true);
11795   }
11796 
11797   if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
11798     // If we need to clone our children onto the new session
11799     // history entry, do so now.
11800     if (aCloneChildren && mOSHE) {
11801       uint32_t cloneID;
11802       mOSHE->GetID(&cloneID);
11803       nsCOMPtr<nsISHEntry> newEntry;
11804       nsSHistory::CloneAndReplace(mOSHE, this, cloneID, entry, true,
11805                                   getter_AddRefs(newEntry));
11806       NS_ASSERTION(entry == newEntry,
11807                    "The new session history should be in the new entry");
11808     }
11809 
11810     // This is the root docshell
11811     bool addToSHistory =
11812         !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
11813     if (!addToSHistory) {
11814       // Replace current entry in session history; If the requested index is
11815       // valid, it indicates the loading was triggered by a history load, and
11816       // we should replace the entry at requested index instead.
11817       int32_t index = 0;
11818       mSessionHistory->GetRequestedIndex(&index);
11819       if (index == -1) {
11820         mSessionHistory->GetIndex(&index);
11821       }
11822       nsCOMPtr<nsISHistoryInternal> shPrivate =
11823           do_QueryInterface(mSessionHistory);
11824       // Replace the current entry with the new entry
11825       if (index >= 0) {
11826         if (shPrivate) {
11827           rv = shPrivate->ReplaceEntry(index, entry);
11828         }
11829       } else {
11830         // If we're trying to replace an inexistant shistory entry, append.
11831         addToSHistory = true;
11832       }
11833     }
11834 
11835     if (addToSHistory) {
11836       // Add to session history
11837       nsCOMPtr<nsISHistoryInternal> shPrivate =
11838           do_QueryInterface(mSessionHistory);
11839       NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
11840       mSessionHistory->GetIndex(&mPreviousTransIndex);
11841 
11842       bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11843       rv = shPrivate->AddEntry(entry, shouldPersist);
11844       mSessionHistory->GetIndex(&mLoadedTransIndex);
11845 #ifdef DEBUG_PAGE_CACHE
11846       printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
11847              mLoadedTransIndex);
11848 #endif
11849     }
11850   } else {
11851     // This is a subframe.
11852     if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11853       rv = AddChildSHEntryToParent(entry, mChildOffset, aCloneChildren);
11854     }
11855   }
11856 
11857   // Return the new SH entry...
11858   if (aNewEntry) {
11859     *aNewEntry = nullptr;
11860     if (NS_SUCCEEDED(rv)) {
11861       entry.forget(aNewEntry);
11862     }
11863   }
11864 
11865   return rv;
11866 }
11867 
LoadHistoryEntry(nsISHEntry * aEntry,uint32_t aLoadType)11868 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) {
11869   if (!IsNavigationAllowed()) {
11870     return NS_OK;
11871   }
11872 
11873   nsCOMPtr<nsIURI> uri;
11874   nsCOMPtr<nsIURI> originalURI;
11875   nsCOMPtr<nsIURI> resultPrincipalURI;
11876   bool loadReplace = false;
11877   nsCOMPtr<nsIInputStream> postData;
11878   nsCOMPtr<nsIURI> referrerURI;
11879   uint32_t referrerPolicy;
11880   nsAutoCString contentType;
11881   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
11882   nsCOMPtr<nsIPrincipal> principalToInherit;
11883 
11884   NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11885 
11886   NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
11887   NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)),
11888                     NS_ERROR_FAILURE);
11889   NS_ENSURE_SUCCESS(
11890       aEntry->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)),
11891       NS_ERROR_FAILURE);
11892   NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace), NS_ERROR_FAILURE);
11893   NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
11894                     NS_ERROR_FAILURE);
11895   NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy),
11896                     NS_ERROR_FAILURE);
11897   NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
11898                     NS_ERROR_FAILURE);
11899   NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
11900   NS_ENSURE_SUCCESS(
11901       aEntry->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal)),
11902       NS_ERROR_FAILURE);
11903   NS_ENSURE_SUCCESS(
11904       aEntry->GetPrincipalToInherit(getter_AddRefs(principalToInherit)),
11905       NS_ERROR_FAILURE);
11906 
11907   // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
11908   // that's the only thing holding a ref to aEntry that will cause aEntry to
11909   // die while we're loading it.  So hold a strong ref to aEntry here, just
11910   // in case.
11911   nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11912   bool isJS;
11913   nsresult rv = uri->SchemeIs("javascript", &isJS);
11914   if (NS_FAILED(rv) || isJS) {
11915     // We're loading a URL that will execute script from inside asyncOpen.
11916     // Replace the current document with about:blank now to prevent
11917     // anything from the current document from leaking into any JavaScript
11918     // code in the URL.
11919     // Don't cache the presentation if we're going to just reload the
11920     // current entry. Caching would lead to trying to save the different
11921     // content viewers in the same nsISHEntry object.
11922     rv = CreateAboutBlankContentViewer(principalToInherit, nullptr,
11923                                        aEntry != mOSHE);
11924 
11925     if (NS_FAILED(rv)) {
11926       // The creation of the intermittent about:blank content
11927       // viewer failed for some reason (potentially because the
11928       // user prevented it). Interrupt the history load.
11929       return NS_OK;
11930     }
11931 
11932     if (!triggeringPrincipal) {
11933       // Ensure that we have a triggeringPrincipal.  Otherwise javascript:
11934       // URIs will pick it up from the about:blank page we just loaded,
11935       // and we don't really want even that in this case.
11936       triggeringPrincipal = NullPrincipal::CreateWithInheritedAttributes(this);
11937     }
11938   }
11939 
11940   /* If there is a valid postdata *and* the user pressed
11941    * reload or shift-reload, take user's permission before we
11942    * repost the data to the server.
11943    */
11944   if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
11945     bool repost;
11946     rv = ConfirmRepost(&repost);
11947     if (NS_FAILED(rv)) {
11948       return rv;
11949     }
11950 
11951     // If the user pressed cancel in the dialog, return.  We're done here.
11952     if (!repost) {
11953       return NS_BINDING_ABORTED;
11954     }
11955   }
11956 
11957   // Do not inherit principal from document (security-critical!);
11958   uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
11959 
11960   nsAutoString srcdoc;
11961   bool isSrcdoc;
11962   nsCOMPtr<nsIURI> baseURI;
11963   aEntry->GetIsSrcdocEntry(&isSrcdoc);
11964   if (isSrcdoc) {
11965     aEntry->GetSrcdocData(srcdoc);
11966     aEntry->GetBaseURI(getter_AddRefs(baseURI));
11967     flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
11968   } else {
11969     srcdoc = VoidString();
11970   }
11971 
11972   // If there is no valid triggeringPrincipal, we deny the load
11973   MOZ_ASSERT(triggeringPrincipal,
11974              "need a valid triggeringPrincipal to load from history");
11975   if (!triggeringPrincipal) {
11976     return NS_ERROR_FAILURE;
11977   }
11978 
11979   // Passing nullptr as aSourceDocShell gives the same behaviour as before
11980   // aSourceDocShell was introduced. According to spec we should be passing
11981   // the source browsing context that was used when the history entry was
11982   // first created. bug 947716 has been created to address this issue.
11983   Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
11984   emplacedResultPrincipalURI.emplace(Move(resultPrincipalURI));
11985   rv = InternalLoad(uri, originalURI, emplacedResultPrincipalURI, false,
11986                     loadReplace,
11987                     false,  // IsFromProcessingFrameAttributes
11988                     referrerURI, referrerPolicy, triggeringPrincipal,
11989                     principalToInherit, flags,
11990                     EmptyString(),      // No window target
11991                     contentType.get(),  // Type hint
11992                     VoidString(),       // No forced file download
11993                     postData,           // Post data stream
11994                     -1,                 // Post data stream length
11995                     nullptr,            // No headers stream
11996                     aLoadType,          // Load type
11997                     aEntry,             // SHEntry
11998                     true, srcdoc,
11999                     nullptr,  // Source docshell, see comment above
12000                     baseURI,
12001                     nullptr,   // No nsIDocShell
12002                     nullptr);  // No nsIRequest
12003   return rv;
12004 }
12005 
12006 NS_IMETHODIMP
GetShouldSaveLayoutState(bool * aShould)12007 nsDocShell::GetShouldSaveLayoutState(bool* aShould) {
12008   *aShould = false;
12009   if (mOSHE) {
12010     // Don't capture historystate and save it in history
12011     // if the page asked not to do so.
12012     mOSHE->GetSaveLayoutStateFlag(aShould);
12013   }
12014 
12015   return NS_OK;
12016 }
12017 
PersistLayoutHistoryState()12018 nsresult nsDocShell::PersistLayoutHistoryState() {
12019   nsresult rv = NS_OK;
12020 
12021   if (mOSHE) {
12022     bool scrollRestorationIsManual = false;
12023     mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
12024 
12025     nsCOMPtr<nsIPresShell> shell = GetPresShell();
12026     nsCOMPtr<nsILayoutHistoryState> layoutState;
12027     if (shell) {
12028       rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
12029     } else if (scrollRestorationIsManual) {
12030       // Even if we don't have layout anymore, we may want to reset the current
12031       // scroll state in layout history.
12032       GetLayoutHistoryState(getter_AddRefs(layoutState));
12033     }
12034 
12035     if (scrollRestorationIsManual && layoutState) {
12036       layoutState->ResetScrollState();
12037     }
12038   }
12039 
12040   return rv;
12041 }
12042 
SwapHistoryEntries(nsISHEntry * aOldEntry,nsISHEntry * aNewEntry)12043 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12044                                     nsISHEntry* aNewEntry) {
12045   if (aOldEntry == mOSHE) {
12046     mOSHE = aNewEntry;
12047   }
12048 
12049   if (aOldEntry == mLSHE) {
12050     mLSHE = aNewEntry;
12051   }
12052 }
12053 
SetHistoryEntry(nsCOMPtr<nsISHEntry> * aPtr,nsISHEntry * aEntry)12054 void nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr,
12055                                  nsISHEntry* aEntry) {
12056   // We need to sync up the docshell and session history trees for
12057   // subframe navigation.  If the load was in a subframe, we forward up to
12058   // the root docshell, which will then recursively sync up all docshells
12059   // to their corresponding entries in the new session history tree.
12060   // If we don't do this, then we can cache a content viewer on the wrong
12061   // cloned entry, and subsequently restore it at the wrong time.
12062 
12063   nsISHEntry* newRootEntry = nsSHistory::GetRootSHEntry(aEntry);
12064   if (newRootEntry) {
12065     // newRootEntry is now the new root entry.
12066     // Find the old root entry as well.
12067 
12068     // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
12069     // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
12070     nsCOMPtr<nsISHEntry> oldRootEntry = nsSHistory::GetRootSHEntry(*aPtr);
12071     if (oldRootEntry) {
12072       nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
12073       GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
12074       nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
12075       if (rootShell) {  // if we're the root just set it, nothing to swap
12076         nsSHistory::SwapEntriesData data = {this, newRootEntry};
12077         nsIDocShell* rootIDocShell = static_cast<nsIDocShell*>(rootShell);
12078         nsDocShell* rootDocShell = static_cast<nsDocShell*>(rootIDocShell);
12079 
12080 #ifdef DEBUG
12081         nsresult rv =
12082 #endif
12083             nsSHistory::SetChildHistoryEntry(oldRootEntry, rootDocShell, 0,
12084                                              &data);
12085         NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
12086       }
12087     }
12088   }
12089 
12090   *aPtr = aEntry;
12091 }
12092 
GetRootSessionHistory(nsISHistory ** aReturn)12093 nsresult nsDocShell::GetRootSessionHistory(nsISHistory** aReturn) {
12094   nsresult rv;
12095 
12096   nsCOMPtr<nsIDocShellTreeItem> root;
12097   // Get the root docshell
12098   rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
12099   // QI to nsIWebNavigation
12100   nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));
12101   if (rootAsWebnav) {
12102     // Get the handle to SH from the root docshell
12103     rv = rootAsWebnav->GetSessionHistory(aReturn);
12104   }
12105   return rv;
12106 }
12107 
GetHttpChannel(nsIChannel * aChannel,nsIHttpChannel ** aReturn)12108 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12109                                     nsIHttpChannel** aReturn) {
12110   NS_ENSURE_ARG_POINTER(aReturn);
12111   if (!aChannel) {
12112     return NS_ERROR_FAILURE;
12113   }
12114 
12115   nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12116   if (multiPartChannel) {
12117     nsCOMPtr<nsIChannel> baseChannel;
12118     multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12119     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12120     *aReturn = httpChannel;
12121     NS_IF_ADDREF(*aReturn);
12122   }
12123   return NS_OK;
12124 }
12125 
ShouldDiscardLayoutState(nsIHttpChannel * aChannel)12126 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12127   // By default layout State will be saved.
12128   if (!aChannel) {
12129     return false;
12130   }
12131 
12132   // figure out if SH should be saving layout state
12133   bool noStore = false;
12134   Unused << aChannel->IsNoStoreResponse(&noStore);
12135   return noStore;
12136 }
12137 
12138 NS_IMETHODIMP
GetEditor(nsIEditor ** aEditor)12139 nsDocShell::GetEditor(nsIEditor** aEditor) {
12140   NS_ENSURE_ARG_POINTER(aEditor);
12141   RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12142   htmlEditor.forget(aEditor);
12143   return NS_OK;
12144 }
12145 
12146 NS_IMETHODIMP
SetEditor(nsIEditor * aEditor)12147 nsDocShell::SetEditor(nsIEditor* aEditor) {
12148   HTMLEditor* htmlEditor = aEditor ? aEditor->AsHTMLEditor() : nullptr;
12149   // If TextEditor comes, throw an error.
12150   if (aEditor && !htmlEditor) {
12151     return NS_ERROR_INVALID_ARG;
12152   }
12153   return SetHTMLEditorInternal(htmlEditor);
12154 }
12155 
GetHTMLEditorInternal()12156 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12157   return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12158 }
12159 
SetHTMLEditorInternal(HTMLEditor * aHTMLEditor)12160 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12161   if (!aHTMLEditor && !mEditorData) {
12162     return NS_OK;
12163   }
12164 
12165   nsresult rv = EnsureEditorData();
12166   if (NS_FAILED(rv)) {
12167     return rv;
12168   }
12169 
12170   return mEditorData->SetHTMLEditor(aHTMLEditor);
12171 }
12172 
12173 NS_IMETHODIMP
GetEditable(bool * aEditable)12174 nsDocShell::GetEditable(bool* aEditable) {
12175   NS_ENSURE_ARG_POINTER(aEditable);
12176   *aEditable = mEditorData && mEditorData->GetEditable();
12177   return NS_OK;
12178 }
12179 
12180 NS_IMETHODIMP
GetHasEditingSession(bool * aHasEditingSession)12181 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12182   NS_ENSURE_ARG_POINTER(aHasEditingSession);
12183 
12184   if (mEditorData) {
12185     nsCOMPtr<nsIEditingSession> editingSession;
12186     mEditorData->GetEditingSession(getter_AddRefs(editingSession));
12187     *aHasEditingSession = (editingSession.get() != nullptr);
12188   } else {
12189     *aHasEditingSession = false;
12190   }
12191 
12192   return NS_OK;
12193 }
12194 
12195 NS_IMETHODIMP
MakeEditable(bool aInWaitForUriLoad)12196 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12197   nsresult rv = EnsureEditorData();
12198   if (NS_FAILED(rv)) {
12199     return rv;
12200   }
12201 
12202   return mEditorData->MakeEditable(aInWaitForUriLoad);
12203 }
12204 
ChannelIsPost(nsIChannel * aChannel)12205 bool nsDocShell::ChannelIsPost(nsIChannel* aChannel) {
12206   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
12207   if (!httpChannel) {
12208     return false;
12209   }
12210 
12211   nsAutoCString method;
12212   Unused << httpChannel->GetRequestMethod(method);
12213   return method.EqualsLiteral("POST");
12214 }
12215 
ExtractLastVisit(nsIChannel * aChannel,nsIURI ** aURI,uint32_t * aChannelRedirectFlags)12216 void nsDocShell::ExtractLastVisit(nsIChannel* aChannel, nsIURI** aURI,
12217                                   uint32_t* aChannelRedirectFlags) {
12218   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12219   if (!props) {
12220     return;
12221   }
12222 
12223   nsresult rv = props->GetPropertyAsInterface(
12224       NS_LITERAL_STRING("docshell.previousURI"), NS_GET_IID(nsIURI),
12225       reinterpret_cast<void**>(aURI));
12226 
12227   if (NS_FAILED(rv)) {
12228     // There is no last visit for this channel, so this must be the first
12229     // link.  Link the visit to the referrer of this request, if any.
12230     // Treat referrer as null if there is an error getting it.
12231     (void)NS_GetReferrerFromChannel(aChannel, aURI);
12232   } else {
12233     rv = props->GetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
12234                                     aChannelRedirectFlags);
12235 
12236     NS_WARNING_ASSERTION(
12237         NS_SUCCEEDED(rv),
12238         "Could not fetch previous flags, URI will be treated like referrer");
12239   }
12240 }
12241 
SaveLastVisit(nsIChannel * aChannel,nsIURI * aURI,uint32_t aChannelRedirectFlags)12242 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12243                                uint32_t aChannelRedirectFlags) {
12244   nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12245   if (!props || !aURI) {
12246     return;
12247   }
12248 
12249   props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
12250                                 aURI);
12251   props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
12252                              aChannelRedirectFlags);
12253 }
12254 
AddURIVisit(nsIURI * aURI,nsIURI * aReferrerURI,nsIURI * aPreviousURI,uint32_t aChannelRedirectFlags,uint32_t aResponseStatus)12255 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aReferrerURI,
12256                              nsIURI* aPreviousURI,
12257                              uint32_t aChannelRedirectFlags,
12258                              uint32_t aResponseStatus) {
12259   MOZ_ASSERT(aURI, "Visited URI is null!");
12260   MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE && mLoadType != LOAD_BYPASS_HISTORY,
12261              "Do not add error or bypass pages to global history");
12262 
12263   // Only content-type docshells save URI visits.  Also don't do
12264   // anything here if we're not supposed to use global history.
12265   if (mItemType != typeContent || !mUseGlobalHistory || UsePrivateBrowsing()) {
12266     return;
12267   }
12268 
12269   nsCOMPtr<IHistory> history = services::GetHistoryService();
12270 
12271   if (history) {
12272     uint32_t visitURIFlags = 0;
12273 
12274     if (!IsFrame()) {
12275       visitURIFlags |= IHistory::TOP_LEVEL;
12276     }
12277 
12278     if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12279       visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12280     } else if (aChannelRedirectFlags &
12281                nsIChannelEventSink::REDIRECT_PERMANENT) {
12282       visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12283     } else {
12284       MOZ_ASSERT(!aChannelRedirectFlags,
12285                  "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12286                  "if any flags in aChannelRedirectFlags is set.");
12287     }
12288 
12289     if (aResponseStatus >= 300 && aResponseStatus < 400) {
12290       visitURIFlags |= IHistory::REDIRECT_SOURCE;
12291     }
12292     // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12293     // simple retry attempt by the user is unlikely to solve them.
12294     // 408 is special cased, since may actually indicate a temporary
12295     // connection problem.
12296     else if (aResponseStatus != 408 &&
12297              ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12298               aResponseStatus == 505)) {
12299       visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12300     }
12301 
12302     (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
12303   } else if (mGlobalHistory) {
12304     // Falls back to sync global history interface.
12305     (void)mGlobalHistory->AddURI(aURI, !!aChannelRedirectFlags, !IsFrame(),
12306                                  aReferrerURI);
12307   }
12308 }
12309 
12310 //*****************************************************************************
12311 // nsDocShell: Helper Routines
12312 //*****************************************************************************
12313 
12314 NS_IMETHODIMP
SetLoadType(uint32_t aLoadType)12315 nsDocShell::SetLoadType(uint32_t aLoadType) {
12316   mLoadType = aLoadType;
12317   return NS_OK;
12318 }
12319 
12320 NS_IMETHODIMP
GetLoadType(uint32_t * aLoadType)12321 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12322   *aLoadType = mLoadType;
12323   return NS_OK;
12324 }
12325 
ConfirmRepost(bool * aRepost)12326 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12327   nsCOMPtr<nsIPrompt> prompter;
12328   CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
12329   if (!prompter) {
12330     return NS_ERROR_NOT_AVAILABLE;
12331   }
12332 
12333   nsCOMPtr<nsIStringBundleService> stringBundleService =
12334       mozilla::services::GetStringBundleService();
12335   if (!stringBundleService) {
12336     return NS_ERROR_FAILURE;
12337   }
12338 
12339   nsCOMPtr<nsIStringBundle> appBundle;
12340   nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
12341                                                   getter_AddRefs(appBundle));
12342   NS_ENSURE_SUCCESS(rv, rv);
12343 
12344   nsCOMPtr<nsIStringBundle> brandBundle;
12345   rv = stringBundleService->CreateBundle(kBrandBundleURL,
12346                                          getter_AddRefs(brandBundle));
12347   NS_ENSURE_SUCCESS(rv, rv);
12348 
12349   NS_ASSERTION(prompter && brandBundle && appBundle,
12350                "Unable to set up repost prompter.");
12351 
12352   nsAutoString brandName;
12353   rv = brandBundle->GetStringFromName("brandShortName", brandName);
12354 
12355   nsAutoString msgString, button0Title;
12356   if (NS_FAILED(rv)) {  // No brand, use the generic version.
12357     rv = appBundle->GetStringFromName("confirmRepostPrompt", msgString);
12358   } else {
12359     // Brand available - if the app has an override file with formatting, the
12360     // app name will be included. Without an override, the prompt will look
12361     // like the generic version.
12362     const char16_t* formatStrings[] = {brandName.get()};
12363     rv = appBundle->FormatStringFromName("confirmRepostPrompt", formatStrings,
12364                                          ArrayLength(formatStrings), msgString);
12365   }
12366   if (NS_FAILED(rv)) {
12367     return rv;
12368   }
12369 
12370   rv = appBundle->GetStringFromName("resendButton.label", button0Title);
12371   if (NS_FAILED(rv)) {
12372     return rv;
12373   }
12374 
12375   // Make the repost prompt tab modal to prevent malicious pages from locking
12376   // up the browser, see bug 1412559 for an example.
12377   if (nsCOMPtr<nsIWritablePropertyBag2> promptBag =
12378           do_QueryInterface(prompter)) {
12379     promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
12380   }
12381 
12382   int32_t buttonPressed;
12383   // The actual value here is irrelevant, but we can't pass an invalid
12384   // bool through XPConnect.
12385   bool checkState = false;
12386   rv = prompter->ConfirmEx(
12387       nullptr, msgString.get(),
12388       (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
12389           (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
12390       button0Title.get(), nullptr, nullptr, nullptr, &checkState,
12391       &buttonPressed);
12392   if (NS_FAILED(rv)) {
12393     return rv;
12394   }
12395 
12396   *aRepost = (buttonPressed == 0);
12397   return NS_OK;
12398 }
12399 
GetPromptAndStringBundle(nsIPrompt ** aPrompt,nsIStringBundle ** aStringBundle)12400 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12401                                               nsIStringBundle** aStringBundle) {
12402   NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12403                     NS_ERROR_FAILURE);
12404 
12405   nsCOMPtr<nsIStringBundleService> stringBundleService =
12406       mozilla::services::GetStringBundleService();
12407   NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12408 
12409   NS_ENSURE_SUCCESS(
12410       stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12411       NS_ERROR_FAILURE);
12412 
12413   return NS_OK;
12414 }
12415 
GetRootScrollFrame()12416 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12417   nsCOMPtr<nsIPresShell> shell = GetPresShell();
12418   NS_ENSURE_TRUE(shell, nullptr);
12419 
12420   return shell->GetRootScrollFrameAsScrollable();
12421 }
12422 
EnsureScriptEnvironment()12423 nsresult nsDocShell::EnsureScriptEnvironment() {
12424   if (mScriptGlobal) {
12425     return NS_OK;
12426   }
12427 
12428   if (mIsBeingDestroyed) {
12429     return NS_ERROR_NOT_AVAILABLE;
12430   }
12431 
12432 #ifdef DEBUG
12433   NS_ASSERTION(!mInEnsureScriptEnv,
12434                "Infinite loop! Calling EnsureScriptEnvironment() from "
12435                "within EnsureScriptEnvironment()!");
12436 
12437   // Yeah, this isn't re-entrant safe, but that's ok since if we
12438   // re-enter this method, we'll infinitely loop...
12439   AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12440   mInEnsureScriptEnv = true;
12441 #endif
12442 
12443   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12444   NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12445 
12446   uint32_t chromeFlags;
12447   browserChrome->GetChromeFlags(&chromeFlags);
12448 
12449   // If our window is modal and we're not opened as chrome, make
12450   // this window a modal content window.
12451   mScriptGlobal = NS_NewScriptGlobalObject(mItemType == typeChrome);
12452   MOZ_ASSERT(mScriptGlobal);
12453 
12454   mScriptGlobal->SetDocShell(this);
12455 
12456   // Ensure the script object is set up to run script.
12457   return mScriptGlobal->EnsureScriptEnvironment();
12458 }
12459 
EnsureEditorData()12460 nsresult nsDocShell::EnsureEditorData() {
12461   MOZ_ASSERT(!mIsBeingDestroyed);
12462 
12463   bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12464   if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12465     // We shouldn't recreate the editor data if it already exists, or
12466     // we're shutting down, or we already have a detached editor data
12467     // stored in the session history. We should only have one editordata
12468     // per docshell.
12469     mEditorData = new nsDocShellEditorData(this);
12470   }
12471 
12472   return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12473 }
12474 
EnsureTransferableHookData()12475 nsresult nsDocShell::EnsureTransferableHookData() {
12476   MOZ_ASSERT(!mIsBeingDestroyed);
12477 
12478   if (!mTransferableHookData) {
12479     mTransferableHookData = new nsTransferableHookData();
12480   }
12481 
12482   return NS_OK;
12483 }
12484 
EnsureFind()12485 nsresult nsDocShell::EnsureFind() {
12486   nsresult rv;
12487   if (!mFind) {
12488     mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
12489     if (NS_FAILED(rv)) {
12490       return rv;
12491     }
12492   }
12493 
12494   // we promise that the nsIWebBrowserFind that we return has been set
12495   // up to point to the focused, or content window, so we have to
12496   // set that up each time.
12497 
12498   nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12499   NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12500 
12501   // default to our window
12502   nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12503   nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12504   nsFocusManager::GetFocusedDescendant(ourWindow,
12505                                        nsFocusManager::eIncludeAllDescendants,
12506                                        getter_AddRefs(windowToSearch));
12507 
12508   nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12509   if (!findInFrames) {
12510     return NS_ERROR_NO_INTERFACE;
12511   }
12512 
12513   rv = findInFrames->SetRootSearchFrame(ourWindow);
12514   if (NS_FAILED(rv)) {
12515     return rv;
12516   }
12517   rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12518   if (NS_FAILED(rv)) {
12519     return rv;
12520   }
12521 
12522   return NS_OK;
12523 }
12524 
IsFrame()12525 bool nsDocShell::IsFrame() {
12526   nsCOMPtr<nsIDocShellTreeItem> parent;
12527   GetSameTypeParent(getter_AddRefs(parent));
12528   return !!parent;
12529 }
12530 
12531 NS_IMETHODIMP
IsBeingDestroyed(bool * aDoomed)12532 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12533   NS_ENSURE_ARG(aDoomed);
12534   *aDoomed = mIsBeingDestroyed;
12535   return NS_OK;
12536 }
12537 
12538 NS_IMETHODIMP
GetIsExecutingOnLoadHandler(bool * aResult)12539 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12540   NS_ENSURE_ARG(aResult);
12541   *aResult = mIsExecutingOnLoadHandler;
12542   return NS_OK;
12543 }
12544 
12545 NS_IMETHODIMP
GetLayoutHistoryState(nsILayoutHistoryState ** aLayoutHistoryState)12546 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12547   if (mOSHE) {
12548     mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
12549   }
12550   return NS_OK;
12551 }
12552 
12553 NS_IMETHODIMP
SetLayoutHistoryState(nsILayoutHistoryState * aLayoutHistoryState)12554 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12555   if (mOSHE) {
12556     mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12557   }
12558   return NS_OK;
12559 }
12560 
InterfaceRequestorProxy(nsIInterfaceRequestor * aRequestor)12561 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12562     nsIInterfaceRequestor* aRequestor) {
12563   if (aRequestor) {
12564     mWeakPtr = do_GetWeakReference(aRequestor);
12565   }
12566 }
12567 
~InterfaceRequestorProxy()12568 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12569   mWeakPtr = nullptr;
12570 }
12571 
NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy,nsIInterfaceRequestor)12572 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12573 
12574 NS_IMETHODIMP
12575 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12576                                                   void** aSink) {
12577   NS_ENSURE_ARG_POINTER(aSink);
12578   nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12579   if (ifReq) {
12580     return ifReq->GetInterface(aIID, aSink);
12581   }
12582   *aSink = nullptr;
12583   return NS_NOINTERFACE;
12584 }
12585 
SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer)12586 nsresult nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer) {
12587   if (!aContentViewer) {
12588     return NS_ERROR_FAILURE;
12589   }
12590 
12591   nsCOMPtr<nsIURI> baseURI;
12592   nsresult rv = NS_ERROR_NOT_AVAILABLE;
12593 
12594   if (sURIFixup) {
12595     rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(baseURI));
12596   }
12597 
12598   // Get the current document and set the base uri
12599   if (baseURI) {
12600     nsIDocument* document = aContentViewer->GetDocument();
12601     if (document) {
12602       document->SetBaseURI(baseURI);
12603     }
12604   }
12605   return rv;
12606 }
12607 
12608 //*****************************************************************************
12609 // nsDocShell::nsIAuthPromptProvider
12610 //*****************************************************************************
12611 
12612 NS_IMETHODIMP
GetAuthPrompt(uint32_t aPromptReason,const nsIID & aIID,void ** aResult)12613 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12614                           void** aResult) {
12615   // a priority prompt request will override a false mAllowAuth setting
12616   bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12617 
12618   if (!mAllowAuth && !priorityPrompt) {
12619     return NS_ERROR_NOT_AVAILABLE;
12620   }
12621 
12622   // we're either allowing auth, or it's a proxy request
12623   nsresult rv;
12624   nsCOMPtr<nsIPromptFactory> wwatch =
12625       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12626   NS_ENSURE_SUCCESS(rv, rv);
12627 
12628   rv = EnsureScriptEnvironment();
12629   NS_ENSURE_SUCCESS(rv, rv);
12630 
12631   // Get the an auth prompter for our window so that the parenting
12632   // of the dialogs works as it should when using tabs.
12633 
12634   return wwatch->GetPrompt(mScriptGlobal->AsOuter(), aIID,
12635                            reinterpret_cast<void**>(aResult));
12636 }
12637 
12638 //*****************************************************************************
12639 // nsDocShell::nsILoadContext
12640 //*****************************************************************************
12641 
12642 NS_IMETHODIMP
GetAssociatedWindow(mozIDOMWindowProxy ** aWindow)12643 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12644   CallGetInterface(this, aWindow);
12645   return NS_OK;
12646 }
12647 
12648 NS_IMETHODIMP
GetTopWindow(mozIDOMWindowProxy ** aWindow)12649 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12650   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
12651   if (win) {
12652     win = win->GetTop();
12653   }
12654   win.forget(aWindow);
12655   return NS_OK;
12656 }
12657 
12658 NS_IMETHODIMP
GetTopFrameElement(nsIDOMElement ** aElement)12659 nsDocShell::GetTopFrameElement(nsIDOMElement** aElement) {
12660   *aElement = nullptr;
12661   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
12662   if (!win) {
12663     return NS_OK;
12664   }
12665 
12666   nsCOMPtr<nsPIDOMWindowOuter> top = win->GetScriptableTop();
12667   NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
12668 
12669   // GetFrameElementInternal, /not/ GetScriptableFrameElement -- if |top| is
12670   // inside <iframe mozbrowser>, we want to return the iframe, not null.
12671   // And we want to cross the content/chrome boundary.
12672   nsCOMPtr<nsIDOMElement> elt =
12673       do_QueryInterface(top->GetFrameElementInternal());
12674   elt.forget(aElement);
12675   return NS_OK;
12676 }
12677 
12678 NS_IMETHODIMP
GetNestedFrameId(uint64_t * aId)12679 nsDocShell::GetNestedFrameId(uint64_t* aId) {
12680   *aId = 0;
12681   return NS_OK;
12682 }
12683 
12684 NS_IMETHODIMP
GetUseTrackingProtection(bool * aUseTrackingProtection)12685 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12686   *aUseTrackingProtection = false;
12687 
12688   static bool sTPEnabled = false;
12689   static bool sTPInPBEnabled = false;
12690   static bool sPrefsInit = false;
12691 
12692   if (!sPrefsInit) {
12693     sPrefsInit = true;
12694     Preferences::AddBoolVarCache(&sTPEnabled,
12695                                  "privacy.trackingprotection.enabled", false);
12696     Preferences::AddBoolVarCache(
12697         &sTPInPBEnabled, "privacy.trackingprotection.pbmode.enabled", false);
12698   }
12699 
12700   if (mUseTrackingProtection || sTPEnabled ||
12701       (UsePrivateBrowsing() && sTPInPBEnabled)) {
12702     *aUseTrackingProtection = true;
12703     return NS_OK;
12704   }
12705 
12706   RefPtr<nsDocShell> parent = GetParentDocshell();
12707   if (parent) {
12708     return parent->GetUseTrackingProtection(aUseTrackingProtection);
12709   }
12710 
12711   return NS_OK;
12712 }
12713 
12714 NS_IMETHODIMP
SetUseTrackingProtection(bool aUseTrackingProtection)12715 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12716   mUseTrackingProtection = aUseTrackingProtection;
12717   return NS_OK;
12718 }
12719 
12720 NS_IMETHODIMP
GetIsContent(bool * aIsContent)12721 nsDocShell::GetIsContent(bool* aIsContent) {
12722   *aIsContent = (mItemType == typeContent);
12723   return NS_OK;
12724 }
12725 
IsOKToLoadURI(nsIURI * aURI)12726 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12727   NS_PRECONDITION(aURI, "Must have a URI!");
12728 
12729   if (!mFiredUnloadEvent) {
12730     return true;
12731   }
12732 
12733   if (!mLoadingURI) {
12734     return false;
12735   }
12736 
12737   nsCOMPtr<nsIScriptSecurityManager> secMan =
12738       do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12739   return secMan &&
12740          NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
12741 }
12742 
12743 //
12744 // Routines for selection and clipboard
12745 //
GetControllerForCommand(const char * aCommand,nsIController ** aResult)12746 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12747                                              nsIController** aResult) {
12748   NS_ENSURE_ARG_POINTER(aResult);
12749   *aResult = nullptr;
12750 
12751   NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12752 
12753   nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12754   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12755 
12756   return root->GetControllerForCommand(aCommand, false /* for any window */,
12757                                        aResult);
12758 }
12759 
12760 NS_IMETHODIMP
IsCommandEnabled(const char * aCommand,bool * aResult)12761 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12762   NS_ENSURE_ARG_POINTER(aResult);
12763   *aResult = false;
12764 
12765   nsresult rv = NS_ERROR_FAILURE;
12766 
12767   nsCOMPtr<nsIController> controller;
12768   rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12769   if (controller) {
12770     rv = controller->IsCommandEnabled(aCommand, aResult);
12771   }
12772 
12773   return rv;
12774 }
12775 
12776 NS_IMETHODIMP
DoCommand(const char * aCommand)12777 nsDocShell::DoCommand(const char* aCommand) {
12778   nsresult rv = NS_ERROR_FAILURE;
12779 
12780   nsCOMPtr<nsIController> controller;
12781   rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12782   if (controller) {
12783     rv = controller->DoCommand(aCommand);
12784   }
12785 
12786   return rv;
12787 }
12788 
12789 NS_IMETHODIMP
DoCommandWithParams(const char * aCommand,nsICommandParams * aParams)12790 nsDocShell::DoCommandWithParams(const char* aCommand,
12791                                 nsICommandParams* aParams) {
12792   nsCOMPtr<nsIController> controller;
12793   nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12794   if (NS_WARN_IF(NS_FAILED(rv))) {
12795     return rv;
12796   }
12797 
12798   nsCOMPtr<nsICommandController> commandController =
12799       do_QueryInterface(controller, &rv);
12800   if (NS_WARN_IF(NS_FAILED(rv))) {
12801     return rv;
12802   }
12803 
12804   return commandController->DoCommandWithParams(aCommand, aParams);
12805 }
12806 
EnsureCommandHandler()12807 nsresult nsDocShell::EnsureCommandHandler() {
12808   if (!mCommandManager) {
12809     nsCOMPtr<nsPICommandUpdater> commandUpdater =
12810         do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
12811     if (!commandUpdater) {
12812       return NS_ERROR_OUT_OF_MEMORY;
12813     }
12814 
12815     nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow();
12816     nsresult rv = commandUpdater->Init(domWindow);
12817     if (NS_SUCCEEDED(rv)) {
12818       mCommandManager = do_QueryInterface(commandUpdater);
12819     }
12820   }
12821 
12822   return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12823 }
12824 
12825 NS_IMETHODIMP
CanCutSelection(bool * aResult)12826 nsDocShell::CanCutSelection(bool* aResult) {
12827   return IsCommandEnabled("cmd_cut", aResult);
12828 }
12829 
12830 NS_IMETHODIMP
CanCopySelection(bool * aResult)12831 nsDocShell::CanCopySelection(bool* aResult) {
12832   return IsCommandEnabled("cmd_copy", aResult);
12833 }
12834 
12835 NS_IMETHODIMP
CanCopyLinkLocation(bool * aResult)12836 nsDocShell::CanCopyLinkLocation(bool* aResult) {
12837   return IsCommandEnabled("cmd_copyLink", aResult);
12838 }
12839 
12840 NS_IMETHODIMP
CanCopyImageLocation(bool * aResult)12841 nsDocShell::CanCopyImageLocation(bool* aResult) {
12842   return IsCommandEnabled("cmd_copyImageLocation", aResult);
12843 }
12844 
12845 NS_IMETHODIMP
CanCopyImageContents(bool * aResult)12846 nsDocShell::CanCopyImageContents(bool* aResult) {
12847   return IsCommandEnabled("cmd_copyImageContents", aResult);
12848 }
12849 
12850 NS_IMETHODIMP
CanPaste(bool * aResult)12851 nsDocShell::CanPaste(bool* aResult) {
12852   return IsCommandEnabled("cmd_paste", aResult);
12853 }
12854 
12855 NS_IMETHODIMP
CutSelection(void)12856 nsDocShell::CutSelection(void) { return DoCommand("cmd_cut"); }
12857 
12858 NS_IMETHODIMP
CopySelection(void)12859 nsDocShell::CopySelection(void) { return DoCommand("cmd_copy"); }
12860 
12861 NS_IMETHODIMP
CopyLinkLocation(void)12862 nsDocShell::CopyLinkLocation(void) { return DoCommand("cmd_copyLink"); }
12863 
12864 NS_IMETHODIMP
CopyImageLocation(void)12865 nsDocShell::CopyImageLocation(void) {
12866   return DoCommand("cmd_copyImageLocation");
12867 }
12868 
12869 NS_IMETHODIMP
CopyImageContents(void)12870 nsDocShell::CopyImageContents(void) {
12871   return DoCommand("cmd_copyImageContents");
12872 }
12873 
12874 NS_IMETHODIMP
Paste(void)12875 nsDocShell::Paste(void) { return DoCommand("cmd_paste"); }
12876 
12877 NS_IMETHODIMP
SelectAll(void)12878 nsDocShell::SelectAll(void) { return DoCommand("cmd_selectAll"); }
12879 
12880 //
12881 // SelectNone
12882 //
12883 // Collapses the current selection, insertion point ends up at beginning
12884 // of previous selection.
12885 //
12886 NS_IMETHODIMP
SelectNone(void)12887 nsDocShell::SelectNone(void) { return DoCommand("cmd_selectNone"); }
12888 
12889 // link handling
12890 
12891 class OnLinkClickEvent : public Runnable {
12892  public:
12893   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent, nsIURI* aURI,
12894                    const char16_t* aTargetSpec, const nsAString& aFileName,
12895                    nsIInputStream* aPostDataStream,
12896                    int64_t aPostDataStreamLength,
12897                    nsIInputStream* aHeadersDataStream, bool aNoOpenerImplied,
12898                    bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12899 
Run()12900   NS_IMETHOD Run() override {
12901     nsAutoPopupStatePusher popupStatePusher(mPopupState);
12902 
12903     // We need to set up an AutoJSAPI here for the following reason: When we do
12904     // OnLinkClickSync we'll eventually end up in nsGlobalWindow::OpenInternal
12905     // which only does popup blocking if !LegacyIsCallerChromeOrNativeCode().
12906     // So we need to fake things so that we don't look like native code as far
12907     // as LegacyIsCallerNativeCode() is concerned.
12908     AutoJSAPI jsapi;
12909     if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12910       mHandler->OnLinkClickSync(mContent, mURI, mTargetSpec.get(), mFileName,
12911                                 mPostDataStream, mPostDataStreamLength,
12912                                 mHeadersDataStream, mNoOpenerImplied, nullptr,
12913                                 nullptr, mTriggeringPrincipal);
12914     }
12915     return NS_OK;
12916   }
12917 
12918  private:
12919   RefPtr<nsDocShell> mHandler;
12920   nsCOMPtr<nsIURI> mURI;
12921   nsString mTargetSpec;
12922   nsString mFileName;
12923   nsCOMPtr<nsIInputStream> mPostDataStream;
12924   int64_t mPostDataStreamLength;
12925   nsCOMPtr<nsIInputStream> mHeadersDataStream;
12926   nsCOMPtr<nsIContent> mContent;
12927   PopupControlState mPopupState;
12928   bool mNoOpenerImplied;
12929   bool mIsTrusted;
12930   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12931 };
12932 
OnLinkClickEvent(nsDocShell * aHandler,nsIContent * aContent,nsIURI * aURI,const char16_t * aTargetSpec,const nsAString & aFileName,nsIInputStream * aPostDataStream,int64_t aPostDataStreamLength,nsIInputStream * aHeadersDataStream,bool aNoOpenerImplied,bool aIsTrusted,nsIPrincipal * aTriggeringPrincipal)12933 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12934                                    nsIURI* aURI, const char16_t* aTargetSpec,
12935                                    const nsAString& aFileName,
12936                                    nsIInputStream* aPostDataStream,
12937                                    int64_t aPostDataStreamLength,
12938                                    nsIInputStream* aHeadersDataStream,
12939                                    bool aNoOpenerImplied, bool aIsTrusted,
12940                                    nsIPrincipal* aTriggeringPrincipal)
12941     : mozilla::Runnable("OnLinkClickEvent"),
12942       mHandler(aHandler),
12943       mURI(aURI),
12944       mTargetSpec(aTargetSpec),
12945       mFileName(aFileName),
12946       mPostDataStream(aPostDataStream),
12947       mPostDataStreamLength(aPostDataStreamLength),
12948       mHeadersDataStream(aHeadersDataStream),
12949       mContent(aContent),
12950       mPopupState(mHandler->mScriptGlobal->GetPopupControlState()),
12951       mNoOpenerImplied(aNoOpenerImplied),
12952       mIsTrusted(aIsTrusted),
12953       mTriggeringPrincipal(aTriggeringPrincipal) {}
12954 
12955 NS_IMETHODIMP
OnLinkClick(nsIContent * aContent,nsIURI * aURI,const char16_t * aTargetSpec,const nsAString & aFileName,nsIInputStream * aPostDataStream,int64_t aPostDataStreamLength,nsIInputStream * aHeadersDataStream,bool aIsTrusted,nsIPrincipal * aTriggeringPrincipal)12956 nsDocShell::OnLinkClick(nsIContent* aContent, nsIURI* aURI,
12957                         const char16_t* aTargetSpec, const nsAString& aFileName,
12958                         nsIInputStream* aPostDataStream,
12959                         int64_t aPostDataStreamLength,
12960                         nsIInputStream* aHeadersDataStream, bool aIsTrusted,
12961                         nsIPrincipal* aTriggeringPrincipal) {
12962   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12963 
12964   if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12965     return NS_OK;
12966   }
12967 
12968   // On history navigation through Back/Forward buttons, don't execute
12969   // automatic JavaScript redirection such as |anchorElement.click()| or
12970   // |formElement.submit()|.
12971   //
12972   // XXX |formElement.submit()| bypasses this checkpoint because it calls
12973   //     nsDocShell::OnLinkClickSync(...) instead.
12974   if (ShouldBlockLoadingForBackButton()) {
12975     return NS_OK;
12976   }
12977 
12978   if (aContent->IsEditable()) {
12979     return NS_OK;
12980   }
12981 
12982   nsresult rv = NS_ERROR_FAILURE;
12983   nsAutoString target;
12984 
12985   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
12986   bool noOpenerImplied = false;
12987   if (browserChrome3) {
12988     nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
12989     nsAutoString oldTarget(aTargetSpec);
12990     rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI, linkNode,
12991                                                mIsAppTab, target);
12992     if (!oldTarget.Equals(target)) {
12993       noOpenerImplied = true;
12994     }
12995   }
12996 
12997   if (NS_FAILED(rv)) {
12998     target = aTargetSpec;
12999   }
13000 
13001   nsCOMPtr<nsIRunnable> ev = new OnLinkClickEvent(
13002       this, aContent, aURI, target.get(), aFileName, aPostDataStream,
13003       aPostDataStreamLength, aHeadersDataStream, noOpenerImplied, aIsTrusted,
13004       aTriggeringPrincipal);
13005   return DispatchToTabGroup(TaskCategory::UI, ev.forget());
13006 }
13007 
IsElementAnchorOrArea(nsIContent * aContent)13008 static bool IsElementAnchorOrArea(nsIContent* aContent) {
13009   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
13010   // or XHTML namespace.
13011   return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
13012 }
13013 
13014 NS_IMETHODIMP
OnLinkClickSync(nsIContent * aContent,nsIURI * aURI,const char16_t * aTargetSpec,const nsAString & aFileName,nsIInputStream * aPostDataStream,int64_t aPostDataStreamLength,nsIInputStream * aHeadersDataStream,bool aNoOpenerImplied,nsIDocShell ** aDocShell,nsIRequest ** aRequest,nsIPrincipal * aTriggeringPrincipal)13015 nsDocShell::OnLinkClickSync(
13016     nsIContent* aContent, nsIURI* aURI, const char16_t* aTargetSpec,
13017     const nsAString& aFileName, nsIInputStream* aPostDataStream,
13018     int64_t aPostDataStreamLength, nsIInputStream* aHeadersDataStream,
13019     bool aNoOpenerImplied, nsIDocShell** aDocShell, nsIRequest** aRequest,
13020     nsIPrincipal* aTriggeringPrincipal) {
13021   // Initialize the DocShell / Request
13022   if (aDocShell) {
13023     *aDocShell = nullptr;
13024   }
13025   if (aRequest) {
13026     *aRequest = nullptr;
13027   }
13028 
13029   if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
13030     return NS_OK;
13031   }
13032 
13033   // XXX When the linking node was HTMLFormElement, it is synchronous event.
13034   //     That is, the caller of this method is not |OnLinkClickEvent::Run()|
13035   //     but |HTMLFormElement::SubmitSubmission(...)|.
13036   if (aContent->IsHTMLElement(nsGkAtoms::form) &&
13037       ShouldBlockLoadingForBackButton()) {
13038     return NS_OK;
13039   }
13040 
13041   if (aContent->IsEditable()) {
13042     return NS_OK;
13043   }
13044 
13045   {
13046     // defer to an external protocol handler if necessary...
13047     nsCOMPtr<nsIExternalProtocolService> extProtService =
13048         do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
13049     if (extProtService) {
13050       nsAutoCString scheme;
13051       aURI->GetScheme(scheme);
13052       if (!scheme.IsEmpty()) {
13053         // if the URL scheme does not correspond to an exposed protocol, then we
13054         // need to hand this link click over to the external protocol handler.
13055         bool isExposed;
13056         nsresult rv =
13057             extProtService->IsExposedProtocol(scheme.get(), &isExposed);
13058         if (NS_SUCCEEDED(rv) && !isExposed) {
13059           return extProtService->LoadURI(aURI, this);
13060         }
13061       }
13062     }
13063   }
13064 
13065   uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
13066   if (IsElementAnchorOrArea(aContent)) {
13067     MOZ_ASSERT(aContent->IsHTMLElement());
13068     nsAutoString referrer;
13069     aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
13070     nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
13071         referrer);
13072     while (tok.hasMoreTokens()) {
13073       const nsAString& token = tok.nextToken();
13074       if (token.LowerCaseEqualsLiteral("noreferrer")) {
13075         flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
13076                  INTERNAL_LOAD_FLAGS_NO_OPENER;
13077         // We now have all the flags we could possibly have, so just stop.
13078         break;
13079       }
13080       if (token.LowerCaseEqualsLiteral("noopener")) {
13081         flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13082       }
13083     }
13084     if (aNoOpenerImplied) {
13085       flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13086     }
13087   }
13088 
13089   // Get the owner document of the link that was clicked, this will be
13090   // the document that the link is in, or the last document that the
13091   // link was in. From that document, we'll get the URI to use as the
13092   // referer, since the current URI in this docshell may be a
13093   // new document that we're in the process of loading.
13094   nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
13095   NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
13096 
13097   // Now check that the refererDoc's inner window is the current inner
13098   // window for mScriptGlobal.  If it's not, then we don't want to
13099   // follow this link.
13100   nsPIDOMWindowInner* refererInner = refererDoc->GetInnerWindow();
13101   NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
13102   if (!mScriptGlobal ||
13103       mScriptGlobal->AsOuter()->GetCurrentInnerWindow() != refererInner) {
13104     // We're no longer the current inner window
13105     return NS_OK;
13106   }
13107 
13108   nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
13109   uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
13110 
13111   // get referrer attribute from clicked link and parse it
13112   // if per element referrer is enabled, the element referrer overrules
13113   // the document wide referrer
13114   if (IsElementAnchorOrArea(aContent)) {
13115     net::ReferrerPolicy refPolEnum =
13116         aContent->AsElement()->GetReferrerPolicyAsEnum();
13117     if (refPolEnum != net::RP_Unset) {
13118       refererPolicy = refPolEnum;
13119     }
13120   }
13121 
13122   // referer could be null here in some odd cases, but that's ok,
13123   // we'll just load the link w/o sending a referer in those cases.
13124 
13125   nsAutoString target(aTargetSpec);
13126 
13127   // If this is an anchor element, grab its type property to use as a hint
13128   nsAutoString typeHint;
13129   RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromContent(aContent);
13130   if (anchor) {
13131     anchor->GetType(typeHint);
13132     NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13133     nsAutoCString type, dummy;
13134     NS_ParseRequestContentType(utf8Hint, type, dummy);
13135     CopyUTF8toUTF16(type, typeHint);
13136   }
13137 
13138   // Clone the URI now, in case a content policy or something messes
13139   // with it under InternalLoad; we do _not_ want to change the URI
13140   // our caller passed in.
13141   nsCOMPtr<nsIURI> clonedURI;
13142   aURI->Clone(getter_AddRefs(clonedURI));
13143   if (!clonedURI) {
13144     return NS_ERROR_OUT_OF_MEMORY;
13145   }
13146 
13147   // if the triggeringPrincipal is not passed explicitly, then we
13148   // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
13149   nsCOMPtr<nsIPrincipal> triggeringPrincipal =
13150       aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
13151 
13152   // Link click (or form submission) can be triggered inside an onload handler,
13153   // and we don't want to add history entry in this case.
13154   bool inOnLoadHandler = false;
13155   GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13156   uint32_t loadType = inOnLoadHandler ? LOAD_NORMAL_REPLACE : LOAD_LINK;
13157 
13158   nsresult rv =
13159       InternalLoad(clonedURI,  // New URI
13160                    nullptr,    // Original URI
13161                    Nothing(),  // Let the protocol handler assign it
13162                    false,
13163                    false,          // LoadReplace
13164                    false,          // IsFromProcessingFrameAttributes
13165                    referer,        // Referer URI
13166                    refererPolicy,  // Referer policy
13167                    triggeringPrincipal, aContent->NodePrincipal(), flags,
13168                    target,  // Window target
13169                    NS_LossyConvertUTF16toASCII(typeHint).get(),
13170                    aFileName,              // Download as file
13171                    aPostDataStream,        // Post data stream
13172                    aPostDataStreamLength,  // Post data stream length
13173                    aHeadersDataStream,     // Headers stream
13174                    loadType,               // Load type
13175                    nullptr,                // No SHEntry
13176                    true,                   // first party site
13177                    VoidString(),           // No srcdoc
13178                    this,                   // We are the source
13179                    nullptr,                // baseURI not needed
13180                    aDocShell,              // DocShell out-param
13181                    aRequest);              // Request out-param
13182   if (NS_SUCCEEDED(rv)) {
13183     nsPingListener::DispatchPings(this, aContent, aURI, referer, refererPolicy);
13184   }
13185   return rv;
13186 }
13187 
13188 NS_IMETHODIMP
OnOverLink(nsIContent * aContent,nsIURI * aURI,const char16_t * aTargetSpec)13189 nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13190                        const char16_t* aTargetSpec) {
13191   if (aContent->IsEditable()) {
13192     return NS_OK;
13193   }
13194 
13195   nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
13196   nsresult rv = NS_ERROR_FAILURE;
13197 
13198   nsCOMPtr<nsIWebBrowserChrome> browserChrome;
13199   if (!browserChrome2) {
13200     browserChrome = do_GetInterface(mTreeOwner);
13201     if (!browserChrome) {
13202       return rv;
13203     }
13204   }
13205 
13206   nsAutoCString spec;
13207   rv = aURI->GetDisplaySpec(spec);
13208   NS_ENSURE_SUCCESS(rv, rv);
13209 
13210   NS_ConvertUTF8toUTF16 uStr(spec);
13211 
13212   mozilla::net::PredictorPredict(
13213       aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13214       aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13215 
13216   if (browserChrome2) {
13217     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
13218     rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
13219                                               uStr, element);
13220   } else {
13221     rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
13222   }
13223   return rv;
13224 }
13225 
13226 NS_IMETHODIMP
OnLeaveLink()13227 nsDocShell::OnLeaveLink() {
13228   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13229   nsresult rv = NS_ERROR_FAILURE;
13230 
13231   if (browserChrome) {
13232     rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
13233                                   EmptyString().get());
13234   }
13235   return rv;
13236 }
13237 
ShouldBlockLoadingForBackButton()13238 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13239   if (!(mLoadType & LOAD_CMD_HISTORY) ||
13240       EventStateManager::IsHandlingUserInput() ||
13241       !Preferences::GetBool("accessibility.blockjsredirection")) {
13242     return false;
13243   }
13244 
13245   bool canGoForward = false;
13246   GetCanGoForward(&canGoForward);
13247   return canGoForward;
13248 }
13249 
PluginsAllowedInCurrentDoc()13250 bool nsDocShell::PluginsAllowedInCurrentDoc() {
13251   if (!mContentViewer) {
13252     return false;
13253   }
13254 
13255   nsIDocument* doc = mContentViewer->GetDocument();
13256   if (!doc) {
13257     return false;
13258   }
13259 
13260   return doc->GetAllowPlugins();
13261 }
13262 
13263 //----------------------------------------------------------------------
13264 // Web Shell Services API
13265 
13266 // This functions is only called when a new charset is detected in loading a
13267 // document. Its name should be changed to "CharsetReloadDocument"
13268 NS_IMETHODIMP
ReloadDocument(const char * aCharset,int32_t aSource)13269 nsDocShell::ReloadDocument(const char* aCharset, int32_t aSource) {
13270   // XXX hack. keep the aCharset and aSource wait to pick it up
13271   nsCOMPtr<nsIContentViewer> cv;
13272   NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
13273   if (cv) {
13274     int32_t hint;
13275     cv->GetHintCharacterSetSource(&hint);
13276     if (aSource > hint) {
13277       nsCString charset(aCharset);
13278       cv->SetHintCharacterSet(charset);
13279       cv->SetHintCharacterSetSource(aSource);
13280       if (eCharsetReloadRequested != mCharsetReloadState) {
13281         mCharsetReloadState = eCharsetReloadRequested;
13282         switch (mLoadType) {
13283           case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13284             return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13285                           LOAD_FLAGS_BYPASS_PROXY);
13286           case LOAD_RELOAD_BYPASS_CACHE:
13287             return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13288           default:
13289             return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13290         }
13291       }
13292     }
13293   }
13294   // return failure if this request is not accepted due to mCharsetReloadState
13295   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13296 }
13297 
13298 NS_IMETHODIMP
StopDocumentLoad(void)13299 nsDocShell::StopDocumentLoad(void) {
13300   if (eCharsetReloadRequested != mCharsetReloadState) {
13301     Stop(nsIWebNavigation::STOP_ALL);
13302     return NS_OK;
13303   }
13304   // return failer if this request is not accepted due to mCharsetReloadState
13305   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13306 }
13307 
13308 NS_IMETHODIMP
SetIsPrinting(bool aIsPrinting)13309 nsDocShell::SetIsPrinting(bool aIsPrinting) {
13310   mIsPrintingOrPP = aIsPrinting;
13311   return NS_OK;
13312 }
13313 
13314 NS_IMETHODIMP
GetPrintPreview(nsIWebBrowserPrint ** aPrintPreview)13315 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview) {
13316   *aPrintPreview = nullptr;
13317 #if NS_PRINT_PREVIEW
13318   nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
13319   if (!print || !print->IsInitializedForPrintPreview()) {
13320     // XXX: Creating a brand new content viewer to host preview every
13321     // time we enter here seems overwork. We could skip ahead to where
13322     // we QI the mContentViewer if the current URI is either about:blank
13323     // or about:printpreview.
13324     Stop(nsIWebNavigation::STOP_ALL);
13325     nsCOMPtr<nsIPrincipal> principal =
13326         NullPrincipal::CreateWithInheritedAttributes(this);
13327     nsCOMPtr<nsIURI> uri;
13328     NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview"));
13329     nsresult rv = CreateAboutBlankContentViewer(principal, uri);
13330     NS_ENSURE_SUCCESS(rv, rv);
13331     // Here we manually set current URI since we have just created a
13332     // brand new content viewer (about:blank) to host preview.
13333     SetCurrentURI(uri, nullptr, true, 0);
13334     print = do_QueryInterface(mContentViewer);
13335     NS_ENSURE_STATE(print);
13336     print->InitializeForPrintPreview();
13337   }
13338   nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
13339   result.forget(aPrintPreview);
13340   return NS_OK;
13341 #else
13342   return NS_ERROR_NOT_IMPLEMENTED;
13343 #endif
13344 }
13345 
13346 #ifdef DEBUG
13347 unsigned long nsDocShell::gNumberOfDocShells = 0;
13348 #endif
13349 
13350 NS_IMETHODIMP
GetCanExecuteScripts(bool * aResult)13351 nsDocShell::GetCanExecuteScripts(bool* aResult) {
13352   *aResult = mCanExecuteScripts;
13353   return NS_OK;
13354 }
13355 
SetFrameType(uint32_t aFrameType)13356 /* [infallible] */ NS_IMETHODIMP nsDocShell::SetFrameType(uint32_t aFrameType) {
13357   mFrameType = aFrameType;
13358   return NS_OK;
13359 }
13360 
GetFrameType(uint32_t * aFrameType)13361 /* [infallible] */ NS_IMETHODIMP nsDocShell::GetFrameType(
13362     uint32_t* aFrameType) {
13363   *aFrameType = mFrameType;
13364   return NS_OK;
13365 }
13366 
GetIsMozBrowser(bool * aIsMozBrowser)13367 /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsMozBrowser(
13368     bool* aIsMozBrowser) {
13369   *aIsMozBrowser = (mFrameType == FRAME_TYPE_BROWSER);
13370   return NS_OK;
13371 }
13372 
GetInheritedFrameType()13373 uint32_t nsDocShell::GetInheritedFrameType() {
13374   if (mFrameType != FRAME_TYPE_REGULAR) {
13375     return mFrameType;
13376   }
13377 
13378   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
13379   GetSameTypeParent(getter_AddRefs(parentAsItem));
13380 
13381   nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
13382   if (!parent) {
13383     return FRAME_TYPE_REGULAR;
13384   }
13385 
13386   return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
13387 }
13388 
GetIsIsolatedMozBrowserElement(bool * aIsIsolatedMozBrowserElement)13389 /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsIsolatedMozBrowserElement(
13390     bool* aIsIsolatedMozBrowserElement) {
13391   bool result = mFrameType == FRAME_TYPE_BROWSER &&
13392                 mOriginAttributes.mInIsolatedMozBrowser;
13393   *aIsIsolatedMozBrowserElement = result;
13394   return NS_OK;
13395 }
13396 
GetIsInIsolatedMozBrowserElement(bool * aIsInIsolatedMozBrowserElement)13397 /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsInIsolatedMozBrowserElement(
13398     bool* aIsInIsolatedMozBrowserElement) {
13399   MOZ_ASSERT(!mOriginAttributes.mInIsolatedMozBrowser ||
13400                  (GetInheritedFrameType() == FRAME_TYPE_BROWSER),
13401              "Isolated mozbrowser should only be true inside browser frames");
13402   bool result = (GetInheritedFrameType() == FRAME_TYPE_BROWSER) &&
13403                 mOriginAttributes.mInIsolatedMozBrowser;
13404   *aIsInIsolatedMozBrowserElement = result;
13405   return NS_OK;
13406 }
13407 
GetIsInMozBrowser(bool * aIsInMozBrowser)13408 /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsInMozBrowser(
13409     bool* aIsInMozBrowser) {
13410   *aIsInMozBrowser = (GetInheritedFrameType() == FRAME_TYPE_BROWSER);
13411   return NS_OK;
13412 }
13413 
GetIsTopLevelContentDocShell(bool * aIsTopLevelContentDocShell)13414 /* [infallible] */ NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13415     bool* aIsTopLevelContentDocShell) {
13416   *aIsTopLevelContentDocShell = false;
13417 
13418   if (mItemType == typeContent) {
13419     nsCOMPtr<nsIDocShellTreeItem> root;
13420     GetSameTypeRootTreeItem(getter_AddRefs(root));
13421     *aIsTopLevelContentDocShell =
13422         root.get() == static_cast<nsIDocShellTreeItem*>(this);
13423   }
13424 
13425   return NS_OK;
13426 }
13427 
13428 // Implements nsILoadContext.originAttributes
13429 NS_IMETHODIMP
GetScriptableOriginAttributes(JS::MutableHandle<JS::Value> aVal)13430 nsDocShell::GetScriptableOriginAttributes(JS::MutableHandle<JS::Value> aVal) {
13431   JSContext* cx = nsContentUtils::GetCurrentJSContext();
13432   MOZ_ASSERT(cx);
13433 
13434   return GetOriginAttributes(cx, aVal);
13435 }
13436 
13437 // Implements nsIDocShell.GetOriginAttributes()
13438 NS_IMETHODIMP
GetOriginAttributes(JSContext * aCx,JS::MutableHandle<JS::Value> aVal)13439 nsDocShell::GetOriginAttributes(JSContext* aCx,
13440                                 JS::MutableHandle<JS::Value> aVal) {
13441   bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
13442   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
13443   return NS_OK;
13444 }
13445 
CanSetOriginAttributes()13446 bool nsDocShell::CanSetOriginAttributes() {
13447   MOZ_ASSERT(mChildList.IsEmpty());
13448   if (!mChildList.IsEmpty()) {
13449     return false;
13450   }
13451 
13452   // TODO: Bug 1273058 - mContentViewer should be null when setting origin
13453   // attributes.
13454   if (mContentViewer) {
13455     nsIDocument* doc = mContentViewer->GetDocument();
13456     if (doc) {
13457       nsIURI* uri = doc->GetDocumentURI();
13458       if (!uri) {
13459         return false;
13460       }
13461       nsCString uriSpec = uri->GetSpecOrDefault();
13462       MOZ_ASSERT(uriSpec.EqualsLiteral("about:blank"));
13463       if (!uriSpec.EqualsLiteral("about:blank")) {
13464         return false;
13465       }
13466     }
13467   }
13468 
13469   return true;
13470 }
13471 
ServiceWorkerAllowedToControlWindow(nsIPrincipal * aPrincipal,nsIURI * aURI)13472 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13473                                                      nsIURI* aURI) {
13474   MOZ_ASSERT(aPrincipal);
13475   MOZ_ASSERT(aURI);
13476 
13477   if (UsePrivateBrowsing() || mSandboxFlags) {
13478     return false;
13479   }
13480 
13481   nsCOMPtr<nsIDocShellTreeItem> parent;
13482   GetSameTypeParent(getter_AddRefs(parent));
13483   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13484   nsPIDOMWindowInner* parentInner =
13485       parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13486 
13487   nsContentUtils::StorageAccess storage =
13488       nsContentUtils::StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13489 
13490   return storage == nsContentUtils::StorageAccess::eAllow;
13491 }
13492 
SetOriginAttributes(const OriginAttributes & aAttrs)13493 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13494   MOZ_ASSERT(!mIsBeingDestroyed);
13495 
13496   if (!CanSetOriginAttributes()) {
13497     return NS_ERROR_FAILURE;
13498   }
13499 
13500   AssertOriginAttributesMatchPrivateBrowsing();
13501   mOriginAttributes = aAttrs;
13502 
13503   bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
13504   // Chrome docshell can not contain OriginAttributes.mPrivateBrowsingId
13505   if (mItemType == typeChrome && isPrivate) {
13506     mOriginAttributes.mPrivateBrowsingId = 0;
13507   }
13508 
13509   SetPrivateBrowsing(isPrivate);
13510   AssertOriginAttributesMatchPrivateBrowsing();
13511 
13512   return NS_OK;
13513 }
13514 
13515 NS_IMETHODIMP
SetOriginAttributesBeforeLoading(JS::Handle<JS::Value> aOriginAttributes)13516 nsDocShell::SetOriginAttributesBeforeLoading(
13517     JS::Handle<JS::Value> aOriginAttributes) {
13518   if (!aOriginAttributes.isObject()) {
13519     return NS_ERROR_INVALID_ARG;
13520   }
13521 
13522   AutoJSAPI jsapi;
13523   if (!jsapi.Init(&aOriginAttributes.toObject())) {
13524     return NS_ERROR_UNEXPECTED;
13525   }
13526 
13527   JSContext* cx = jsapi.cx();
13528   if (NS_WARN_IF(!cx)) {
13529     return NS_ERROR_FAILURE;
13530   }
13531 
13532   OriginAttributes attrs;
13533   if (!aOriginAttributes.isObject() || !attrs.Init(cx, aOriginAttributes)) {
13534     return NS_ERROR_INVALID_ARG;
13535   }
13536 
13537   return SetOriginAttributes(attrs);
13538 }
13539 
13540 NS_IMETHODIMP
SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,JSContext * aCx)13541 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13542                                 JSContext* aCx) {
13543   OriginAttributes attrs;
13544   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13545     return NS_ERROR_INVALID_ARG;
13546   }
13547 
13548   return SetOriginAttributes(attrs);
13549 }
13550 
13551 NS_IMETHODIMP
GetAsyncPanZoomEnabled(bool * aOut)13552 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13553   if (nsIPresShell* presShell = GetPresShell()) {
13554     *aOut = presShell->AsyncPanZoomEnabled();
13555     return NS_OK;
13556   }
13557 
13558   // If we don't have a presShell, fall back to the default platform value of
13559   // whether or not APZ is enabled.
13560   *aOut = gfxPlatform::AsyncPanZoomEnabled();
13561   return NS_OK;
13562 }
13563 
HasUnloadedParent()13564 bool nsDocShell::HasUnloadedParent() {
13565   RefPtr<nsDocShell> parent = GetParentDocshell();
13566   while (parent) {
13567     bool inUnload = false;
13568     parent->GetIsInUnload(&inUnload);
13569     if (inUnload) {
13570       return true;
13571     }
13572     parent = parent->GetParentDocshell();
13573   }
13574   return false;
13575 }
13576 
UpdateGlobalHistoryTitle(nsIURI * aURI)13577 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13578   if (mUseGlobalHistory && !UsePrivateBrowsing()) {
13579     nsCOMPtr<IHistory> history = services::GetHistoryService();
13580     if (history) {
13581       history->SetURITitle(aURI, mTitle);
13582     } else if (mGlobalHistory) {
13583       mGlobalHistory->SetPageTitle(aURI, nsString(mTitle));
13584     }
13585   }
13586 }
13587 
IsInvisible()13588 bool nsDocShell::IsInvisible() { return mInvisible; }
13589 
SetInvisible(bool aInvisible)13590 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13591 
SetOpener(nsITabParent * aOpener)13592 void nsDocShell::SetOpener(nsITabParent* aOpener) {
13593   mOpener = do_GetWeakReference(aOpener);
13594 }
13595 
GetOpener()13596 nsITabParent* nsDocShell::GetOpener() {
13597   nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
13598   return opener;
13599 }
13600 
13601 // The caller owns |aAsyncCause| here.
NotifyJSRunToCompletionStart(const char * aReason,const char16_t * aFunctionName,const char16_t * aFilename,const uint32_t aLineNumber,JS::Handle<JS::Value> aAsyncStack,const char * aAsyncCause)13602 void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
13603                                               const char16_t* aFunctionName,
13604                                               const char16_t* aFilename,
13605                                               const uint32_t aLineNumber,
13606                                               JS::Handle<JS::Value> aAsyncStack,
13607                                               const char* aAsyncCause) {
13608   // If first start, mark interval start.
13609   if (mJSRunToCompletionDepth == 0) {
13610     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
13611     if (timelines && timelines->HasConsumer(this)) {
13612       timelines->AddMarkerForDocShell(
13613           this, Move(mozilla::MakeUnique<JavascriptTimelineMarker>(
13614                     aReason, aFunctionName, aFilename, aLineNumber,
13615                     MarkerTracingType::START, aAsyncStack, aAsyncCause)));
13616     }
13617   }
13618 
13619   mJSRunToCompletionDepth++;
13620 }
13621 
NotifyJSRunToCompletionStop()13622 void nsDocShell::NotifyJSRunToCompletionStop() {
13623   mJSRunToCompletionDepth--;
13624 
13625   // If last stop, mark interval end.
13626   if (mJSRunToCompletionDepth == 0) {
13627     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
13628     if (timelines && timelines->HasConsumer(this)) {
13629       timelines->AddMarkerForDocShell(this, "Javascript",
13630                                       MarkerTracingType::END);
13631     }
13632   }
13633 }
13634 
MaybeNotifyKeywordSearchLoading(const nsString & aProvider,const nsString & aKeyword)13635 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13636                                                  const nsString& aKeyword) {
13637   if (aProvider.IsEmpty()) {
13638     return;
13639   }
13640 
13641   if (XRE_IsContentProcess()) {
13642     dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
13643     if (contentChild) {
13644       contentChild->SendNotifyKeywordSearchLoading(aProvider, aKeyword);
13645     }
13646     return;
13647   }
13648 
13649 #ifdef MOZ_TOOLKIT_SEARCH
13650   nsCOMPtr<nsIBrowserSearchService> searchSvc =
13651       do_GetService("@mozilla.org/browser/search-service;1");
13652   if (searchSvc) {
13653     nsCOMPtr<nsISearchEngine> searchEngine;
13654     searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
13655     if (searchEngine) {
13656       nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13657       if (obsSvc) {
13658         // Note that "keyword-search" refers to a search via the url
13659         // bar, not a bookmarks keyword search.
13660         obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
13661       }
13662     }
13663   }
13664 #endif
13665 }
13666 
13667 NS_IMETHODIMP
ShouldPrepareForIntercept(nsIURI * aURI,nsIChannel * aChannel,bool * aShouldIntercept)13668 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13669                                       bool* aShouldIntercept) {
13670   return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13671                                                          aShouldIntercept);
13672 }
13673 
13674 NS_IMETHODIMP
ChannelIntercepted(nsIInterceptedChannel * aChannel)13675 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13676   return mInterceptController->ChannelIntercepted(aChannel);
13677 }
13678 
InFrameSwap()13679 bool nsDocShell::InFrameSwap() {
13680   RefPtr<nsDocShell> shell = this;
13681   do {
13682     if (shell->mInFrameSwap) {
13683       return true;
13684     }
13685     shell = shell->GetParentDocshell();
13686   } while (shell);
13687   return false;
13688 }
13689 
TakeInitialClientSource()13690 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13691   return Move(mInitialClientSource);
13692 }
13693 
13694 NS_IMETHODIMP
IssueWarning(uint32_t aWarning,bool aAsError)13695 nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError) {
13696   if (mContentViewer) {
13697     nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
13698     if (doc) {
13699       doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
13700     }
13701   }
13702   return NS_OK;
13703 }
13704 
13705 NS_IMETHODIMP
GetEditingSession(nsIEditingSession ** aEditSession)13706 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13707   if (!NS_SUCCEEDED(EnsureEditorData())) {
13708     return NS_ERROR_FAILURE;
13709   }
13710 
13711   mEditorData->GetEditingSession(aEditSession);
13712   return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13713 }
13714 
13715 NS_IMETHODIMP
GetScriptableTabChild(nsITabChild ** aTabChild)13716 nsDocShell::GetScriptableTabChild(nsITabChild** aTabChild) {
13717   *aTabChild = GetTabChild().take();
13718   return *aTabChild ? NS_OK : NS_ERROR_FAILURE;
13719 }
13720 
GetTabChild()13721 already_AddRefed<nsITabChild> nsDocShell::GetTabChild() {
13722   nsCOMPtr<nsIDocShellTreeOwner> owner(mTreeOwner);
13723   nsCOMPtr<nsITabChild> tc = do_GetInterface(owner);
13724   return tc.forget();
13725 }
13726 
GetCommandManager()13727 nsICommandManager* nsDocShell::GetCommandManager() {
13728   NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13729   return mCommandManager;
13730 }
13731 
13732 NS_IMETHODIMP
GetIsOnlyToplevelInTabGroup(bool * aResult)13733 nsDocShell::GetIsOnlyToplevelInTabGroup(bool* aResult) {
13734   MOZ_ASSERT(aResult);
13735 
13736   nsPIDOMWindowOuter* outer = GetWindow();
13737   MOZ_ASSERT(outer);
13738 
13739   // If we are not toplevel then we are not the only toplevel window in the tab
13740   // group.
13741   if (outer->GetScriptableParentOrNull()) {
13742     *aResult = false;
13743     return NS_OK;
13744   }
13745 
13746   // If we have any other toplevel windows in our tab group, then we are not the
13747   // only toplevel window in the tab group.
13748   nsTArray<nsPIDOMWindowOuter*> toplevelWindows =
13749       outer->TabGroup()->GetTopLevelWindows();
13750   if (toplevelWindows.Length() > 1) {
13751     *aResult = false;
13752     return NS_OK;
13753   }
13754   MOZ_ASSERT(toplevelWindows.Length() == 1);
13755   MOZ_ASSERT(toplevelWindows[0] == outer);
13756 
13757   *aResult = true;
13758   return NS_OK;
13759 }
13760 
13761 NS_IMETHODIMP
GetAwaitingLargeAlloc(bool * aResult)13762 nsDocShell::GetAwaitingLargeAlloc(bool* aResult) {
13763   MOZ_ASSERT(aResult);
13764   nsCOMPtr<nsITabChild> tabChild = GetTabChild();
13765   if (!tabChild) {
13766     *aResult = false;
13767     return NS_OK;
13768   }
13769   *aResult = static_cast<TabChild*>(tabChild.get())->IsAwaitingLargeAlloc();
13770   return NS_OK;
13771 }
13772 
NS_IMETHODIMP_(void)13773 NS_IMETHODIMP_(void)
13774 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13775   aAttrs = mOriginAttributes;
13776 }
13777 
GetHTMLEditor()13778 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13779   nsDocShell* docShell = static_cast<nsDocShell*>(this);
13780   return docShell->GetHTMLEditorInternal();
13781 }
13782 
SetHTMLEditor(HTMLEditor * aHTMLEditor)13783 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13784   nsDocShell* docShell = static_cast<nsDocShell*>(this);
13785   return docShell->SetHTMLEditorInternal(aHTMLEditor);
13786 }
13787 
13788 NS_IMETHODIMP
GetDisplayMode(uint32_t * aDisplayMode)13789 nsDocShell::GetDisplayMode(uint32_t* aDisplayMode) {
13790   NS_ENSURE_ARG_POINTER(aDisplayMode);
13791   *aDisplayMode = mDisplayMode;
13792   return NS_OK;
13793 }
13794 
13795 NS_IMETHODIMP
SetDisplayMode(uint32_t aDisplayMode)13796 nsDocShell::SetDisplayMode(uint32_t aDisplayMode) {
13797   if (!(aDisplayMode == nsIDocShell::DISPLAY_MODE_BROWSER ||
13798         aDisplayMode == nsIDocShell::DISPLAY_MODE_STANDALONE ||
13799         aDisplayMode == nsIDocShell::DISPLAY_MODE_FULLSCREEN ||
13800         aDisplayMode == nsIDocShell::DISPLAY_MODE_MINIMAL_UI)) {
13801     return NS_ERROR_INVALID_ARG;
13802   }
13803 
13804   if (aDisplayMode != mDisplayMode) {
13805     mDisplayMode = aDisplayMode;
13806 
13807     RefPtr<nsPresContext> presContext;
13808     if (NS_SUCCEEDED(GetPresContext(getter_AddRefs(presContext)))) {
13809       presContext->MediaFeatureValuesChangedAllDocuments(
13810           {MediaFeatureChangeReason::DisplayModeChange});
13811     }
13812   }
13813 
13814   return NS_OK;
13815 }
13816 
13817 NS_IMETHODIMP
SetColorMatrix(float * aMatrix,uint32_t aMatrixLen)13818 nsDocShell::SetColorMatrix(float* aMatrix, uint32_t aMatrixLen) {
13819   if (aMatrixLen == 20) {
13820     mColorMatrix.reset(new gfx::Matrix5x4());
13821     MOZ_ASSERT(aMatrixLen * sizeof(*aMatrix) ==
13822                sizeof(mColorMatrix->components));
13823     memcpy(mColorMatrix->components, aMatrix, sizeof(mColorMatrix->components));
13824   } else if (aMatrixLen == 0) {
13825     mColorMatrix.reset();
13826   } else {
13827     return NS_ERROR_INVALID_ARG;
13828   }
13829 
13830   nsIPresShell* presShell = GetPresShell();
13831   if (!presShell) {
13832     return NS_ERROR_FAILURE;
13833   }
13834 
13835   nsIFrame* frame = presShell->GetRootFrame();
13836   if (!frame) {
13837     return NS_ERROR_FAILURE;
13838   }
13839 
13840   frame->SchedulePaint();
13841 
13842   return NS_OK;
13843 }
13844 
13845 NS_IMETHODIMP
GetColorMatrix(uint32_t * aMatrixLen,float ** aMatrix)13846 nsDocShell::GetColorMatrix(uint32_t* aMatrixLen, float** aMatrix) {
13847   NS_ENSURE_ARG_POINTER(aMatrixLen);
13848   *aMatrixLen = 0;
13849 
13850   NS_ENSURE_ARG_POINTER(aMatrix);
13851   *aMatrix = nullptr;
13852 
13853   if (mColorMatrix) {
13854     *aMatrix = (float*)moz_xmalloc(20 * sizeof(float));
13855     if (!*aMatrix) {
13856       return NS_ERROR_OUT_OF_MEMORY;
13857     }
13858 
13859     MOZ_ASSERT(20 * sizeof(float) == sizeof(mColorMatrix->components));
13860     *aMatrixLen = 20;
13861     memcpy(*aMatrix, mColorMatrix->components, 20 * sizeof(float));
13862   }
13863 
13864   return NS_OK;
13865 }
13866