Bug 1473362 [wpt PR 11778] - Update wpt metadata, a=testonly
[gecko.git] / docshell / base / nsDocShell.cpp
blob6a973f8314271da5214696bf4bf030032476dd11
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDocShell.h"
9 #include <algorithm>
11 #ifdef XP_WIN
12 #include <process.h>
13 #define getpid _getpid
14 #else
15 #include <unistd.h> // for getpid()
16 #endif
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/AutoRestore.h"
21 #include "mozilla/BasePrincipal.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/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"
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/ServiceWorkerUtils.h"
52 #include "mozilla/dom/TabChild.h"
53 #include "mozilla/dom/TabGroup.h"
54 #include "mozilla/dom/ToJSValue.h"
55 #include "mozilla/dom/ChildSHistory.h"
58 #include "mozilla/net/ReferrerPolicy.h"
60 #include "nsIApplicationCacheChannel.h"
61 #include "nsIApplicationCacheContainer.h"
62 #include "nsIAppShell.h"
63 #include "nsIAsyncVerifyRedirectCallback.h"
64 #include "nsIAuthPrompt.h"
65 #include "nsIAuthPrompt2.h"
66 #include "nsICachingChannel.h"
67 #include "nsICaptivePortalService.h"
68 #include "nsIChannel.h"
69 #include "nsIChannelEventSink.h"
70 #include "nsIClassOfService.h"
71 #include "nsICommandManager.h"
72 #include "nsIConsoleReportCollector.h"
73 #include "nsIContent.h"
74 #include "nsIContentInlines.h"
75 #include "nsIContentSecurityPolicy.h"
76 #include "nsIContentViewer.h"
77 #include "nsIController.h"
78 #include "nsICookieService.h"
79 #include "nsIDocShellTreeItem.h"
80 #include "nsIDocShellTreeOwner.h"
81 #include "nsIDocument.h"
82 #include "nsIDocumentLoaderFactory.h"
83 #include "nsIDOMStorage.h"
84 #include "nsIDOMWindow.h"
85 #include "nsIEditingSession.h"
86 #include "nsIExternalProtocolService.h"
87 #include "nsIFormPOSTActionChannel.h"
88 #include "nsIFrame.h"
89 #include "nsIGlobalObject.h"
90 #include "nsIHttpChannel.h"
91 #include "nsIHttpChannelInternal.h"
92 #include "nsIIDNService.h"
93 #include "nsIInputStreamChannel.h"
94 #include "nsIInterfaceRequestorUtils.h"
95 #include "nsIJARChannel.h"
96 #include "nsILayoutHistoryState.h"
97 #include "nsILoadInfo.h"
98 #include "nsIMultiPartChannel.h"
99 #include "nsINestedURI.h"
100 #include "nsINetworkPredictor.h"
101 #include "nsINode.h"
102 #include "nsINSSErrorsService.h"
103 #include "nsIObserverService.h"
104 #include "nsIOService.h"
105 #include "nsIPrincipal.h"
106 #include "nsIPrivacyTransitionObserver.h"
107 #include "nsIPrompt.h"
108 #include "nsIPromptFactory.h"
109 #include "nsIReflowObserver.h"
110 #include "nsIScriptChannel.h"
111 #include "nsIScriptObjectPrincipal.h"
112 #include "nsIScriptSecurityManager.h"
113 #include "nsIScrollableFrame.h"
114 #include "nsIScrollObserver.h"
115 #include "nsISecureBrowserUI.h"
116 #include "nsISecurityUITelemetry.h"
117 #include "nsISeekableStream.h"
118 #include "nsISelectionDisplay.h"
119 #include "nsISHContainer.h"
120 #include "nsISHEntry.h"
121 #include "nsISHistory.h"
122 #include "nsISHistoryInternal.h"
123 #include "nsISiteSecurityService.h"
124 #include "nsISocketProvider.h"
125 #include "nsIStringBundle.h"
126 #include "nsIStructuredCloneContainer.h"
127 #include "nsISupportsPrimitives.h"
128 #include "nsITabChild.h"
129 #include "nsITextToSubURI.h"
130 #include "nsITimedChannel.h"
131 #include "nsITimer.h"
132 #include "nsITransportSecurityInfo.h"
133 #include "nsIUploadChannel.h"
134 #include "nsIURIFixup.h"
135 #include "nsIURILoader.h"
136 #include "nsIURIMutator.h"
137 #include "nsIURL.h"
138 #include "nsIViewSourceChannel.h"
139 #include "nsIWebBrowserChrome.h"
140 #include "nsIWebBrowserChrome3.h"
141 #include "nsIWebBrowserChromeFocus.h"
142 #include "nsIWebBrowserFind.h"
143 #include "nsIWebProgress.h"
144 #include "nsIWidget.h"
145 #include "nsIWindowWatcher.h"
146 #include "nsIWritablePropertyBag2.h"
147 #include "nsIWyciwygChannel.h"
149 #include "nsPICommandUpdater.h"
150 #include "nsPIDOMWindow.h"
151 #include "nsPILoadGroupInternal.h"
152 #include "nsPIWindowRoot.h"
154 #include "IHistory.h"
155 #include "IUrlClassifierUITelemetry.h"
157 #include "mozIThirdPartyUtil.h"
159 #include "nsArray.h"
160 #include "nsArrayUtils.h"
161 #include "nsAutoPtr.h"
162 #include "nsCDefaultURIFixup.h"
163 #include "nsCExternalHandlerService.h"
164 #include "nsContentDLF.h"
165 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
166 #include "nsContentSecurityManager.h"
167 #include "nsContentUtils.h"
168 #include "nsCURILoader.h"
169 #include "nsDocShellCID.h"
170 #include "nsDocShellEditorData.h"
171 #include "nsDocShellEnumerator.h"
172 #include "nsDocShellLoadInfo.h"
173 #include "nsDocShellLoadTypes.h"
174 #include "nsDOMCID.h"
175 #include "nsDOMNavigationTiming.h"
176 #include "nsDSURIContentListener.h"
177 #include "nsError.h"
178 #include "nsEscape.h"
179 #include "nsFocusManager.h"
180 #include "nsGlobalWindow.h"
181 #include "nsJSEnvironment.h"
182 #include "nsNetCID.h"
183 #include "nsNetUtil.h"
184 #include "nsObjectLoadingContent.h"
185 #include "nsPingListener.h"
186 #include "nsPoint.h"
187 #include "nsQueryObject.h"
188 #include "nsRect.h"
189 #include "nsRefreshTimer.h"
190 #include "nsSandboxFlags.h"
191 #include "nsSHistory.h"
192 #include "nsStructuredCloneContainer.h"
193 #include "nsSubDocumentFrame.h"
194 #include "nsView.h"
195 #include "nsViewManager.h"
196 #include "nsViewSourceHandler.h"
197 #include "nsWhitespaceTokenizer.h"
198 #include "nsWidgetsCID.h"
199 #include "nsXULAppAPI.h"
201 #include "GeckoProfiler.h"
202 #include "Navigator.h"
203 #include "NullPrincipal.h"
204 #include "prenv.h"
205 #include "URIUtils.h"
207 #include "timeline/JavascriptTimelineMarker.h"
209 #ifdef MOZ_PLACES
210 #include "nsIFaviconService.h"
211 #include "mozIPlacesPendingOperation.h"
212 #endif
214 #if NS_PRINT_PREVIEW
215 #include "nsIDocumentViewerPrint.h"
216 #include "nsIWebBrowserPrint.h"
217 #endif
219 #ifdef MOZ_TOOLKIT_SEARCH
220 #include "nsIBrowserSearchService.h"
221 #endif
223 using namespace mozilla;
224 using namespace mozilla::dom;
225 using namespace mozilla::net;
227 // Threshold value in ms for META refresh based redirects
228 #define REFRESH_REDIRECT_TIMER 15000
230 // Hint for native dispatch of events on how long to delay after
231 // all documents have loaded in milliseconds before favoring normal
232 // native event dispatch priorites over performance
233 // Can be overridden with docshell.event_starvation_delay_hint pref.
234 #define NS_EVENT_STARVATION_DELAY_HINT 2000
236 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
238 // True means sUseErrorPages has been added to
239 // preferences var cache.
240 static bool gAddedPreferencesVarCache = false;
242 // Number of documents currently loading
243 static int32_t gNumberOfDocumentsLoading = 0;
245 // Global count of existing docshells.
246 static int32_t gDocShellCount = 0;
248 // Global count of docshells with the private attribute set
249 static uint32_t gNumberOfPrivateDocShells = 0;
251 // True means we validate window targets to prevent frameset
252 // spoofing. Initialize this to a non-bolean value so we know to check
253 // the pref on the creation of the first docshell.
254 static uint32_t gValidateOrigin = 0xffffffff;
256 #ifdef DEBUG
257 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
258 #endif
259 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");;
261 const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
262 const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties";
264 bool nsDocShell::sUseErrorPages = false;
266 // Global reference to the URI fixup service.
267 nsIURIFixup* nsDocShell::sURIFixup = nullptr;
269 static void
270 FavorPerformanceHint(bool aPerfOverStarvation)
272 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
273 if (appShell) {
274 appShell->FavorPerformanceHint(
275 aPerfOverStarvation,
276 Preferences::GetUint("docshell.event_starvation_delay_hint",
277 NS_EVENT_STARVATION_DELAY_HINT));
281 static void
282 IncreasePrivateDocShellCount()
284 gNumberOfPrivateDocShells++;
285 if (gNumberOfPrivateDocShells > 1 ||
286 !XRE_IsContentProcess()) {
287 return;
290 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
291 cc->SendPrivateDocShellsExist(true);
294 static void
295 DecreasePrivateDocShellCount()
297 MOZ_ASSERT(gNumberOfPrivateDocShells > 0);
298 gNumberOfPrivateDocShells--;
299 if (!gNumberOfPrivateDocShells) {
300 if (XRE_IsContentProcess()) {
301 dom::ContentChild* cc = dom::ContentChild::GetSingleton();
302 cc->SendPrivateDocShellsExist(false);
303 return;
306 nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
307 if (obsvc) {
308 obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
313 nsDocShell::nsDocShell()
314 : nsDocLoader()
315 , mForcedCharset(nullptr)
316 , mParentCharset(nullptr)
317 , mTreeOwner(nullptr)
318 , mChromeEventHandler(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 , mInEnsureScriptEnv(false)
345 #endif
346 , mCreated(false)
347 , mAllowSubframes(true)
348 , mAllowPlugins(true)
349 , mAllowJavascript(true)
350 , mAllowMetaRedirects(true)
351 , mAllowImages(true)
352 , mAllowMedia(true)
353 , mAllowDNSPrefetch(true)
354 , mAllowWindowControl(true)
355 , mAllowContentRetargeting(true)
356 , mAllowContentRetargetingOnChildren(true)
357 , mUseErrorPages(false)
358 , mObserveErrorPages(true)
359 , mCSSErrorReportingEnabled(false)
360 , mAllowAuth(true)
361 , mAllowKeywordFixup(false)
362 , mIsOffScreenBrowser(false)
363 , mIsActive(true)
364 , mDisableMetaRefreshWhenInactive(false)
365 , mIsAppTab(false)
366 , mUseGlobalHistory(false)
367 , mUseRemoteTabs(false)
368 , mUseTrackingProtection(false)
369 , mDeviceSizeIsPageSize(false)
370 , mWindowDraggingAllowed(false)
371 , mInFrameSwap(false)
372 , mInheritPrivateBrowsingId(true)
373 , mCanExecuteScripts(false)
374 , mFiredUnloadEvent(false)
375 , mEODForCurrentDocument(false)
376 , mURIResultedInDocument(false)
377 , mIsBeingDestroyed(false)
378 , mIsExecutingOnLoadHandler(false)
379 , mIsPrintingOrPP(false)
380 , mSavingOldViewer(false)
381 , mDynamicallyCreated(false)
382 , mAffectPrivateSessionLifetime(true)
383 , mInvisible(false)
384 , mHasLoadedNonBlankURI(false)
385 , mBlankTiming(false)
387 mHistoryID.m0 = 0;
388 mHistoryID.m1 = 0;
389 mHistoryID.m2 = 0;
390 AssertOriginAttributesMatchPrivateBrowsing();
392 nsContentUtils::GenerateUUIDInPlace(mHistoryID);
394 if (gDocShellCount++ == 0) {
395 NS_ASSERTION(sURIFixup == nullptr,
396 "Huh, sURIFixup not null in first nsDocShell ctor!");
398 CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
401 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
403 #ifdef DEBUG
404 // We're counting the number of |nsDocShells| to help find leaks
405 ++gNumberOfDocShells;
406 if (!PR_GetEnv("MOZ_QUIET")) {
407 printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %s]\n",
408 (void*)this,
409 gNumberOfDocShells,
410 getpid(),
411 nsIDToCString(mHistoryID).get());
413 #endif
416 nsDocShell::~nsDocShell()
418 MOZ_ASSERT(!mObserved);
420 // Avoid notifying observers while we're in the dtor.
421 mIsBeingDestroyed = true;
423 Destroy();
425 if (mSessionHistory) {
426 mSessionHistory->LegacySHistoryInternal()->SetRootDocShell(nullptr);
429 if (--gDocShellCount == 0) {
430 NS_IF_RELEASE(sURIFixup);
433 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
435 #ifdef DEBUG
436 // We're counting the number of |nsDocShells| to help find leaks
437 --gNumberOfDocShells;
438 if (!PR_GetEnv("MOZ_QUIET")) {
439 printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s]\n",
440 (void*)this,
441 gNumberOfDocShells,
442 getpid(),
443 nsIDToCString(mHistoryID).get());
445 #endif
448 nsresult
449 nsDocShell::Init()
451 MOZ_ASSERT(!mIsBeingDestroyed);
453 nsresult rv = nsDocLoader::Init();
454 NS_ENSURE_SUCCESS(rv, rv);
456 NS_ASSERTION(mLoadGroup, "Something went wrong!");
458 mContentListener = new nsDSURIContentListener(this);
459 rv = mContentListener->Init();
460 NS_ENSURE_SUCCESS(rv, rv);
462 // If parent intercept is not enabled then we must forward to
463 // the network controller from docshell. We also enable if we're
464 // in the parent process in order to support non-e10s configurations.
465 if (!ServiceWorkerParentInterceptEnabled() || XRE_IsParentProcess()) {
466 mInterceptController = new ServiceWorkerInterceptController();
469 // We want to hold a strong ref to the loadgroup, so it better hold a weak
470 // ref to us... use an InterfaceRequestorProxy to do this.
471 nsCOMPtr<nsIInterfaceRequestor> proxy =
472 new InterfaceRequestorProxy(static_cast<nsIInterfaceRequestor*>(this));
473 mLoadGroup->SetNotificationCallbacks(proxy);
475 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this);
476 NS_ENSURE_SUCCESS(rv, rv);
478 // Add as |this| a progress listener to itself. A little weird, but
479 // simpler than reproducing all the listener-notification logic in
480 // overrides of the various methods via which nsDocLoader can be
481 // notified. Note that this holds an nsWeakPtr to ourselves, so it's ok.
482 return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
483 nsIWebProgress::NOTIFY_STATE_NETWORK);
486 void
487 nsDocShell::DestroyChildren()
489 nsCOMPtr<nsIDocShellTreeItem> shell;
490 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
491 while (iter.HasMore()) {
492 shell = do_QueryObject(iter.GetNext());
493 NS_ASSERTION(shell, "docshell has null child");
495 if (shell) {
496 shell->SetTreeOwner(nullptr);
500 nsDocLoader::DestroyChildren();
503 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell,
504 nsDocLoader,
505 mSessionStorageManager,
506 mScriptGlobal,
507 mInitialClientSource,
508 mSessionHistory)
510 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
511 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
513 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
514 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
515 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
516 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
517 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
518 NS_INTERFACE_MAP_ENTRY(nsIScrollable)
519 NS_INTERFACE_MAP_ENTRY(nsITextScroll)
520 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
521 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
522 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
523 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
524 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
525 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
526 NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
527 NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
528 NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
529 NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
530 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
531 mInterceptController)
532 NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
533 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
535 NS_IMETHODIMP
536 nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
538 MOZ_ASSERT(aSink, "null out param");
540 *aSink = nullptr;
542 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
543 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
544 *aSink = mCommandManager;
545 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
546 *aSink = mContentListener;
547 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
548 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
549 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
550 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
551 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
552 NS_SUCCEEDED(EnsureScriptEnvironment())) {
553 return mScriptGlobal->QueryInterface(aIID, aSink);
554 } else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
555 NS_SUCCEEDED(EnsureContentViewer())) {
556 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
557 doc.forget(aSink);
558 return *aSink ? NS_OK : NS_NOINTERFACE;
559 } else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
560 *aSink = nullptr;
562 // Return application cache associated with this docshell, if any
564 nsCOMPtr<nsIContentViewer> contentViewer;
565 GetContentViewer(getter_AddRefs(contentViewer));
566 if (!contentViewer) {
567 return NS_ERROR_NO_INTERFACE;
570 nsCOMPtr<nsIDocument> doc = contentViewer->GetDocument();
571 NS_ASSERTION(doc, "Should have a document.");
572 if (!doc) {
573 return NS_ERROR_NO_INTERFACE;
576 #if defined(DEBUG)
577 MOZ_LOG(gDocShellLog, LogLevel::Debug,
578 ("nsDocShell[%p]: returning app cache container %p",
579 this, doc.get()));
580 #endif
581 return doc->QueryInterface(aIID, aSink);
582 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
583 NS_SUCCEEDED(EnsureScriptEnvironment())) {
584 nsresult rv;
585 nsCOMPtr<nsIWindowWatcher> wwatch =
586 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
587 NS_ENSURE_SUCCESS(rv, rv);
589 // Get the an auth prompter for our window so that the parenting
590 // of the dialogs works as it should when using tabs.
591 nsIPrompt* prompt;
592 rv = wwatch->GetNewPrompter(mScriptGlobal->AsOuter(), &prompt);
593 NS_ENSURE_SUCCESS(rv, rv);
595 *aSink = prompt;
596 return NS_OK;
597 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
598 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
599 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ?
600 NS_OK : NS_NOINTERFACE;
601 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
602 RefPtr<ChildSHistory> shistory = GetSessionHistory();
603 if (shistory) {
604 // XXX(nika): Stop exposing nsISHistory through GetInterface.
605 nsCOMPtr<nsISHistory> legacy = shistory->LegacySHistory();
606 legacy.forget(aSink);
607 return NS_OK;
609 return NS_NOINTERFACE;
610 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
611 nsresult rv = EnsureFind();
612 if (NS_FAILED(rv)) {
613 return rv;
616 *aSink = mFind;
617 NS_ADDREF((nsISupports*)*aSink);
618 return NS_OK;
619 } else if (aIID.Equals(NS_GET_IID(nsIEditingSession))) {
620 nsCOMPtr<nsIEditingSession> es;
621 GetEditingSession(getter_AddRefs(es));
622 es.forget(aSink);
623 return *aSink ? NS_OK : NS_NOINTERFACE;
624 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
625 nsIPresShell* shell = GetPresShell();
626 if (shell) {
627 return shell->QueryInterface(aIID, aSink);
629 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
630 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
631 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
632 if (NS_SUCCEEDED(rv) && treeOwner) {
633 return treeOwner->QueryInterface(aIID, aSink);
635 } else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
636 *aSink = GetTabChild().take();
637 return *aSink ? NS_OK : NS_ERROR_FAILURE;
638 } else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
639 RefPtr<TabChild> tabChild = TabChild::GetFrom(this);
640 nsCOMPtr<nsIContentFrameMessageManager> mm;
641 if (tabChild) {
642 mm = tabChild->GetMessageManager();
643 } else {
644 if (nsPIDOMWindowOuter* win = GetWindow()) {
645 mm = do_QueryInterface(win->GetParentTarget());
648 *aSink = mm.get();
649 } else {
650 return nsDocLoader::GetInterface(aIID, aSink);
653 NS_IF_ADDREF(((nsISupports*)*aSink));
654 return *aSink ? NS_OK : NS_NOINTERFACE;
657 NS_IMETHODIMP
658 nsDocShell::LoadURI(nsIURI* aURI,
659 nsIDocShellLoadInfo* aLoadInfo,
660 uint32_t aLoadFlags,
661 bool aFirstParty)
663 MOZ_ASSERT(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0,
664 "Unexpected flags");
665 MOZ_ASSERT((aLoadFlags & 0xf) == 0, "Should not have these flags set");
667 // Note: we allow loads to get through here even if mFiredUnloadEvent is
668 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
669 // so we pass false as the second parameter to IsNavigationAllowed.
670 // However, we don't allow the page to change location *in the middle of*
671 // firing beforeunload, so we do need to check if *beforeunload* is currently
672 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
673 if (!IsNavigationAllowed(true, false)) {
674 return NS_OK; // JS may not handle returning of an error code
677 nsCOMPtr<nsIURI> referrer;
678 nsCOMPtr<nsIURI> originalURI;
679 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
680 bool loadReplace = false;
681 nsCOMPtr<nsIInputStream> postStream;
682 nsCOMPtr<nsIInputStream> headersStream;
683 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
684 bool inheritPrincipal = false;
685 bool principalIsExplicit = false;
686 bool sendReferrer = true;
687 uint32_t referrerPolicy = RP_Unset;
688 bool isSrcdoc = false;
689 nsCOMPtr<nsISHEntry> shEntry;
690 nsString target;
691 nsAutoString srcdoc;
692 bool forceAllowDataURI = false;
693 bool originalFrameSrc = false;
694 nsCOMPtr<nsIDocShell> sourceDocShell;
695 nsCOMPtr<nsIURI> baseURI;
697 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
699 NS_ENSURE_ARG(aURI);
701 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
702 mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
703 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
706 // Extract the info from the DocShellLoadInfo struct...
707 if (aLoadInfo) {
708 aLoadInfo->GetReferrer(getter_AddRefs(referrer));
709 aLoadInfo->GetOriginalURI(getter_AddRefs(originalURI));
710 GetMaybeResultPrincipalURI(aLoadInfo, resultPrincipalURI);
711 aLoadInfo->GetLoadReplace(&loadReplace);
712 nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
713 aLoadInfo->GetLoadType(&lt);
714 // Get the appropriate loadType from nsIDocShellLoadInfo type
715 loadType = ConvertDocShellInfoLoadTypeToLoadType(lt);
717 aLoadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
718 aLoadInfo->GetInheritPrincipal(&inheritPrincipal);
719 aLoadInfo->GetPrincipalIsExplicit(&principalIsExplicit);
720 aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
721 aLoadInfo->GetTarget(getter_Copies(target));
722 aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
723 aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
724 aLoadInfo->GetSendReferrer(&sendReferrer);
725 aLoadInfo->GetReferrerPolicy(&referrerPolicy);
726 aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
727 aLoadInfo->GetSrcdocData(srcdoc);
728 aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
729 aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
730 aLoadInfo->GetForceAllowDataURI(&forceAllowDataURI);
731 aLoadInfo->GetOriginalFrameSrc(&originalFrameSrc);
734 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
735 ("nsDocShell[%p]: loading %s with flags 0x%08x",
736 this, aURI->GetSpecOrDefault().get(), aLoadFlags));
738 if (!shEntry &&
739 !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) {
740 // First verify if this is a subframe.
741 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
742 GetSameTypeParent(getter_AddRefs(parentAsItem));
743 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
744 uint32_t parentLoadType;
746 if (parentDS && parentDS != static_cast<nsIDocShell*>(this)) {
747 /* OK. It is a subframe. Checkout the
748 * parent's loadtype. If the parent was loaded thro' a history
749 * mechanism, then get the SH entry for the child from the parent.
750 * This is done to restore frameset navigation while going back/forward.
751 * If the parent was loaded through any other loadType, set the
752 * child's loadType too accordingly, so that session history does not
753 * get confused.
756 // Get the parent's load type
757 parentDS->GetLoadType(&parentLoadType);
759 // Get the ShEntry for the child from the parent
760 nsCOMPtr<nsISHEntry> currentSH;
761 bool oshe = false;
762 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
763 bool dynamicallyAddedChild = mDynamicallyCreated;
764 if (!dynamicallyAddedChild && !oshe && currentSH) {
765 currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
767 if (!dynamicallyAddedChild) {
768 // Only use the old SHEntry, if we're sure enough that
769 // it wasn't originally for some other frame.
770 parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
773 // Make some decisions on the child frame's loadType based on the
774 // parent's loadType, if the subframe hasn't loaded anything into it.
776 // In some cases privileged scripts may try to get the DOMWindow
777 // reference of this docshell before the loading starts, causing the
778 // initial about:blank content viewer being created and mCurrentURI being
779 // set. To handle this case we check if mCurrentURI is about:blank and
780 // currentSHEntry is null.
781 nsCOMPtr<nsISHEntry> currentChildEntry;
782 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
783 if (!mCurrentURI || (NS_IsAboutBlank(mCurrentURI) && !currentChildEntry)) {
784 // This is a newly created frame. Check for exception cases first.
785 // By default the subframe will inherit the parent's loadType.
786 if (shEntry && (parentLoadType == LOAD_NORMAL ||
787 parentLoadType == LOAD_LINK ||
788 parentLoadType == LOAD_NORMAL_EXTERNAL)) {
789 // The parent was loaded normally. In this case, this *brand new*
790 // child really shouldn't have a SHEntry. If it does, it could be
791 // because the parent is replacing an existing frame with a new frame,
792 // in the onLoadHandler. We don't want this url to get into session
793 // history. Clear off shEntry, and set load type to
794 // LOAD_BYPASS_HISTORY.
795 bool inOnLoadHandler = false;
796 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
797 if (inOnLoadHandler) {
798 loadType = LOAD_NORMAL_REPLACE;
799 shEntry = nullptr;
801 } else if (parentLoadType == LOAD_REFRESH) {
802 // Clear shEntry. For refresh loads, we have to load
803 // what comes thro' the pipe, not what's in history.
804 shEntry = nullptr;
805 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
806 (shEntry &&
807 ((parentLoadType & LOAD_CMD_HISTORY) ||
808 (parentLoadType == LOAD_RELOAD_NORMAL) ||
809 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
810 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
811 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
812 // If the parent url, bypassed history or was loaded from
813 // history, pass on the parent's loadType to the new child
814 // frame too, so that the child frame will also
815 // avoid getting into history.
816 loadType = parentLoadType;
817 } else if (parentLoadType == LOAD_ERROR_PAGE) {
818 // If the parent document is an error page, we don't
819 // want to update global/session history. However,
820 // this child frame is not an error page.
821 loadType = LOAD_BYPASS_HISTORY;
822 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
823 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
824 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
825 // the new frame should inherit the parent's load type so that it also
826 // bypasses the cache and/or proxy
827 loadType = parentLoadType;
829 } else {
830 // This is a pre-existing subframe. If
831 // 1. The load of this frame was not originally initiated by session
832 // history directly (i.e. (!shEntry) condition succeeded, but it can
833 // still be a history load on parent which causes this frame being
834 // loaded), and
835 // 2. mCurrentURI is not null, nor the initial about:blank,
836 // it is possible that a parent's onLoadHandler or even self's
837 // onLoadHandler is loading a new page in this child. Check parent's and
838 // self's busy flag and if it is set, we don't want this onLoadHandler
839 // load to get in to session history.
840 uint32_t parentBusy = BUSY_FLAGS_NONE;
841 uint32_t selfBusy = BUSY_FLAGS_NONE;
842 parentDS->GetBusyFlags(&parentBusy);
843 GetBusyFlags(&selfBusy);
844 if (parentBusy & BUSY_FLAGS_BUSY ||
845 selfBusy & BUSY_FLAGS_BUSY) {
846 loadType = LOAD_NORMAL_REPLACE;
847 shEntry = nullptr;
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;
861 } // !shEntry
863 if (shEntry) {
864 #ifdef DEBUG
865 MOZ_LOG(gDocShellLog, LogLevel::Debug,
866 ("nsDocShell[%p]: loading from session history", this));
867 #endif
869 return LoadHistoryEntry(shEntry, loadType);
872 // On history navigation via Back/Forward buttons, don't execute
873 // automatic JavaScript redirection such as |location.href = ...| or
874 // |window.open()|
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;
883 // Perform the load...
885 // We need a principalToInherit.
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.
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;
918 principalToInherit = nullptr;
919 inheritPrincipal = true;
920 } else if (nsContentUtils::IsExpandedPrincipal(principalToInherit)) {
921 if (principalIsExplicit) {
922 return NS_ERROR_DOM_SECURITY_ERR;
924 // Don't inherit from the current page. Just do the safe thing
925 // and pretend that we were loaded by a nullprincipal.
927 // We didn't inherit OriginAttributes here as ExpandedPrincipal doesn't
928 // have origin attributes.
929 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(this);
930 inheritPrincipal = false;
933 if (!principalToInherit && !inheritPrincipal && !principalIsExplicit) {
934 // See if there's system or chrome JS code running
935 inheritPrincipal = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
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 = NullPrincipal::CreateWithInheritedAttributes(this, aFirstParty);
945 // If the triggeringPrincipal is not passed explicitly, we first try to create
946 // a principal from the referrer, since the referrer URI reflects the web origin
947 // that triggered the load. If there is no referrer URI, we fall back to using
948 // the SystemPrincipal. It's safe to assume that no provided triggeringPrincipal
949 // and no referrer simulate a load that was triggered by the system.
950 // It's important to note that this block of code needs to appear *after* the block
951 // where we munge the principalToInherit, because otherwise we would never enter
952 // code blocks checking if the principalToInherit is null and we will end up with
953 // a wrong inheritPrincipal flag.
954 if (!triggeringPrincipal) {
955 if (referrer) {
956 nsresult rv = CreatePrincipalFromReferrer(referrer,
957 getter_AddRefs(triggeringPrincipal));
958 NS_ENSURE_SUCCESS(rv, rv);
960 else {
961 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
965 uint32_t flags = 0;
967 if (inheritPrincipal) {
968 flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
971 if (!sendReferrer) {
972 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
975 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
976 flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
979 if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) {
980 flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
983 if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) {
984 flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER;
987 if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) {
988 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES;
991 if (isSrcdoc) {
992 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
995 if (forceAllowDataURI) {
996 flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
999 if (originalFrameSrc) {
1000 flags |= INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC;
1003 return InternalLoad(aURI,
1004 originalURI,
1005 resultPrincipalURI,
1006 loadReplace,
1007 referrer,
1008 referrerPolicy,
1009 triggeringPrincipal,
1010 principalToInherit,
1011 flags,
1012 target,
1013 nullptr, // No type hint
1014 VoidString(), // No forced download
1015 postStream,
1016 headersStream,
1017 loadType,
1018 nullptr, // No SHEntry
1019 aFirstParty,
1020 srcdoc,
1021 sourceDocShell,
1022 baseURI,
1023 nullptr, // No nsIDocShell
1024 nullptr); // No nsIRequest
1027 NS_IMETHODIMP
1028 nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo** aLoadInfo)
1030 nsDocShellLoadInfo* loadInfo = new nsDocShellLoadInfo();
1031 nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);
1033 localRef.forget(aLoadInfo);
1034 return NS_OK;
1038 * Reset state to a new content model within the current document and the
1039 * document viewer. Called by the document before initiating an out of band
1040 * document.write().
1042 NS_IMETHODIMP
1043 nsDocShell::PrepareForNewContentModel()
1045 mEODForCurrentDocument = false;
1046 return NS_OK;
1049 NS_IMETHODIMP
1050 nsDocShell::FirePageHideNotification(bool aIsUnload)
1052 FirePageHideNotificationInternal(aIsUnload, false);
1053 return NS_OK;
1056 void
1057 nsDocShell::FirePageHideNotificationInternal(bool aIsUnload,
1058 bool aSkipCheckingDynEntries)
1060 if (mContentViewer && !mFiredUnloadEvent) {
1061 // Keep an explicit reference since calling PageHide could release
1062 // mContentViewer
1063 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1064 mFiredUnloadEvent = true;
1066 if (mTiming) {
1067 mTiming->NotifyUnloadEventStart();
1070 contentViewer->PageHide(aIsUnload);
1072 if (mTiming) {
1073 mTiming->NotifyUnloadEventEnd();
1076 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1077 uint32_t n = mChildList.Length();
1078 kids.SetCapacity(n);
1079 for (uint32_t i = 0; i < n; i++) {
1080 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1083 n = kids.Length();
1084 for (uint32_t i = 0; i < n; ++i) {
1085 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1086 if (child) {
1087 // Skip checking dynamic subframe entries in our children.
1088 child->FirePageHideNotificationInternal(aIsUnload, true);
1092 // If the document is unloading, remove all dynamic subframe entries.
1093 if (aIsUnload && !aSkipCheckingDynEntries) {
1094 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
1095 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE));
1096 if (rootSH && container) {
1097 int32_t index = rootSH->Index();
1098 rootSH->LegacySHistoryInternal()->RemoveDynEntries(index, container);
1102 // Now make sure our editor, if any, is detached before we go
1103 // any farther.
1104 DetachEditorFromWindow();
1108 nsresult
1109 nsDocShell::DispatchToTabGroup(TaskCategory aCategory,
1110 already_AddRefed<nsIRunnable>&& aRunnable)
1112 // Hold the ref so we won't forget to release it.
1113 nsCOMPtr<nsIRunnable> runnable(aRunnable);
1114 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1115 if (!win) {
1116 // Window should only be unavailable after destroyed.
1117 MOZ_ASSERT(mIsBeingDestroyed);
1118 return NS_ERROR_FAILURE;
1121 if (win->GetDocGroup()) {
1122 return win->GetDocGroup()->Dispatch(aCategory, runnable.forget());
1124 RefPtr<mozilla::dom::TabGroup> tabGroup = win->TabGroup();
1125 return tabGroup->Dispatch(aCategory, runnable.forget());
1128 NS_IMETHODIMP
1129 nsDocShell::DispatchLocationChangeEvent()
1131 return DispatchToTabGroup(
1132 TaskCategory::Other,
1133 NewRunnableMethod("nsDocShell::FireDummyOnLocationChange",
1134 this,
1135 &nsDocShell::FireDummyOnLocationChange));
1138 bool
1139 nsDocShell::MaybeInitTiming()
1141 if (mTiming && !mBlankTiming) {
1142 return false;
1145 bool canBeReset = false;
1147 if (mScriptGlobal && mBlankTiming) {
1148 nsPIDOMWindowInner* innerWin =
1149 mScriptGlobal->AsOuter()->GetCurrentInnerWindow();
1150 if (innerWin && innerWin->GetPerformance()) {
1151 mTiming = innerWin->GetPerformance()->GetDOMTiming();
1152 mBlankTiming = false;
1156 if (!mTiming) {
1157 mTiming = new nsDOMNavigationTiming(this);
1158 canBeReset = true;
1161 mTiming->NotifyNavigationStart(
1162 mIsActive ? nsDOMNavigationTiming::DocShellState::eActive
1163 : nsDOMNavigationTiming::DocShellState::eInactive);
1165 return canBeReset;
1168 void
1169 nsDocShell::MaybeResetInitTiming(bool aReset)
1171 if (aReset) {
1172 mTiming = nullptr;
1176 nsDOMNavigationTiming*
1177 nsDocShell::GetNavigationTiming() const
1179 return mTiming;
1183 // Bug 13871: Prevent frameset spoofing
1185 // This routine answers: 'Is origin's document from same domain as
1186 // target's document?'
1188 // file: uris are considered the same domain for the purpose of
1189 // frame navigation regardless of script accessibility (bug 420425)
1191 /* static */ bool
1192 nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
1193 nsIDocShellTreeItem* aTargetTreeItem)
1195 // We want to bypass this check for chrome callers, but only if there's
1196 // JS on the stack. System callers still need to do it.
1197 if (nsContentUtils::GetCurrentJSContext() &&
1198 nsContentUtils::IsCallerChrome()) {
1199 return true;
1202 MOZ_ASSERT(aOriginTreeItem && aTargetTreeItem, "need two docshells");
1204 // Get origin document principal
1205 nsCOMPtr<nsIDocument> originDocument = aOriginTreeItem->GetDocument();
1206 NS_ENSURE_TRUE(originDocument, false);
1208 // Get target principal
1209 nsCOMPtr<nsIDocument> targetDocument = aTargetTreeItem->GetDocument();
1210 NS_ENSURE_TRUE(targetDocument, false);
1212 bool equal;
1213 nsresult rv = originDocument->NodePrincipal()->Equals(
1214 targetDocument->NodePrincipal(), &equal);
1215 if (NS_SUCCEEDED(rv) && equal) {
1216 return true;
1219 // Not strictly equal, special case if both are file: uris
1220 bool originIsFile = false;
1221 bool targetIsFile = false;
1222 nsCOMPtr<nsIURI> originURI;
1223 nsCOMPtr<nsIURI> targetURI;
1224 nsCOMPtr<nsIURI> innerOriginURI;
1225 nsCOMPtr<nsIURI> innerTargetURI;
1227 rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI));
1228 if (NS_SUCCEEDED(rv) && originURI) {
1229 innerOriginURI = NS_GetInnermostURI(originURI);
1232 rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI));
1233 if (NS_SUCCEEDED(rv) && targetURI) {
1234 innerTargetURI = NS_GetInnermostURI(targetURI);
1237 return innerOriginURI && innerTargetURI &&
1238 NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) &&
1239 NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) &&
1240 originIsFile && targetIsFile;
1243 nsresult
1244 nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
1246 NS_ENSURE_ARG_POINTER(aPresContext);
1247 *aPresContext = nullptr;
1249 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
1250 while (viewer) {
1251 nsCOMPtr<nsIContentViewer> prevViewer;
1252 viewer->GetPreviousViewer(getter_AddRefs(prevViewer));
1253 if (!prevViewer) {
1254 return viewer->GetPresContext(aPresContext);
1256 viewer = prevViewer;
1259 return NS_OK;
1262 NS_IMETHODIMP
1263 nsDocShell::GetPresContext(nsPresContext** aPresContext)
1265 NS_ENSURE_ARG_POINTER(aPresContext);
1266 *aPresContext = nullptr;
1268 if (!mContentViewer) {
1269 return NS_OK;
1272 return mContentViewer->GetPresContext(aPresContext);
1275 NS_IMETHODIMP_(nsIPresShell*)
1276 nsDocShell::GetPresShell()
1278 RefPtr<nsPresContext> presContext;
1279 (void)GetPresContext(getter_AddRefs(presContext));
1280 return presContext ? presContext->GetPresShell() : nullptr;
1283 NS_IMETHODIMP
1284 nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell)
1286 nsresult rv = NS_OK;
1288 NS_ENSURE_ARG_POINTER(aPresShell);
1289 *aPresShell = nullptr;
1291 RefPtr<nsPresContext> presContext;
1292 (void)GetEldestPresContext(getter_AddRefs(presContext));
1294 if (presContext) {
1295 NS_IF_ADDREF(*aPresShell = presContext->GetPresShell());
1298 return rv;
1301 NS_IMETHODIMP
1302 nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer)
1304 NS_ENSURE_ARG_POINTER(aContentViewer);
1306 *aContentViewer = mContentViewer;
1307 NS_IF_ADDREF(*aContentViewer);
1308 return NS_OK;
1311 NS_IMETHODIMP
1312 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler)
1314 // Weak reference. Don't addref.
1315 mChromeEventHandler = aChromeEventHandler;
1317 if (mScriptGlobal) {
1318 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1321 return NS_OK;
1324 NS_IMETHODIMP
1325 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler)
1327 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1328 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
1329 handler.forget(aChromeEventHandler);
1330 return NS_OK;
1333 NS_IMETHODIMP
1334 nsDocShell::SetCurrentURI(nsIURI* aURI)
1336 // Note that securityUI will set STATE_IS_INSECURE, even if
1337 // the scheme of |aURI| is "https".
1338 SetCurrentURI(aURI, nullptr, true, 0);
1339 return NS_OK;
1342 bool
1343 nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1344 bool aFireOnLocationChange, uint32_t aLocationFlags)
1346 MOZ_ASSERT(!mIsBeingDestroyed);
1348 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1349 ("DOCSHELL %p SetCurrentURI %s\n",
1350 this, aURI ? aURI->GetSpecOrDefault().get() : ""));
1352 // We don't want to send a location change when we're displaying an error
1353 // page, and we don't want to change our idea of "current URI" either
1354 if (mLoadType == LOAD_ERROR_PAGE) {
1355 return false;
1358 mCurrentURI = aURI;
1360 if (!NS_IsAboutBlank(mCurrentURI)) {
1361 mHasLoadedNonBlankURI = true;
1364 bool isRoot = false; // Is this the root docshell
1365 bool isSubFrame = false; // Is this a subframe navigation?
1367 nsCOMPtr<nsIDocShellTreeItem> root;
1369 GetSameTypeRootTreeItem(getter_AddRefs(root));
1370 if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
1371 // This is the root docshell
1372 isRoot = true;
1374 if (mLSHE) {
1375 mLSHE->GetIsSubFrame(&isSubFrame);
1378 if (!isSubFrame && !isRoot) {
1380 * We don't want to send OnLocationChange notifications when
1381 * a subframe is being loaded for the first time, while
1382 * visiting a frameset page
1384 return false;
1387 if (aFireOnLocationChange) {
1388 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1390 return !aFireOnLocationChange;
1393 NS_IMETHODIMP
1394 nsDocShell::GetCharset(nsACString& aCharset)
1396 aCharset.Truncate();
1398 nsIPresShell* presShell = GetPresShell();
1399 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1400 nsIDocument* doc = presShell->GetDocument();
1401 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1402 doc->GetDocumentCharacterSet()->Name(aCharset);
1403 return NS_OK;
1406 NS_IMETHODIMP
1407 nsDocShell::GatherCharsetMenuTelemetry()
1409 nsCOMPtr<nsIContentViewer> viewer;
1410 GetContentViewer(getter_AddRefs(viewer));
1411 if (!viewer) {
1412 return NS_OK;
1415 nsIDocument* doc = viewer->GetDocument();
1416 if (!doc || doc->WillIgnoreCharsetOverride()) {
1417 return NS_OK;
1420 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true);
1422 bool isFileURL = false;
1423 nsIURI* url = doc->GetOriginalURI();
1424 if (url) {
1425 url->SchemeIs("file", &isFileURL);
1428 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1429 switch (charsetSource) {
1430 case kCharsetFromTopLevelDomain:
1431 // Unlabeled doc on a domain that we map to a fallback encoding
1432 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7);
1433 break;
1434 case kCharsetFromFallback:
1435 case kCharsetFromDocTypeDefault:
1436 case kCharsetFromCache:
1437 case kCharsetFromParentFrame:
1438 case kCharsetFromHintPrevDoc:
1439 // Changing charset on an unlabeled doc.
1440 if (isFileURL) {
1441 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0);
1442 } else {
1443 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1);
1445 break;
1446 case kCharsetFromAutoDetection:
1447 // Changing charset on unlabeled doc where chardet fired
1448 if (isFileURL) {
1449 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2);
1450 } else {
1451 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3);
1453 break;
1454 case kCharsetFromMetaPrescan:
1455 case kCharsetFromMetaTag:
1456 case kCharsetFromChannel:
1457 // Changing charset on a doc that had a charset label.
1458 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4);
1459 break;
1460 case kCharsetFromParentForced:
1461 case kCharsetFromUserForced:
1462 // Changing charset on a document that already had an override.
1463 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5);
1464 break;
1465 case kCharsetFromIrreversibleAutoDetection:
1466 case kCharsetFromOtherComponent:
1467 case kCharsetFromByteOrderMark:
1468 case kCharsetUninitialized:
1469 default:
1470 // Bug. This isn't supposed to happen.
1471 Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6);
1472 break;
1474 return NS_OK;
1477 NS_IMETHODIMP
1478 nsDocShell::SetCharset(const nsACString& aCharset)
1480 // set the charset override
1481 return SetForcedCharset(aCharset);
1484 NS_IMETHODIMP
1485 nsDocShell::SetForcedCharset(const nsACString& aCharset)
1487 if (aCharset.IsEmpty()) {
1488 mForcedCharset = nullptr;
1489 return NS_OK;
1491 const Encoding* encoding = Encoding::ForLabel(aCharset);
1492 if (!encoding) {
1493 // Reject unknown labels
1494 return NS_ERROR_INVALID_ARG;
1496 if (!encoding->IsAsciiCompatible() && encoding != ISO_2022_JP_ENCODING) {
1497 // Reject XSS hazards
1498 return NS_ERROR_INVALID_ARG;
1500 mForcedCharset = encoding;
1501 return NS_OK;
1504 NS_IMETHODIMP
1505 nsDocShell::GetForcedCharset(nsACString& aResult)
1507 if (mForcedCharset) {
1508 mForcedCharset->Name(aResult);
1509 } else {
1510 aResult.Truncate();
1512 return NS_OK;
1515 void
1516 nsDocShell::SetParentCharset(const Encoding*& aCharset,
1517 int32_t aCharsetSource,
1518 nsIPrincipal* aPrincipal)
1520 mParentCharset = aCharset;
1521 mParentCharsetSource = aCharsetSource;
1522 mParentCharsetPrincipal = aPrincipal;
1525 void
1526 nsDocShell::GetParentCharset(const Encoding*& aCharset,
1527 int32_t* aCharsetSource,
1528 nsIPrincipal** aPrincipal)
1530 aCharset = mParentCharset;
1531 *aCharsetSource = mParentCharsetSource;
1532 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1535 NS_IMETHODIMP
1536 nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded)
1538 nsCOMPtr<nsIDocument> doc(GetDocument());
1539 *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
1540 return NS_OK;
1543 NS_IMETHODIMP
1544 nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked)
1546 nsCOMPtr<nsIDocument> doc(GetDocument());
1547 *aHasMixedActiveContentBlocked =
1548 doc && doc->GetHasMixedActiveContentBlocked();
1549 return NS_OK;
1552 NS_IMETHODIMP
1553 nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded)
1555 nsCOMPtr<nsIDocument> doc(GetDocument());
1556 *aHasMixedDisplayContentLoaded =
1557 doc && doc->GetHasMixedDisplayContentLoaded();
1558 return NS_OK;
1561 NS_IMETHODIMP
1562 nsDocShell::GetHasMixedDisplayContentBlocked(
1563 bool* aHasMixedDisplayContentBlocked)
1565 nsCOMPtr<nsIDocument> doc(GetDocument());
1566 *aHasMixedDisplayContentBlocked =
1567 doc && doc->GetHasMixedDisplayContentBlocked();
1568 return NS_OK;
1571 NS_IMETHODIMP
1572 nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked)
1574 nsCOMPtr<nsIDocument> doc(GetDocument());
1575 *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
1576 return NS_OK;
1579 NS_IMETHODIMP
1580 nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded)
1582 nsCOMPtr<nsIDocument> doc(GetDocument());
1583 *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
1584 return NS_OK;
1587 NS_IMETHODIMP
1588 nsDocShell::GetAllowPlugins(bool* aAllowPlugins)
1590 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1592 *aAllowPlugins = mAllowPlugins;
1593 return NS_OK;
1596 NS_IMETHODIMP
1597 nsDocShell::SetAllowPlugins(bool aAllowPlugins)
1599 mAllowPlugins = aAllowPlugins;
1600 // XXX should enable or disable a plugin host
1601 return NS_OK;
1604 NS_IMETHODIMP
1605 nsDocShell::GetAllowJavascript(bool* aAllowJavascript)
1607 NS_ENSURE_ARG_POINTER(aAllowJavascript);
1609 *aAllowJavascript = mAllowJavascript;
1610 return NS_OK;
1613 NS_IMETHODIMP
1614 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled)
1616 MOZ_ASSERT(aEnabled);
1617 *aEnabled = mCSSErrorReportingEnabled;
1618 return NS_OK;
1621 NS_IMETHODIMP
1622 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled)
1624 mCSSErrorReportingEnabled = aEnabled;
1625 return NS_OK;
1628 NS_IMETHODIMP
1629 nsDocShell::SetAllowJavascript(bool aAllowJavascript)
1631 mAllowJavascript = aAllowJavascript;
1632 RecomputeCanExecuteScripts();
1633 return NS_OK;
1636 NS_IMETHODIMP
1637 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
1639 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1640 AssertOriginAttributesMatchPrivateBrowsing();
1641 *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
1642 return NS_OK;
1645 NS_IMETHODIMP
1646 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
1648 if (!CanSetOriginAttributes()) {
1649 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1651 return changed ? NS_ERROR_FAILURE : NS_OK;
1654 return SetPrivateBrowsing(aUsePrivateBrowsing);
1657 NS_IMETHODIMP
1658 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
1660 MOZ_ASSERT(!mIsBeingDestroyed);
1662 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1663 if (changed) {
1664 mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0;
1666 if (mItemType != typeChrome) {
1667 mOriginAttributes.SyncAttributesWithPrivateBrowsing(aUsePrivateBrowsing);
1670 if (mAffectPrivateSessionLifetime) {
1671 if (aUsePrivateBrowsing) {
1672 IncreasePrivateDocShellCount();
1673 } else {
1674 DecreasePrivateDocShellCount();
1679 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
1680 while (iter.HasMore()) {
1681 nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
1682 if (shell) {
1683 shell->SetPrivateBrowsing(aUsePrivateBrowsing);
1687 if (changed) {
1688 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1689 while (iter.HasMore()) {
1690 nsWeakPtr ref = iter.GetNext();
1691 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1692 if (!obs) {
1693 mPrivacyObservers.RemoveElement(ref);
1694 } else {
1695 obs->PrivateModeChanged(aUsePrivateBrowsing);
1700 AssertOriginAttributesMatchPrivateBrowsing();
1701 return NS_OK;
1704 NS_IMETHODIMP
1705 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult)
1707 NS_ENSURE_ARG_POINTER(aResult);
1709 *aResult = mHasLoadedNonBlankURI;
1710 return NS_OK;
1713 NS_IMETHODIMP
1714 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs)
1716 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1718 *aUseRemoteTabs = mUseRemoteTabs;
1719 return NS_OK;
1722 NS_IMETHODIMP
1723 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs)
1725 if (aUseRemoteTabs) {
1726 CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"),
1727 NS_LITERAL_CSTRING("1"));
1730 mUseRemoteTabs = aUseRemoteTabs;
1731 return NS_OK;
1734 NS_IMETHODIMP
1735 nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
1737 MOZ_ASSERT(!mIsBeingDestroyed);
1739 bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
1740 if (change && UsePrivateBrowsing()) {
1741 AssertOriginAttributesMatchPrivateBrowsing();
1742 if (aAffectLifetime) {
1743 IncreasePrivateDocShellCount();
1744 } else {
1745 DecreasePrivateDocShellCount();
1748 mAffectPrivateSessionLifetime = aAffectLifetime;
1750 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
1751 while (iter.HasMore()) {
1752 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
1753 if (shell) {
1754 shell->SetAffectPrivateSessionLifetime(aAffectLifetime);
1757 return NS_OK;
1760 NS_IMETHODIMP
1761 nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime)
1763 *aAffectLifetime = mAffectPrivateSessionLifetime;
1764 return NS_OK;
1767 NS_IMETHODIMP
1768 nsDocShell::AddWeakPrivacyTransitionObserver(
1769 nsIPrivacyTransitionObserver* aObserver)
1771 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1772 if (!weakObs) {
1773 return NS_ERROR_NOT_AVAILABLE;
1775 return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
1778 NS_IMETHODIMP
1779 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver)
1781 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1782 if (!weakObs) {
1783 return NS_ERROR_FAILURE;
1785 return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
1788 NS_IMETHODIMP
1789 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver)
1791 nsWeakPtr obs = do_GetWeakReference(aObserver);
1792 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1795 NS_IMETHODIMP
1796 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1797 DOMHighResTimeStamp aStart,
1798 DOMHighResTimeStamp aEnd)
1800 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1801 while (iter.HasMore()) {
1802 nsWeakPtr ref = iter.GetNext();
1803 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1804 if (!obs) {
1805 mReflowObservers.RemoveElement(ref);
1806 } else if (aInterruptible) {
1807 obs->ReflowInterruptible(aStart, aEnd);
1808 } else {
1809 obs->Reflow(aStart, aEnd);
1812 return NS_OK;
1815 NS_IMETHODIMP
1816 nsDocShell::GetAllowMetaRedirects(bool* aReturn)
1818 NS_ENSURE_ARG_POINTER(aReturn);
1820 *aReturn = mAllowMetaRedirects;
1821 return NS_OK;
1824 NS_IMETHODIMP
1825 nsDocShell::SetAllowMetaRedirects(bool aValue)
1827 mAllowMetaRedirects = aValue;
1828 return NS_OK;
1831 NS_IMETHODIMP
1832 nsDocShell::GetAllowSubframes(bool* aAllowSubframes)
1834 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1836 *aAllowSubframes = mAllowSubframes;
1837 return NS_OK;
1840 NS_IMETHODIMP
1841 nsDocShell::SetAllowSubframes(bool aAllowSubframes)
1843 mAllowSubframes = aAllowSubframes;
1844 return NS_OK;
1847 NS_IMETHODIMP
1848 nsDocShell::GetAllowImages(bool* aAllowImages)
1850 NS_ENSURE_ARG_POINTER(aAllowImages);
1852 *aAllowImages = mAllowImages;
1853 return NS_OK;
1856 NS_IMETHODIMP
1857 nsDocShell::SetAllowImages(bool aAllowImages)
1859 mAllowImages = aAllowImages;
1860 return NS_OK;
1863 NS_IMETHODIMP
1864 nsDocShell::GetAllowMedia(bool* aAllowMedia)
1866 *aAllowMedia = mAllowMedia;
1867 return NS_OK;
1870 NS_IMETHODIMP
1871 nsDocShell::SetAllowMedia(bool aAllowMedia)
1873 mAllowMedia = aAllowMedia;
1875 // Mute or unmute audio contexts attached to the inner window.
1876 if (mScriptGlobal) {
1877 if (nsPIDOMWindowInner* innerWin =
1878 mScriptGlobal->AsOuter()->GetCurrentInnerWindow()) {
1879 if (aAllowMedia) {
1880 innerWin->UnmuteAudioContexts();
1881 } else {
1882 innerWin->MuteAudioContexts();
1887 return NS_OK;
1890 NS_IMETHODIMP
1891 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch)
1893 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1894 return NS_OK;
1897 NS_IMETHODIMP
1898 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
1900 mAllowDNSPrefetch = aAllowDNSPrefetch;
1901 return NS_OK;
1904 NS_IMETHODIMP
1905 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl)
1907 *aAllowWindowControl = mAllowWindowControl;
1908 return NS_OK;
1911 NS_IMETHODIMP
1912 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl)
1914 mAllowWindowControl = aAllowWindowControl;
1915 return NS_OK;
1918 NS_IMETHODIMP
1919 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
1921 *aAllowContentRetargeting = mAllowContentRetargeting;
1922 return NS_OK;
1925 NS_IMETHODIMP
1926 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
1928 mAllowContentRetargetingOnChildren = aAllowContentRetargeting;
1929 mAllowContentRetargeting = aAllowContentRetargeting;
1930 return NS_OK;
1933 NS_IMETHODIMP
1934 nsDocShell::GetAllowContentRetargetingOnChildren(
1935 bool* aAllowContentRetargetingOnChildren)
1937 *aAllowContentRetargetingOnChildren = mAllowContentRetargetingOnChildren;
1938 return NS_OK;
1941 NS_IMETHODIMP
1942 nsDocShell::SetAllowContentRetargetingOnChildren(
1943 bool aAllowContentRetargetingOnChildren)
1945 mAllowContentRetargetingOnChildren = aAllowContentRetargetingOnChildren;
1946 return NS_OK;
1949 NS_IMETHODIMP
1950 nsDocShell::GetInheritPrivateBrowsingId(bool* aInheritPrivateBrowsingId)
1952 *aInheritPrivateBrowsingId = mInheritPrivateBrowsingId;
1953 return NS_OK;
1956 NS_IMETHODIMP
1957 nsDocShell::SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId)
1959 mInheritPrivateBrowsingId = aInheritPrivateBrowsingId;
1960 return NS_OK;
1963 NS_IMETHODIMP
1964 nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
1966 NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
1968 // Browsers and apps have their mFullscreenAllowed retrieved from their
1969 // corresponding iframe in their parent upon creation.
1970 if (mFullscreenAllowed != CHECK_ATTRIBUTES) {
1971 *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS);
1972 return NS_OK;
1975 // Assume false until we determine otherwise...
1976 *aFullscreenAllowed = false;
1978 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1979 if (!win) {
1980 return NS_OK;
1982 if (nsCOMPtr<Element> frameElement = win->GetFrameElementInternal()) {
1983 if (frameElement->IsXULElement()) {
1984 if (frameElement->HasAttr(kNameSpaceID_None,
1985 nsGkAtoms::disablefullscreen)) {
1986 // Document inside this frame is explicitly disabled.
1987 return NS_OK;
1989 } else {
1990 // We do not allow document inside any containing element other
1991 // than iframe to enter fullscreen.
1992 if (frameElement->IsHTMLElement(nsGkAtoms::iframe)) {
1993 // If any ancestor iframe does not have allowfullscreen attribute
1994 // set, then fullscreen is not allowed.
1995 if (!frameElement->HasAttr(kNameSpaceID_None,
1996 nsGkAtoms::allowfullscreen) &&
1997 !frameElement->HasAttr(kNameSpaceID_None,
1998 nsGkAtoms::mozallowfullscreen)) {
1999 return NS_OK;
2001 } else if (frameElement->IsHTMLElement(nsGkAtoms::embed)) {
2002 // Respect allowfullscreen only if this is a rewritten YouTube embed.
2003 nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent =
2004 do_QueryInterface(frameElement);
2005 if (!objectLoadingContent) {
2006 return NS_OK;
2008 nsObjectLoadingContent* olc =
2009 static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
2010 if (!olc->IsRewrittenYoutubeEmbed()) {
2011 return NS_OK;
2013 // We don't have to check prefixed attributes because Flash does not
2014 // support them.
2015 if (!frameElement->HasAttr(kNameSpaceID_None,
2016 nsGkAtoms::allowfullscreen)) {
2017 return NS_OK;
2019 } else {
2020 // neither iframe nor embed
2021 return NS_OK;
2026 // If we have no parent then we're the root docshell; no ancestor of the
2027 // original docshell doesn't have a allowfullscreen attribute, so
2028 // report fullscreen as allowed.
2029 RefPtr<nsDocShell> parent = GetParentDocshell();
2030 if (!parent) {
2031 *aFullscreenAllowed = true;
2032 return NS_OK;
2035 // Otherwise, we have a parent, continue the checking for
2036 // mozFullscreenAllowed in the parent docshell's ancestors.
2037 return parent->GetFullscreenAllowed(aFullscreenAllowed);
2040 NS_IMETHODIMP
2041 nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed)
2043 if (!nsIDocShell::GetIsMozBrowser()) {
2044 // Only allow setting of fullscreenAllowed on content/process boundaries.
2045 // At non-boundaries the fullscreenAllowed attribute is calculated based on
2046 // whether all enclosing frames have the "mozFullscreenAllowed" attribute
2047 // set to "true". fullscreenAllowed is set at the process boundaries to
2048 // propagate the value of the parent's "mozFullscreenAllowed" attribute
2049 // across process boundaries.
2050 return NS_ERROR_UNEXPECTED;
2052 mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS);
2053 return NS_OK;
2056 ScreenOrientationInternal
2057 nsDocShell::OrientationLock()
2059 return mOrientationLock;
2062 void
2063 nsDocShell::SetOrientationLock(ScreenOrientationInternal aOrientationLock)
2065 mOrientationLock = aOrientationLock;
2068 NS_IMETHODIMP
2069 nsDocShell::GetMayEnableCharacterEncodingMenu(
2070 bool* aMayEnableCharacterEncodingMenu)
2072 *aMayEnableCharacterEncodingMenu = false;
2073 if (!mContentViewer) {
2074 return NS_OK;
2076 nsIDocument* doc = mContentViewer->GetDocument();
2077 if (!doc) {
2078 return NS_OK;
2080 if (doc->WillIgnoreCharsetOverride()) {
2081 return NS_OK;
2084 *aMayEnableCharacterEncodingMenu = true;
2085 return NS_OK;
2088 NS_IMETHODIMP
2089 nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection,
2090 nsISimpleEnumerator** aResult)
2092 NS_ENSURE_ARG_POINTER(aResult);
2093 *aResult = nullptr;
2095 RefPtr<nsDocShellEnumerator> docShellEnum;
2096 if (aDirection == ENUMERATE_FORWARDS) {
2097 docShellEnum = new nsDocShellForwardsEnumerator;
2098 } else {
2099 docShellEnum = new nsDocShellBackwardsEnumerator;
2102 nsresult rv = docShellEnum->SetEnumDocShellType(aItemType);
2103 if (NS_FAILED(rv)) {
2104 return rv;
2107 rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem*)this);
2108 if (NS_FAILED(rv)) {
2109 return rv;
2112 rv = docShellEnum->First();
2113 if (NS_FAILED(rv)) {
2114 return rv;
2117 rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator),
2118 (void**)aResult);
2120 return rv;
2123 NS_IMETHODIMP
2124 nsDocShell::GetAppType(uint32_t* aAppType)
2126 *aAppType = mAppType;
2127 return NS_OK;
2130 NS_IMETHODIMP
2131 nsDocShell::SetAppType(uint32_t aAppType)
2133 mAppType = aAppType;
2134 return NS_OK;
2137 NS_IMETHODIMP
2138 nsDocShell::GetAllowAuth(bool* aAllowAuth)
2140 *aAllowAuth = mAllowAuth;
2141 return NS_OK;
2144 NS_IMETHODIMP
2145 nsDocShell::SetAllowAuth(bool aAllowAuth)
2147 mAllowAuth = aAllowAuth;
2148 return NS_OK;
2151 NS_IMETHODIMP
2152 nsDocShell::GetZoom(float* aZoom)
2154 NS_ENSURE_ARG_POINTER(aZoom);
2155 *aZoom = 1.0f;
2156 return NS_OK;
2159 NS_IMETHODIMP
2160 nsDocShell::SetZoom(float aZoom)
2162 return NS_ERROR_NOT_IMPLEMENTED;
2165 NS_IMETHODIMP
2166 nsDocShell::GetMarginWidth(int32_t* aWidth)
2168 NS_ENSURE_ARG_POINTER(aWidth);
2170 *aWidth = mMarginWidth;
2171 return NS_OK;
2174 NS_IMETHODIMP
2175 nsDocShell::SetMarginWidth(int32_t aWidth)
2177 mMarginWidth = aWidth;
2178 return NS_OK;
2181 NS_IMETHODIMP
2182 nsDocShell::GetMarginHeight(int32_t* aHeight)
2184 NS_ENSURE_ARG_POINTER(aHeight);
2186 *aHeight = mMarginHeight;
2187 return NS_OK;
2190 NS_IMETHODIMP
2191 nsDocShell::SetMarginHeight(int32_t aHeight)
2193 mMarginHeight = aHeight;
2194 return NS_OK;
2197 NS_IMETHODIMP
2198 nsDocShell::GetBusyFlags(uint32_t* aBusyFlags)
2200 NS_ENSURE_ARG_POINTER(aBusyFlags);
2202 *aBusyFlags = mBusyFlags;
2203 return NS_OK;
2206 NS_IMETHODIMP
2207 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation, bool* aTookFocus)
2209 NS_ENSURE_ARG_POINTER(aTookFocus);
2211 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2212 if (chromeFocus) {
2213 if (aForward) {
2214 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2215 } else {
2216 *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2218 } else {
2219 *aTookFocus = false;
2222 return NS_OK;
2225 NS_IMETHODIMP
2226 nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI)
2228 NS_IF_ADDREF(*aSecurityUI = mSecurityUI);
2229 return NS_OK;
2232 NS_IMETHODIMP
2233 nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI)
2235 MOZ_ASSERT(!mIsBeingDestroyed);
2237 mSecurityUI = aSecurityUI;
2238 mSecurityUI->SetDocShell(this);
2239 return NS_OK;
2242 NS_IMETHODIMP
2243 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate)
2245 NS_IF_ADDREF(*aLoadURIDelegate = mLoadURIDelegate);
2246 return NS_OK;
2249 NS_IMETHODIMP
2250 nsDocShell::SetLoadURIDelegate(nsILoadURIDelegate* aLoadURIDelegate)
2252 mLoadURIDelegate = aLoadURIDelegate;
2253 return NS_OK;
2256 NS_IMETHODIMP
2257 nsDocShell::GetUseErrorPages(bool* aUseErrorPages)
2259 *aUseErrorPages = UseErrorPages();
2260 return NS_OK;
2263 NS_IMETHODIMP
2264 nsDocShell::SetUseErrorPages(bool aUseErrorPages)
2266 // If mUseErrorPages is set explicitly, stop using sUseErrorPages.
2267 if (mObserveErrorPages) {
2268 mObserveErrorPages = false;
2270 mUseErrorPages = aUseErrorPages;
2271 return NS_OK;
2274 NS_IMETHODIMP
2275 nsDocShell::GetPreviousTransIndex(int32_t* aPreviousTransIndex)
2277 *aPreviousTransIndex = mPreviousTransIndex;
2278 return NS_OK;
2281 NS_IMETHODIMP
2282 nsDocShell::GetLoadedTransIndex(int32_t* aLoadedTransIndex)
2284 *aLoadedTransIndex = mLoadedTransIndex;
2285 return NS_OK;
2288 NS_IMETHODIMP
2289 nsDocShell::HistoryPurged(int32_t aNumEntries)
2291 // These indices are used for fastback cache eviction, to determine
2292 // which session history entries are candidates for content viewer
2293 // eviction. We need to adjust by the number of entries that we
2294 // just purged from history, so that we look at the right session history
2295 // entries during eviction.
2296 mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries);
2297 mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries);
2299 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2300 while (iter.HasMore()) {
2301 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2302 if (shell) {
2303 shell->HistoryPurged(aNumEntries);
2307 return NS_OK;
2310 nsresult
2311 nsDocShell::HistoryTransactionRemoved(int32_t aIndex)
2313 // These indices are used for fastback cache eviction, to determine
2314 // which session history entries are candidates for content viewer
2315 // eviction. We need to adjust by the number of entries that we
2316 // just purged from history, so that we look at the right session history
2317 // entries during eviction.
2318 if (aIndex == mPreviousTransIndex) {
2319 mPreviousTransIndex = -1;
2320 } else if (aIndex < mPreviousTransIndex) {
2321 --mPreviousTransIndex;
2323 if (mLoadedTransIndex == aIndex) {
2324 mLoadedTransIndex = 0;
2325 } else if (aIndex < mLoadedTransIndex) {
2326 --mLoadedTransIndex;
2329 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2330 while (iter.HasMore()) {
2331 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
2332 if (shell) {
2333 static_cast<nsDocShell*>(shell.get())->HistoryTransactionRemoved(aIndex);
2337 return NS_OK;
2340 NS_IMETHODIMP
2341 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
2343 bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
2344 if (currentValue == aValue) {
2345 return NS_OK;
2348 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2349 if (!timelines) {
2350 return NS_OK;
2353 if (aValue) {
2354 MOZ_ASSERT(!timelines->HasConsumer(this));
2355 timelines->AddConsumer(this);
2356 MOZ_ASSERT(timelines->HasConsumer(this));
2357 UseEntryScriptProfiling();
2358 } else {
2359 MOZ_ASSERT(timelines->HasConsumer(this));
2360 timelines->RemoveConsumer(this);
2361 MOZ_ASSERT(!timelines->HasConsumer(this));
2362 UnuseEntryScriptProfiling();
2365 return NS_OK;
2368 NS_IMETHODIMP
2369 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
2371 *aValue = !!mObserved;
2372 return NS_OK;
2375 nsresult
2376 nsDocShell::PopProfileTimelineMarkers(
2377 JSContext* aCx,
2378 JS::MutableHandle<JS::Value> aOut)
2380 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
2381 if (!timelines) {
2382 return NS_OK;
2385 nsTArray<dom::ProfileTimelineMarker> store;
2386 SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
2388 timelines->PopMarkers(this, aCx, store);
2390 if (!ToJSValue(aCx, store, aOut)) {
2391 JS_ClearPendingException(aCx);
2392 return NS_ERROR_UNEXPECTED;
2395 return NS_OK;
2398 nsresult
2399 nsDocShell::Now(DOMHighResTimeStamp* aWhen)
2401 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2402 return NS_OK;
2405 NS_IMETHODIMP
2406 nsDocShell::SetWindowDraggingAllowed(bool aValue)
2408 RefPtr<nsDocShell> parent = GetParentDocshell();
2409 if (!aValue && mItemType == typeChrome && !parent) {
2410 // Window dragging is always allowed for top level
2411 // chrome docshells.
2412 return NS_ERROR_FAILURE;
2414 mWindowDraggingAllowed = aValue;
2415 return NS_OK;
2418 NS_IMETHODIMP
2419 nsDocShell::GetWindowDraggingAllowed(bool* aValue)
2421 // window dragging regions in CSS (-moz-window-drag:drag)
2422 // can be slow. Default behavior is to only allow it for
2423 // chrome top level windows.
2424 RefPtr<nsDocShell> parent = GetParentDocshell();
2425 if (mItemType == typeChrome && !parent) {
2426 // Top level chrome window
2427 *aValue = true;
2428 } else {
2429 *aValue = mWindowDraggingAllowed;
2431 return NS_OK;
2434 nsIDOMStorageManager*
2435 nsDocShell::TopSessionStorageManager()
2437 nsresult rv;
2439 nsCOMPtr<nsIDocShellTreeItem> topItem;
2440 rv = GetSameTypeRootTreeItem(getter_AddRefs(topItem));
2441 if (NS_FAILED(rv)) {
2442 return nullptr;
2445 if (!topItem) {
2446 return nullptr;
2449 nsDocShell* topDocShell = static_cast<nsDocShell*>(topItem.get());
2450 if (topDocShell != this) {
2451 return topDocShell->TopSessionStorageManager();
2454 if (!mSessionStorageManager) {
2455 mSessionStorageManager =
2456 do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1");
2459 return mSessionStorageManager;
2462 NS_IMETHODIMP
2463 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult)
2465 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2466 return NS_OK;
2469 nsIChannel*
2470 nsDocShell::GetCurrentDocChannel()
2472 if (mContentViewer) {
2473 nsIDocument* doc = mContentViewer->GetDocument();
2474 if (doc) {
2475 return doc->GetChannel();
2478 return nullptr;
2481 NS_IMETHODIMP
2482 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver)
2484 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2485 if (!weakObs) {
2486 return NS_ERROR_FAILURE;
2488 return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
2491 NS_IMETHODIMP
2492 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver)
2494 nsWeakPtr obs = do_GetWeakReference(aObserver);
2495 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2498 void
2499 nsDocShell::NotifyAsyncPanZoomStarted()
2501 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2502 while (iter.HasMore()) {
2503 nsWeakPtr ref = iter.GetNext();
2504 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2505 if (obs) {
2506 obs->AsyncPanZoomStarted();
2507 } else {
2508 mScrollObservers.RemoveElement(ref);
2513 void
2514 nsDocShell::NotifyAsyncPanZoomStopped()
2516 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2517 while (iter.HasMore()) {
2518 nsWeakPtr ref = iter.GetNext();
2519 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2520 if (obs) {
2521 obs->AsyncPanZoomStopped();
2522 } else {
2523 mScrollObservers.RemoveElement(ref);
2528 NS_IMETHODIMP
2529 nsDocShell::NotifyScrollObservers()
2531 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2532 while (iter.HasMore()) {
2533 nsWeakPtr ref = iter.GetNext();
2534 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2535 if (obs) {
2536 obs->ScrollPositionChanged();
2537 } else {
2538 mScrollObservers.RemoveElement(ref);
2541 return NS_OK;
2544 //*****************************************************************************
2545 // nsDocShell::nsIDocShellTreeItem
2546 //*****************************************************************************
2548 NS_IMETHODIMP
2549 nsDocShell::GetName(nsAString& aName)
2551 aName = mName;
2552 return NS_OK;
2555 NS_IMETHODIMP
2556 nsDocShell::SetName(const nsAString& aName)
2558 mName = aName;
2559 return NS_OK;
2562 NS_IMETHODIMP
2563 nsDocShell::NameEquals(const nsAString& aName, bool* aResult)
2565 NS_ENSURE_ARG_POINTER(aResult);
2566 *aResult = mName.Equals(aName);
2567 return NS_OK;
2570 NS_IMETHODIMP
2571 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
2573 aCustomUserAgent = mCustomUserAgent;
2574 return NS_OK;
2577 NS_IMETHODIMP
2578 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent)
2580 mCustomUserAgent = aCustomUserAgent;
2581 RefPtr<nsGlobalWindowInner> win = mScriptGlobal ?
2582 mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2583 if (win) {
2584 Navigator* navigator = win->Navigator();
2585 if (navigator) {
2586 navigator->ClearUserAgentCache();
2590 uint32_t childCount = mChildList.Length();
2591 for (uint32_t i = 0; i < childCount; ++i) {
2592 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
2593 if (childShell) {
2594 childShell->SetCustomUserAgent(aCustomUserAgent);
2597 return NS_OK;
2600 NS_IMETHODIMP
2601 nsDocShell::GetTouchEventsOverride(uint32_t* aTouchEventsOverride)
2603 NS_ENSURE_ARG_POINTER(aTouchEventsOverride);
2605 *aTouchEventsOverride = mTouchEventsOverride;
2606 return NS_OK;
2609 NS_IMETHODIMP
2610 nsDocShell::SetTouchEventsOverride(uint32_t aTouchEventsOverride)
2612 if (!(aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE ||
2613 aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED ||
2614 aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED)) {
2615 return NS_ERROR_INVALID_ARG;
2618 mTouchEventsOverride = aTouchEventsOverride;
2620 uint32_t childCount = mChildList.Length();
2621 for (uint32_t i = 0; i < childCount; ++i) {
2622 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
2623 if (childShell) {
2624 childShell->SetTouchEventsOverride(aTouchEventsOverride);
2627 return NS_OK;
2630 /* virtual */ int32_t
2631 nsDocShell::ItemType()
2633 return mItemType;
2636 NS_IMETHODIMP
2637 nsDocShell::GetItemType(int32_t* aItemType)
2639 NS_ENSURE_ARG_POINTER(aItemType);
2641 *aItemType = ItemType();
2642 return NS_OK;
2645 NS_IMETHODIMP
2646 nsDocShell::SetItemType(int32_t aItemType)
2648 NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
2650 // Only allow setting the type on root docshells. Those would be the ones
2651 // that have the docloader service as mParent or have no mParent at all.
2652 nsCOMPtr<nsIDocumentLoader> docLoaderService =
2653 do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
2654 NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED);
2656 NS_ENSURE_STATE(!mParent || mParent == docLoaderService);
2658 mItemType = aItemType;
2660 // disable auth prompting for anything but content
2661 mAllowAuth = mItemType == typeContent;
2663 RefPtr<nsPresContext> presContext = nullptr;
2664 GetPresContext(getter_AddRefs(presContext));
2665 if (presContext) {
2666 presContext->UpdateIsChrome();
2669 return NS_OK;
2672 NS_IMETHODIMP
2673 nsDocShell::GetParent(nsIDocShellTreeItem** aParent)
2675 if (!mParent) {
2676 *aParent = nullptr;
2677 } else {
2678 CallQueryInterface(mParent, aParent);
2680 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2681 // don't want to throw; we just want to return null.
2682 return NS_OK;
2685 already_AddRefed<nsDocShell>
2686 nsDocShell::GetParentDocshell()
2688 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2689 return docshell.forget().downcast<nsDocShell>();
2692 void
2693 nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
2695 MOZ_ASSERT(!mIsBeingDestroyed);
2697 // If there is an existing document then there is no need to create
2698 // a client for a future initial about:blank document.
2699 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
2700 mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
2701 MOZ_DIAGNOSTIC_ASSERT(
2702 mScriptGlobal->GetCurrentInnerWindowInternal()->GetClientInfo().isSome());
2703 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2704 return;
2707 // Don't recreate the initial client source. We call this multiple times
2708 // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
2709 if (mInitialClientSource) {
2710 return;
2713 // Don't pre-allocate the client when we are sandboxed. The inherited
2714 // principal does not take sandboxing into account.
2715 // TODO: Refactor sandboxing principal code out so we can use it here.
2716 if (!aPrincipal && mSandboxFlags) {
2717 return;
2720 nsIPrincipal* principal = aPrincipal ? aPrincipal
2721 : GetInheritedPrincipal(false);
2723 // Sometimes there is no principal available when we are called from
2724 // CreateAboutBlankContentViewer. For example, sometimes the principal
2725 // is only extracted from the load context after the document is created
2726 // in nsDocument::ResetToURI(). Ideally we would do something similar
2727 // here, but for now lets just avoid the issue by not preallocating the
2728 // client.
2729 if (!principal) {
2730 return;
2733 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2734 if (!win) {
2735 return;
2738 mInitialClientSource =
2739 ClientManager::CreateSource(ClientType::Window,
2740 win->EventTargetFor(TaskCategory::Other),
2741 principal);
2742 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2744 // Mark the initial client as execution ready, but owned by the docshell.
2745 // If the client is actually used this will cause ClientSource to force
2746 // the creation of the initial about:blank by calling nsDocShell::GetDocument().
2747 mInitialClientSource->DocShellExecutionReady(this);
2749 // Next, check to see if the parent is controlled.
2750 nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
2751 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2752 nsPIDOMWindowInner* parentInner =
2753 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2754 if (!parentInner) {
2755 return;
2758 nsCOMPtr<nsIURI> uri;
2759 MOZ_ALWAYS_SUCCEEDS(
2760 NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank")));
2762 // We're done if there is no parent controller or if this docshell
2763 // is not permitted to control for some reason.
2764 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2765 if (controller.isNothing() || !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2766 return;
2769 mInitialClientSource->InheritController(controller.ref());
2772 Maybe<ClientInfo>
2773 nsDocShell::GetInitialClientInfo() const
2775 if (mInitialClientSource) {
2776 Maybe<ClientInfo> result;
2777 result.emplace(mInitialClientSource->Info());
2778 return result;
2781 nsGlobalWindowInner* innerWindow =
2782 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2783 nsIDocument* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2785 if (!doc || !doc->IsInitialDocument()) {
2786 return Maybe<ClientInfo>();
2789 return innerWindow->GetClientInfo();
2792 void
2793 nsDocShell::RecomputeCanExecuteScripts()
2795 bool old = mCanExecuteScripts;
2796 RefPtr<nsDocShell> parent = GetParentDocshell();
2798 // If we have no tree owner, that means that we've been detached from the
2799 // docshell tree (this is distinct from having no parent dochshell, which
2800 // is the case for root docshells). It would be nice to simply disallow
2801 // script in detached docshells, but bug 986542 demonstrates that this
2802 // behavior breaks at least one website.
2804 // So instead, we use our previous value, unless mAllowJavascript has been
2805 // explicitly set to false.
2806 if (!mTreeOwner) {
2807 mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript;
2808 // If scripting has been explicitly disabled on our docshell, we're done.
2809 } else if (!mAllowJavascript) {
2810 mCanExecuteScripts = false;
2811 // If we have a parent, inherit.
2812 } else if (parent) {
2813 mCanExecuteScripts = parent->mCanExecuteScripts;
2814 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2815 // script. Allow.
2816 } else {
2817 mCanExecuteScripts = true;
2820 // Inform our active DOM window.
2822 // This will pass the outer, which will be in the scope of the active inner.
2823 if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) {
2824 xpc::Scriptability& scriptability =
2825 xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject());
2826 scriptability.SetDocShellAllowsScript(mCanExecuteScripts);
2829 // If our value has changed, our children might be affected. Recompute their
2830 // value as well.
2831 if (old != mCanExecuteScripts) {
2832 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
2833 while (iter.HasMore()) {
2834 static_cast<nsDocShell*>(iter.GetNext())->RecomputeCanExecuteScripts();
2839 nsresult
2840 nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
2842 bool wasFrame = IsFrame();
2843 #ifdef DEBUG
2844 bool wasPrivate = UsePrivateBrowsing();
2845 #endif
2847 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2848 NS_ENSURE_SUCCESS(rv, rv);
2850 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2851 if (wasFrame != IsFrame() && priorityGroup) {
2852 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2855 // Curse ambiguous nsISupports inheritance!
2856 nsISupports* parent = GetAsSupports(aParent);
2858 // If parent is another docshell, we inherit all their flags for
2859 // allowing plugins, scripting etc.
2860 bool value;
2861 nsString customUserAgent;
2862 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2863 if (parentAsDocShell) {
2864 if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
2865 SetAllowPlugins(value);
2867 if (mAllowJavascript && NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
2868 SetAllowJavascript(value);
2870 if (mAllowMetaRedirects && NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2871 SetAllowMetaRedirects(value);
2873 if (mAllowSubframes && NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2874 SetAllowSubframes(value);
2876 if (mAllowImages && NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2877 SetAllowImages(value);
2879 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2880 if (mAllowWindowControl && NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2881 SetAllowWindowControl(value);
2883 SetAllowContentRetargeting(mAllowContentRetargeting &&
2884 parentAsDocShell->GetAllowContentRetargetingOnChildren());
2885 if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
2886 SetIsActive(value);
2888 if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
2889 !customUserAgent.IsEmpty()) {
2890 SetCustomUserAgent(customUserAgent);
2892 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2893 value = false;
2895 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2896 if (mInheritPrivateBrowsingId) {
2897 value = parentAsDocShell->GetAffectPrivateSessionLifetime();
2898 SetAffectPrivateSessionLifetime(value);
2900 uint32_t flags;
2901 if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
2902 SetDefaultLoadFlags(flags);
2904 uint32_t touchEventsOverride;
2905 if (NS_SUCCEEDED(parentAsDocShell->GetTouchEventsOverride(&touchEventsOverride))) {
2906 SetTouchEventsOverride(touchEventsOverride);
2910 nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
2911 if (parentAsLoadContext && mInheritPrivateBrowsingId &&
2912 NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
2913 SetPrivateBrowsing(value);
2916 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2917 if (parentURIListener) {
2918 mContentListener->SetParentContentListener(parentURIListener);
2921 // Our parent has changed. Recompute scriptability.
2922 RecomputeCanExecuteScripts();
2924 NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(),
2925 "Private browsing state changed while inheritance was disabled");
2927 return NS_OK;
2930 NS_IMETHODIMP
2931 nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent)
2933 NS_ENSURE_ARG_POINTER(aParent);
2934 *aParent = nullptr;
2936 if (nsIDocShell::GetIsMozBrowser()) {
2937 return NS_OK;
2940 nsCOMPtr<nsIDocShellTreeItem> parent =
2941 do_QueryInterface(GetAsSupports(mParent));
2942 if (!parent) {
2943 return NS_OK;
2946 if (parent->ItemType() == mItemType) {
2947 parent.swap(*aParent);
2949 return NS_OK;
2952 NS_IMETHODIMP
2953 nsDocShell::GetSameTypeParentIgnoreBrowserBoundaries(nsIDocShell** aParent)
2955 NS_ENSURE_ARG_POINTER(aParent);
2956 *aParent = nullptr;
2958 nsCOMPtr<nsIDocShellTreeItem> parent =
2959 do_QueryInterface(GetAsSupports(mParent));
2960 if (!parent) {
2961 return NS_OK;
2964 if (parent->ItemType() == mItemType) {
2965 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
2966 parentDS.forget(aParent);
2968 return NS_OK;
2971 NS_IMETHODIMP
2972 nsDocShell::GetRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
2974 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2976 RefPtr<nsDocShell> root = this;
2977 RefPtr<nsDocShell> parent = root->GetParentDocshell();
2978 while (parent) {
2979 root = parent;
2980 parent = root->GetParentDocshell();
2983 root.forget(aRootTreeItem);
2984 return NS_OK;
2987 NS_IMETHODIMP
2988 nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
2990 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2991 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2993 nsCOMPtr<nsIDocShellTreeItem> parent;
2994 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)),
2995 NS_ERROR_FAILURE);
2996 while (parent) {
2997 *aRootTreeItem = parent;
2998 NS_ENSURE_SUCCESS(
2999 (*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)),
3000 NS_ERROR_FAILURE);
3002 NS_ADDREF(*aRootTreeItem);
3003 return NS_OK;
3006 NS_IMETHODIMP
3007 nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserBoundaries(nsIDocShell** aRootTreeItem)
3009 NS_ENSURE_ARG_POINTER(aRootTreeItem);
3010 *aRootTreeItem = static_cast<nsIDocShell *>(this);
3012 nsCOMPtr<nsIDocShell> parent;
3013 NS_ENSURE_SUCCESS(GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent)),
3014 NS_ERROR_FAILURE);
3015 while (parent) {
3016 *aRootTreeItem = parent;
3017 NS_ENSURE_SUCCESS((*aRootTreeItem)->
3018 GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent)),
3019 NS_ERROR_FAILURE);
3021 NS_ADDREF(*aRootTreeItem);
3022 return NS_OK;
3025 /* static */
3026 bool
3027 nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
3028 nsIDocShellTreeItem* aAccessingItem,
3029 bool aConsiderOpener)
3031 MOZ_ASSERT(aTargetItem, "Must have target item!");
3033 if (!gValidateOrigin || !aAccessingItem) {
3034 // Good to go
3035 return true;
3038 // XXXbz should we care if aAccessingItem or the document therein is
3039 // chrome? Should those get extra privileges?
3041 // For historical context, see:
3043 // Bug 13871: Prevent frameset spoofing
3044 // Bug 103638: Targets with same name in different windows open in wrong
3045 // window with javascript
3046 // Bug 408052: Adopt "ancestor" frame navigation policy
3048 // Now do a security check.
3050 // Disallow navigation if the two frames are not part of the same app, or if
3051 // they have different is-in-browser-element states.
3053 // Allow navigation if
3054 // 1) aAccessingItem can script aTargetItem or one of its ancestors in
3055 // the frame hierarchy or
3056 // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant
3057 // 3) aTargetItem is a top-level frame and aAccessingItem can target
3058 // its opener per rule (1) or (2).
3060 if (aTargetItem == aAccessingItem) {
3061 // A frame is allowed to navigate itself.
3062 return true;
3065 nsCOMPtr<nsIDocShell> targetDS = do_QueryInterface(aTargetItem);
3066 nsCOMPtr<nsIDocShell> accessingDS = do_QueryInterface(aAccessingItem);
3067 if (!targetDS || !accessingDS) {
3068 // We must be able to convert both to nsIDocShell.
3069 return false;
3072 if (targetDS->GetIsInIsolatedMozBrowserElement() !=
3073 accessingDS->GetIsInIsolatedMozBrowserElement()) {
3074 return false;
3077 nsCOMPtr<nsIDocShellTreeItem> accessingRoot;
3078 aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot));
3079 nsCOMPtr<nsIDocShell> accessingRootDS = do_QueryInterface(accessingRoot);
3081 nsCOMPtr<nsIDocShellTreeItem> targetRoot;
3082 aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot));
3083 nsCOMPtr<nsIDocShell> targetRootDS = do_QueryInterface(targetRoot);
3085 OriginAttributes targetOA =
3086 static_cast<nsDocShell*>(targetDS.get())->GetOriginAttributes();
3087 OriginAttributes accessingOA =
3088 static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes();
3090 // When the first party isolation is on, the top-level docShell may not have
3091 // the firstPartyDomain in its originAttributes, but its document will have
3092 // it. So we get the firstPartyDomain from the nodePrincipal of the document
3093 // before we compare the originAttributes.
3094 if (OriginAttributes::IsFirstPartyEnabled()) {
3095 if (aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent &&
3096 (accessingDS == accessingRootDS || accessingDS->GetIsMozBrowser())) {
3098 nsCOMPtr<nsIDocument> accessingDoc = aAccessingItem->GetDocument();
3100 if (accessingDoc) {
3101 nsCOMPtr<nsIPrincipal> accessingPrincipal = accessingDoc->NodePrincipal();
3103 accessingOA.mFirstPartyDomain =
3104 accessingPrincipal->OriginAttributesRef().mFirstPartyDomain;
3108 if (aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent &&
3109 (targetDS == targetRootDS || targetDS->GetIsMozBrowser())) {
3111 nsCOMPtr<nsIDocument> targetDoc = aAccessingItem->GetDocument();
3113 if (targetDoc) {
3114 nsCOMPtr<nsIPrincipal> targetPrincipal = targetDoc->NodePrincipal();
3116 targetOA.mFirstPartyDomain =
3117 targetPrincipal->OriginAttributesRef().mFirstPartyDomain;
3122 if (targetOA != accessingOA) {
3123 return false;
3126 // A private document can't access a non-private one, and vice versa.
3127 if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
3128 static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
3129 return false;
3132 if (aTargetItem == accessingRoot) {
3133 // A frame can navigate its root.
3134 return true;
3137 // Check if aAccessingItem can navigate one of aTargetItem's ancestors.
3138 nsCOMPtr<nsIDocShellTreeItem> target = aTargetItem;
3139 do {
3140 if (ValidateOrigin(aAccessingItem, target)) {
3141 return true;
3144 nsCOMPtr<nsIDocShellTreeItem> parent;
3145 target->GetSameTypeParent(getter_AddRefs(parent));
3146 parent.swap(target);
3147 } while (target);
3149 if (aTargetItem != targetRoot) {
3150 // target is a subframe, not in accessor's frame hierarchy, and all its
3151 // ancestors have origins different from that of the accessor. Don't
3152 // allow access.
3153 return false;
3156 if (!aConsiderOpener) {
3157 // All done here
3158 return false;
3161 nsCOMPtr<nsPIDOMWindowOuter> targetWindow = aTargetItem->GetWindow();
3162 if (!targetWindow) {
3163 NS_ERROR("This should not happen, really");
3164 return false;
3167 nsCOMPtr<mozIDOMWindowProxy> targetOpener = targetWindow->GetOpener();
3168 nsCOMPtr<nsIWebNavigation> openerWebNav(do_GetInterface(targetOpener));
3169 nsCOMPtr<nsIDocShellTreeItem> openerItem(do_QueryInterface(openerWebNav));
3171 if (!openerItem) {
3172 return false;
3175 return CanAccessItem(openerItem, aAccessingItem, false);
3178 static bool
3179 ItemIsActive(nsIDocShellTreeItem* aItem)
3181 if (nsCOMPtr<nsPIDOMWindowOuter> window = aItem->GetWindow()) {
3182 auto* win = nsGlobalWindowOuter::Cast(window);
3183 if (!win->GetClosedOuter()) {
3184 return true;
3188 return false;
3191 NS_IMETHODIMP
3192 nsDocShell::FindItemWithName(const nsAString& aName,
3193 nsIDocShellTreeItem* aRequestor,
3194 nsIDocShellTreeItem* aOriginalRequestor,
3195 bool aSkipTabGroup,
3196 nsIDocShellTreeItem** aResult)
3198 NS_ENSURE_ARG_POINTER(aResult);
3200 // If we don't find one, we return NS_OK and a null result
3201 *aResult = nullptr;
3203 if (aName.IsEmpty()) {
3204 return NS_OK;
3207 if (aRequestor) {
3208 // If aRequestor is not null we don't need to check special names, so
3209 // just hand straight off to the search by actual name function.
3210 return DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3211 aSkipTabGroup, aResult);
3212 } else {
3213 // This is the entry point into the target-finding algorithm. Check
3214 // for special names. This should only be done once, hence the check
3215 // for a null aRequestor.
3217 nsCOMPtr<nsIDocShellTreeItem> foundItem;
3218 if (aName.LowerCaseEqualsLiteral("_self")) {
3219 foundItem = this;
3220 } else if (aName.LowerCaseEqualsLiteral("_blank")) {
3221 // Just return null. Caller must handle creating a new window with
3222 // a blank name himself.
3223 return NS_OK;
3224 } else if (aName.LowerCaseEqualsLiteral("_parent")) {
3225 GetSameTypeParent(getter_AddRefs(foundItem));
3226 if (!foundItem) {
3227 foundItem = this;
3229 } else if (aName.LowerCaseEqualsLiteral("_top")) {
3230 GetSameTypeRootTreeItem(getter_AddRefs(foundItem));
3231 NS_ASSERTION(foundItem, "Must have this; worst case it's us!");
3232 } else {
3233 // Do the search for item by an actual name.
3234 DoFindItemWithName(aName, aRequestor, aOriginalRequestor,
3235 aSkipTabGroup, getter_AddRefs(foundItem));
3238 if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
3239 foundItem = nullptr;
3242 // DoFindItemWithName only returns active items and we don't check if
3243 // the item is active for the special cases.
3244 if (foundItem) {
3245 foundItem.swap(*aResult);
3247 return NS_OK;
3251 void
3252 nsDocShell::AssertOriginAttributesMatchPrivateBrowsing() {
3253 // Chrome docshells must not have a private browsing OriginAttribute
3254 // Content docshells must maintain the equality:
3255 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
3256 if (mItemType == typeChrome) {
3257 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
3258 } else {
3259 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId);
3263 nsresult
3264 nsDocShell::DoFindItemWithName(const nsAString& aName,
3265 nsIDocShellTreeItem* aRequestor,
3266 nsIDocShellTreeItem* aOriginalRequestor,
3267 bool aSkipTabGroup,
3268 nsIDocShellTreeItem** aResult)
3270 // First we check our name.
3271 if (mName.Equals(aName) && ItemIsActive(this) &&
3272 CanAccessItem(this, aOriginalRequestor)) {
3273 NS_ADDREF(*aResult = this);
3274 return NS_OK;
3277 // Second we check our children making sure not to ask a child if
3278 // it is the aRequestor.
3279 #ifdef DEBUG
3280 nsresult rv =
3281 #endif
3282 FindChildWithName(aName, true, true, aRequestor, aOriginalRequestor,
3283 aResult);
3284 NS_ASSERTION(NS_SUCCEEDED(rv),
3285 "FindChildWithName should not be failing here.");
3286 if (*aResult) {
3287 return NS_OK;
3290 // Third if we have a parent and it isn't the requestor then we
3291 // should ask it to do the search. If it is the requestor we
3292 // should just stop here and let the parent do the rest. If we
3293 // don't have a parent, then we should ask the
3294 // docShellTreeOwner to do the search.
3295 nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem =
3296 do_QueryInterface(GetAsSupports(mParent));
3297 if (parentAsTreeItem) {
3298 if (parentAsTreeItem == aRequestor) {
3299 return NS_OK;
3302 // If we have a same-type parent, respecting browser and app boundaries.
3303 // NOTE: Could use GetSameTypeParent if the issues described in bug 1310344 are fixed.
3304 if (!GetIsMozBrowser() && parentAsTreeItem->ItemType() == mItemType) {
3305 return parentAsTreeItem->FindItemWithName(
3306 aName,
3307 static_cast<nsIDocShellTreeItem*>(this),
3308 aOriginalRequestor,
3309 /* aSkipTabGroup = */ false,
3310 aResult);
3314 // If we have a null parent or the parent is not of the same type, we need to
3315 // give up on finding it in our tree, and start looking in our TabGroup.
3316 nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
3317 if (window && !aSkipTabGroup) {
3318 RefPtr<mozilla::dom::TabGroup> tabGroup = window->TabGroup();
3319 tabGroup->FindItemWithName(aName, aRequestor, aOriginalRequestor, aResult);
3322 return NS_OK;
3325 bool
3326 nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
3328 // If no target then not sandboxed.
3329 if (!aTargetDocShell) {
3330 return false;
3333 // We cannot be sandboxed from ourselves.
3334 if (aTargetDocShell == this) {
3335 return false;
3338 // Default the sandbox flags to our flags, so that if we can't retrieve the
3339 // active document, we will still enforce our own.
3340 uint32_t sandboxFlags = mSandboxFlags;
3341 if (mContentViewer) {
3342 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
3343 if (doc) {
3344 sandboxFlags = doc->GetSandboxFlags();
3348 // If no flags, we are not sandboxed at all.
3349 if (!sandboxFlags) {
3350 return false;
3353 // If aTargetDocShell has an ancestor, it is not top level.
3354 nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
3355 aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
3356 if (ancestorOfTarget) {
3357 do {
3358 // We are not sandboxed if we are an ancestor of target.
3359 if (ancestorOfTarget == this) {
3360 return false;
3362 nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
3363 ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
3364 tempTreeItem.swap(ancestorOfTarget);
3365 } while (ancestorOfTarget);
3367 // Otherwise, we are sandboxed from aTargetDocShell.
3368 return true;
3371 // aTargetDocShell is top level, are we the "one permitted sandboxed
3372 // navigator", i.e. did we open aTargetDocShell?
3373 nsCOMPtr<nsIDocShell> permittedNavigator;
3374 aTargetDocShell->GetOnePermittedSandboxedNavigator(
3375 getter_AddRefs(permittedNavigator));
3376 if (permittedNavigator == this) {
3377 return false;
3380 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
3381 // from our top.
3382 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
3383 nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
3384 GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
3385 if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
3386 return false;
3390 // Otherwise, we are sandboxed from aTargetDocShell.
3391 return true;
3394 NS_IMETHODIMP
3395 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner)
3397 NS_ENSURE_ARG_POINTER(aTreeOwner);
3399 *aTreeOwner = mTreeOwner;
3400 NS_IF_ADDREF(*aTreeOwner);
3401 return NS_OK;
3404 NS_IMETHODIMP
3405 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner)
3407 if (mIsBeingDestroyed && aTreeOwner) {
3408 return NS_ERROR_FAILURE;
3411 // Don't automatically set the progress based on the tree owner for frames
3412 if (!IsFrame()) {
3413 nsCOMPtr<nsIWebProgress> webProgress =
3414 do_QueryInterface(GetAsSupports(this));
3416 if (webProgress) {
3417 nsCOMPtr<nsIWebProgressListener> oldListener =
3418 do_QueryInterface(mTreeOwner);
3419 nsCOMPtr<nsIWebProgressListener> newListener =
3420 do_QueryInterface(aTreeOwner);
3422 if (oldListener) {
3423 webProgress->RemoveProgressListener(oldListener);
3426 if (newListener) {
3427 webProgress->AddProgressListener(newListener,
3428 nsIWebProgress::NOTIFY_ALL);
3433 mTreeOwner = aTreeOwner; // Weak reference per API
3435 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3436 while (iter.HasMore()) {
3437 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3438 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3440 if (child->ItemType() == mItemType) {
3441 child->SetTreeOwner(aTreeOwner);
3445 // Our tree owner has changed. Recompute scriptability.
3447 // Note that this is near-redundant with the recomputation in
3448 // SetDocLoaderParent(), but not so for the root DocShell, where the call to
3449 // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(),
3450 // and we never set another parent. Given that this is neither expensive nor
3451 // performance-critical, let's be safe and unconditionally recompute this
3452 // state whenever dependent state changes.
3453 RecomputeCanExecuteScripts();
3455 return NS_OK;
3458 NS_IMETHODIMP
3459 nsDocShell::SetChildOffset(int32_t aChildOffset)
3461 mChildOffset = aChildOffset;
3462 return NS_OK;
3465 NS_IMETHODIMP
3466 nsDocShell::GetChildOffset(int32_t* aChildOffset)
3468 *aChildOffset = mChildOffset;
3469 return NS_OK;
3472 NS_IMETHODIMP
3473 nsDocShell::GetHistoryID(nsID** aID)
3475 *aID = mHistoryID.Clone();
3476 return NS_OK;
3479 const nsID
3480 nsDocShell::HistoryID()
3482 return mHistoryID;
3485 NS_IMETHODIMP
3486 nsDocShell::GetIsInUnload(bool* aIsInUnload)
3488 *aIsInUnload = mFiredUnloadEvent;
3489 return NS_OK;
3492 NS_IMETHODIMP
3493 nsDocShell::GetChildCount(int32_t* aChildCount)
3495 NS_ENSURE_ARG_POINTER(aChildCount);
3496 *aChildCount = mChildList.Length();
3497 return NS_OK;
3500 NS_IMETHODIMP
3501 nsDocShell::AddChild(nsIDocShellTreeItem* aChild)
3503 NS_ENSURE_ARG_POINTER(aChild);
3505 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3506 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3508 // Make sure we're not creating a loop in the docshell tree
3509 nsDocLoader* ancestor = this;
3510 do {
3511 if (childAsDocLoader == ancestor) {
3512 return NS_ERROR_ILLEGAL_VALUE;
3514 ancestor = ancestor->GetParent();
3515 } while (ancestor);
3517 // Make sure to remove the child from its current parent.
3518 nsDocLoader* childsParent = childAsDocLoader->GetParent();
3519 if (childsParent) {
3520 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
3521 NS_ENSURE_SUCCESS(rv, rv);
3524 // Make sure to clear the treeowner in case this child is a different type
3525 // from us.
3526 aChild->SetTreeOwner(nullptr);
3528 nsresult res = AddChildLoader(childAsDocLoader);
3529 NS_ENSURE_SUCCESS(res, res);
3530 NS_ASSERTION(!mChildList.IsEmpty(),
3531 "child list must not be empty after a successful add");
3533 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
3534 bool dynamic = false;
3535 childDocShell->GetCreatedDynamically(&dynamic);
3536 if (!dynamic) {
3537 nsCOMPtr<nsISHEntry> currentSH;
3538 bool oshe = false;
3539 GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
3540 if (currentSH) {
3541 currentSH->HasDynamicallyAddedChild(&dynamic);
3544 childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1);
3546 /* Set the child's global history if the parent has one */
3547 if (mUseGlobalHistory) {
3548 childDocShell->SetUseGlobalHistory(true);
3551 if (aChild->ItemType() != mItemType) {
3552 return NS_OK;
3555 aChild->SetTreeOwner(mTreeOwner);
3557 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
3558 if (!childAsDocShell) {
3559 return NS_OK;
3562 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
3564 // Now take this document's charset and set the child's parentCharset field
3565 // to it. We'll later use that field, in the loading process, for the
3566 // charset choosing algorithm.
3567 // If we fail, at any point, we just return NS_OK.
3568 // This code has some performance impact. But this will be reduced when
3569 // the current charset will finally be stored as an Atom, avoiding the
3570 // alias resolution extra look-up.
3572 // we are NOT going to propagate the charset is this Chrome's docshell
3573 if (mItemType == nsIDocShellTreeItem::typeChrome) {
3574 return NS_OK;
3577 // get the parent's current charset
3578 if (!mContentViewer) {
3579 return NS_OK;
3581 nsIDocument* doc = mContentViewer->GetDocument();
3582 if (!doc) {
3583 return NS_OK;
3586 bool isWyciwyg = false;
3588 if (mCurrentURI) {
3589 // Check if the url is wyciwyg
3590 mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
3593 if (!isWyciwyg) {
3594 // If this docshell is loaded from a wyciwyg: URI, don't
3595 // advertise our charset since it does not in any way reflect
3596 // the actual source charset, which is what we're trying to
3597 // expose here.
3599 const Encoding* parentCS = doc->GetDocumentCharacterSet();
3600 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
3601 // set the child's parentCharset
3602 childAsDocShell->SetParentCharset(parentCS,
3603 charsetSource,
3604 doc->NodePrincipal());
3607 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
3608 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
3610 return NS_OK;
3613 NS_IMETHODIMP
3614 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild)
3616 NS_ENSURE_ARG_POINTER(aChild);
3618 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3619 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3621 nsresult rv = RemoveChildLoader(childAsDocLoader);
3622 NS_ENSURE_SUCCESS(rv, rv);
3624 aChild->SetTreeOwner(nullptr);
3626 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3629 NS_IMETHODIMP
3630 nsDocShell::GetChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild)
3632 NS_ENSURE_ARG_POINTER(aChild);
3634 #ifdef DEBUG
3635 if (aIndex < 0) {
3636 NS_WARNING("Negative index passed to GetChildAt");
3637 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3638 NS_WARNING("Too large an index passed to GetChildAt");
3640 #endif
3642 nsIDocumentLoader* child = ChildAt(aIndex);
3643 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3645 return CallQueryInterface(child, aChild);
3648 NS_IMETHODIMP
3649 nsDocShell::FindChildWithName(const nsAString& aName,
3650 bool aRecurse, bool aSameType,
3651 nsIDocShellTreeItem* aRequestor,
3652 nsIDocShellTreeItem* aOriginalRequestor,
3653 nsIDocShellTreeItem** aResult)
3655 NS_ENSURE_ARG_POINTER(aResult);
3657 // if we don't find one, we return NS_OK and a null result
3658 *aResult = nullptr;
3660 if (aName.IsEmpty()) {
3661 return NS_OK;
3664 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
3665 while (iter.HasMore()) {
3666 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(iter.GetNext());
3667 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
3668 int32_t childType = child->ItemType();
3670 if (aSameType && (childType != mItemType)) {
3671 continue;
3674 bool childNameEquals = false;
3675 child->NameEquals(aName, &childNameEquals);
3676 if (childNameEquals && ItemIsActive(child) &&
3677 CanAccessItem(child, aOriginalRequestor)) {
3678 child.swap(*aResult);
3679 break;
3682 // Only ask it to check children if it is same type
3683 if (childType != mItemType) {
3684 continue;
3687 // Only ask the child if it isn't the requestor
3688 if (aRecurse && (aRequestor != child)) {
3689 // See if child contains the shell with the given name
3690 #ifdef DEBUG
3691 nsresult rv =
3692 #endif
3693 child->FindChildWithName(aName, true, aSameType,
3694 static_cast<nsIDocShellTreeItem*>(this),
3695 aOriginalRequestor, aResult);
3696 NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not fail here");
3697 if (*aResult) {
3698 // found it
3699 return NS_OK;
3703 return NS_OK;
3706 NS_IMETHODIMP
3707 nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry** aResult)
3709 nsresult rv = NS_OK;
3711 NS_ENSURE_ARG_POINTER(aResult);
3712 *aResult = nullptr;
3714 // A nsISHEntry for a child is *only* available when the parent is in
3715 // the progress of loading a document too...
3717 if (mLSHE) {
3718 /* Before looking for the subframe's url, check
3719 * the expiration status of the parent. If the parent
3720 * has expired from cache, then subframes will not be
3721 * loaded from history in certain situations.
3723 bool parentExpired = false;
3724 mLSHE->GetExpirationStatus(&parentExpired);
3726 /* Get the parent's Load Type so that it can be set on the child too.
3727 * By default give a loadHistory value
3729 uint32_t loadType = nsIDocShellLoadInfo::loadHistory;
3730 mLSHE->GetLoadType(&loadType);
3731 // If the user did a shift-reload on this frameset page,
3732 // we don't want to load the subframes from history.
3733 if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache ||
3734 loadType == nsIDocShellLoadInfo::loadReloadBypassProxy ||
3735 loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache ||
3736 loadType == nsIDocShellLoadInfo::loadRefresh) {
3737 return rv;
3740 /* If the user pressed reload and the parent frame has expired
3741 * from cache, we do not want to load the child frame from history.
3743 if (parentExpired && (loadType == nsIDocShellLoadInfo::loadReloadNormal)) {
3744 // The parent has expired. Return null.
3745 *aResult = nullptr;
3746 return rv;
3749 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE));
3750 if (container) {
3751 // Get the child subframe from session history.
3752 rv = container->GetChildAt(aChildOffset, aResult);
3753 if (*aResult) {
3754 (*aResult)->SetLoadType(loadType);
3758 return rv;
3761 NS_IMETHODIMP
3762 nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
3763 int32_t aChildOffset, uint32_t aLoadType,
3764 bool aCloneChildren)
3766 nsresult rv = NS_OK;
3768 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
3769 /* You get here if you are currently building a
3770 * hierarchy ie.,you just visited a frameset page
3772 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mLSHE, &rv));
3773 if (container) {
3774 if (NS_FAILED(container->ReplaceChild(aNewEntry))) {
3775 rv = container->AddChild(aNewEntry, aChildOffset);
3778 } else if (!aCloneRef) {
3779 /* This is an initial load in some subframe. Just append it if we can */
3780 nsCOMPtr<nsISHContainer> container(do_QueryInterface(mOSHE, &rv));
3781 if (container) {
3782 rv = container->AddChild(aNewEntry, aChildOffset);
3784 } else {
3785 rv = AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset,
3786 aLoadType, aCloneChildren);
3788 return rv;
3791 nsresult
3792 nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef,
3793 nsISHEntry* aNewEntry,
3794 int32_t aChildOffset,
3795 uint32_t aLoadType,
3796 bool aCloneChildren)
3798 nsresult rv = NS_OK;
3799 if (mSessionHistory) {
3800 /* You are currently in the rootDocShell.
3801 * You will get here when a subframe has a new url
3802 * to load and you have walked up the tree all the
3803 * way to the top to clone the current SHEntry hierarchy
3804 * and replace the subframe where a new url was loaded with
3805 * a new entry.
3807 nsCOMPtr<nsISHEntry> currentHE;
3808 int32_t index = mSessionHistory->Index();
3809 if (index < 0) {
3810 return NS_ERROR_FAILURE;
3813 rv = mSessionHistory->LegacySHistory()->GetEntryAtIndex(
3814 index, false, getter_AddRefs(currentHE));
3815 NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
3817 nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
3818 if (currentEntry) {
3819 uint32_t cloneID = 0;
3820 nsCOMPtr<nsISHEntry> nextEntry;
3821 aCloneRef->GetID(&cloneID);
3822 rv = nsSHistory::CloneAndReplace(currentEntry, this, cloneID,
3823 aNewEntry, aCloneChildren, getter_AddRefs(nextEntry));
3825 if (NS_SUCCEEDED(rv)) {
3826 rv = mSessionHistory->LegacySHistoryInternal()->AddEntry(nextEntry, true);
3829 } else {
3830 /* Just pass this along */
3831 nsCOMPtr<nsIDocShell> parent =
3832 do_QueryInterface(GetAsSupports(mParent), &rv);
3833 if (parent) {
3834 rv = static_cast<nsDocShell*>(parent.get())->AddChildSHEntryInternal(
3835 aCloneRef, aNewEntry, aChildOffset, aLoadType, aCloneChildren);
3838 return rv;
3841 nsresult
3842 nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry, int32_t aChildOffset,
3843 bool aCloneChildren)
3845 /* You will get here when you are in a subframe and
3846 * a new url has been loaded on you.
3847 * The mOSHE in this subframe will be the previous url's
3848 * mOSHE. This mOSHE will be used as the identification
3849 * for this subframe in the CloneAndReplace function.
3852 // In this case, we will end up calling AddEntry, which increases the
3853 // current index by 1
3854 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3855 if (rootSH) {
3856 mPreviousTransIndex = rootSH->Index();
3859 nsresult rv;
3860 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
3861 if (parent) {
3862 rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType,
3863 aCloneChildren);
3866 if (rootSH) {
3867 mLoadedTransIndex = rootSH->Index();
3868 #ifdef DEBUG_PAGE_CACHE
3869 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
3870 mLoadedTransIndex);
3871 #endif
3874 return rv;
3877 NS_IMETHODIMP
3878 nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory)
3880 mUseGlobalHistory = aUseGlobalHistory;
3881 if (!aUseGlobalHistory) {
3882 return NS_OK;
3885 nsCOMPtr<IHistory> history = services::GetHistoryService();
3886 return history ? NS_OK : NS_ERROR_FAILURE;
3889 NS_IMETHODIMP
3890 nsDocShell::GetUseGlobalHistory(bool* aUseGlobalHistory)
3892 *aUseGlobalHistory = mUseGlobalHistory;
3893 return NS_OK;
3896 NS_IMETHODIMP
3897 nsDocShell::RemoveFromSessionHistory()
3899 nsCOMPtr<nsIDocShellTreeItem> root;
3900 GetSameTypeRootTreeItem(getter_AddRefs(root));
3901 nsCOMPtr<nsIWebNavigation> rootAsWebnav = do_QueryInterface(root);
3902 if (!rootAsWebnav) {
3903 return NS_OK;
3905 RefPtr<ChildSHistory> sessionHistory = rootAsWebnav->GetSessionHistory();
3906 if (!sessionHistory) {
3907 return NS_OK;
3909 int32_t index = sessionHistory->Index();
3910 AutoTArray<nsID, 16> ids({mHistoryID});
3911 sessionHistory->LegacySHistoryInternal()->RemoveEntries(ids, index);
3912 return NS_OK;
3915 NS_IMETHODIMP
3916 nsDocShell::SetCreatedDynamically(bool aDynamic)
3918 mDynamicallyCreated = aDynamic;
3919 return NS_OK;
3922 NS_IMETHODIMP
3923 nsDocShell::GetCreatedDynamically(bool* aDynamic)
3925 *aDynamic = mDynamicallyCreated;
3926 return NS_OK;
3929 NS_IMETHODIMP
3930 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE)
3932 *aOSHE = false;
3933 *aEntry = nullptr;
3934 if (mLSHE) {
3935 NS_ADDREF(*aEntry = mLSHE);
3936 } else if (mOSHE) {
3937 NS_ADDREF(*aEntry = mOSHE);
3938 *aOSHE = true;
3940 return NS_OK;
3943 nsIScriptGlobalObject*
3944 nsDocShell::GetScriptGlobalObject()
3946 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3947 return mScriptGlobal;
3950 nsIDocument*
3951 nsDocShell::GetDocument()
3953 NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
3954 return mContentViewer->GetDocument();
3957 nsPIDOMWindowOuter*
3958 nsDocShell::GetWindow()
3960 if (NS_FAILED(EnsureScriptEnvironment())) {
3961 return nullptr;
3963 return mScriptGlobal->AsOuter();
3966 NS_IMETHODIMP
3967 nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
3969 if (mDeviceSizeIsPageSize != aValue) {
3970 mDeviceSizeIsPageSize = aValue;
3971 RefPtr<nsPresContext> presContext;
3972 GetPresContext(getter_AddRefs(presContext));
3973 if (presContext) {
3974 presContext->MediaFeatureValuesChanged({
3975 MediaFeatureChangeReason::DeviceSizeIsPageSizeChange });
3978 return NS_OK;
3981 NS_IMETHODIMP
3982 nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
3984 *aValue = mDeviceSizeIsPageSize;
3985 return NS_OK;
3988 void
3989 nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
3991 nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
3992 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3993 if (!rootSH || !shcontainer) {
3994 return;
3997 int32_t count = 0;
3998 shcontainer->GetChildCount(&count);
3999 AutoTArray<nsID, 16> ids;
4000 for (int32_t i = 0; i < count; ++i) {
4001 nsCOMPtr<nsISHEntry> child;
4002 shcontainer->GetChildAt(i, getter_AddRefs(child));
4003 if (child) {
4004 ids.AppendElement(child->DocshellID());
4007 int32_t index = rootSH->Index();
4008 rootSH->LegacySHistoryInternal()->RemoveEntries(ids, index);
4011 //-------------------------------------
4012 //-- Helper Method for Print discovery
4013 //-------------------------------------
4014 bool
4015 nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog)
4017 if (mIsPrintingOrPP && aDisplayErrorDialog) {
4018 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
4021 return mIsPrintingOrPP;
4024 bool
4025 nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
4026 bool aCheckIfUnloadFired)
4028 bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) &&
4029 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
4030 if (!isAllowed) {
4031 return false;
4033 if (!mContentViewer) {
4034 return true;
4036 bool firingBeforeUnload;
4037 mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
4038 return !firingBeforeUnload;
4041 //*****************************************************************************
4042 // nsDocShell::nsIWebNavigation
4043 //*****************************************************************************
4045 NS_IMETHODIMP
4046 nsDocShell::GetCanGoBack(bool* aCanGoBack)
4048 *aCanGoBack = false;
4049 if (!IsNavigationAllowed(false)) {
4050 return NS_OK; // JS may not handle returning of an error code
4052 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4053 if (rootSH) {
4054 *aCanGoBack = rootSH->CanGo(-1);
4055 return NS_OK;
4057 return NS_ERROR_FAILURE;
4060 NS_IMETHODIMP
4061 nsDocShell::GetCanGoForward(bool* aCanGoForward)
4063 *aCanGoForward = false;
4064 if (!IsNavigationAllowed(false)) {
4065 return NS_OK; // JS may not handle returning of an error code
4067 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4068 if (rootSH) {
4069 *aCanGoForward = rootSH->CanGo(1);
4070 return NS_OK;
4072 return NS_ERROR_FAILURE;
4075 NS_IMETHODIMP
4076 nsDocShell::GoBack()
4078 if (!IsNavigationAllowed()) {
4079 return NS_OK; // JS may not handle returning of an error code
4081 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4082 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
4083 ErrorResult rv;
4084 rootSH->Go(-1, rv);
4085 return rv.StealNSResult();
4088 NS_IMETHODIMP
4089 nsDocShell::GoForward()
4091 if (!IsNavigationAllowed()) {
4092 return NS_OK; // JS may not handle returning of an error code
4094 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4095 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
4096 ErrorResult rv;
4097 rootSH->Go(1, rv);
4098 return rv.StealNSResult();
4101 // XXX(nika): We may want to stop exposing this API in the child process? Going
4102 // to a specific index from multiple different processes could definitely race.
4103 NS_IMETHODIMP
4104 nsDocShell::GotoIndex(int32_t aIndex)
4106 if (!IsNavigationAllowed()) {
4107 return NS_OK; // JS may not handle returning of an error code
4109 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4110 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
4111 return rootSH->LegacySHistoryWebNav()->GotoIndex(aIndex);
4114 NS_IMETHODIMP
4115 nsDocShell::LoadURI(const char16_t* aURI,
4116 uint32_t aLoadFlags,
4117 nsIURI* aReferringURI,
4118 nsIInputStream* aPostStream,
4119 nsIInputStream* aHeaderStream,
4120 nsIPrincipal* aTriggeringPrincipal)
4122 return LoadURIWithOptions(aURI, aLoadFlags, aReferringURI,
4123 RP_Unset, aPostStream,
4124 aHeaderStream, nullptr, aTriggeringPrincipal);
4127 NS_IMETHODIMP
4128 nsDocShell::LoadURIWithOptions(const char16_t* aURI,
4129 uint32_t aLoadFlags,
4130 nsIURI* aReferringURI,
4131 uint32_t aReferrerPolicy,
4132 nsIInputStream* aPostStream,
4133 nsIInputStream* aHeaderStream,
4134 nsIURI* aBaseURI,
4135 nsIPrincipal* aTriggeringPrincipal)
4137 NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
4139 if (!IsNavigationAllowed()) {
4140 return NS_OK; // JS may not handle returning of an error code
4142 nsCOMPtr<nsIURI> uri;
4143 nsCOMPtr<nsIInputStream> postStream(aPostStream);
4144 nsresult rv = NS_OK;
4146 // Create a URI from our string; if that succeeds, we want to
4147 // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
4148 // flag.
4150 NS_ConvertUTF16toUTF8 uriString(aURI);
4151 // Cleanup the empty spaces that might be on each end.
4152 uriString.Trim(" ");
4153 // Eliminate embedded newlines, which single-line text fields now allow:
4154 uriString.StripCRLF();
4155 NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE);
4157 rv = NS_NewURI(getter_AddRefs(uri), uriString);
4158 if (uri) {
4159 aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
4162 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
4163 if (sURIFixup) {
4164 // Call the fixup object. This will clobber the rv from NS_NewURI
4165 // above, but that's fine with us. Note that we need to do this even
4166 // if NS_NewURI returned a URI, because fixup handles nested URIs, etc
4167 // (things like view-source:mozilla.org for example).
4168 uint32_t fixupFlags = 0;
4169 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4170 fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
4172 if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
4173 fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
4175 nsCOMPtr<nsIInputStream> fixupStream;
4176 rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
4177 getter_AddRefs(fixupStream),
4178 getter_AddRefs(fixupInfo));
4180 if (NS_SUCCEEDED(rv)) {
4181 fixupInfo->GetPreferredURI(getter_AddRefs(uri));
4182 fixupInfo->SetConsumer(GetAsSupports(this));
4185 if (fixupStream) {
4186 // GetFixupURIInfo only returns a post data stream if it succeeded
4187 // and changed the URI, in which case we should override the
4188 // passed-in post data.
4189 postStream = fixupStream;
4192 if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
4193 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4194 if (serv) {
4195 serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
4199 // else no fixup service so just use the URI we created and see
4200 // what happens
4202 if (NS_ERROR_MALFORMED_URI == rv) {
4203 if (DisplayLoadError(rv, uri, aURI, nullptr) &&
4204 (aLoadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
4205 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
4209 if (NS_FAILED(rv) || !uri) {
4210 return NS_ERROR_FAILURE;
4213 PopupControlState popupState;
4214 if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) {
4215 popupState = openAllowed;
4216 aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS;
4217 } else {
4218 popupState = openOverridden;
4220 nsAutoPopupStatePusher statePusher(popupState);
4222 bool forceAllowDataURI =
4223 aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
4225 // Don't pass certain flags that aren't needed and end up confusing
4226 // ConvertLoadTypeToDocShellInfoLoadType. We do need to ensure that they are
4227 // passed to LoadURI though, since it uses them.
4228 uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS);
4229 aLoadFlags &= ~EXTRA_LOAD_FLAGS;
4231 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
4232 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
4233 if (NS_FAILED(rv)) {
4234 return rv;
4238 * If the user "Disables Protection on This Page", we have to make sure to
4239 * remember the users decision when opening links in child tabs [Bug 906190]
4241 uint32_t loadType;
4242 if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) {
4243 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags);
4244 } else {
4245 loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);
4248 loadInfo->SetLoadType(ConvertLoadTypeToDocShellInfoLoadType(loadType));
4249 loadInfo->SetPostDataStream(postStream);
4250 loadInfo->SetReferrer(aReferringURI);
4251 loadInfo->SetReferrerPolicy(aReferrerPolicy);
4252 loadInfo->SetHeadersStream(aHeaderStream);
4253 loadInfo->SetBaseURI(aBaseURI);
4254 loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
4255 loadInfo->SetForceAllowDataURI(forceAllowDataURI);
4257 if (fixupInfo) {
4258 nsAutoString searchProvider, keyword;
4259 fixupInfo->GetKeywordProviderName(searchProvider);
4260 fixupInfo->GetKeywordAsSent(keyword);
4261 MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
4264 rv = LoadURI(uri, loadInfo, extraFlags, true);
4266 // Save URI string in case it's needed later when
4267 // sending to search engine service in EndPageLoad()
4268 mOriginalUriString = uriString;
4270 return rv;
4273 NS_IMETHODIMP
4274 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
4275 const char16_t* aURL,
4276 nsIChannel* aFailedChannel,
4277 bool* aDisplayedErrorPage)
4279 *aDisplayedErrorPage = false;
4280 // Get prompt and string bundle servcies
4281 nsCOMPtr<nsIPrompt> prompter;
4282 nsCOMPtr<nsIStringBundle> stringBundle;
4283 GetPromptAndStringBundle(getter_AddRefs(prompter),
4284 getter_AddRefs(stringBundle));
4286 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
4287 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
4289 const char* error = nullptr;
4290 // The key used to select the appropriate error message from the properties file.
4291 const char* errorDescriptionID = nullptr;
4292 const uint32_t kMaxFormatStrArgs = 3;
4293 nsAutoString formatStrs[kMaxFormatStrArgs];
4294 uint32_t formatStrCount = 0;
4295 bool addHostPort = false;
4296 nsresult rv = NS_OK;
4297 nsAutoString messageStr;
4298 nsAutoCString cssClass;
4299 nsAutoCString errorPage;
4301 errorPage.AssignLiteral("neterror");
4303 // Turn the error code into a human readable error message.
4304 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
4305 NS_ENSURE_ARG_POINTER(aURI);
4307 // Extract the schemes into a comma delimited list.
4308 nsAutoCString scheme;
4309 aURI->GetScheme(scheme);
4310 CopyASCIItoUTF16(scheme, formatStrs[0]);
4311 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
4312 while (nestedURI) {
4313 nsCOMPtr<nsIURI> tempURI;
4314 nsresult rv2;
4315 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
4316 if (NS_SUCCEEDED(rv2) && tempURI) {
4317 tempURI->GetScheme(scheme);
4318 formatStrs[0].AppendLiteral(", ");
4319 AppendASCIItoUTF16(scheme, formatStrs[0]);
4321 nestedURI = do_QueryInterface(tempURI);
4323 formatStrCount = 1;
4324 error = "unknownProtocolFound";
4325 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
4326 NS_ENSURE_ARG_POINTER(aURI);
4327 error = "fileNotFound";
4328 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
4329 NS_ENSURE_ARG_POINTER(aURI);
4330 error = "fileAccessDenied";
4331 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
4332 NS_ENSURE_ARG_POINTER(aURI);
4333 // Get the host
4334 nsAutoCString host;
4335 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
4336 innermostURI->GetHost(host);
4337 CopyUTF8toUTF16(host, formatStrs[0]);
4338 formatStrCount = 1;
4339 errorDescriptionID = "dnsNotFound2";
4340 error = "dnsNotFound";
4341 } else if (NS_ERROR_CONNECTION_REFUSED == aError) {
4342 NS_ENSURE_ARG_POINTER(aURI);
4343 addHostPort = true;
4344 error = "connectionFailure";
4345 } else if (NS_ERROR_NET_INTERRUPT == aError) {
4346 NS_ENSURE_ARG_POINTER(aURI);
4347 addHostPort = true;
4348 error = "netInterrupt";
4349 } else if (NS_ERROR_NET_TIMEOUT == aError) {
4350 NS_ENSURE_ARG_POINTER(aURI);
4351 // Get the host
4352 nsAutoCString host;
4353 aURI->GetHost(host);
4354 CopyUTF8toUTF16(host, formatStrs[0]);
4355 formatStrCount = 1;
4356 error = "netTimeout";
4357 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
4358 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
4359 // CSP error
4360 cssClass.AssignLiteral("neterror");
4361 error = "cspBlocked";
4362 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
4363 nsCOMPtr<nsINSSErrorsService> nsserr =
4364 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
4366 uint32_t errorClass;
4367 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
4368 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
4371 nsCOMPtr<nsISupports> securityInfo;
4372 nsCOMPtr<nsITransportSecurityInfo> tsi;
4373 if (aFailedChannel) {
4374 aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
4376 tsi = do_QueryInterface(securityInfo);
4377 if (tsi) {
4378 uint32_t securityState;
4379 tsi->GetSecurityState(&securityState);
4380 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
4381 error = "sslv3Used";
4382 addHostPort = true;
4383 } else if (securityState & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
4384 error = "weakCryptoUsed";
4385 addHostPort = true;
4387 } else {
4388 // No channel, let's obtain the generic error message
4389 if (nsserr) {
4390 nsserr->GetErrorMessage(aError, messageStr);
4393 // We don't have a message string here anymore but DisplayLoadError
4394 // requires a non-empty messageStr.
4395 messageStr.Truncate();
4396 messageStr.AssignLiteral(u" ");
4397 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
4398 error = "nssBadCert";
4400 // If this is an HTTP Strict Transport Security host or a pinned host
4401 // and the certificate is bad, don't allow overrides (RFC 6797 section
4402 // 12.1, HPKP draft spec section 2.6).
4403 uint32_t flags =
4404 UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
4405 bool isStsHost = false;
4406 bool isPinnedHost = false;
4407 if (XRE_IsParentProcess()) {
4408 nsCOMPtr<nsISiteSecurityService> sss =
4409 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
4410 NS_ENSURE_SUCCESS(rv, rv);
4411 rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
4412 flags, mOriginAttributes, nullptr, nullptr,
4413 &isStsHost);
4414 NS_ENSURE_SUCCESS(rv, rv);
4415 rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
4416 flags, mOriginAttributes, nullptr, nullptr,
4417 &isPinnedHost);
4418 NS_ENSURE_SUCCESS(rv, rv);
4419 } else {
4420 mozilla::dom::ContentChild* cc =
4421 mozilla::dom::ContentChild::GetSingleton();
4422 mozilla::ipc::URIParams uri;
4423 SerializeURI(aURI, uri);
4424 cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
4425 mOriginAttributes, &isStsHost);
4426 cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags,
4427 mOriginAttributes, &isPinnedHost);
4430 if (Preferences::GetBool(
4431 "browser.xul.error_pages.expert_bad_cert", false)) {
4432 cssClass.AssignLiteral("expertBadCert");
4435 // HSTS/pinning takes precedence over the expert bad cert pref. We
4436 // never want to show the "Add Exception" button for these sites.
4437 // In the future we should differentiate between an HSTS host and a
4438 // pinned host and display a more informative message to the user.
4439 if (isStsHost || isPinnedHost) {
4440 cssClass.AssignLiteral("badStsCert");
4443 uint32_t bucketId;
4444 if (isStsHost) {
4445 // measuring STS separately allows us to measure click through
4446 // rates easily
4447 bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS;
4448 } else {
4449 bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP;
4452 // See if an alternate cert error page is registered
4453 nsAutoCString alternateErrorPage;
4454 nsresult rv =
4455 Preferences::GetCString("security.alternate_certificate_error_page",
4456 alternateErrorPage);
4457 if (NS_SUCCEEDED(rv)) {
4458 errorPage.Assign(alternateErrorPage);
4461 if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) {
4462 Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId);
4464 } else {
4465 error = "nssFailure2";
4467 } else if (NS_ERROR_PHISHING_URI == aError ||
4468 NS_ERROR_MALWARE_URI == aError ||
4469 NS_ERROR_UNWANTED_URI == aError ||
4470 NS_ERROR_HARMFUL_URI == aError) {
4471 nsAutoCString host;
4472 aURI->GetHost(host);
4473 CopyUTF8toUTF16(host, formatStrs[0]);
4474 formatStrCount = 1;
4476 // Malware and phishing detectors may want to use an alternate error
4477 // page, but if the pref's not set, we'll fall back on the standard page
4478 nsAutoCString alternateErrorPage;
4479 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
4480 alternateErrorPage);
4481 if (NS_SUCCEEDED(rv)) {
4482 errorPage.Assign(alternateErrorPage);
4485 uint32_t bucketId;
4486 bool sendTelemetry = false;
4487 if (NS_ERROR_PHISHING_URI == aError) {
4488 sendTelemetry = true;
4489 error = "deceptiveBlocked";
4490 bucketId = IsFrame() ? IUrlClassifierUITelemetry::WARNING_PHISHING_PAGE_FRAME
4491 : IUrlClassifierUITelemetry::WARNING_PHISHING_PAGE_TOP;
4492 } else if (NS_ERROR_MALWARE_URI == aError) {
4493 sendTelemetry = true;
4494 error = "malwareBlocked";
4495 bucketId = IsFrame() ? 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() ? IUrlClassifierUITelemetry::WARNING_UNWANTED_PAGE_FRAME
4501 : IUrlClassifierUITelemetry::WARNING_UNWANTED_PAGE_TOP;
4502 } else if (NS_ERROR_HARMFUL_URI == aError) {
4503 sendTelemetry = true;
4504 error = "harmfulBlocked";
4505 bucketId = IsFrame() ? IUrlClassifierUITelemetry::WARNING_HARMFUL_PAGE_FRAME
4506 : IUrlClassifierUITelemetry::WARNING_HARMFUL_PAGE_TOP;
4509 if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) {
4510 Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UI_EVENTS, bucketId);
4513 cssClass.AssignLiteral("blacklist");
4514 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
4515 errorPage.AssignLiteral("tabcrashed");
4516 error = "tabcrashed";
4518 nsCOMPtr<EventTarget> handler = mChromeEventHandler;
4519 if (handler) {
4520 nsCOMPtr<Element> element = do_QueryInterface(handler);
4521 element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), messageStr);
4524 // DisplayLoadError requires a non-empty messageStr to proceed and call
4525 // LoadErrorPage. If the page doesn't have a title, we will use a blank
4526 // space which will be trimmed and thus treated as empty by the front-end.
4527 if (messageStr.IsEmpty()) {
4528 messageStr.AssignLiteral(u" ");
4530 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
4531 errorPage.AssignLiteral("restartrequired");
4532 error = "restartrequired";
4534 // DisplayLoadError requires a non-empty messageStr to proceed and call
4535 // LoadErrorPage. If the page doesn't have a title, we will use a blank
4536 // space which will be trimmed and thus treated as empty by the front-end.
4537 if (messageStr.IsEmpty()) {
4538 messageStr.AssignLiteral(u" ");
4540 } else {
4541 // Errors requiring simple formatting
4542 switch (aError) {
4543 case NS_ERROR_MALFORMED_URI:
4544 // URI is malformed
4545 error = "malformedURI";
4546 errorDescriptionID = "malformedURI2";
4547 break;
4548 case NS_ERROR_REDIRECT_LOOP:
4549 // Doc failed to load because the server generated too many redirects
4550 error = "redirectLoop";
4551 break;
4552 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
4553 // Doc failed to load because PSM is not installed
4554 error = "unknownSocketType";
4555 break;
4556 case NS_ERROR_NET_RESET:
4557 // Doc failed to load because the server kept reseting the connection
4558 // before we could read any data from it
4559 error = "netReset";
4560 break;
4561 case NS_ERROR_DOCUMENT_NOT_CACHED:
4562 // Doc failed to load because the cache does not contain a copy of
4563 // the document.
4564 error = "notCached";
4565 break;
4566 case NS_ERROR_OFFLINE:
4567 // Doc failed to load because we are offline.
4568 error = "netOffline";
4569 break;
4570 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
4571 // Doc navigation attempted while Printing or Print Preview
4572 error = "isprinting";
4573 break;
4574 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
4575 // Port blocked for security reasons
4576 addHostPort = true;
4577 error = "deniedPortAccess";
4578 break;
4579 case NS_ERROR_UNKNOWN_PROXY_HOST:
4580 // Proxy hostname could not be resolved.
4581 error = "proxyResolveFailure";
4582 break;
4583 case NS_ERROR_PROXY_CONNECTION_REFUSED:
4584 // Proxy connection was refused.
4585 error = "proxyConnectFailure";
4586 break;
4587 case NS_ERROR_INVALID_CONTENT_ENCODING:
4588 // Bad Content Encoding.
4589 error = "contentEncodingError";
4590 break;
4591 case NS_ERROR_REMOTE_XUL:
4592 error = "remoteXUL";
4593 break;
4594 case NS_ERROR_UNSAFE_CONTENT_TYPE:
4595 // Channel refused to load from an unrecognized content type.
4596 error = "unsafeContentType";
4597 break;
4598 case NS_ERROR_CORRUPTED_CONTENT:
4599 // Broken Content Detected. e.g. Content-MD5 check failure.
4600 error = "corruptedContentErrorv2";
4601 break;
4602 case NS_ERROR_INTERCEPTION_FAILED:
4603 // ServiceWorker intercepted request, but something went wrong.
4604 error = "corruptedContentErrorv2";
4605 break;
4606 case NS_ERROR_NET_INADEQUATE_SECURITY:
4607 // Server negotiated bad TLS for HTTP/2.
4608 error = "inadequateSecurityError";
4609 addHostPort = true;
4610 break;
4611 case NS_ERROR_BLOCKED_BY_POLICY:
4612 // Page blocked by policy
4613 error = "blockedByPolicy";
4614 break;
4615 default:
4616 break;
4620 // Test if the error should be displayed
4621 if (!error) {
4622 return NS_OK;
4625 if (!errorDescriptionID) {
4626 errorDescriptionID = error;
4629 // Test if the error needs to be formatted
4630 if (!messageStr.IsEmpty()) {
4631 // already obtained message
4632 } else {
4633 if (addHostPort) {
4634 // Build up the host:port string.
4635 nsAutoCString hostport;
4636 if (aURI) {
4637 aURI->GetHostPort(hostport);
4638 } else {
4639 hostport.Assign('?');
4641 CopyUTF8toUTF16(hostport, formatStrs[formatStrCount++]);
4644 nsAutoCString spec;
4645 rv = NS_ERROR_NOT_AVAILABLE;
4646 if (aURI) {
4647 // displaying "file://" is aesthetically unpleasing and could even be
4648 // confusing to the user
4649 bool isFileURI = false;
4650 rv = aURI->SchemeIs("file", &isFileURI);
4651 if (NS_SUCCEEDED(rv) && isFileURI) {
4652 aURI->GetPathQueryRef(spec);
4653 } else {
4654 aURI->GetSpec(spec);
4657 nsCOMPtr<nsITextToSubURI> textToSubURI(
4658 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
4659 if (NS_SUCCEEDED(rv)) {
4660 rv = textToSubURI->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"), spec,
4661 formatStrs[formatStrCount]);
4663 } else {
4664 spec.Assign('?');
4666 if (NS_FAILED(rv)) {
4667 CopyUTF8toUTF16(spec, formatStrs[formatStrCount]);
4669 rv = NS_OK;
4670 ++formatStrCount;
4672 const char16_t* strs[kMaxFormatStrArgs];
4673 for (uint32_t i = 0; i < formatStrCount; i++) {
4674 strs[i] = formatStrs[i].get();
4676 nsAutoString str;
4677 rv = stringBundle->FormatStringFromName(errorDescriptionID, strs, formatStrCount, str);
4678 NS_ENSURE_SUCCESS(rv, rv);
4679 messageStr.Assign(str.get());
4682 // Display the error as a page or an alert prompt
4683 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
4685 if (NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) {
4686 bool isSecureURI = false;
4687 rv = aURI->SchemeIs("https", &isSecureURI);
4688 if (NS_SUCCEEDED(rv) && isSecureURI) {
4689 // Maybe TLS intolerant. Treat this as an SSL error.
4690 error = "nssFailure2";
4694 if (UseErrorPages()) {
4695 // Display an error page
4696 nsresult loadedPage = LoadErrorPage(aURI, aURL, errorPage.get(),
4697 error, messageStr.get(),
4698 cssClass.get(), aFailedChannel);
4699 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
4700 } else {
4701 // The prompter reqires that our private window has a document (or it
4702 // asserts). Satisfy that assertion now since GetDoc will force
4703 // creation of one if it hasn't already been created.
4704 if (mScriptGlobal) {
4705 Unused << mScriptGlobal->GetDoc();
4708 // Display a message box
4709 prompter->Alert(nullptr, messageStr.get());
4712 return NS_OK;
4715 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
4717 nsresult
4718 nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
4719 const char* aErrorPage,
4720 const char* aErrorType,
4721 const char16_t* aDescription,
4722 const char* aCSSClass,
4723 nsIChannel* aFailedChannel)
4725 MOZ_ASSERT(!mIsBeingDestroyed);
4727 #if defined(DEBUG)
4728 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
4729 nsAutoCString chanName;
4730 if (aFailedChannel) {
4731 aFailedChannel->GetName(chanName);
4732 } else {
4733 chanName.AssignLiteral("<no channel>");
4736 MOZ_LOG(gDocShellLog, LogLevel::Debug,
4737 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this,
4738 aURI ? aURI->GetSpecOrDefault().get() : "",
4739 NS_ConvertUTF16toUTF8(aURL).get(),
4740 chanName.get()));
4742 #endif
4743 mFailedChannel = aFailedChannel;
4744 mFailedURI = aURI;
4745 mFailedLoadType = mLoadType;
4747 if (mLSHE) {
4748 // Abandon mLSHE's BFCache entry and create a new one. This way, if
4749 // we go back or forward to another SHEntry with the same doc
4750 // identifier, the error page won't persist.
4751 mLSHE->AbandonBFCacheEntry();
4754 nsAutoCString url;
4755 if (aURI) {
4756 nsresult rv = aURI->GetSpec(url);
4757 NS_ENSURE_SUCCESS(rv, rv);
4758 } else if (aURL) {
4759 CopyUTF16toUTF8(aURL, url);
4760 } else {
4761 return NS_ERROR_INVALID_POINTER;
4764 // Create a URL to pass all the error information through to the page.
4766 #undef SAFE_ESCAPE
4767 #define SAFE_ESCAPE(output, input, params) \
4768 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
4769 return NS_ERROR_OUT_OF_MEMORY; \
4772 nsCString escapedUrl, escapedError, escapedDescription,
4773 escapedCSSClass;
4774 SAFE_ESCAPE(escapedUrl, url, url_Path);
4775 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
4776 SAFE_ESCAPE(escapedDescription,
4777 NS_ConvertUTF16toUTF8(aDescription), url_Path);
4778 if (aCSSClass) {
4779 nsCString cssClass(aCSSClass);
4780 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
4782 nsCString errorPageUrl("about:");
4783 errorPageUrl.AppendASCII(aErrorPage);
4784 errorPageUrl.AppendLiteral("?e=");
4786 errorPageUrl.AppendASCII(escapedError.get());
4787 errorPageUrl.AppendLiteral("&u=");
4788 errorPageUrl.AppendASCII(escapedUrl.get());
4789 if ((strcmp(aErrorPage, "blocked") == 0) &&
4790 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
4791 errorPageUrl.AppendLiteral("&o=1");
4793 if (!escapedCSSClass.IsEmpty()) {
4794 errorPageUrl.AppendLiteral("&s=");
4795 errorPageUrl.AppendASCII(escapedCSSClass.get());
4797 errorPageUrl.AppendLiteral("&c=UTF-8");
4799 nsAutoCString frameType(FrameTypeToString(mFrameType));
4800 errorPageUrl.AppendLiteral("&f=");
4801 errorPageUrl.AppendASCII(frameType.get());
4803 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
4804 int32_t cpsState;
4805 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
4806 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
4807 errorPageUrl.AppendLiteral("&captive=true");
4810 // netError.xhtml's getDescription only handles the "d" parameter at the
4811 // end of the URL, so append it last.
4812 errorPageUrl.AppendLiteral("&d=");
4813 errorPageUrl.AppendASCII(escapedDescription.get());
4815 nsCOMPtr<nsIURI> errorPageURI;
4816 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
4817 NS_ENSURE_SUCCESS(rv, rv);
4819 return InternalLoad(errorPageURI, nullptr, Nothing(), false, nullptr, RP_Unset,
4820 nsContentUtils::GetSystemPrincipal(), nullptr,
4821 INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
4822 nullptr, VoidString(), nullptr, nullptr,
4823 LOAD_ERROR_PAGE, nullptr, true, VoidString(), this,
4824 nullptr, nullptr, nullptr);
4827 NS_IMETHODIMP
4828 nsDocShell::Reload(uint32_t aReloadFlags)
4830 if (!IsNavigationAllowed()) {
4831 return NS_OK; // JS may not handle returning of an error code
4833 nsresult rv;
4834 NS_ASSERTION(((aReloadFlags & 0xf) == 0),
4835 "Reload command not updated to use load flags!");
4836 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4837 "Don't pass these flags to Reload");
4839 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4840 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4842 // Send notifications to the HistoryListener if any, about the impending
4843 // reload
4844 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4845 bool canReload = true;
4846 if (rootSH) {
4847 rootSH->LegacySHistoryInternal()
4848 ->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &canReload);
4851 if (!canReload) {
4852 return NS_OK;
4855 /* If you change this part of code, make sure bug 45297 does not re-occur */
4856 if (mOSHE) {
4857 rv = LoadHistoryEntry(mOSHE, loadType);
4858 } else if (mLSHE) { // In case a reload happened before the current load is done
4859 rv = LoadHistoryEntry(mLSHE, loadType);
4860 } else {
4861 nsCOMPtr<nsIDocument> doc(GetDocument());
4863 if (!doc) {
4864 return NS_OK;
4867 // Do not inherit owner from document
4868 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4869 nsAutoString srcdoc;
4870 nsCOMPtr<nsIURI> baseURI;
4871 nsCOMPtr<nsIURI> originalURI;
4872 nsCOMPtr<nsIURI> resultPrincipalURI;
4873 bool loadReplace = false;
4875 nsIPrincipal* triggeringPrincipal = doc->NodePrincipal();
4876 nsAutoString contentTypeHint;
4877 doc->GetContentType(contentTypeHint);
4879 if (doc->IsSrcdocDocument()) {
4880 doc->GetSrcdocData(srcdoc);
4881 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4882 baseURI = doc->GetBaseURI();
4884 nsCOMPtr<nsIChannel> chan = doc->GetChannel();
4885 if (chan) {
4886 uint32_t loadFlags;
4887 chan->GetLoadFlags(&loadFlags);
4888 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4889 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4890 if (httpChan) {
4891 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4894 nsCOMPtr<nsILoadInfo> loadInfo = chan->GetLoadInfo();
4895 if (loadInfo) {
4896 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4900 MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal");
4902 // Stack variables to ensure changes to the member variables don't affect to
4903 // the call.
4904 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4905 nsCOMPtr<nsIURI> referrerURI = mReferrerURI;
4906 uint32_t referrerPolicy = mReferrerPolicy;
4908 // Reload always rewrites result principal URI.
4909 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4910 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4911 rv = InternalLoad(currentURI,
4912 originalURI,
4913 emplacedResultPrincipalURI,
4914 loadReplace,
4915 referrerURI,
4916 referrerPolicy,
4917 triggeringPrincipal,
4918 triggeringPrincipal,
4919 flags,
4920 EmptyString(), // No window target
4921 NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
4922 VoidString(), // No forced download
4923 nullptr, // No post data
4924 nullptr, // No headers data
4925 loadType, // Load type
4926 nullptr, // No SHEntry
4927 true,
4928 srcdoc, // srcdoc argument for iframe
4929 this, // For reloads we are the source
4930 baseURI,
4931 nullptr, // No nsIDocShell
4932 nullptr); // No nsIRequest
4935 return rv;
4938 NS_IMETHODIMP
4939 nsDocShell::Stop(uint32_t aStopFlags)
4941 // Revoke any pending event related to content viewer restoration
4942 mRestorePresentationEvent.Revoke();
4944 if (mLoadType == LOAD_ERROR_PAGE) {
4945 if (mLSHE) {
4946 // Since error page loads never unset mLSHE, do so now
4947 SetHistoryEntry(&mOSHE, mLSHE);
4948 SetHistoryEntry(&mLSHE, nullptr);
4951 mFailedChannel = nullptr;
4952 mFailedURI = nullptr;
4955 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4956 // Stop the document loading
4957 if (mContentViewer) {
4958 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4959 cv->Stop();
4963 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4964 // Suspend any timers that were set for this loader. We'll clear
4965 // them out for good in CreateContentViewer.
4966 if (mRefreshURIList) {
4967 SuspendRefreshURIs();
4968 mSavedRefreshURIList.swap(mRefreshURIList);
4969 mRefreshURIList = nullptr;
4972 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4973 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4974 // redundant apis!
4975 Stop();
4978 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
4979 while (iter.HasMore()) {
4980 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(iter.GetNext()));
4981 if (shellAsNav) {
4982 shellAsNav->Stop(aStopFlags);
4986 return NS_OK;
4989 NS_IMETHODIMP
4990 nsDocShell::GetDocument(nsIDocument** aDocument)
4992 NS_ENSURE_ARG_POINTER(aDocument);
4993 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4995 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
4996 if (!doc) {
4997 return NS_ERROR_NOT_AVAILABLE;
5000 doc.forget(aDocument);
5001 return NS_OK;
5004 NS_IMETHODIMP
5005 nsDocShell::GetCurrentURI(nsIURI** aURI)
5007 NS_ENSURE_ARG_POINTER(aURI);
5009 nsCOMPtr<nsIURI> uri = mCurrentURI;
5010 uri.forget(aURI);
5011 return NS_OK;
5014 NS_IMETHODIMP
5015 nsDocShell::GetReferringURI(nsIURI** aURI)
5017 NS_ENSURE_ARG_POINTER(aURI);
5019 *aURI = mReferrerURI;
5020 NS_IF_ADDREF(*aURI);
5022 return NS_OK;
5025 NS_IMETHODIMP
5026 nsDocShell::InitSessionHistory()
5028 MOZ_ASSERT(!mIsBeingDestroyed);
5030 // Make sure that we are the root DocShell, and set a handle to root docshell
5031 // in the session history.
5032 nsCOMPtr<nsIDocShellTreeItem> root;
5033 GetSameTypeRootTreeItem(getter_AddRefs(root));
5034 if (root != this) {
5035 return NS_ERROR_FAILURE;
5038 mSessionHistory = new ChildSHistory(this);
5039 return NS_OK;
5042 NS_IMETHODIMP
5043 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory)
5045 NS_ENSURE_ARG_POINTER(aSessionHistory);
5046 RefPtr<ChildSHistory> shistory = mSessionHistory;
5047 shistory.forget(aSessionHistory);
5048 return NS_OK;
5051 //*****************************************************************************
5052 // nsDocShell::nsIWebPageDescriptor
5053 //*****************************************************************************
5055 NS_IMETHODIMP
5056 nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType)
5058 nsCOMPtr<nsISHEntry> shEntryIn(do_QueryInterface(aPageDescriptor));
5060 // Currently, the opaque 'page descriptor' is an nsISHEntry...
5061 if (!shEntryIn) {
5062 return NS_ERROR_INVALID_POINTER;
5065 // Now clone shEntryIn, since we might end up modifying it later on, and we
5066 // want a page descriptor to be reusable.
5067 nsCOMPtr<nsISHEntry> shEntry;
5068 nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
5069 NS_ENSURE_SUCCESS(rv, rv);
5071 // Give our cloned shEntry a new bfcache entry so this load is independent
5072 // of all other loads. (This is important, in particular, for bugs 582795
5073 // and 585298.)
5074 rv = shEntry->AbandonBFCacheEntry();
5075 NS_ENSURE_SUCCESS(rv, rv);
5078 // load the page as view-source
5080 if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) {
5081 nsCOMPtr<nsIURI> oldUri, newUri;
5082 nsCString spec, newSpec;
5084 // Create a new view-source URI and replace the original.
5085 rv = shEntry->GetURI(getter_AddRefs(oldUri));
5086 if (NS_FAILED(rv)) {
5087 return rv;
5090 oldUri->GetSpec(spec);
5091 newSpec.AppendLiteral("view-source:");
5092 newSpec.Append(spec);
5094 rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
5095 if (NS_FAILED(rv)) {
5096 return rv;
5098 shEntry->SetURI(newUri);
5099 shEntry->SetOriginalURI(nullptr);
5100 shEntry->SetResultPrincipalURI(nullptr);
5101 // shEntry's current triggering principal is whoever loaded that page initially.
5102 // But now we're doing another load of the page, via an API that is only exposed
5103 // to system code. The triggering principal for this load should be the system
5104 // principal.
5105 shEntry->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
5108 rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
5109 return rv;
5112 NS_IMETHODIMP
5113 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor)
5115 MOZ_ASSERT(aPageDescriptor, "Null out param?");
5117 *aPageDescriptor = nullptr;
5119 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
5120 if (src) {
5121 nsCOMPtr<nsISHEntry> dest;
5123 nsresult rv = src->Clone(getter_AddRefs(dest));
5124 if (NS_FAILED(rv)) {
5125 return rv;
5128 // null out inappropriate cloned attributes...
5129 dest->SetParent(nullptr);
5130 dest->SetIsSubFrame(false);
5132 return CallQueryInterface(dest, aPageDescriptor);
5135 return NS_ERROR_NOT_AVAILABLE;
5138 //*****************************************************************************
5139 // nsDocShell::nsIBaseWindow
5140 //*****************************************************************************
5142 NS_IMETHODIMP
5143 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
5144 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
5145 int32_t aWidth, int32_t aHeight)
5147 SetParentWidget(aParentWidget);
5148 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
5150 return NS_OK;
5153 NS_IMETHODIMP
5154 nsDocShell::Create()
5156 if (mCreated) {
5157 // We've already been created
5158 return NS_OK;
5161 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5162 "Unexpected item type in docshell");
5164 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
5165 mCreated = true;
5167 if (gValidateOrigin == 0xffffffff) {
5168 // Check pref to see if we should prevent frameset spoofing
5169 gValidateOrigin =
5170 Preferences::GetBool("browser.frame.validate_origin", true);
5173 // Should we use XUL error pages instead of alerts if possible?
5174 mUseErrorPages =
5175 Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
5177 if (!gAddedPreferencesVarCache) {
5178 Preferences::AddBoolVarCache(&sUseErrorPages,
5179 "browser.xul.error_pages.enabled",
5180 mUseErrorPages);
5181 gAddedPreferencesVarCache = true;
5184 mDisableMetaRefreshWhenInactive =
5185 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
5186 mDisableMetaRefreshWhenInactive);
5188 mDeviceSizeIsPageSize =
5189 Preferences::GetBool("docshell.device_size_is_page_size",
5190 mDeviceSizeIsPageSize);
5192 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5193 if (serv) {
5194 const char* msg = mItemType == typeContent ?
5195 NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE;
5196 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5199 return NS_OK;
5202 NS_IMETHODIMP
5203 nsDocShell::Destroy()
5205 // XXX: We allow this function to be called just once. If you are going to
5206 // reset new variables in this function, please make sure the variables will
5207 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
5208 // in the setter functions for the variables would be enough.
5209 if (mIsBeingDestroyed) {
5210 return NS_ERROR_DOCSHELL_DYING;
5213 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
5214 "Unexpected item type in docshell");
5216 AssertOriginAttributesMatchPrivateBrowsing();
5218 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
5219 if (serv) {
5220 const char* msg = mItemType == typeContent ?
5221 NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY;
5222 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
5225 mIsBeingDestroyed = true;
5227 // Brak the cycle with the initial client, if present.
5228 mInitialClientSource.reset();
5230 // Make sure we don't record profile timeline markers anymore
5231 SetRecordProfileTimelineMarkers(false);
5233 // Remove our pref observers
5234 if (mObserveErrorPages) {
5235 mObserveErrorPages = false;
5238 // Make sure to blow away our mLoadingURI just in case. No loads
5239 // from inside this pagehide.
5240 mLoadingURI = nullptr;
5242 // Fire unload event before we blow anything away.
5243 (void)FirePageHideNotification(true);
5245 // Clear pointers to any detached nsEditorData that's lying
5246 // around in shistory entries. Breaks cycle. See bug 430921.
5247 if (mOSHE) {
5248 mOSHE->SetEditorData(nullptr);
5250 if (mLSHE) {
5251 mLSHE->SetEditorData(nullptr);
5254 // Note: mContentListener can be null if Init() failed and we're being
5255 // called from the destructor.
5256 if (mContentListener) {
5257 mContentListener->DropDocShellReference();
5258 mContentListener->SetParentContentListener(nullptr);
5259 // Note that we do NOT set mContentListener to null here; that
5260 // way if someone tries to do a load in us after this point
5261 // the nsDSURIContentListener will block it. All of which
5262 // means that we should do this before calling Stop(), of
5263 // course.
5266 // Stop any URLs that are currently being loaded...
5267 Stop(nsIWebNavigation::STOP_ALL);
5269 mEditorData = nullptr;
5271 // Save the state of the current document, before destroying the window.
5272 // This is needed to capture the state of a frameset when the new document
5273 // causes the frameset to be destroyed...
5274 PersistLayoutHistoryState();
5276 // Remove this docshell from its parent's child list
5277 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
5278 do_QueryInterface(GetAsSupports(mParent));
5279 if (docShellParentAsItem) {
5280 docShellParentAsItem->RemoveChild(this);
5283 if (mContentViewer) {
5284 mContentViewer->Close(nullptr);
5285 mContentViewer->Destroy();
5286 mContentViewer = nullptr;
5289 nsDocLoader::Destroy();
5291 mParentWidget = nullptr;
5292 mCurrentURI = nullptr;
5294 if (mScriptGlobal) {
5295 mScriptGlobal->DetachFromDocShell();
5296 mScriptGlobal = nullptr;
5299 if (mSessionHistory) {
5300 // We want to destroy these content viewers now rather than
5301 // letting their destruction wait for the session history
5302 // entries to get garbage collected. (Bug 488394)
5303 mSessionHistory->EvictLocalContentViewers();
5304 mSessionHistory = nullptr;
5307 SetTreeOwner(nullptr);
5309 mOnePermittedSandboxedNavigator = nullptr;
5311 // required to break ref cycle
5312 mSecurityUI = nullptr;
5314 // Cancel any timers that were set for this docshell; this is needed
5315 // to break the cycle between us and the timers.
5316 CancelRefreshURITimers();
5318 if (UsePrivateBrowsing()) {
5319 mPrivateBrowsingId = 0;
5320 mOriginAttributes.SyncAttributesWithPrivateBrowsing(false);
5321 if (mAffectPrivateSessionLifetime) {
5322 DecreasePrivateDocShellCount();
5326 return NS_OK;
5329 NS_IMETHODIMP
5330 nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale)
5332 if (mParentWidget) {
5333 *aScale = mParentWidget->GetDefaultScale().scale;
5334 return NS_OK;
5337 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5338 if (ownerWindow) {
5339 return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale);
5342 *aScale = 1.0;
5343 return NS_OK;
5346 NS_IMETHODIMP
5347 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale)
5349 if (mParentWidget) {
5350 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
5351 return NS_OK;
5354 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5355 if (ownerWindow) {
5356 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
5359 *aScale = 1.0;
5360 return NS_OK;
5363 NS_IMETHODIMP
5364 nsDocShell::SetPosition(int32_t aX, int32_t aY)
5366 mBounds.MoveTo(aX, aY);
5368 if (mContentViewer) {
5369 NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
5372 return NS_OK;
5375 NS_IMETHODIMP
5376 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY)
5378 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
5379 if (ownerWindow) {
5380 return ownerWindow->SetPositionDesktopPix(aX, aY);
5383 double scale = 1.0;
5384 GetDevicePixelsPerDesktopPixel(&scale);
5385 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
5388 NS_IMETHODIMP
5389 nsDocShell::GetPosition(int32_t* aX, int32_t* aY)
5391 return GetPositionAndSize(aX, aY, nullptr, nullptr);
5394 NS_IMETHODIMP
5395 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint)
5397 int32_t x = 0, y = 0;
5398 GetPosition(&x, &y);
5399 return SetPositionAndSize(x, y, aWidth, aHeight,
5400 aRepaint ? nsIBaseWindow::eRepaint : 0);
5403 NS_IMETHODIMP
5404 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight)
5406 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
5409 NS_IMETHODIMP
5410 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
5411 int32_t aHeight, uint32_t aFlags)
5413 mBounds.SetRect(aX, aY, aWidth, aHeight);
5415 // Hold strong ref, since SetBounds can make us null out mContentViewer
5416 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
5417 if (viewer) {
5418 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize) ?
5419 nsIContentViewer::eDelayResize : 0;
5420 // XXX Border figured in here or is that handled elsewhere?
5421 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
5422 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
5425 return NS_OK;
5428 NS_IMETHODIMP
5429 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
5430 int32_t* aHeight)
5432 if (mParentWidget) {
5433 // ensure size is up-to-date if window has changed resolution
5434 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
5435 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
5438 // We should really consider just getting this information from
5439 // our window instead of duplicating the storage and code...
5440 if (aWidth || aHeight) {
5441 // Caller wants to know our size; make sure to give them up to
5442 // date information.
5443 nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(mParent)));
5444 if (doc) {
5445 doc->FlushPendingNotifications(FlushType::Layout);
5449 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
5450 return NS_OK;
5453 void
5454 nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
5455 int32_t* aHeight)
5457 if (aX) {
5458 *aX = mBounds.X();
5460 if (aY) {
5461 *aY = mBounds.Y();
5463 if (aWidth) {
5464 *aWidth = mBounds.Width();
5466 if (aHeight) {
5467 *aHeight = mBounds.Height();
5471 NS_IMETHODIMP
5472 nsDocShell::Repaint(bool aForce)
5474 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5475 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5477 nsViewManager* viewManager = presShell->GetViewManager();
5478 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
5480 viewManager->InvalidateAllViews();
5481 return NS_OK;
5484 NS_IMETHODIMP
5485 nsDocShell::GetParentWidget(nsIWidget** aParentWidget)
5487 NS_ENSURE_ARG_POINTER(aParentWidget);
5489 *aParentWidget = mParentWidget;
5490 NS_IF_ADDREF(*aParentWidget);
5492 return NS_OK;
5495 NS_IMETHODIMP
5496 nsDocShell::SetParentWidget(nsIWidget* aParentWidget)
5498 MOZ_ASSERT(!mIsBeingDestroyed);
5499 mParentWidget = aParentWidget;
5501 return NS_OK;
5504 NS_IMETHODIMP
5505 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow)
5507 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
5509 if (mParentWidget) {
5510 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
5511 } else {
5512 *aParentNativeWindow = nullptr;
5515 return NS_OK;
5518 NS_IMETHODIMP
5519 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow)
5521 return NS_ERROR_NOT_IMPLEMENTED;
5524 NS_IMETHODIMP
5525 nsDocShell::GetNativeHandle(nsAString& aNativeHandle)
5527 // the nativeHandle should be accessed from nsIXULWindow
5528 return NS_ERROR_NOT_IMPLEMENTED;
5531 NS_IMETHODIMP
5532 nsDocShell::GetVisibility(bool* aVisibility)
5534 NS_ENSURE_ARG_POINTER(aVisibility);
5536 *aVisibility = false;
5538 if (!mContentViewer) {
5539 return NS_OK;
5542 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5543 if (!presShell) {
5544 return NS_OK;
5547 // get the view manager
5548 nsViewManager* vm = presShell->GetViewManager();
5549 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
5551 // get the root view
5552 nsView* view = vm->GetRootView(); // views are not ref counted
5553 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
5555 // if our root view is hidden, we are not visible
5556 if (view->GetVisibility() == nsViewVisibility_kHide) {
5557 return NS_OK;
5560 // otherwise, we must walk up the document and view trees checking
5561 // for a hidden view, unless we're an off screen browser, which
5562 // would make this test meaningless.
5564 RefPtr<nsDocShell> docShell = this;
5565 RefPtr<nsDocShell> parentItem = docShell->GetParentDocshell();
5566 while (parentItem) {
5567 presShell = docShell->GetPresShell();
5569 nsCOMPtr<nsIPresShell> pPresShell = parentItem->GetPresShell();
5571 // Null-check for crash in bug 267804
5572 if (!pPresShell) {
5573 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
5574 return NS_OK;
5577 vm = presShell->GetViewManager();
5578 if (vm) {
5579 view = vm->GetRootView();
5582 if (view) {
5583 view = view->GetParent(); // anonymous inner view
5584 if (view) {
5585 view = view->GetParent(); // subdocumentframe's view
5589 nsIFrame* frame = view ? view->GetFrame() : nullptr;
5590 bool isDocShellOffScreen = false;
5591 docShell->GetIsOffScreenBrowser(&isDocShellOffScreen);
5592 if (frame &&
5593 !frame->IsVisibleConsideringAncestors(
5594 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) &&
5595 !isDocShellOffScreen) {
5596 return NS_OK;
5599 docShell = parentItem;
5600 parentItem = docShell->GetParentDocshell();
5603 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5604 if (!treeOwnerAsWin) {
5605 *aVisibility = true;
5606 return NS_OK;
5609 // Check with the tree owner as well to give embedders a chance to
5610 // expose visibility as well.
5611 return treeOwnerAsWin->GetVisibility(aVisibility);
5614 NS_IMETHODIMP
5615 nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen)
5617 mIsOffScreenBrowser = aIsOffScreen;
5618 return NS_OK;
5621 NS_IMETHODIMP
5622 nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen)
5624 *aIsOffScreen = mIsOffScreenBrowser;
5625 return NS_OK;
5628 NS_IMETHODIMP
5629 nsDocShell::SetIsActive(bool aIsActive)
5631 // We disallow setting active on chrome docshells.
5632 if (mItemType == nsIDocShellTreeItem::typeChrome) {
5633 return NS_ERROR_INVALID_ARG;
5636 // Keep track ourselves.
5637 mIsActive = aIsActive;
5639 // Tell the PresShell about it.
5640 nsCOMPtr<nsIPresShell> pshell = GetPresShell();
5641 if (pshell) {
5642 pshell->SetIsActive(aIsActive);
5645 // Tell the window about it
5646 if (mScriptGlobal) {
5647 mScriptGlobal->SetIsBackground(!aIsActive);
5648 if (nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc()) {
5649 // Update orientation when the top-level browsing context becomes active.
5650 if (aIsActive) {
5651 nsCOMPtr<nsIDocShellTreeItem> parent;
5652 GetSameTypeParent(getter_AddRefs(parent));
5653 if (!parent) {
5654 // We only care about the top-level browsing context.
5655 uint16_t orientation = OrientationLock();
5656 ScreenOrientation::UpdateActiveOrientationLock(orientation);
5660 doc->PostVisibilityUpdateEvent();
5664 // Tell the nsDOMNavigationTiming about it
5665 RefPtr<nsDOMNavigationTiming> timing = mTiming;
5666 if (!timing && mContentViewer) {
5667 nsIDocument* doc = mContentViewer->GetDocument();
5668 if (doc) {
5669 timing = doc->GetNavigationTiming();
5672 if (timing) {
5673 timing->NotifyDocShellStateChanged(
5674 aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
5675 : nsDOMNavigationTiming::DocShellState::eInactive);
5678 // Recursively tell all of our children, but don't tell <iframe mozbrowser>
5679 // children; they handle their state separately.
5680 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5681 while (iter.HasMore()) {
5682 nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5683 if (!docshell) {
5684 continue;
5687 if (!docshell->GetIsMozBrowser()) {
5688 docshell->SetIsActive(aIsActive);
5692 // Restart or stop meta refresh timers if necessary
5693 if (mDisableMetaRefreshWhenInactive) {
5694 if (mIsActive) {
5695 ResumeRefreshURIs();
5696 } else {
5697 SuspendRefreshURIs();
5701 return NS_OK;
5704 NS_IMETHODIMP
5705 nsDocShell::GetIsActive(bool* aIsActive)
5707 *aIsActive = mIsActive;
5708 return NS_OK;
5711 NS_IMETHODIMP
5712 nsDocShell::SetIsAppTab(bool aIsAppTab)
5714 mIsAppTab = aIsAppTab;
5715 return NS_OK;
5718 NS_IMETHODIMP
5719 nsDocShell::GetIsAppTab(bool* aIsAppTab)
5721 *aIsAppTab = mIsAppTab;
5722 return NS_OK;
5725 NS_IMETHODIMP
5726 nsDocShell::SetSandboxFlags(uint32_t aSandboxFlags)
5728 mSandboxFlags = aSandboxFlags;
5729 return NS_OK;
5732 NS_IMETHODIMP
5733 nsDocShell::GetSandboxFlags(uint32_t* aSandboxFlags)
5735 *aSandboxFlags = mSandboxFlags;
5736 return NS_OK;
5739 NS_IMETHODIMP
5740 nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
5742 if (mOnePermittedSandboxedNavigator) {
5743 NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
5744 return NS_OK;
5747 MOZ_ASSERT(!mIsBeingDestroyed);
5749 mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
5750 NS_ASSERTION(mOnePermittedSandboxedNavigator,
5751 "One Permitted Sandboxed Navigator must support weak references.");
5753 return NS_OK;
5756 NS_IMETHODIMP
5757 nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
5759 NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
5760 nsCOMPtr<nsIDocShell> permittedNavigator =
5761 do_QueryReferent(mOnePermittedSandboxedNavigator);
5762 permittedNavigator.forget(aSandboxedNavigator);
5763 return NS_OK;
5766 NS_IMETHODIMP
5767 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
5769 mDefaultLoadFlags = aDefaultLoadFlags;
5771 // Tell the load group to set these flags all requests in the group
5772 if (mLoadGroup) {
5773 mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
5774 } else {
5775 NS_WARNING("nsDocShell::SetDefaultLoadFlags has no loadGroup to propagate the flags to");
5778 // Recursively tell all of our children. We *do not* skip
5779 // <iframe mozbrowser> children - if someone sticks custom flags in this
5780 // docShell then they too get the same flags.
5781 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
5782 while (iter.HasMore()) {
5783 nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
5784 if (!docshell) {
5785 continue;
5787 docshell->SetDefaultLoadFlags(aDefaultLoadFlags);
5789 return NS_OK;
5792 NS_IMETHODIMP
5793 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags)
5795 *aDefaultLoadFlags = mDefaultLoadFlags;
5796 return NS_OK;
5799 NS_IMETHODIMP
5800 nsDocShell::SetMixedContentChannel(nsIChannel* aMixedContentChannel)
5802 #ifdef DEBUG
5803 // if the channel is non-null
5804 if (aMixedContentChannel) {
5805 // Get the root docshell.
5806 nsCOMPtr<nsIDocShellTreeItem> root;
5807 GetSameTypeRootTreeItem(getter_AddRefs(root));
5808 NS_WARNING_ASSERTION(root.get() == static_cast<nsIDocShellTreeItem*>(this),
5809 "Setting mMixedContentChannel on a docshell that is "
5810 "not the root docshell");
5812 #endif
5813 mMixedContentChannel = aMixedContentChannel;
5814 return NS_OK;
5817 NS_IMETHODIMP
5818 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel)
5820 NS_ENSURE_ARG_POINTER(aFailedChannel);
5821 nsIDocument* doc = GetDocument();
5822 if (!doc) {
5823 *aFailedChannel = nullptr;
5824 return NS_OK;
5826 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
5827 return NS_OK;
5830 NS_IMETHODIMP
5831 nsDocShell::GetMixedContentChannel(nsIChannel** aMixedContentChannel)
5833 NS_ENSURE_ARG_POINTER(aMixedContentChannel);
5834 NS_IF_ADDREF(*aMixedContentChannel = mMixedContentChannel);
5835 return NS_OK;
5838 NS_IMETHODIMP
5839 nsDocShell::GetAllowMixedContentAndConnectionData(bool* aRootHasSecureConnection,
5840 bool* aAllowMixedContent,
5841 bool* aIsRootDocShell)
5843 *aRootHasSecureConnection = true;
5844 *aAllowMixedContent = false;
5845 *aIsRootDocShell = false;
5847 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
5848 GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
5849 NS_ASSERTION(sameTypeRoot,
5850 "No document shell root tree item from document shell tree item!");
5851 *aIsRootDocShell =
5852 sameTypeRoot.get() == static_cast<nsIDocShellTreeItem*>(this);
5854 // now get the document from sameTypeRoot
5855 nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
5856 if (rootDoc) {
5857 nsCOMPtr<nsIPrincipal> rootPrincipal = rootDoc->NodePrincipal();
5859 // For things with system principal (e.g. scratchpad) there is no uri
5860 // aRootHasSecureConnection should be false.
5861 nsCOMPtr<nsIURI> rootUri;
5862 if (nsContentUtils::IsSystemPrincipal(rootPrincipal) ||
5863 NS_FAILED(rootPrincipal->GetURI(getter_AddRefs(rootUri))) || !rootUri ||
5864 NS_FAILED(rootUri->SchemeIs("https", aRootHasSecureConnection))) {
5865 *aRootHasSecureConnection = false;
5868 // Check the root doc's channel against the root docShell's
5869 // mMixedContentChannel to see if they are the same. If they are the same,
5870 // the user has overriden the block.
5871 nsCOMPtr<nsIDocShell> rootDocShell = do_QueryInterface(sameTypeRoot);
5872 nsCOMPtr<nsIChannel> mixedChannel;
5873 rootDocShell->GetMixedContentChannel(getter_AddRefs(mixedChannel));
5874 *aAllowMixedContent =
5875 mixedChannel && (mixedChannel == rootDoc->GetChannel());
5878 return NS_OK;
5881 NS_IMETHODIMP
5882 nsDocShell::SetVisibility(bool aVisibility)
5884 // Show()/Hide() may change mContentViewer.
5885 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
5886 if (!cv) {
5887 return NS_OK;
5889 if (aVisibility) {
5890 cv->Show();
5891 } else {
5892 cv->Hide();
5895 return NS_OK;
5898 NS_IMETHODIMP
5899 nsDocShell::GetEnabled(bool* aEnabled)
5901 NS_ENSURE_ARG_POINTER(aEnabled);
5902 *aEnabled = true;
5903 return NS_ERROR_NOT_IMPLEMENTED;
5906 NS_IMETHODIMP
5907 nsDocShell::SetEnabled(bool aEnabled)
5909 return NS_ERROR_NOT_IMPLEMENTED;
5912 NS_IMETHODIMP
5913 nsDocShell::SetFocus()
5915 return NS_OK;
5918 NS_IMETHODIMP
5919 nsDocShell::GetMainWidget(nsIWidget** aMainWidget)
5921 // We don't create our own widget, so simply return the parent one.
5922 return GetParentWidget(aMainWidget);
5925 NS_IMETHODIMP
5926 nsDocShell::GetTitle(nsAString& aTitle)
5928 aTitle = mTitle;
5929 return NS_OK;
5932 NS_IMETHODIMP
5933 nsDocShell::SetTitle(const nsAString& aTitle)
5935 // Store local title
5936 mTitle = aTitle;
5938 nsCOMPtr<nsIDocShellTreeItem> parent;
5939 GetSameTypeParent(getter_AddRefs(parent));
5941 // When title is set on the top object it should then be passed to the
5942 // tree owner.
5943 if (!parent) {
5944 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5945 if (treeOwnerAsWin) {
5946 treeOwnerAsWin->SetTitle(aTitle);
5950 AssertOriginAttributesMatchPrivateBrowsing();
5951 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
5952 UpdateGlobalHistoryTitle(mCurrentURI);
5955 // Update SessionHistory with the document's title.
5956 if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
5957 mLoadType != LOAD_ERROR_PAGE) {
5958 mOSHE->SetTitle(mTitle);
5961 return NS_OK;
5964 nsresult
5965 nsDocShell::GetCurScrollPos(int32_t aScrollOrientation, int32_t* aCurPos)
5967 NS_ENSURE_ARG_POINTER(aCurPos);
5969 nsIScrollableFrame* sf = GetRootScrollFrame();
5970 if (!sf) {
5971 return NS_ERROR_FAILURE;
5974 nsPoint pt = sf->GetScrollPosition();
5976 switch (aScrollOrientation) {
5977 case ScrollOrientation_X:
5978 *aCurPos = pt.x;
5979 return NS_OK;
5981 case ScrollOrientation_Y:
5982 *aCurPos = pt.y;
5983 return NS_OK;
5985 default:
5986 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
5990 nsresult
5991 nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
5992 int32_t aCurVerticalPos)
5994 nsIScrollableFrame* sf = GetRootScrollFrame();
5995 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5997 nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
5998 if (sf->GetScrollbarStyles().mScrollBehavior ==
5999 NS_STYLE_SCROLL_BEHAVIOR_SMOOTH) {
6000 scrollMode = nsIScrollableFrame::SMOOTH_MSD;
6003 sf->ScrollTo(nsPoint(aCurHorizontalPos, aCurVerticalPos), scrollMode);
6004 return NS_OK;
6007 //*****************************************************************************
6008 // nsDocShell::nsIScrollable
6009 //*****************************************************************************
6011 NS_IMETHODIMP
6012 nsDocShell::GetDefaultScrollbarPreferences(int32_t aScrollOrientation,
6013 int32_t* aScrollbarPref)
6015 NS_ENSURE_ARG_POINTER(aScrollbarPref);
6016 switch (aScrollOrientation) {
6017 case ScrollOrientation_X:
6018 *aScrollbarPref = mDefaultScrollbarPref.x;
6019 return NS_OK;
6021 case ScrollOrientation_Y:
6022 *aScrollbarPref = mDefaultScrollbarPref.y;
6023 return NS_OK;
6025 default:
6026 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
6028 return NS_ERROR_FAILURE;
6031 NS_IMETHODIMP
6032 nsDocShell::SetDefaultScrollbarPreferences(int32_t aScrollOrientation,
6033 int32_t aScrollbarPref)
6035 switch (aScrollOrientation) {
6036 case ScrollOrientation_X:
6037 mDefaultScrollbarPref.x = aScrollbarPref;
6038 return NS_OK;
6040 case ScrollOrientation_Y:
6041 mDefaultScrollbarPref.y = aScrollbarPref;
6042 return NS_OK;
6044 default:
6045 NS_ENSURE_TRUE(false, NS_ERROR_INVALID_ARG);
6047 return NS_ERROR_FAILURE;
6050 NS_IMETHODIMP
6051 nsDocShell::GetScrollbarVisibility(bool* aVerticalVisible,
6052 bool* aHorizontalVisible)
6054 nsIScrollableFrame* sf = GetRootScrollFrame();
6055 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6057 uint32_t scrollbarVisibility = sf->GetScrollbarVisibility();
6058 if (aVerticalVisible) {
6059 *aVerticalVisible =
6060 (scrollbarVisibility & nsIScrollableFrame::VERTICAL) != 0;
6062 if (aHorizontalVisible) {
6063 *aHorizontalVisible =
6064 (scrollbarVisibility & nsIScrollableFrame::HORIZONTAL) != 0;
6067 return NS_OK;
6070 //*****************************************************************************
6071 // nsDocShell::nsITextScroll
6072 //*****************************************************************************
6074 NS_IMETHODIMP
6075 nsDocShell::ScrollByLines(int32_t aNumLines)
6077 nsIScrollableFrame* sf = GetRootScrollFrame();
6078 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6080 sf->ScrollBy(nsIntPoint(0, aNumLines), nsIScrollableFrame::LINES,
6081 nsIScrollableFrame::SMOOTH);
6082 return NS_OK;
6085 NS_IMETHODIMP
6086 nsDocShell::ScrollByPages(int32_t aNumPages)
6088 nsIScrollableFrame* sf = GetRootScrollFrame();
6089 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
6091 sf->ScrollBy(nsIntPoint(0, aNumPages), nsIScrollableFrame::PAGES,
6092 nsIScrollableFrame::SMOOTH);
6093 return NS_OK;
6096 //*****************************************************************************
6097 // nsDocShell::nsIRefreshURI
6098 //*****************************************************************************
6100 NS_IMETHODIMP
6101 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
6102 int32_t aDelay, bool aRepeat,
6103 bool aMetaRefresh)
6105 MOZ_ASSERT(!mIsBeingDestroyed);
6107 NS_ENSURE_ARG(aURI);
6109 /* Check if Meta refresh/redirects are permitted. Some
6110 * embedded applications may not want to do this.
6111 * Must do this before sending out NOTIFY_REFRESH events
6112 * because listeners may have side effects (e.g. displaying a
6113 * button to manually trigger the refresh later).
6115 bool allowRedirects = true;
6116 GetAllowMetaRedirects(&allowRedirects);
6117 if (!allowRedirects) {
6118 return NS_OK;
6121 // If any web progress listeners are listening for NOTIFY_REFRESH events,
6122 // give them a chance to block this refresh.
6123 bool sameURI;
6124 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
6125 if (NS_FAILED(rv)) {
6126 sameURI = false;
6128 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
6129 return NS_OK;
6132 nsCOMPtr<nsITimerCallback> refreshTimer =
6133 new nsRefreshTimer(this, aURI, aPrincipal, aDelay, aRepeat, aMetaRefresh);
6135 uint32_t busyFlags = 0;
6136 GetBusyFlags(&busyFlags);
6138 if (!mRefreshURIList) {
6139 mRefreshURIList = nsArray::Create();
6142 if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
6143 // We don't want to create the timer right now. Instead queue up the request
6144 // and trigger the timer in EndPageLoad() or whenever we become active.
6145 mRefreshURIList->AppendElement(refreshTimer);
6146 } else {
6147 // There is no page loading going on right now. Create the
6148 // timer and fire it right away.
6149 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
6150 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
6152 nsCOMPtr<nsITimer> timer;
6153 MOZ_TRY_VAR(timer,
6154 NS_NewTimerWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT,
6155 win->TabGroup()->EventTargetFor(TaskCategory::Network)));
6157 mRefreshURIList->AppendElement(timer); // owning timer ref
6159 return NS_OK;
6162 nsresult
6163 nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
6164 nsIPrincipal* aPrincipal,
6165 int32_t aDelay,
6166 bool aMetaRefresh,
6167 nsITimer* aTimer)
6169 MOZ_ASSERT(aTimer, "Must have a timer here");
6171 // Remove aTimer from mRefreshURIList if needed
6172 if (mRefreshURIList) {
6173 uint32_t n = 0;
6174 mRefreshURIList->GetLength(&n);
6176 for (uint32_t i = 0; i < n; ++i) {
6177 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6178 if (timer == aTimer) {
6179 mRefreshURIList->RemoveElementAt(i);
6180 break;
6185 return ForceRefreshURI(aURI, aPrincipal, aDelay, aMetaRefresh);
6188 NS_IMETHODIMP
6189 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDelay, bool aMetaRefresh)
6191 NS_ENSURE_ARG(aURI);
6193 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
6194 CreateLoadInfo(getter_AddRefs(loadInfo));
6195 NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
6197 /* We do need to pass in a referrer, but we don't want it to
6198 * be sent to the server.
6200 loadInfo->SetSendReferrer(false);
6202 /* for most refreshes the current URI is an appropriate
6203 * internal referrer
6205 loadInfo->SetReferrer(mCurrentURI);
6207 // Set the triggering pricipal to aPrincipal if available, or current
6208 // document's principal otherwise.
6209 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
6210 if (!principal) {
6211 nsCOMPtr<nsIDocument> doc = GetDocument();
6212 if (!doc) {
6213 return NS_ERROR_FAILURE;
6215 principal = doc->NodePrincipal();
6217 loadInfo->SetTriggeringPrincipal(principal);
6218 loadInfo->SetPrincipalIsExplicit(true);
6220 /* Check if this META refresh causes a redirection
6221 * to another site.
6223 bool equalUri = false;
6224 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
6225 if (NS_SUCCEEDED(rv) && (!equalUri) && aMetaRefresh &&
6226 aDelay <= REFRESH_REDIRECT_TIMER) {
6227 /* It is a META refresh based redirection within the threshold time
6228 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
6229 * Pass a REPLACE flag to LoadURI().
6231 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadNormalReplace);
6233 /* for redirects we mimic HTTP, which passes the
6234 * original referrer
6236 nsCOMPtr<nsIURI> internalReferrer;
6237 GetReferringURI(getter_AddRefs(internalReferrer));
6238 if (internalReferrer) {
6239 loadInfo->SetReferrer(internalReferrer);
6241 } else {
6242 loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
6246 * LoadURI(...) will cancel all refresh timers... This causes the
6247 * Timer and its refreshData instance to be released...
6249 LoadURI(aURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, true);
6251 return NS_OK;
6254 nsresult
6255 nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
6256 nsIPrincipal* aPrincipal,
6257 const nsACString& aHeader)
6259 // Refresh headers are parsed with the following format in mind
6260 // <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
6261 // By the time we are here, the following is true:
6262 // header = "REFRESH"
6263 // content = "5; URL=http://uri" // note the URL attribute is
6264 // optional, if it is absent, the currently loaded url is used.
6265 // Also note that the seconds and URL separator can be either
6266 // a ';' or a ','. The ',' separator should be illegal but CNN
6267 // is using it.
6269 // We need to handle the following strings, where
6270 // - X is a set of digits
6271 // - URI is either a relative or absolute URI
6273 // Note that URI should start with "url=" but we allow omission
6275 // "" || ";" || ","
6276 // empty string. use the currently loaded URI
6277 // and refresh immediately.
6278 // "X" || "X;" || "X,"
6279 // Refresh the currently loaded URI in X seconds.
6280 // "X; URI" || "X, URI"
6281 // Refresh using URI as the destination in X seconds.
6282 // "URI" || "; URI" || ", URI"
6283 // Refresh immediately using URI as the destination.
6285 // Currently, anything immediately following the URI, if
6286 // separated by any char in the set "'\"\t\r\n " will be
6287 // ignored. So "10; url=go.html ; foo=bar" will work,
6288 // and so will "10; url='go.html'; foo=bar". However,
6289 // "10; url=go.html; foo=bar" will result in the uri
6290 // "go.html;" since ';' and ',' are valid uri characters.
6292 // Note that we need to remove any tokens wrapping the URI.
6293 // These tokens currently include spaces, double and single
6294 // quotes.
6296 // when done, seconds is 0 or the given number of seconds
6297 // uriAttrib is empty or the URI specified
6298 MOZ_ASSERT(aPrincipal);
6300 nsAutoCString uriAttrib;
6301 int32_t seconds = 0;
6302 bool specifiesSeconds = false;
6304 nsACString::const_iterator iter, tokenStart, doneIterating;
6306 aHeader.BeginReading(iter);
6307 aHeader.EndReading(doneIterating);
6309 // skip leading whitespace
6310 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6311 ++iter;
6314 tokenStart = iter;
6316 // skip leading + and -
6317 if (iter != doneIterating && (*iter == '-' || *iter == '+')) {
6318 ++iter;
6321 // parse number
6322 while (iter != doneIterating && (*iter >= '0' && *iter <= '9')) {
6323 seconds = seconds * 10 + (*iter - '0');
6324 specifiesSeconds = true;
6325 ++iter;
6328 if (iter != doneIterating) {
6329 // if we started with a '-', number is negative
6330 if (*tokenStart == '-') {
6331 seconds = -seconds;
6334 // skip to next ';' or ','
6335 nsACString::const_iterator iterAfterDigit = iter;
6336 while (iter != doneIterating && !(*iter == ';' || *iter == ',')) {
6337 if (specifiesSeconds) {
6338 // Non-whitespace characters here mean that the string is
6339 // malformed but tolerate sites that specify a decimal point,
6340 // even though meta refresh only works on whole seconds.
6341 if (iter == iterAfterDigit &&
6342 !nsCRT::IsAsciiSpace(*iter) && *iter != '.') {
6343 // The characters between the seconds and the next
6344 // section are just garbage!
6345 // e.g. content="2a0z+,URL=http://www.mozilla.org/"
6346 // Just ignore this redirect.
6347 return NS_ERROR_FAILURE;
6348 } else if (nsCRT::IsAsciiSpace(*iter)) {
6349 // We've had at least one whitespace so tolerate the mistake
6350 // and drop through.
6351 // e.g. content="10 foo"
6352 ++iter;
6353 break;
6356 ++iter;
6359 // skip any remaining whitespace
6360 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6361 ++iter;
6364 // skip ';' or ','
6365 if (iter != doneIterating && (*iter == ';' || *iter == ',')) {
6366 ++iter;
6369 // skip whitespace
6370 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6371 ++iter;
6375 // possible start of URI
6376 tokenStart = iter;
6378 // skip "url = " to real start of URI
6379 if (iter != doneIterating && (*iter == 'u' || *iter == 'U')) {
6380 ++iter;
6381 if (iter != doneIterating && (*iter == 'r' || *iter == 'R')) {
6382 ++iter;
6383 if (iter != doneIterating && (*iter == 'l' || *iter == 'L')) {
6384 ++iter;
6386 // skip whitespace
6387 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6388 ++iter;
6391 if (iter != doneIterating && *iter == '=') {
6392 ++iter;
6394 // skip whitespace
6395 while (iter != doneIterating && nsCRT::IsAsciiSpace(*iter)) {
6396 ++iter;
6399 // found real start of URI
6400 tokenStart = iter;
6406 // skip a leading '"' or '\''.
6408 bool isQuotedURI = false;
6409 if (tokenStart != doneIterating &&
6410 (*tokenStart == '"' || *tokenStart == '\'')) {
6411 isQuotedURI = true;
6412 ++tokenStart;
6415 // set iter to start of URI
6416 iter = tokenStart;
6418 // tokenStart here points to the beginning of URI
6420 // grab the rest of the URI
6421 while (iter != doneIterating) {
6422 if (isQuotedURI && (*iter == '"' || *iter == '\'')) {
6423 break;
6425 ++iter;
6428 // move iter one back if the last character is a '"' or '\''
6429 if (iter != tokenStart && isQuotedURI) {
6430 --iter;
6431 if (!(*iter == '"' || *iter == '\'')) {
6432 ++iter;
6436 // URI is whatever's contained from tokenStart to iter.
6437 // note: if tokenStart == doneIterating, so is iter.
6439 nsresult rv = NS_OK;
6441 nsCOMPtr<nsIURI> uri;
6442 bool specifiesURI = false;
6443 if (tokenStart == iter) {
6444 uri = aBaseURI;
6445 } else {
6446 uriAttrib = Substring(tokenStart, iter);
6447 // NS_NewURI takes care of any whitespace surrounding the URL
6448 rv = NS_NewURI(getter_AddRefs(uri), uriAttrib, nullptr, aBaseURI);
6449 specifiesURI = true;
6452 // No URI or seconds were specified
6453 if (!specifiesSeconds && !specifiesURI) {
6454 // Do nothing because the alternative is to spin around in a refresh
6455 // loop forever!
6456 return NS_ERROR_FAILURE;
6459 if (NS_SUCCEEDED(rv)) {
6460 nsCOMPtr<nsIScriptSecurityManager> securityManager(
6461 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
6462 if (NS_SUCCEEDED(rv)) {
6463 rv = securityManager->CheckLoadURIWithPrincipal(
6464 aPrincipal, uri,
6465 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT);
6467 if (NS_SUCCEEDED(rv)) {
6468 bool isjs = true;
6469 rv = NS_URIChainHasFlags(
6470 uri, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
6471 NS_ENSURE_SUCCESS(rv, rv);
6473 if (isjs) {
6474 return NS_ERROR_FAILURE;
6478 if (NS_SUCCEEDED(rv)) {
6479 // Since we can't travel back in time yet, just pretend
6480 // negative numbers do nothing at all.
6481 if (seconds < 0) {
6482 return NS_ERROR_FAILURE;
6485 rv = RefreshURI(uri, aPrincipal, seconds * 1000, false, true);
6489 return rv;
6492 NS_IMETHODIMP
6493 nsDocShell::SetupRefreshURI(nsIChannel* aChannel)
6495 nsresult rv;
6496 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel, &rv));
6497 if (NS_SUCCEEDED(rv)) {
6498 nsAutoCString refreshHeader;
6499 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
6500 refreshHeader);
6502 if (!refreshHeader.IsEmpty()) {
6503 nsCOMPtr<nsIScriptSecurityManager> secMan =
6504 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
6505 NS_ENSURE_SUCCESS(rv, rv);
6507 nsCOMPtr<nsIPrincipal> principal;
6508 rv = secMan->GetChannelResultPrincipal(aChannel,
6509 getter_AddRefs(principal));
6510 NS_ENSURE_SUCCESS(rv, rv);
6512 SetupReferrerFromChannel(aChannel);
6513 rv = SetupRefreshURIFromHeader(mCurrentURI, principal, refreshHeader);
6514 if (NS_SUCCEEDED(rv)) {
6515 return NS_REFRESHURI_HEADER_FOUND;
6519 return rv;
6522 static void
6523 DoCancelRefreshURITimers(nsIMutableArray* aTimerList)
6525 if (!aTimerList) {
6526 return;
6529 uint32_t n = 0;
6530 aTimerList->GetLength(&n);
6532 while (n) {
6533 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
6535 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
6537 if (timer) {
6538 timer->Cancel();
6543 NS_IMETHODIMP
6544 nsDocShell::CancelRefreshURITimers()
6546 DoCancelRefreshURITimers(mRefreshURIList);
6547 DoCancelRefreshURITimers(mSavedRefreshURIList);
6548 mRefreshURIList = nullptr;
6549 mSavedRefreshURIList = nullptr;
6551 return NS_OK;
6554 NS_IMETHODIMP
6555 nsDocShell::GetRefreshPending(bool* aResult)
6557 if (!mRefreshURIList) {
6558 *aResult = false;
6559 return NS_OK;
6562 uint32_t count;
6563 nsresult rv = mRefreshURIList->GetLength(&count);
6564 if (NS_SUCCEEDED(rv)) {
6565 *aResult = (count != 0);
6567 return rv;
6570 NS_IMETHODIMP
6571 nsDocShell::SuspendRefreshURIs()
6573 if (mRefreshURIList) {
6574 uint32_t n = 0;
6575 mRefreshURIList->GetLength(&n);
6577 for (uint32_t i = 0; i < n; ++i) {
6578 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
6579 if (!timer) {
6580 continue; // this must be a nsRefreshURI already
6583 // Replace this timer object with a nsRefreshTimer object.
6584 nsCOMPtr<nsITimerCallback> callback;
6585 timer->GetCallback(getter_AddRefs(callback));
6587 timer->Cancel();
6589 nsCOMPtr<nsITimerCallback> rt = do_QueryInterface(callback);
6590 NS_ASSERTION(rt,
6591 "RefreshURIList timer callbacks should only be RefreshTimer objects");
6593 mRefreshURIList->ReplaceElementAt(rt, i);
6597 // Suspend refresh URIs for our child shells as well.
6598 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6599 while (iter.HasMore()) {
6600 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6601 if (shell) {
6602 shell->SuspendRefreshURIs();
6606 return NS_OK;
6609 NS_IMETHODIMP
6610 nsDocShell::ResumeRefreshURIs()
6612 RefreshURIFromQueue();
6614 // Resume refresh URIs for our child shells as well.
6615 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
6616 while (iter.HasMore()) {
6617 nsCOMPtr<nsIDocShell> shell = do_QueryObject(iter.GetNext());
6618 if (shell) {
6619 shell->ResumeRefreshURIs();
6623 return NS_OK;
6626 nsresult
6627 nsDocShell::RefreshURIFromQueue()
6629 if (!mRefreshURIList) {
6630 return NS_OK;
6632 uint32_t n = 0;
6633 mRefreshURIList->GetLength(&n);
6635 while (n) {
6636 nsCOMPtr<nsITimerCallback> refreshInfo =
6637 do_QueryElementAt(mRefreshURIList, --n);
6639 if (refreshInfo) {
6640 // This is the nsRefreshTimer object, waiting to be
6641 // setup in a timer object and fired.
6642 // Create the timer and trigger it.
6643 uint32_t delay =
6644 static_cast<nsRefreshTimer*>(
6645 static_cast<nsITimerCallback*>(refreshInfo))->GetDelay();
6646 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
6647 if (win) {
6648 nsCOMPtr<nsITimer> timer;
6649 NS_NewTimerWithCallback(getter_AddRefs(timer),
6650 refreshInfo, delay, nsITimer::TYPE_ONE_SHOT,
6651 win->TabGroup()->EventTargetFor(TaskCategory::Network));
6653 if (timer) {
6654 // Replace the nsRefreshTimer element in the queue with
6655 // its corresponding timer object, so that in case another
6656 // load comes through before the timer can go off, the timer will
6657 // get cancelled in CancelRefreshURITimer()
6658 mRefreshURIList->ReplaceElementAt(timer, n);
6664 return NS_OK;
6667 nsresult
6668 nsDocShell::Embed(nsIContentViewer* aContentViewer,
6669 const char* aCommand, nsISupports* aExtraInfo)
6671 // Save the LayoutHistoryState of the previous document, before
6672 // setting up new document
6673 PersistLayoutHistoryState();
6675 nsresult rv = SetupNewViewer(aContentViewer);
6676 NS_ENSURE_SUCCESS(rv, rv);
6678 // If we are loading a wyciwyg url from history, change the base URI for
6679 // the document to the original http url that created the document.write().
6680 // This makes sure that all relative urls in a document.written page loaded
6681 // via history work properly.
6682 if (mCurrentURI &&
6683 (mLoadType & LOAD_CMD_HISTORY ||
6684 mLoadType == LOAD_RELOAD_NORMAL ||
6685 mLoadType == LOAD_RELOAD_CHARSET_CHANGE ||
6686 mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
6687 mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)) {
6688 bool isWyciwyg = false;
6689 // Check if the url is wyciwyg
6690 rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
6691 if (isWyciwyg && NS_SUCCEEDED(rv)) {
6692 SetBaseUrlForWyciwyg(aContentViewer);
6695 // XXX What if SetupNewViewer fails?
6696 if (mLSHE) {
6697 // Restore the editing state, if it's stored in session history.
6698 if (mLSHE->HasDetachedEditor()) {
6699 ReattachEditorToWindow(mLSHE);
6701 // Set history.state
6702 SetDocCurrentStateObj(mLSHE);
6704 SetHistoryEntry(&mOSHE, mLSHE);
6707 bool updateHistory = true;
6709 // Determine if this type of load should update history
6710 switch (mLoadType) {
6711 case LOAD_NORMAL_REPLACE:
6712 case LOAD_STOP_CONTENT_AND_REPLACE:
6713 case LOAD_RELOAD_BYPASS_CACHE:
6714 case LOAD_RELOAD_BYPASS_PROXY:
6715 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
6716 case LOAD_REPLACE_BYPASS_CACHE:
6717 updateHistory = false;
6718 break;
6719 default:
6720 break;
6723 if (!updateHistory) {
6724 SetLayoutHistoryState(nullptr);
6727 return NS_OK;
6731 //*****************************************************************************
6732 // nsDocShell::nsIWebProgressListener
6733 //*****************************************************************************
6735 NS_IMETHODIMP
6736 nsDocShell::OnProgressChange(nsIWebProgress* aProgress,
6737 nsIRequest* aRequest,
6738 int32_t aCurSelfProgress,
6739 int32_t aMaxSelfProgress,
6740 int32_t aCurTotalProgress,
6741 int32_t aMaxTotalProgress)
6743 return NS_OK;
6746 NS_IMETHODIMP
6747 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
6748 uint32_t aStateFlags, nsresult aStatus)
6750 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
6751 // Save timing statistics.
6752 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6753 nsCOMPtr<nsIURI> uri;
6754 channel->GetURI(getter_AddRefs(uri));
6755 nsAutoCString aURI;
6756 uri->GetAsciiSpec(aURI);
6758 nsCOMPtr<nsIWyciwygChannel> wcwgChannel(do_QueryInterface(aRequest));
6759 nsCOMPtr<nsIWebProgress> webProgress =
6760 do_QueryInterface(GetAsSupports(this));
6762 // We don't update navigation timing for wyciwyg channels
6763 if (this == aProgress && !wcwgChannel) {
6764 mozilla::Unused << MaybeInitTiming();
6765 mTiming->NotifyFetchStart(uri,
6766 ConvertLoadTypeToNavigationType(mLoadType));
6769 // Was the wyciwyg document loaded on this docshell?
6770 if (wcwgChannel && !mLSHE && (mItemType == typeContent) &&
6771 aProgress == webProgress.get()) {
6772 bool equalUri = true;
6773 // Store the wyciwyg url in session history, only if it is
6774 // being loaded fresh for the first time. We don't want
6775 // multiple entries for successive loads
6776 if (mCurrentURI &&
6777 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
6778 !equalUri) {
6779 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
6780 GetSameTypeParent(getter_AddRefs(parentAsItem));
6781 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
6782 bool inOnLoadHandler = false;
6783 if (parentDS) {
6784 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
6786 if (inOnLoadHandler) {
6787 // We're handling parent's load event listener, which causes
6788 // document.write in a subdocument.
6789 // Need to clear the session history for all child
6790 // docshells so that we can handle them like they would
6791 // all be added dynamically.
6792 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
6793 if (parent) {
6794 bool oshe = false;
6795 nsCOMPtr<nsISHEntry> entry;
6796 parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
6797 static_cast<nsDocShell*>(parent.get())->ClearFrameHistory(entry);
6801 // This is a document.write(). Get the made-up url
6802 // from the channel and store it in session history.
6803 // Pass false for aCloneChildren, since we're creating
6804 // a new DOM here.
6805 AddToSessionHistory(uri, wcwgChannel, nullptr, nullptr, false,
6806 getter_AddRefs(mLSHE));
6807 SetCurrentURI(uri, aRequest, true, 0);
6808 // Save history state of the previous page
6809 PersistLayoutHistoryState();
6810 // We'll never get an Embed() for this load, so just go ahead
6811 // and SetHistoryEntry now.
6812 SetHistoryEntry(&mOSHE, mLSHE);
6815 // Page has begun to load
6816 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD;
6818 if ((aStateFlags & STATE_RESTORING) == 0) {
6819 // Show the progress cursor if the pref is set
6820 if (nsContentUtils::UseActivityCursor()) {
6821 nsCOMPtr<nsIWidget> mainWidget;
6822 GetMainWidget(getter_AddRefs(mainWidget));
6823 if (mainWidget) {
6824 mainWidget->SetCursor(eCursor_spinning);
6828 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
6829 // Page is loading
6830 mBusyFlags = BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING;
6831 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
6832 // Page has finished loading
6833 mBusyFlags = BUSY_FLAGS_NONE;
6835 // Hide the progress cursor if the pref is set
6836 if (nsContentUtils::UseActivityCursor()) {
6837 nsCOMPtr<nsIWidget> mainWidget;
6838 GetMainWidget(getter_AddRefs(mainWidget));
6839 if (mainWidget) {
6840 mainWidget->SetCursor(eCursor_standard);
6844 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
6845 nsCOMPtr<nsIWebProgress> webProgress =
6846 do_QueryInterface(GetAsSupports(this));
6847 // Is the document stop notification for this document?
6848 if (aProgress == webProgress.get()) {
6849 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
6850 EndPageLoad(aProgress, channel, aStatus);
6853 // note that redirect state changes will go through here as well, but it
6854 // is better to handle those in OnRedirectStateChange where more
6855 // information is available.
6856 return NS_OK;
6859 NS_IMETHODIMP
6860 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
6861 nsIURI* aURI, uint32_t aFlags)
6863 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
6864 return NS_OK;
6867 void
6868 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
6869 nsIChannel* aNewChannel,
6870 uint32_t aRedirectFlags,
6871 uint32_t aStateFlags)
6873 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
6874 "Calling OnRedirectStateChange when there is no redirect");
6876 // If mixed content is allowed for the old channel, we forward
6877 // the permission to the new channel if it has the same origin
6878 // as the old one.
6879 if (mMixedContentChannel && mMixedContentChannel == aOldChannel) {
6880 nsresult rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, aNewChannel);
6881 if (NS_SUCCEEDED(rv)) {
6882 SetMixedContentChannel(aNewChannel); // Same origin: forward permission.
6883 } else {
6884 SetMixedContentChannel(nullptr); // Different origin: clear mMixedContentChannel.
6888 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
6889 return; // not a toplevel document
6892 nsCOMPtr<nsIURI> oldURI, newURI;
6893 aOldChannel->GetURI(getter_AddRefs(oldURI));
6894 aNewChannel->GetURI(getter_AddRefs(newURI));
6895 if (!oldURI || !newURI) {
6896 return;
6899 // Below a URI visit is saved (see AddURIVisit method doc).
6900 // The visit chain looks something like:
6901 // ...
6902 // Site N - 1
6903 // => Site N
6904 // (redirect to =>) Site N + 1 (we are here!)
6906 // Get N - 1 and transition type
6907 nsCOMPtr<nsIURI> previousURI;
6908 uint32_t previousFlags = 0;
6909 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
6911 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
6912 ChannelIsPost(aOldChannel)) {
6913 // 1. Internal redirects are ignored because they are specific to the
6914 // channel implementation.
6915 // 2. POSTs are not saved by global history.
6917 // Regardless, we need to propagate the previous visit to the new
6918 // channel.
6919 SaveLastVisit(aNewChannel, previousURI, previousFlags);
6920 } else {
6921 nsCOMPtr<nsIURI> referrer;
6922 // Treat referrer as null if there is an error getting it.
6923 (void)NS_GetReferrerFromChannel(aOldChannel, getter_AddRefs(referrer));
6925 // Get the HTTP response code, if available.
6926 uint32_t responseStatus = 0;
6927 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
6928 if (httpChannel) {
6929 Unused << httpChannel->GetResponseStatus(&responseStatus);
6932 // Add visit N -1 => N
6933 AddURIVisit(oldURI, referrer, previousURI, previousFlags, responseStatus);
6935 // Since N + 1 could be the final destination, we will not save N => N + 1
6936 // here. OnNewURI will do that, so we will cache it.
6937 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
6940 // check if the new load should go through the application cache.
6941 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
6942 do_QueryInterface(aNewChannel);
6943 if (appCacheChannel) {
6944 if (GeckoProcessType_Default != XRE_GetProcessType()) {
6945 // Permission will be checked in the parent process.
6946 appCacheChannel->SetChooseApplicationCache(true);
6947 } else {
6948 nsCOMPtr<nsIScriptSecurityManager> secMan =
6949 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
6951 if (secMan) {
6952 nsCOMPtr<nsIPrincipal> principal;
6953 secMan->GetDocShellCodebasePrincipal(newURI, this,
6954 getter_AddRefs(principal));
6955 appCacheChannel->SetChooseApplicationCache(
6956 NS_ShouldCheckAppCache(principal));
6961 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
6962 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
6963 mLoadType = LOAD_NORMAL_REPLACE;
6964 SetHistoryEntry(&mLSHE, nullptr);
6968 NS_IMETHODIMP
6969 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress,
6970 nsIRequest* aRequest,
6971 nsresult aStatus, const char16_t* aMessage)
6973 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
6974 return NS_OK;
6977 NS_IMETHODIMP
6978 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress,
6979 nsIRequest* aRequest, uint32_t aState)
6981 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
6982 return NS_OK;
6985 nsresult
6986 nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6987 nsIChannel* aChannel, nsresult aStatus)
6989 if (!aChannel) {
6990 return NS_ERROR_NULL_POINTER;
6993 // Make sure to discard the initial client if we never created the initial
6994 // about:blank document. Do this before possibly returning from the method
6995 // due to an error.
6996 mInitialClientSource.reset();
6998 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6999 if (reporter) {
7000 nsCOMPtr<nsILoadGroup> loadGroup;
7001 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
7002 if (loadGroup) {
7003 reporter->FlushConsoleReports(loadGroup);
7004 } else {
7005 reporter->FlushConsoleReports(GetDocument());
7009 nsCOMPtr<nsIURI> url;
7010 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
7011 if (NS_FAILED(rv)) {
7012 return rv;
7015 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
7016 if (timingChannel) {
7017 TimeStamp channelCreationTime;
7018 rv = timingChannel->GetChannelCreation(&channelCreationTime);
7019 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
7020 Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
7021 channelCreationTime);
7022 nsCOMPtr<nsPILoadGroupInternal> internalLoadGroup =
7023 do_QueryInterface(mLoadGroup);
7024 if (internalLoadGroup) {
7025 internalLoadGroup->OnEndPageLoad(aChannel);
7030 // Timing is picked up by the window, we don't need it anymore
7031 mTiming = nullptr;
7033 // clean up reload state for meta charset
7034 if (eCharsetReloadRequested == mCharsetReloadState) {
7035 mCharsetReloadState = eCharsetReloadStopOrigional;
7036 } else {
7037 mCharsetReloadState = eCharsetReloadInit;
7040 // Save a pointer to the currently-loading history entry.
7041 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
7042 // entry further down in this method.
7043 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
7044 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
7047 // one of many safeguards that prevent death and destruction if
7048 // someone is so very very rude as to bring this window down
7049 // during this load handler.
7051 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7053 // Notify the ContentViewer that the Document has finished loading. This
7054 // will cause any OnLoad(...) and PopState(...) handlers to fire.
7055 if (!mEODForCurrentDocument && mContentViewer) {
7056 mIsExecutingOnLoadHandler = true;
7057 mContentViewer->LoadComplete(aStatus);
7058 mIsExecutingOnLoadHandler = false;
7060 mEODForCurrentDocument = true;
7062 // If all documents have completed their loading
7063 // favor native event dispatch priorities
7064 // over performance
7065 if (--gNumberOfDocumentsLoading == 0) {
7066 // Hint to use normal native event dispatch priorities
7067 FavorPerformanceHint(false);
7070 /* Check if the httpChannel has any cache-control related response headers,
7071 * like no-store, no-cache. If so, update SHEntry so that
7072 * when a user goes back/forward to this page, we appropriately do
7073 * form value restoration or load from server.
7075 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
7076 if (!httpChannel) {
7077 // HttpChannel could be hiding underneath a Multipart channel.
7078 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
7081 if (httpChannel) {
7082 // figure out if SH should be saving layout state.
7083 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
7084 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
7085 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
7086 mLSHE->SetSaveLayoutStateFlag(false);
7090 // Clear mLSHE after calling the onLoadHandlers. This way, if the
7091 // onLoadHandler tries to load something different in
7092 // itself or one of its children, we can deal with it appropriately.
7093 if (mLSHE) {
7094 mLSHE->SetLoadType(nsIDocShellLoadInfo::loadHistory);
7096 // Clear the mLSHE reference to indicate document loading is done one
7097 // way or another.
7098 SetHistoryEntry(&mLSHE, nullptr);
7100 // if there's a refresh header in the channel, this method
7101 // will set it up for us.
7102 if (mIsActive || !mDisableMetaRefreshWhenInactive)
7103 RefreshURIFromQueue();
7105 // Test whether this is the top frame or a subframe
7106 bool isTopFrame = true;
7107 nsCOMPtr<nsIDocShellTreeItem> targetParentTreeItem;
7108 rv = GetSameTypeParent(getter_AddRefs(targetParentTreeItem));
7109 if (NS_SUCCEEDED(rv) && targetParentTreeItem) {
7110 isTopFrame = false;
7114 // If the page load failed, then deal with the error condition...
7115 // Errors are handled as follows:
7116 // 1. Check to see if it's a file not found error or bad content
7117 // encoding error.
7118 // 2. Send the URI to a keyword server (if enabled)
7119 // 3. If the error was DNS failure, then add www and .com to the URI
7120 // (if appropriate).
7121 // 4. Throw an error dialog box...
7123 if (url && NS_FAILED(aStatus)) {
7124 if (aStatus == NS_ERROR_FILE_NOT_FOUND ||
7125 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
7126 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
7127 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
7128 DisplayLoadError(aStatus, url, nullptr, aChannel);
7129 return NS_OK;
7132 // Handle iframe document not loading error because source was
7133 // a tracking URL. We make a note of this iframe node by including
7134 // it in a dedicated array of blocked tracking nodes under its parent
7135 // document. (document of parent window of blocked document)
7136 if (isTopFrame == false && aStatus == NS_ERROR_TRACKING_URI) {
7137 // frameElement is our nsIContent to be annotated
7138 RefPtr<Element> frameElement;
7139 nsPIDOMWindowOuter* thisWindow = GetWindow();
7140 if (!thisWindow) {
7141 return NS_OK;
7144 frameElement = thisWindow->GetFrameElement();
7145 if (!frameElement) {
7146 return NS_OK;
7149 // Parent window
7150 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7151 GetSameTypeParent(getter_AddRefs(parentItem));
7152 if (!parentItem) {
7153 return NS_OK;
7156 nsCOMPtr<nsIDocument> parentDoc;
7157 parentDoc = parentItem->GetDocument();
7158 if (!parentDoc) {
7159 return NS_OK;
7162 parentDoc->AddBlockedTrackingNode(frameElement);
7164 return NS_OK;
7167 if (sURIFixup) {
7169 // Try and make an alternative URI from the old one
7171 nsCOMPtr<nsIURI> newURI;
7172 nsCOMPtr<nsIInputStream> newPostData;
7174 nsAutoCString oldSpec;
7175 url->GetSpec(oldSpec);
7178 // First try keyword fixup
7180 nsAutoString keywordProviderName, keywordAsSent;
7181 if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
7182 bool keywordsEnabled = Preferences::GetBool("keyword.enabled", false);
7184 nsAutoCString host;
7185 url->GetHost(host);
7187 nsAutoCString scheme;
7188 url->GetScheme(scheme);
7190 int32_t dotLoc = host.FindChar('.');
7192 // we should only perform a keyword search under the following
7193 // conditions:
7194 // (0) Pref keyword.enabled is true
7195 // (1) the url scheme is http (or https)
7196 // (2) the url does not have a protocol scheme
7197 // If we don't enforce such a policy, then we end up doing
7198 // keyword searchs on urls we don't intend like imap, file,
7199 // mailbox, etc. This could lead to a security problem where we
7200 // send data to the keyword server that we shouldn't be.
7201 // Someone needs to clean up keywords in general so we can
7202 // determine on a per url basis if we want keywords
7203 // enabled...this is just a bandaid...
7204 if (keywordsEnabled && !scheme.IsEmpty() &&
7205 (scheme.Find("http") != 0)) {
7206 keywordsEnabled = false;
7209 if (keywordsEnabled && (kNotFound == dotLoc)) {
7210 nsCOMPtr<nsIURIFixupInfo> info;
7211 // only send non-qualified hosts to the keyword server
7212 if (!mOriginalUriString.IsEmpty()) {
7213 sURIFixup->KeywordToURI(mOriginalUriString,
7214 getter_AddRefs(newPostData),
7215 getter_AddRefs(info));
7216 } else {
7218 // If this string was passed through nsStandardURL by
7219 // chance, then it may have been converted from UTF-8 to
7220 // ACE, which would result in a completely bogus keyword
7221 // query. Here we try to recover the original Unicode
7222 // value, but this is not 100% correct since the value may
7223 // have been normalized per the IDN normalization rules.
7225 // Since we don't have access to the exact original string
7226 // that was entered by the user, this will just have to do.
7227 bool isACE;
7228 nsAutoCString utf8Host;
7229 nsCOMPtr<nsIIDNService> idnSrv =
7230 do_GetService(NS_IDNSERVICE_CONTRACTID);
7231 if (idnSrv &&
7232 NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
7233 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
7234 sURIFixup->KeywordToURI(utf8Host,
7235 getter_AddRefs(newPostData),
7236 getter_AddRefs(info));
7237 } else {
7238 sURIFixup->KeywordToURI(host,
7239 getter_AddRefs(newPostData),
7240 getter_AddRefs(info));
7244 info->GetPreferredURI(getter_AddRefs(newURI));
7245 if (newURI) {
7246 info->GetKeywordAsSent(keywordAsSent);
7247 info->GetKeywordProviderName(keywordProviderName);
7249 } // end keywordsEnabled
7253 // Now try change the address, e.g. turn http://foo into
7254 // http://www.foo.com
7256 if (aStatus == NS_ERROR_UNKNOWN_HOST ||
7257 aStatus == NS_ERROR_NET_RESET) {
7258 bool doCreateAlternate = true;
7260 // Skip fixup for anything except a normal document load
7261 // operation on the topframe.
7263 if (mLoadType != LOAD_NORMAL || !isTopFrame) {
7264 doCreateAlternate = false;
7265 } else {
7266 // Test if keyword lookup produced a new URI or not
7267 if (newURI) {
7268 bool sameURI = false;
7269 url->Equals(newURI, &sameURI);
7270 if (!sameURI) {
7271 // Keyword lookup made a new URI so no need to try
7272 // an alternate one.
7273 doCreateAlternate = false;
7277 if (doCreateAlternate) {
7278 // Skip doing this if our channel was redirected, because we
7279 // shouldn't be guessing things about the post-redirect URI.
7280 nsLoadFlags loadFlags = 0;
7281 if (NS_FAILED(aChannel->GetLoadFlags(&loadFlags)) ||
7282 (loadFlags & nsIChannel::LOAD_REPLACE)) {
7283 doCreateAlternate = false;
7287 if (doCreateAlternate) {
7288 newURI = nullptr;
7289 newPostData = nullptr;
7290 keywordProviderName.Truncate();
7291 keywordAsSent.Truncate();
7292 sURIFixup->CreateFixupURI(oldSpec,
7293 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
7294 getter_AddRefs(newPostData),
7295 getter_AddRefs(newURI));
7299 // Did we make a new URI that is different to the old one? If so
7300 // load it.
7302 if (newURI) {
7303 // Make sure the new URI is different from the old one,
7304 // otherwise there's little point trying to load it again.
7305 bool sameURI = false;
7306 url->Equals(newURI, &sameURI);
7307 if (!sameURI) {
7308 nsAutoCString newSpec;
7309 newURI->GetSpec(newSpec);
7310 NS_ConvertUTF8toUTF16 newSpecW(newSpec);
7312 // This notification is meant for Firefox Health Report so it
7313 // can increment counts from the search engine
7314 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
7316 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
7317 nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo
7318 ? loadInfo->TriggeringPrincipal()
7319 : nsContentUtils::GetSystemPrincipal();
7320 return LoadURI(newSpecW.get(), // URI string
7321 LOAD_FLAGS_NONE, // Load flags
7322 nullptr, // Referring URI
7323 newPostData, // Post data stream
7324 nullptr, // Headers stream
7325 triggeringPrincipal); // TriggeringPrincipal
7330 // Well, fixup didn't work :-(
7331 // It is time to throw an error dialog box, and be done with it...
7333 // Errors to be shown only on top-level frames
7334 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
7335 aStatus == NS_ERROR_CONNECTION_REFUSED ||
7336 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
7337 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
7338 aStatus == NS_ERROR_BLOCKED_BY_POLICY) &&
7339 (isTopFrame || UseErrorPages())) {
7340 DisplayLoadError(aStatus, url, nullptr, aChannel);
7341 } else if (aStatus == NS_ERROR_NET_TIMEOUT ||
7342 aStatus == NS_ERROR_REDIRECT_LOOP ||
7343 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
7344 aStatus == NS_ERROR_NET_INTERRUPT ||
7345 aStatus == NS_ERROR_NET_RESET ||
7346 aStatus == NS_ERROR_OFFLINE ||
7347 aStatus == NS_ERROR_MALWARE_URI ||
7348 aStatus == NS_ERROR_PHISHING_URI ||
7349 aStatus == NS_ERROR_UNWANTED_URI ||
7350 aStatus == NS_ERROR_HARMFUL_URI ||
7351 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
7352 aStatus == NS_ERROR_REMOTE_XUL ||
7353 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
7354 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
7355 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
7356 // Errors to be shown for any frame
7357 DisplayLoadError(aStatus, url, nullptr, aChannel);
7358 } else if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
7359 // Non-caching channels will simply return NS_ERROR_OFFLINE.
7360 // Caching channels would have to look at their flags to work
7361 // out which error to return. Or we can fix up the error here.
7362 if (!(mLoadType & LOAD_CMD_HISTORY)) {
7363 aStatus = NS_ERROR_OFFLINE;
7365 DisplayLoadError(aStatus, url, nullptr, aChannel);
7367 } else if (url && NS_SUCCEEDED(aStatus)) {
7368 // If we have a host
7369 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
7370 if (loadInfo) {
7371 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
7375 return NS_OK;
7378 //*****************************************************************************
7379 // nsDocShell: Content Viewer Management
7380 //*****************************************************************************
7382 nsresult
7383 nsDocShell::EnsureContentViewer()
7385 if (mContentViewer) {
7386 return NS_OK;
7388 if (mIsBeingDestroyed) {
7389 return NS_ERROR_FAILURE;
7392 nsCOMPtr<nsIURI> baseURI;
7393 nsIPrincipal* principal = GetInheritedPrincipal(false);
7394 nsCOMPtr<nsIDocShellTreeItem> parentItem;
7395 GetSameTypeParent(getter_AddRefs(parentItem));
7396 if (parentItem) {
7397 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
7398 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
7399 if (parentElement) {
7400 baseURI = parentElement->GetBaseURI();
7405 nsresult rv = CreateAboutBlankContentViewer(principal, baseURI);
7407 NS_ENSURE_STATE(mContentViewer);
7409 if (NS_SUCCEEDED(rv)) {
7410 nsCOMPtr<nsIDocument> doc(GetDocument());
7411 NS_ASSERTION(doc,
7412 "Should have doc if CreateAboutBlankContentViewer "
7413 "succeeded!");
7415 doc->SetIsInitialDocument(true);
7417 // Documents created using EnsureContentViewer may be transient
7418 // placeholders created by framescripts before content has a chance to
7419 // load. In some cases, window.open(..., "noopener") will create such a
7420 // document (in a new TabGroup) and then synchronously tear it down, firing
7421 // a "pagehide" event. Doing so violates our assertions about
7422 // DocGroups. It's easier to silence the assertion here than to avoid
7423 // creating the extra document.
7424 doc->IgnoreDocGroupMismatches();
7427 return rv;
7430 nsresult
7431 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
7432 nsIURI* aBaseURI,
7433 bool aTryToSaveOldPresentation,
7434 bool aCheckPermitUnload)
7436 nsCOMPtr<nsIDocument> blankDoc;
7437 nsCOMPtr<nsIContentViewer> viewer;
7438 nsresult rv = NS_ERROR_FAILURE;
7440 /* mCreatingDocument should never be true at this point. However, it's
7441 a theoretical possibility. We want to know about it and make it stop,
7442 and this sounds like a job for an assertion. */
7443 NS_ASSERTION(!mCreatingDocument,
7444 "infinite(?) loop creating document averted");
7445 if (mCreatingDocument) {
7446 return NS_ERROR_FAILURE;
7449 // mContentViewer->PermitUnload may release |this| docshell.
7450 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
7452 AutoRestore<bool> creatingDocument(mCreatingDocument);
7453 mCreatingDocument = true;
7455 if (aPrincipal && !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
7456 mItemType != typeChrome) {
7457 MOZ_ASSERT(aPrincipal->OriginAttributesRef() == mOriginAttributes);
7460 // Make sure timing is created. But first record whether we had it
7461 // already, so we don't clobber the timing for an in-progress load.
7462 bool hadTiming = mTiming;
7463 bool toBeReset = MaybeInitTiming();
7464 if (mContentViewer) {
7465 if (aCheckPermitUnload) {
7466 // We've got a content viewer already. Make sure the user
7467 // permits us to discard the current document and replace it
7468 // with about:blank. And also ensure we fire the unload events
7469 // in the current document.
7471 // Unload gets fired first for
7472 // document loaded from the session history.
7473 mTiming->NotifyBeforeUnload();
7475 bool okToUnload;
7476 rv = mContentViewer->PermitUnload(&okToUnload);
7478 if (NS_SUCCEEDED(rv) && !okToUnload) {
7479 // The user chose not to unload the page, interrupt the load.
7480 MaybeResetInitTiming(toBeReset);
7481 return NS_ERROR_FAILURE;
7483 if (mTiming) {
7484 mTiming->NotifyUnloadAccepted(mCurrentURI);
7488 mSavingOldViewer = aTryToSaveOldPresentation &&
7489 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr);
7491 // Make sure to blow away our mLoadingURI just in case. No loads
7492 // from inside this pagehide.
7493 mLoadingURI = nullptr;
7495 // Stop any in-progress loading, so that we don't accidentally trigger any
7496 // PageShow notifications from Embed() interrupting our loading below.
7497 Stop();
7499 // Notify the current document that it is about to be unloaded!!
7501 // It is important to fire the unload() notification *before* any state
7502 // is changed within the DocShell - otherwise, javascript will get the
7503 // wrong information :-(
7505 (void)FirePageHideNotification(!mSavingOldViewer);
7506 // pagehide notification might destroy this docshell.
7507 if (mIsBeingDestroyed) {
7508 return NS_ERROR_DOCSHELL_DYING;
7512 // Now make sure we don't think we're in the middle of firing unload after
7513 // this point. This will make us fire unload when the about:blank document
7514 // unloads... but that's ok, more or less. Would be nice if it fired load
7515 // too, of course.
7516 mFiredUnloadEvent = false;
7518 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
7519 nsContentUtils::FindInternalContentViewer(NS_LITERAL_CSTRING("text/html"));
7521 if (docFactory) {
7522 nsCOMPtr<nsIPrincipal> principal;
7523 if (mSandboxFlags & SANDBOXED_ORIGIN) {
7524 if (aPrincipal) {
7525 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
7526 } else {
7527 principal = NullPrincipal::CreateWithInheritedAttributes(this);
7529 } else {
7530 principal = aPrincipal;
7533 MaybeCreateInitialClientSource(principal);
7535 // generate (about:blank) document to load
7536 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal, this);
7537 if (blankDoc) {
7538 // Hack: set the base URI manually, since this document never
7539 // got Reset() with a channel.
7540 blankDoc->SetBaseURI(aBaseURI);
7542 // Copy our sandbox flags to the document. These are immutable
7543 // after being set here.
7544 blankDoc->SetSandboxFlags(mSandboxFlags);
7546 // create a content viewer for us and the new document
7547 docFactory->CreateInstanceForDocument(
7548 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
7549 getter_AddRefs(viewer));
7551 // hook 'em up
7552 if (viewer) {
7553 viewer->SetContainer(this);
7554 rv = Embed(viewer, "", 0);
7555 NS_ENSURE_SUCCESS(rv, rv);
7557 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, true, 0);
7558 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
7563 // The transient about:blank viewer doesn't have a session history entry.
7564 SetHistoryEntry(&mOSHE, nullptr);
7566 // Clear out our mTiming like we would in EndPageLoad, if we didn't
7567 // have one before entering this function.
7568 if (!hadTiming) {
7569 mTiming = nullptr;
7570 mBlankTiming = true;
7573 return rv;
7576 NS_IMETHODIMP
7577 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal)
7579 return CreateAboutBlankContentViewer(aPrincipal, nullptr);
7582 NS_IMETHODIMP
7583 nsDocShell::ForceCreateAboutBlankContentViewer(nsIPrincipal* aPrincipal)
7585 return CreateAboutBlankContentViewer(aPrincipal, nullptr, true, false);
7588 bool
7589 nsDocShell::CanSavePresentation(uint32_t aLoadType,
7590 nsIRequest* aNewRequest,
7591 nsIDocument* aNewDocument)
7593 if (!mOSHE) {
7594 return false; // no entry to save into
7597 nsCOMPtr<nsIContentViewer> viewer;
7598 mOSHE->GetContentViewer(getter_AddRefs(viewer));
7599 if (viewer) {
7600 NS_WARNING("mOSHE already has a content viewer!");
7601 return false;
7604 // Only save presentation for "normal" loads and link loads. Anything else
7605 // probably wants to refetch the page, so caching the old presentation
7606 // would be incorrect.
7607 if (aLoadType != LOAD_NORMAL &&
7608 aLoadType != LOAD_HISTORY &&
7609 aLoadType != LOAD_LINK &&
7610 aLoadType != LOAD_STOP_CONTENT &&
7611 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
7612 aLoadType != LOAD_ERROR_PAGE) {
7613 return false;
7616 // If the session history entry has the saveLayoutState flag set to false,
7617 // then we should not cache the presentation.
7618 bool canSaveState;
7619 mOSHE->GetSaveLayoutStateFlag(&canSaveState);
7620 if (!canSaveState) {
7621 return false;
7624 // If the document is not done loading, don't cache it.
7625 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
7626 return false;
7629 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
7630 return false;
7633 // Avoid doing the work of saving the presentation state in the case where
7634 // the content viewer cache is disabled.
7635 if (nsSHistory::GetMaxTotalViewers() == 0) {
7636 return false;
7639 // Don't cache the content viewer if we're in a subframe.
7640 nsCOMPtr<nsIDocShellTreeItem> root;
7641 GetSameTypeParent(getter_AddRefs(root));
7642 if (root && root != this) {
7643 return false; // this is a subframe load
7646 // If the document does not want its presentation cached, then don't.
7647 nsCOMPtr<nsIDocument> doc = mScriptGlobal->GetExtantDoc();
7648 return doc && doc->CanSavePresentation(aNewRequest);
7651 void
7652 nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry)
7654 MOZ_ASSERT(!mIsBeingDestroyed);
7656 NS_ASSERTION(!mEditorData,
7657 "Why reattach an editor when we already have one?");
7658 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
7659 "Reattaching when there's not a detached editor.");
7661 if (mEditorData || !aSHEntry) {
7662 return;
7665 mEditorData = aSHEntry->ForgetEditorData();
7666 if (mEditorData) {
7667 #ifdef DEBUG
7668 nsresult rv =
7669 #endif
7670 mEditorData->ReattachToWindow(this);
7671 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7675 void
7676 nsDocShell::DetachEditorFromWindow()
7678 if (!mEditorData || mEditorData->WaitingForLoad()) {
7679 // If there's nothing to detach, or if the editor data is actually set
7680 // up for the _new_ page that's coming in, don't detach.
7681 return;
7684 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7685 "Detaching editor when it's already detached.");
7687 nsresult res = mEditorData->DetachFromWindow();
7688 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7690 if (NS_SUCCEEDED(res)) {
7691 // Make mOSHE hold the owning ref to the editor data.
7692 if (mOSHE) {
7693 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
7694 "We should not set the editor data again once after we "
7695 "detached the editor data during destroying this docshell");
7696 mOSHE->SetEditorData(mEditorData.forget());
7697 } else {
7698 mEditorData = nullptr;
7702 #ifdef DEBUG
7704 bool isEditable;
7705 GetEditable(&isEditable);
7706 NS_ASSERTION(!isEditable,
7707 "Window is still editable after detaching editor.");
7709 #endif // DEBUG
7712 nsresult
7713 nsDocShell::CaptureState()
7715 if (!mOSHE || mOSHE == mLSHE) {
7716 // No entry to save into, or we're replacing the existing entry.
7717 return NS_ERROR_FAILURE;
7720 if (!mScriptGlobal) {
7721 return NS_ERROR_FAILURE;
7724 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7725 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7727 #ifdef DEBUG_PAGE_CACHE
7728 nsCOMPtr<nsIURI> uri;
7729 mOSHE->GetURI(getter_AddRefs(uri));
7730 nsAutoCString spec;
7731 if (uri) {
7732 uri->GetSpec(spec);
7734 printf("Saving presentation into session history\n");
7735 printf(" SH URI: %s\n", spec.get());
7736 #endif
7738 nsresult rv = mOSHE->SetWindowState(windowState);
7739 NS_ENSURE_SUCCESS(rv, rv);
7741 // Suspend refresh URIs and save off the timer queue
7742 rv = mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7743 NS_ENSURE_SUCCESS(rv, rv);
7745 // Capture the current content viewer bounds.
7746 if (mContentViewer) {
7747 nsIntRect bounds;
7748 mContentViewer->GetBounds(bounds);
7749 rv = mOSHE->SetViewerBounds(bounds);
7750 NS_ENSURE_SUCCESS(rv, rv);
7753 // Capture the docshell hierarchy.
7754 mOSHE->ClearChildShells();
7756 uint32_t childCount = mChildList.Length();
7757 for (uint32_t i = 0; i < childCount; ++i) {
7758 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7759 NS_ASSERTION(childShell, "null child shell");
7761 mOSHE->AddChildShell(childShell);
7764 return NS_OK;
7767 NS_IMETHODIMP
7768 nsDocShell::RestorePresentationEvent::Run()
7770 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7771 NS_WARNING("RestoreFromHistory failed");
7773 return NS_OK;
7776 NS_IMETHODIMP
7777 nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop)
7779 nsresult rv;
7780 if (!aContentViewer) {
7781 rv = EnsureContentViewer();
7782 NS_ENSURE_SUCCESS(rv, rv);
7784 aContentViewer = mContentViewer;
7787 // Dispatch events for restoring the presentation. We try to simulate
7788 // the progress notifications loading the document would cause, so we add
7789 // the document's channel to the loadgroup to initiate stateChange
7790 // notifications.
7792 nsCOMPtr<nsIDocument> doc = aContentViewer->GetDocument();
7793 if (doc) {
7794 nsIChannel* channel = doc->GetChannel();
7795 if (channel) {
7796 mEODForCurrentDocument = false;
7797 mIsRestoringDocument = true;
7798 mLoadGroup->AddRequest(channel, nullptr);
7799 mIsRestoringDocument = false;
7803 if (!aTop) {
7804 // This point corresponds to us having gotten OnStartRequest or
7805 // STATE_START, so do the same thing that CreateContentViewer does at
7806 // this point to ensure that unload/pagehide events for this document
7807 // will fire when it's unloaded again.
7808 mFiredUnloadEvent = false;
7810 // For non-top frames, there is no notion of making sure that the
7811 // previous document is in the domwindow when STATE_START notifications
7812 // happen. We can just call BeginRestore for all of the child shells
7813 // now.
7814 rv = BeginRestoreChildren();
7815 NS_ENSURE_SUCCESS(rv, rv);
7818 return NS_OK;
7821 nsresult
7822 nsDocShell::BeginRestoreChildren()
7824 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7825 while (iter.HasMore()) {
7826 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7827 if (child) {
7828 nsresult rv = child->BeginRestore(nullptr, false);
7829 NS_ENSURE_SUCCESS(rv, rv);
7832 return NS_OK;
7835 NS_IMETHODIMP
7836 nsDocShell::FinishRestore()
7838 // First we call finishRestore() on our children. In the simulated load,
7839 // all of the child frames finish loading before the main document.
7841 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
7842 while (iter.HasMore()) {
7843 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
7844 if (child) {
7845 child->FinishRestore();
7849 if (mOSHE && mOSHE->HasDetachedEditor()) {
7850 ReattachEditorToWindow(mOSHE);
7853 nsCOMPtr<nsIDocument> doc = GetDocument();
7854 if (doc) {
7855 // Finally, we remove the request from the loadgroup. This will
7856 // cause onStateChange(STATE_STOP) to fire, which will fire the
7857 // pageshow event to the chrome.
7859 nsIChannel* channel = doc->GetChannel();
7860 if (channel) {
7861 mIsRestoringDocument = true;
7862 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7863 mIsRestoringDocument = false;
7867 return NS_OK;
7870 NS_IMETHODIMP
7871 nsDocShell::GetRestoringDocument(bool* aRestoring)
7873 *aRestoring = mIsRestoringDocument;
7874 return NS_OK;
7877 nsresult
7878 nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, bool* aRestoring)
7880 MOZ_ASSERT(!mIsBeingDestroyed);
7882 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7883 "RestorePresentation should only be called for history loads");
7885 nsCOMPtr<nsIContentViewer> viewer;
7886 aSHEntry->GetContentViewer(getter_AddRefs(viewer));
7888 #ifdef DEBUG_PAGE_CACHE
7889 nsCOMPtr<nsIURI> uri;
7890 aSHEntry->GetURI(getter_AddRefs(uri));
7892 nsAutoCString spec;
7893 if (uri) {
7894 uri->GetSpec(spec);
7896 #endif
7898 *aRestoring = false;
7900 if (!viewer) {
7901 #ifdef DEBUG_PAGE_CACHE
7902 printf("no saved presentation for uri: %s\n", spec.get());
7903 #endif
7904 return NS_OK;
7907 // We need to make sure the content viewer's container is this docshell.
7908 // In subframe navigation, it's possible for the docshell that the
7909 // content viewer was originally loaded into to be replaced with a
7910 // different one. We don't currently support restoring the presentation
7911 // in that case.
7913 nsCOMPtr<nsIDocShell> container;
7914 viewer->GetContainer(getter_AddRefs(container));
7915 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7916 #ifdef DEBUG_PAGE_CACHE
7917 printf("No valid container, clearing presentation\n");
7918 #endif
7919 aSHEntry->SetContentViewer(nullptr);
7920 return NS_ERROR_FAILURE;
7923 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7925 #ifdef DEBUG_PAGE_CACHE
7926 printf("restoring presentation from session history: %s\n", spec.get());
7927 #endif
7929 SetHistoryEntry(&mLSHE, aSHEntry);
7931 // Post an event that will remove the request after we've returned
7932 // to the event loop. This mimics the way it is called by nsIChannel
7933 // implementations.
7935 // Revoke any pending restore (just in case)
7936 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7937 "should only have one RestorePresentationEvent");
7938 mRestorePresentationEvent.Revoke();
7940 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7941 nsresult rv = DispatchToTabGroup(TaskCategory::Other,
7942 RefPtr<RestorePresentationEvent>(evt).forget());
7943 if (NS_SUCCEEDED(rv)) {
7944 mRestorePresentationEvent = evt.get();
7945 // The rest of the restore processing will happen on our event
7946 // callback.
7947 *aRestoring = true;
7950 return rv;
7953 namespace {
7954 class MOZ_STACK_CLASS PresentationEventForgetter
7956 public:
7957 explicit PresentationEventForgetter(
7958 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7959 aRestorePresentationEvent)
7960 : mRestorePresentationEvent(aRestorePresentationEvent)
7961 , mEvent(aRestorePresentationEvent.get())
7965 ~PresentationEventForgetter()
7967 Forget();
7970 void Forget()
7972 if (mRestorePresentationEvent.get() == mEvent) {
7973 mRestorePresentationEvent.Forget();
7974 mEvent = nullptr;
7978 private:
7979 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7980 mRestorePresentationEvent;
7981 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7984 } // namespace
7986 bool
7987 nsDocShell::SandboxFlagsImplyCookies(const uint32_t &aSandboxFlags)
7989 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7992 nsresult
7993 nsDocShell::RestoreFromHistory()
7995 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7996 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7998 // This section of code follows the same ordering as CreateContentViewer.
7999 if (!mLSHE) {
8000 return NS_ERROR_FAILURE;
8003 nsCOMPtr<nsIContentViewer> viewer;
8004 mLSHE->GetContentViewer(getter_AddRefs(viewer));
8005 if (!viewer) {
8006 return NS_ERROR_FAILURE;
8009 if (mSavingOldViewer) {
8010 // We determined that it was safe to cache the document presentation
8011 // at the time we initiated the new load. We need to check whether
8012 // it's still safe to do so, since there may have been DOM mutations
8013 // or new requests initiated.
8014 nsCOMPtr<nsIDocument> doc = viewer->GetDocument();
8015 nsIRequest* request = nullptr;
8016 if (doc) {
8017 request = doc->GetChannel();
8019 mSavingOldViewer = CanSavePresentation(mLoadType, request, doc);
8022 nsCOMPtr<nsIContentViewer> oldCv(mContentViewer);
8023 nsCOMPtr<nsIContentViewer> newCv(viewer);
8024 int32_t minFontSize = 0;
8025 float textZoom = 1.0f;
8026 float pageZoom = 1.0f;
8027 float overrideDPPX = 0.0f;
8029 bool styleDisabled = false;
8030 if (oldCv && newCv) {
8031 oldCv->GetMinFontSize(&minFontSize);
8032 oldCv->GetTextZoom(&textZoom);
8033 oldCv->GetFullZoom(&pageZoom);
8034 oldCv->GetOverrideDPPX(&overrideDPPX);
8035 oldCv->GetAuthorStyleDisabled(&styleDisabled);
8038 // Protect against mLSHE going away via a load triggered from
8039 // pagehide or unload.
8040 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
8042 // Make sure to blow away our mLoadingURI just in case. No loads
8043 // from inside this pagehide.
8044 mLoadingURI = nullptr;
8046 // Notify the old content viewer that it's being hidden.
8047 FirePageHideNotification(!mSavingOldViewer);
8048 // pagehide notification might destroy this docshell.
8049 if (mIsBeingDestroyed) {
8050 return NS_ERROR_DOCSHELL_DYING;
8053 // If mLSHE was changed as a result of the pagehide event, then
8054 // something else was loaded. Don't finish restoring.
8055 if (mLSHE != origLSHE) {
8056 return NS_OK;
8059 // Add the request to our load group. We do this before swapping out
8060 // the content viewers so that consumers of STATE_START can access
8061 // the old document. We only deal with the toplevel load at this time --
8062 // to be consistent with normal document loading, subframes cannot start
8063 // loading until after data arrives, which is after STATE_START completes.
8065 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
8066 mRestorePresentationEvent.get();
8067 Stop();
8068 // Make sure we're still restoring the same presentation.
8069 // If we aren't, docshell is in process doing another load already.
8070 NS_ENSURE_STATE(currentPresentationRestoration ==
8071 mRestorePresentationEvent.get());
8072 BeginRestore(viewer, true);
8073 NS_ENSURE_STATE(currentPresentationRestoration ==
8074 mRestorePresentationEvent.get());
8075 forgetter.Forget();
8077 // Set mFiredUnloadEvent = false so that the unload handler for the
8078 // *new* document will fire.
8079 mFiredUnloadEvent = false;
8081 mURIResultedInDocument = true;
8082 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
8083 if (rootSH) {
8084 mPreviousTransIndex = rootSH->Index();
8085 rootSH->LegacySHistoryInternal()->UpdateIndex();
8086 mLoadedTransIndex = rootSH->Index();
8087 #ifdef DEBUG_PAGE_CACHE
8088 printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
8089 mLoadedTransIndex);
8090 #endif
8093 // Rather than call Embed(), we will retrieve the viewer from the session
8094 // history entry and swap it in.
8095 // XXX can we refactor this so that we can just call Embed()?
8096 PersistLayoutHistoryState();
8097 nsresult rv;
8098 if (mContentViewer) {
8099 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8100 if (mOSHE) {
8101 mOSHE->SyncPresentationState();
8103 mSavingOldViewer = false;
8107 mSavedRefreshURIList = nullptr;
8109 // In cases where we use a transient about:blank viewer between loads,
8110 // we never show the transient viewer, so _its_ previous viewer is never
8111 // unhooked from the view hierarchy. Destroy any such previous viewer now,
8112 // before we grab the root view sibling, so that we don't grab a view
8113 // that's about to go away.
8115 if (mContentViewer) {
8116 nsCOMPtr<nsIContentViewer> previousViewer;
8117 mContentViewer->GetPreviousViewer(getter_AddRefs(previousViewer));
8118 if (previousViewer) {
8119 mContentViewer->SetPreviousViewer(nullptr);
8120 previousViewer->Destroy();
8124 // Save off the root view's parent and sibling so that we can insert the
8125 // new content viewer's root view at the same position. Also save the
8126 // bounds of the root view's widget.
8128 nsView* rootViewSibling = nullptr;
8129 nsView* rootViewParent = nullptr;
8130 nsIntRect newBounds(0, 0, 0, 0);
8132 nsCOMPtr<nsIPresShell> oldPresShell = GetPresShell();
8133 if (oldPresShell) {
8134 nsViewManager* vm = oldPresShell->GetViewManager();
8135 if (vm) {
8136 nsView* oldRootView = vm->GetRootView();
8138 if (oldRootView) {
8139 rootViewSibling = oldRootView->GetNextSibling();
8140 rootViewParent = oldRootView->GetParent();
8142 mContentViewer->GetBounds(newBounds);
8147 nsCOMPtr<nsIContent> container;
8148 nsCOMPtr<nsIDocument> sibling;
8149 if (rootViewParent && rootViewParent->GetParent()) {
8150 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
8151 container = frame ? frame->GetContent() : nullptr;
8153 if (rootViewSibling) {
8154 nsIFrame* frame = rootViewSibling->GetFrame();
8155 sibling =
8156 frame ? frame->PresShell()->GetDocument() : nullptr;
8159 // Transfer ownership to mContentViewer. By ensuring that either the
8160 // docshell or the session history, but not both, have references to the
8161 // content viewer, we prevent the viewer from being torn down after
8162 // Destroy() is called.
8164 if (mContentViewer) {
8165 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8166 viewer->SetPreviousViewer(mContentViewer);
8168 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8169 // We don't plan to save a viewer in mOSHE; tell it to drop
8170 // any other state it's holding.
8171 mOSHE->SyncPresentationState();
8174 // Order the mContentViewer setup just like Embed does.
8175 mContentViewer = nullptr;
8177 // Now that we're about to switch documents, forget all of our children.
8178 // Note that we cached them as needed up in CaptureState above.
8179 DestroyChildren();
8181 mContentViewer.swap(viewer);
8183 // Grab all of the related presentation from the SHEntry now.
8184 // Clearing the viewer from the SHEntry will clear all of this state.
8185 nsCOMPtr<nsISupports> windowState;
8186 mLSHE->GetWindowState(getter_AddRefs(windowState));
8187 mLSHE->SetWindowState(nullptr);
8189 bool sticky;
8190 mLSHE->GetSticky(&sticky);
8192 nsCOMPtr<nsIDocument> document = mContentViewer->GetDocument();
8194 nsCOMArray<nsIDocShellTreeItem> childShells;
8195 int32_t i = 0;
8196 nsCOMPtr<nsIDocShellTreeItem> child;
8197 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
8198 child) {
8199 childShells.AppendObject(child);
8202 // get the previous content viewer size
8203 nsIntRect oldBounds(0, 0, 0, 0);
8204 mLSHE->GetViewerBounds(oldBounds);
8206 // Restore the refresh URI list. The refresh timers will be restarted
8207 // when EndPageLoad() is called.
8208 nsCOMPtr<nsIMutableArray> refreshURIList;
8209 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
8211 // Reattach to the window object.
8212 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
8213 rv = mContentViewer->Open(windowState, mLSHE);
8214 mIsRestoringDocument = false;
8216 // Hack to keep nsDocShellEditorData alive across the
8217 // SetContentViewer(nullptr) call below.
8218 nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
8220 // Now remove it from the cached presentation.
8221 mLSHE->SetContentViewer(nullptr);
8222 mEODForCurrentDocument = false;
8224 mLSHE->SetEditorData(data.forget());
8226 #ifdef DEBUG
8228 nsCOMPtr<nsIMutableArray> refreshURIs;
8229 mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIs));
8230 nsCOMPtr<nsIDocShellTreeItem> childShell;
8231 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
8232 NS_ASSERTION(!refreshURIs && !childShell,
8233 "SHEntry should have cleared presentation state");
8235 #endif
8237 // Restore the sticky state of the viewer. The viewer has set this state
8238 // on the history entry in Destroy() just before marking itself non-sticky,
8239 // to avoid teardown of the presentation.
8240 mContentViewer->SetSticky(sticky);
8242 NS_ENSURE_SUCCESS(rv, rv);
8244 // mLSHE is now our currently-loaded document.
8245 SetHistoryEntry(&mOSHE, mLSHE);
8247 // XXX special wyciwyg handling in Embed()?
8249 // We aren't going to restore any items from the LayoutHistoryState,
8250 // but we don't want them to stay around in case the page is reloaded.
8251 SetLayoutHistoryState(nullptr);
8253 // This is the end of our Embed() replacement
8255 mSavingOldViewer = false;
8256 mEODForCurrentDocument = false;
8258 // Tell the event loop to favor plevents over user events, see comments
8259 // in CreateContentViewer.
8260 if (++gNumberOfDocumentsLoading == 1) {
8261 FavorPerformanceHint(true);
8264 if (oldCv && newCv) {
8265 newCv->SetMinFontSize(minFontSize);
8266 newCv->SetTextZoom(textZoom);
8267 newCv->SetFullZoom(pageZoom);
8268 newCv->SetOverrideDPPX(overrideDPPX);
8269 newCv->SetAuthorStyleDisabled(styleDisabled);
8272 if (document) {
8273 RefPtr<nsDocShell> parent = GetParentDocshell();
8274 if (parent) {
8275 nsCOMPtr<nsIDocument> d = parent->GetDocument();
8276 if (d) {
8277 if (d->EventHandlingSuppressed()) {
8278 document->SuppressEventHandling(d->EventHandlingSuppressed());
8283 // Use the uri from the mLSHE we had when we entered this function
8284 // (which need not match the document's URI if anchors are involved),
8285 // since that's the history entry we're loading. Note that if we use
8286 // origLSHE we don't have to worry about whether the entry in question
8287 // is still mLSHE or whether it's now mOSHE.
8288 nsCOMPtr<nsIURI> uri;
8289 origLSHE->GetURI(getter_AddRefs(uri));
8290 SetCurrentURI(uri, document->GetChannel(), true, 0);
8293 // This is the end of our CreateContentViewer() replacement.
8294 // Now we simulate a load. First, we restore the state of the javascript
8295 // window object.
8296 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
8297 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
8299 // Now, dispatch a title change event which would happen as the
8300 // <head> is parsed.
8301 document->NotifyPossibleTitleChange(false);
8303 // Now we simulate appending child docshells for subframes.
8304 for (i = 0; i < childShells.Count(); ++i) {
8305 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
8306 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
8308 // Make sure to not clobber the state of the child. Since AddChild
8309 // always clobbers it, save it off first.
8310 bool allowPlugins;
8311 childShell->GetAllowPlugins(&allowPlugins);
8313 bool allowJavascript;
8314 childShell->GetAllowJavascript(&allowJavascript);
8316 bool allowRedirects;
8317 childShell->GetAllowMetaRedirects(&allowRedirects);
8319 bool allowSubframes;
8320 childShell->GetAllowSubframes(&allowSubframes);
8322 bool allowImages;
8323 childShell->GetAllowImages(&allowImages);
8325 bool allowMedia = childShell->GetAllowMedia();
8327 bool allowDNSPrefetch;
8328 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
8330 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
8331 bool allowContentRetargetingOnChildren =
8332 childShell->GetAllowContentRetargetingOnChildren();
8334 uint32_t defaultLoadFlags;
8335 childShell->GetDefaultLoadFlags(&defaultLoadFlags);
8337 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
8338 // the child inherits our state. Among other things, this means that the
8339 // child inherits our mIsActive mPrivateBrowsingId, which is what we want.
8340 AddChild(childItem);
8342 childShell->SetAllowPlugins(allowPlugins);
8343 childShell->SetAllowJavascript(allowJavascript);
8344 childShell->SetAllowMetaRedirects(allowRedirects);
8345 childShell->SetAllowSubframes(allowSubframes);
8346 childShell->SetAllowImages(allowImages);
8347 childShell->SetAllowMedia(allowMedia);
8348 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
8349 childShell->SetAllowContentRetargeting(allowContentRetargeting);
8350 childShell->SetAllowContentRetargetingOnChildren(
8351 allowContentRetargetingOnChildren);
8352 childShell->SetDefaultLoadFlags(defaultLoadFlags);
8354 rv = childShell->BeginRestore(nullptr, false);
8355 NS_ENSURE_SUCCESS(rv, rv);
8358 // Make sure to restore the window state after adding the child shells back
8359 // to the tree. This is necessary for Thaw() and Resume() to propagate
8360 // properly.
8361 rv = privWin->RestoreWindowState(windowState);
8362 NS_ENSURE_SUCCESS(rv, rv);
8364 nsCOMPtr<nsIPresShell> shell = GetPresShell();
8366 // We may be displayed on a different monitor (or in a different
8367 // HiDPI mode) than when we got into the history list. So we need
8368 // to check if this has happened. See bug 838239.
8370 // Because the prescontext normally handles resolution changes via
8371 // a runnable (see nsPresContext::UIResolutionChanged), its device
8372 // context won't be -immediately- updated as a result of calling
8373 // shell->BackingScaleFactorChanged().
8375 // But we depend on that device context when adjusting the view size
8376 // via mContentViewer->SetBounds(newBounds) below. So we need to
8377 // explicitly tell it to check for changed resolution here.
8378 if (shell && shell->GetPresContext()->DeviceContext()->CheckDPIChange()) {
8379 shell->BackingScaleFactorChanged();
8382 nsViewManager* newVM = shell ? shell->GetViewManager() : nullptr;
8383 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
8385 // Insert the new root view at the correct location in the view tree.
8386 if (container) {
8387 nsSubDocumentFrame* subDocFrame =
8388 do_QueryFrame(container->GetPrimaryFrame());
8389 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
8390 } else {
8391 rootViewParent = nullptr;
8393 if (sibling &&
8394 sibling->GetShell() &&
8395 sibling->GetShell()->GetViewManager()) {
8396 rootViewSibling = sibling->GetShell()->GetViewManager()->GetRootView();
8397 } else {
8398 rootViewSibling = nullptr;
8400 if (rootViewParent && newRootView &&
8401 newRootView->GetParent() != rootViewParent) {
8402 nsViewManager* parentVM = rootViewParent->GetViewManager();
8403 if (parentVM) {
8404 // InsertChild(parent, child, sib, true) inserts the child after
8405 // sib in content order, which is before sib in view order. BUT
8406 // when sib is null it inserts at the end of the the document
8407 // order, i.e., first in view order. But when oldRootSibling is
8408 // null, the old root as at the end of the view list --- last in
8409 // content order --- and we want to call InsertChild(parent, child,
8410 // nullptr, false) in that case.
8411 parentVM->InsertChild(rootViewParent, newRootView,
8412 rootViewSibling,
8413 rootViewSibling ? true : false);
8415 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
8416 "error in InsertChild");
8420 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
8422 // If parent is suspended, increase suspension count.
8423 // This can't be done as early as event suppression since this
8424 // depends on docshell tree.
8425 privWinInner->SyncStateFromParentWindow();
8427 // Now that all of the child docshells have been put into place, we can
8428 // restart the timers for the window and all of the child frames.
8429 privWinInner->Resume();
8431 // Restore the refresh URI list. The refresh timers will be restarted
8432 // when EndPageLoad() is called.
8433 mRefreshURIList = refreshURIList;
8435 // Meta-refresh timers have been restarted for this shell, but not
8436 // for our children. Walk the child shells and restart their timers.
8437 nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
8438 while (iter.HasMore()) {
8439 nsCOMPtr<nsIDocShell> child = do_QueryObject(iter.GetNext());
8440 if (child) {
8441 child->ResumeRefreshURIs();
8445 // Make sure this presentation is the same size as the previous
8446 // presentation. If this is not the same size we showed it at last time,
8447 // then we need to resize the widget.
8449 // XXXbryner This interacts poorly with Firefox's infobar. If the old
8450 // presentation had the infobar visible, then we will resize the new
8451 // presentation to that smaller size. However, firing the locationchanged
8452 // event will hide the infobar, which will immediately resize the window
8453 // back to the larger size. A future optimization might be to restore
8454 // the presentation at the "wrong" size, then fire the locationchanged
8455 // event and check whether the docshell's new size is the same as the
8456 // cached viewer size (skipping the resize if they are equal).
8458 if (newRootView) {
8459 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
8460 #ifdef DEBUG_PAGE_CACHE
8461 printf("resize widget(%d, %d, %d, %d)\n", newBounds.x,
8462 newBounds.y, newBounds.width, newBounds.height);
8463 #endif
8464 mContentViewer->SetBounds(newBounds);
8465 } else {
8466 nsIScrollableFrame* rootScrollFrame =
8467 shell->GetRootScrollFrameAsScrollable();
8468 if (rootScrollFrame) {
8469 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
8474 // The FinishRestore call below can kill these, null them out so we don't
8475 // have invalid pointer lying around.
8476 newRootView = rootViewSibling = rootViewParent = nullptr;
8477 newVM = nullptr;
8479 // Simulate the completion of the load.
8480 nsDocShell::FinishRestore();
8482 // Restart plugins, and paint the content.
8483 if (shell) {
8484 shell->Thaw();
8487 return privWin->FireDelayedDOMEvents();
8490 nsresult
8491 nsDocShell::CreateContentViewer(const nsACString& aContentType,
8492 nsIRequest* aRequest,
8493 nsIStreamListener** aContentHandler)
8495 *aContentHandler = nullptr;
8497 if (!mTreeOwner || mIsBeingDestroyed) {
8498 // If we don't have a tree owner, then we're in the process of being
8499 // destroyed. Rather than continue trying to load something, just give up.
8500 return NS_ERROR_DOCSHELL_DYING;
8503 // Can we check the content type of the current content viewer
8504 // and reuse it without destroying it and re-creating it?
8506 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
8508 // Instantiate the content viewer object
8509 nsCOMPtr<nsIContentViewer> viewer;
8510 nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
8511 aContentHandler, getter_AddRefs(viewer));
8513 if (NS_FAILED(rv)) {
8514 return rv;
8517 // Notify the current document that it is about to be unloaded!!
8519 // It is important to fire the unload() notification *before* any state
8520 // is changed within the DocShell - otherwise, javascript will get the
8521 // wrong information :-(
8524 if (mSavingOldViewer) {
8525 // We determined that it was safe to cache the document presentation
8526 // at the time we initiated the new load. We need to check whether
8527 // it's still safe to do so, since there may have been DOM mutations
8528 // or new requests initiated.
8529 nsCOMPtr<nsIDocument> doc = viewer->GetDocument();
8530 mSavingOldViewer = CanSavePresentation(mLoadType, aRequest, doc);
8533 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
8535 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8536 if (aOpenedChannel) {
8537 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
8539 FirePageHideNotification(!mSavingOldViewer);
8540 if (mIsBeingDestroyed) {
8541 // Force to stop the newly created orphaned viewer.
8542 viewer->Stop();
8543 return NS_ERROR_DOCSHELL_DYING;
8545 mLoadingURI = nullptr;
8547 // Set mFiredUnloadEvent = false so that the unload handler for the
8548 // *new* document will fire.
8549 mFiredUnloadEvent = false;
8551 // we've created a new document so go ahead and call
8552 // OnLoadingSite(), but don't fire OnLocationChange()
8553 // notifications before we've called Embed(). See bug 284993.
8554 mURIResultedInDocument = true;
8555 bool errorOnLocationChangeNeeded = false;
8556 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
8557 nsCOMPtr<nsIURI> failedURI;
8559 if (mLoadType == LOAD_ERROR_PAGE) {
8560 // We need to set the SH entry and our current URI here and not
8561 // at the moment we load the page. We want the same behavior
8562 // of Stop() as for a normal page load. See bug 514232 for details.
8564 // Revert mLoadType to load type to state the page load failed,
8565 // following function calls need it.
8566 mLoadType = mFailedLoadType;
8569 nsIDocument* doc = viewer->GetDocument();
8570 if (doc) {
8571 doc->SetFailedChannel(failedChannel);
8574 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
8575 if (failedChannel) {
8576 // Make sure we have a URI to set currentURI.
8577 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
8579 else {
8580 // if there is no failed channel we have to explicitly provide
8581 // a triggeringPrincipal for the history entry.
8582 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
8585 if (!failedURI) {
8586 failedURI = mFailedURI;
8588 if (!failedURI) {
8589 // We need a URI object to store a session history entry, so make up a URI
8590 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
8593 // When we don't have failedURI, something wrong will happen. See
8594 // bug 291876.
8595 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
8597 mFailedChannel = nullptr;
8598 mFailedURI = nullptr;
8600 // Create an shistory entry for the old load.
8601 if (failedURI) {
8602 errorOnLocationChangeNeeded = OnNewURI(
8603 failedURI, failedChannel, triggeringPrincipal,
8604 nullptr, mLoadType, false, false, false);
8607 // Be sure to have a correct mLSHE, it may have been cleared by
8608 // EndPageLoad. See bug 302115.
8609 if (mSessionHistory && !mLSHE) {
8610 int32_t idx;
8611 mSessionHistory->LegacySHistory()->GetRequestedIndex(&idx);
8612 if (idx == -1) {
8613 idx = mSessionHistory->Index();
8615 mSessionHistory->LegacySHistory()->
8616 GetEntryAtIndex(idx, false, getter_AddRefs(mLSHE));
8619 mLoadType = LOAD_ERROR_PAGE;
8622 bool onLocationChangeNeeded = OnLoadingSite(aOpenedChannel, false);
8624 // let's try resetting the load group if we need to...
8625 nsCOMPtr<nsILoadGroup> currentLoadGroup;
8626 NS_ENSURE_SUCCESS(
8627 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
8628 NS_ERROR_FAILURE);
8630 if (currentLoadGroup != mLoadGroup) {
8631 nsLoadFlags loadFlags = 0;
8633 // Cancel any URIs that are currently loading...
8634 // XXX: Need to do this eventually Stop();
8636 // Retarget the document to this loadgroup...
8638 /* First attach the channel to the right loadgroup
8639 * and then remove from the old loadgroup. This
8640 * puts the notifications in the right order and
8641 * we don't null-out mLSHE in OnStateChange() for
8642 * all redirected urls
8644 aOpenedChannel->SetLoadGroup(mLoadGroup);
8646 // Mark the channel as being a document URI...
8647 aOpenedChannel->GetLoadFlags(&loadFlags);
8648 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
8649 if (SandboxFlagsImplyCookies(mSandboxFlags)) {
8650 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
8653 aOpenedChannel->SetLoadFlags(loadFlags);
8655 mLoadGroup->AddRequest(aRequest, nullptr);
8656 if (currentLoadGroup) {
8657 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
8660 // Update the notification callbacks, so that progress and
8661 // status information are sent to the right docshell...
8662 aOpenedChannel->SetNotificationCallbacks(this);
8665 NS_ENSURE_SUCCESS(Embed(viewer, "", nullptr), NS_ERROR_FAILURE);
8667 mSavedRefreshURIList = nullptr;
8668 mSavingOldViewer = false;
8669 mEODForCurrentDocument = false;
8671 // if this document is part of a multipart document,
8672 // the ID can be used to distinguish it from the other parts.
8673 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
8674 if (multiPartChannel) {
8675 nsCOMPtr<nsIPresShell> shell = GetPresShell();
8676 if (NS_SUCCEEDED(rv) && shell) {
8677 nsIDocument* doc = shell->GetDocument();
8678 if (doc) {
8679 uint32_t partID;
8680 multiPartChannel->GetPartID(&partID);
8681 doc->SetPartID(partID);
8686 // Give hint to native plevent dispatch mechanism. If a document
8687 // is loading the native plevent dispatch mechanism should favor
8688 // performance over normal native event dispatch priorities.
8689 if (++gNumberOfDocumentsLoading == 1) {
8690 // Hint to favor performance for the plevent notification mechanism.
8691 // We want the pages to load as fast as possible even if its means
8692 // native messages might be starved.
8693 FavorPerformanceHint(true);
8696 if (errorOnLocationChangeNeeded){
8697 FireOnLocationChange(this, failedChannel, failedURI,
8698 LOCATION_CHANGE_ERROR_PAGE);
8699 } else if (onLocationChangeNeeded) {
8700 FireOnLocationChange(this, aRequest, mCurrentURI, 0);
8703 return NS_OK;
8706 nsresult
8707 nsDocShell::NewContentViewerObj(const nsACString& aContentType,
8708 nsIRequest* aRequest, nsILoadGroup* aLoadGroup,
8709 nsIStreamListener** aContentHandler,
8710 nsIContentViewer** aViewer)
8712 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8714 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8715 nsContentUtils::FindInternalContentViewer(aContentType);
8716 if (!docLoaderFactory) {
8717 return NS_ERROR_FAILURE;
8720 // Now create an instance of the content viewer nsLayoutDLF makes the
8721 // determination if it should be a "view-source" instead of "view"
8722 nsresult rv = docLoaderFactory->CreateInstance("view",
8723 aOpenedChannel,
8724 aLoadGroup, aContentType,
8725 this,
8726 nullptr,
8727 aContentHandler,
8728 aViewer);
8729 NS_ENSURE_SUCCESS(rv, rv);
8731 (*aViewer)->SetContainer(this);
8732 return NS_OK;
8735 nsresult
8736 nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer)
8738 MOZ_ASSERT(!mIsBeingDestroyed);
8741 // Copy content viewer state from previous or parent content viewer.
8743 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8745 // Do NOT to maintain a reference to the old content viewer outside
8746 // of this "copying" block, or it will not be destroyed until the end of
8747 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8749 // In this block of code, if we get an error result, we return it
8750 // but if we get a null pointer, that's perfectly legal for parent
8751 // and parentContentViewer.
8754 int32_t x = 0;
8755 int32_t y = 0;
8756 int32_t cx = 0;
8757 int32_t cy = 0;
8759 // This will get the size from the current content viewer or from the
8760 // Init settings
8761 DoGetPositionAndSize(&x, &y, &cx, &cy);
8763 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8764 NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)),
8765 NS_ERROR_FAILURE);
8766 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8768 const Encoding* forceCharset = nullptr;
8769 const Encoding* hintCharset = nullptr;
8770 int32_t hintCharsetSource;
8771 int32_t minFontSize;
8772 float textZoom;
8773 float pageZoom;
8774 float overrideDPPX;
8775 bool styleDisabled;
8776 // |newMUDV| also serves as a flag to set the data from the above vars
8777 nsCOMPtr<nsIContentViewer> newCv;
8779 if (mContentViewer || parent) {
8780 nsCOMPtr<nsIContentViewer> oldCv;
8781 if (mContentViewer) {
8782 // Get any interesting state from old content viewer
8783 // XXX: it would be far better to just reuse the document viewer ,
8784 // since we know we're just displaying the same document as before
8785 oldCv = mContentViewer;
8787 // Tell the old content viewer to hibernate in session history when
8788 // it is destroyed.
8790 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8791 if (mOSHE) {
8792 mOSHE->SyncPresentationState();
8794 mSavingOldViewer = false;
8796 } else {
8797 // No old content viewer, so get state from parent's content viewer
8798 parent->GetContentViewer(getter_AddRefs(oldCv));
8801 if (oldCv) {
8802 newCv = aNewViewer;
8803 if (newCv) {
8804 forceCharset = oldCv->GetForceCharset();
8805 hintCharset = oldCv->GetHintCharset();
8806 NS_ENSURE_SUCCESS(oldCv->GetHintCharacterSetSource(&hintCharsetSource),
8807 NS_ERROR_FAILURE);
8808 NS_ENSURE_SUCCESS(oldCv->GetMinFontSize(&minFontSize),
8809 NS_ERROR_FAILURE);
8810 NS_ENSURE_SUCCESS(oldCv->GetTextZoom(&textZoom),
8811 NS_ERROR_FAILURE);
8812 NS_ENSURE_SUCCESS(oldCv->GetFullZoom(&pageZoom),
8813 NS_ERROR_FAILURE);
8814 NS_ENSURE_SUCCESS(oldCv->GetOverrideDPPX(&overrideDPPX),
8815 NS_ERROR_FAILURE);
8816 NS_ENSURE_SUCCESS(oldCv->GetAuthorStyleDisabled(&styleDisabled),
8817 NS_ERROR_FAILURE);
8822 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8823 bool isActive = false;
8824 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8825 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
8826 if (contentViewer) {
8827 // Stop any activity that may be happening in the old document before
8828 // releasing it...
8829 contentViewer->Stop();
8831 // Try to extract the canvas background color from the old
8832 // presentation shell, so we can use it for the next document.
8833 nsCOMPtr<nsIPresShell> shell;
8834 contentViewer->GetPresShell(getter_AddRefs(shell));
8836 if (shell) {
8837 bgcolor = shell->GetCanvasBackground();
8838 isActive = shell->IsActive();
8841 contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8842 aNewViewer->SetPreviousViewer(contentViewer);
8844 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8845 // We don't plan to save a viewer in mOSHE; tell it to drop
8846 // any other state it's holding.
8847 mOSHE->SyncPresentationState();
8850 mContentViewer = nullptr;
8852 // Now that we're about to switch documents, forget all of our children.
8853 // Note that we cached them as needed up in CaptureState above.
8854 DestroyChildren();
8856 mContentViewer = aNewViewer;
8858 nsCOMPtr<nsIWidget> widget;
8859 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8861 nsIntRect bounds(x, y, cx, cy);
8863 mContentViewer->SetNavigationTiming(mTiming);
8865 if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
8866 mContentViewer = nullptr;
8867 NS_WARNING("ContentViewer Initialization failed");
8868 return NS_ERROR_FAILURE;
8871 // If we have old state to copy, set the old state onto the new content
8872 // viewer
8873 if (newCv) {
8874 newCv->SetForceCharset(forceCharset);
8875 newCv->SetHintCharset(hintCharset);
8876 NS_ENSURE_SUCCESS(newCv->SetHintCharacterSetSource(hintCharsetSource),
8877 NS_ERROR_FAILURE);
8878 NS_ENSURE_SUCCESS(newCv->SetMinFontSize(minFontSize),
8879 NS_ERROR_FAILURE);
8880 NS_ENSURE_SUCCESS(newCv->SetTextZoom(textZoom),
8881 NS_ERROR_FAILURE);
8882 NS_ENSURE_SUCCESS(newCv->SetFullZoom(pageZoom),
8883 NS_ERROR_FAILURE);
8884 NS_ENSURE_SUCCESS(newCv->SetOverrideDPPX(overrideDPPX),
8885 NS_ERROR_FAILURE);
8886 NS_ENSURE_SUCCESS(newCv->SetAuthorStyleDisabled(styleDisabled),
8887 NS_ERROR_FAILURE);
8890 // Stuff the bgcolor from the old pres shell into the new
8891 // pres shell. This improves page load continuity.
8892 nsCOMPtr<nsIPresShell> shell;
8893 mContentViewer->GetPresShell(getter_AddRefs(shell));
8895 if (shell) {
8896 shell->SetCanvasBackground(bgcolor);
8897 if (isActive) {
8898 shell->SetIsActive(isActive);
8902 // XXX: It looks like the LayoutState gets restored again in Embed()
8903 // right after the call to SetupNewViewer(...)
8905 // We don't show the mContentViewer yet, since we want to draw the old page
8906 // until we have enough of the new page to show. Just return with the new
8907 // viewer still set to hidden.
8909 return NS_OK;
8912 nsresult
8913 nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry)
8915 NS_ENSURE_STATE(mContentViewer);
8916 nsCOMPtr<nsIDocument> document = GetDocument();
8917 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8919 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8920 if (aShEntry) {
8921 nsresult rv = aShEntry->GetStateData(getter_AddRefs(scContainer));
8922 NS_ENSURE_SUCCESS(rv, rv);
8924 // If aShEntry is null, just set the document's state object to null.
8927 // It's OK for scContainer too be null here; that just means there's no
8928 // state data associated with this history entry.
8929 document->SetStateObject(scContainer);
8931 return NS_OK;
8934 nsresult
8935 nsDocShell::CheckLoadingPermissions()
8937 // This method checks whether the caller may load content into
8938 // this docshell. Even though we've done our best to hide windows
8939 // from code that doesn't have the right to access them, it's
8940 // still possible for an evil site to open a window and access
8941 // frames in the new window through window.frames[] (which is
8942 // allAccess for historic reasons), so we still need to do this
8943 // check on load.
8944 nsresult rv = NS_OK;
8946 if (!gValidateOrigin || !IsFrame()) {
8947 // Origin validation was turned off, or we're not a frame.
8948 // Permit all loads.
8950 return rv;
8953 // Note - The check for a current JSContext here isn't necessarily sensical.
8954 // It's just designed to preserve the old semantics during a mass-conversion
8955 // patch.
8956 if (!nsContentUtils::GetCurrentJSContext()) {
8957 return NS_OK;
8960 // Check if the caller is from the same origin as this docshell,
8961 // or any of its ancestors.
8962 nsCOMPtr<nsIDocShellTreeItem> item(this);
8963 do {
8964 nsCOMPtr<nsIScriptGlobalObject> sgo = do_GetInterface(item);
8965 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8967 nsIPrincipal* p;
8968 if (!sop || !(p = sop->GetPrincipal())) {
8969 return NS_ERROR_UNEXPECTED;
8972 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8973 // Same origin, permit load
8974 return NS_OK;
8977 nsCOMPtr<nsIDocShellTreeItem> tmp;
8978 item->GetSameTypeParent(getter_AddRefs(tmp));
8979 item.swap(tmp);
8980 } while (item);
8982 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8985 //*****************************************************************************
8986 // nsDocShell: Site Loading
8987 //*****************************************************************************
8989 void
8990 nsDocShell::CopyFavicon(nsIURI* aOldURI,
8991 nsIURI* aNewURI,
8992 nsIPrincipal* aLoadingPrincipal,
8993 bool aInPrivateBrowsing)
8995 if (XRE_IsContentProcess()) {
8996 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8997 if (contentChild) {
8998 mozilla::ipc::URIParams oldURI, newURI;
8999 SerializeURI(aOldURI, oldURI);
9000 SerializeURI(aNewURI, newURI);
9001 contentChild->SendCopyFavicon(oldURI, newURI,
9002 IPC::Principal(aLoadingPrincipal),
9003 aInPrivateBrowsing);
9005 return;
9008 #ifdef MOZ_PLACES
9009 nsCOMPtr<nsIFaviconService> favSvc =
9010 do_GetService("@mozilla.org/browser/favicon-service;1");
9011 if (favSvc) {
9012 favSvc->CopyFavicons(aOldURI, aNewURI,
9013 aInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE
9014 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE, nullptr);
9016 #endif
9019 class InternalLoadEvent : public Runnable
9021 public:
9022 InternalLoadEvent(nsDocShell* aDocShell,
9023 nsIURI* aURI,
9024 nsIURI* aOriginalURI,
9025 Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
9026 bool aLoadReplace,
9027 nsIURI* aReferrer, uint32_t aReferrerPolicy,
9028 nsIPrincipal* aTriggeringPrincipal,
9029 nsIPrincipal* aPrincipalToInherit,
9030 uint32_t aFlags,
9031 const char* aTypeHint,
9032 nsIInputStream* aPostData,
9033 nsIInputStream* aHeadersData,
9034 uint32_t aLoadType,
9035 nsISHEntry* aSHEntry,
9036 bool aFirstParty,
9037 const nsAString& aSrcdoc,
9038 nsIDocShell* aSourceDocShell,
9039 nsIURI* aBaseURI)
9040 : mozilla::Runnable("InternalLoadEvent")
9041 , mSrcdoc(aSrcdoc)
9042 , mDocShell(aDocShell)
9043 , mURI(aURI)
9044 , mOriginalURI(aOriginalURI)
9045 , mResultPrincipalURI(aResultPrincipalURI)
9046 , mLoadReplace(aLoadReplace)
9047 , mReferrer(aReferrer)
9048 , mReferrerPolicy(aReferrerPolicy)
9049 , mTriggeringPrincipal(aTriggeringPrincipal)
9050 , mPrincipalToInherit(aPrincipalToInherit)
9051 , mPostData(aPostData)
9052 , mHeadersData(aHeadersData)
9053 , mSHEntry(aSHEntry)
9054 , mFlags(aFlags)
9055 , mLoadType(aLoadType)
9056 , mFirstParty(aFirstParty)
9057 , mSourceDocShell(aSourceDocShell)
9058 , mBaseURI(aBaseURI)
9060 // Make sure to keep null things null as needed
9061 if (aTypeHint) {
9062 mTypeHint = aTypeHint;
9063 } else {
9064 mTypeHint.SetIsVoid(true);
9068 NS_IMETHOD
9069 Run() override
9071 return mDocShell->InternalLoad(mURI, mOriginalURI, mResultPrincipalURI,
9072 mLoadReplace,
9073 mReferrer,
9074 mReferrerPolicy,
9075 mTriggeringPrincipal, mPrincipalToInherit,
9076 mFlags, EmptyString(),
9077 mTypeHint.IsVoid() ? nullptr
9078 : mTypeHint.get(),
9079 VoidString(), mPostData,
9080 mHeadersData, mLoadType, mSHEntry,
9081 mFirstParty, mSrcdoc, mSourceDocShell,
9082 mBaseURI, nullptr,
9083 nullptr);
9086 private:
9087 nsCString mTypeHint;
9088 nsString mSrcdoc;
9090 RefPtr<nsDocShell> mDocShell;
9091 nsCOMPtr<nsIURI> mURI;
9092 nsCOMPtr<nsIURI> mOriginalURI;
9093 Maybe<nsCOMPtr<nsIURI>> mResultPrincipalURI;
9094 bool mLoadReplace;
9095 nsCOMPtr<nsIURI> mReferrer;
9096 uint32_t mReferrerPolicy;
9097 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
9098 nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
9099 nsCOMPtr<nsIInputStream> mPostData;
9100 nsCOMPtr<nsIInputStream> mHeadersData;
9101 nsCOMPtr<nsISHEntry> mSHEntry;
9102 uint32_t mFlags;
9103 uint32_t mLoadType;
9104 bool mFirstParty;
9105 nsCOMPtr<nsIDocShell> mSourceDocShell;
9106 nsCOMPtr<nsIURI> mBaseURI;
9110 * Returns true if we started an asynchronous load (i.e., from the network), but
9111 * the document we're loading there hasn't yet become this docshell's active
9112 * document.
9114 * When JustStartedNetworkLoad is true, you should be careful about modifying
9115 * mLoadType and mLSHE. These are both set when the asynchronous load first
9116 * starts, and the load expects that, when it eventually runs InternalLoad,
9117 * mLoadType and mLSHE will have their original values.
9119 bool
9120 nsDocShell::JustStartedNetworkLoad()
9122 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
9125 nsresult
9126 nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
9127 nsIPrincipal** aResult)
9129 nsCOMPtr<nsIPrincipal> prin =
9130 BasePrincipal::CreateCodebasePrincipal(aReferrer, mOriginAttributes);
9131 prin.forget(aResult);
9133 return *aResult ? NS_OK : NS_ERROR_FAILURE;
9136 NS_IMETHODIMP
9137 nsDocShell::InternalLoad(nsIURI* aURI,
9138 nsIURI* aOriginalURI,
9139 Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
9140 bool aLoadReplace,
9141 nsIURI* aReferrer,
9142 uint32_t aReferrerPolicy,
9143 nsIPrincipal* aTriggeringPrincipal,
9144 nsIPrincipal* aPrincipalToInherit,
9145 uint32_t aFlags,
9146 const nsAString& aWindowTarget,
9147 const char* aTypeHint,
9148 const nsAString& aFileName,
9149 nsIInputStream* aPostData,
9150 nsIInputStream* aHeadersData,
9151 uint32_t aLoadType,
9152 nsISHEntry* aSHEntry,
9153 bool aFirstParty,
9154 const nsAString& aSrcdoc,
9155 nsIDocShell* aSourceDocShell,
9156 nsIURI* aBaseURI,
9157 nsIDocShell** aDocShell,
9158 nsIRequest** aRequest)
9160 MOZ_ASSERT(aTriggeringPrincipal, "need a valid TriggeringPrincipal");
9162 nsresult rv = NS_OK;
9163 mOriginalUriString.Truncate();
9165 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9166 ("DOCSHELL %p InternalLoad %s\n",
9167 this, aURI ? aURI->GetSpecOrDefault().get() : ""));
9168 // Initialize aDocShell/aRequest
9169 if (aDocShell) {
9170 *aDocShell = nullptr;
9172 if (aRequest) {
9173 *aRequest = nullptr;
9176 if (!aURI) {
9177 return NS_ERROR_NULL_POINTER;
9180 NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
9182 NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
9184 rv = EnsureScriptEnvironment();
9185 if (NS_FAILED(rv)) {
9186 return rv;
9189 // wyciwyg urls can only be loaded through history. Any normal load of
9190 // wyciwyg through docshell is illegal. Disallow such loads.
9191 if (aLoadType & LOAD_CMD_NORMAL) {
9192 bool isWyciwyg = false;
9193 rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
9194 if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv)) {
9195 return NS_ERROR_FAILURE;
9199 bool isJavaScript = false;
9200 if (NS_FAILED(aURI->SchemeIs("javascript", &isJavaScript))) {
9201 isJavaScript = false;
9204 bool isTargetTopLevelDocShell = false;
9205 nsCOMPtr<nsIDocShell> targetDocShell;
9206 if (!aWindowTarget.IsEmpty()) {
9207 // Locate the target DocShell.
9208 nsCOMPtr<nsIDocShellTreeItem> targetItem;
9209 // Only _self, _parent, and _top are supported in noopener case. But we
9210 // have to be careful to not apply that to the noreferrer case. See bug
9211 // 1358469.
9212 bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
9213 (aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
9214 if (allowNamedTarget ||
9215 aWindowTarget.LowerCaseEqualsLiteral("_self") ||
9216 aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
9217 aWindowTarget.LowerCaseEqualsLiteral("_top")) {
9218 rv = FindItemWithName(aWindowTarget, nullptr, this, false,
9219 getter_AddRefs(targetItem));
9220 NS_ENSURE_SUCCESS(rv, rv);
9223 targetDocShell = do_QueryInterface(targetItem);
9224 if (targetDocShell) {
9225 // If the targetDocShell and the rootDocShell are the same, then the
9226 // targetDocShell is the top level document and hence we should
9227 // consider this TYPE_DOCUMENT
9229 // For example:
9230 // 1. target="_top"
9231 // 2. target="_parent", where this docshell is in the 2nd level of
9232 // docshell tree.
9233 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
9234 targetDocShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
9235 NS_ASSERTION(sameTypeRoot,
9236 "No document shell root tree item from targetDocShell!");
9237 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(sameTypeRoot);
9238 NS_ASSERTION(rootShell,
9239 "No root docshell from document shell root tree item.");
9240 isTargetTopLevelDocShell = targetDocShell == rootShell;
9241 } else {
9242 // If the targetDocShell doesn't exist, then this is a new docShell
9243 // and we should consider this a TYPE_DOCUMENT load
9245 // For example, when target="_blank"
9246 isTargetTopLevelDocShell = true;
9250 // The contentType will be INTERNAL_(I)FRAME if:
9251 // 1. This docshell is for iframe.
9252 // 2. AND aWindowTarget is not a new window, nor a top-level window.
9254 // This variable will be used when we call NS_CheckContentLoadPolicy, and
9255 // later when we call DoURILoad.
9256 uint32_t contentType;
9257 if (IsFrame() && !isTargetTopLevelDocShell) {
9258 nsCOMPtr<Element> requestingElement =
9259 mScriptGlobal->AsOuter()->GetFrameElementInternal();
9260 if (requestingElement) {
9261 contentType = requestingElement->IsHTMLElement(nsGkAtoms::iframe) ?
9262 nsIContentPolicy::TYPE_INTERNAL_IFRAME : nsIContentPolicy::TYPE_INTERNAL_FRAME;
9263 } else {
9264 // If we have lost our frame element by now, just assume we're
9265 // an iframe since that's more common.
9266 contentType = nsIContentPolicy::TYPE_INTERNAL_IFRAME;
9268 } else {
9269 contentType = nsIContentPolicy::TYPE_DOCUMENT;
9270 isTargetTopLevelDocShell = true;
9273 // If there's no targetDocShell, that means we are about to create a new
9274 // window (or aWindowTarget is empty). Perform a content policy check before
9275 // creating the window. Please note for all other docshell loads
9276 // content policy checks are performed within the contentSecurityManager
9277 // when the channel is about to be openend.
9278 if (!targetDocShell && !aWindowTarget.IsEmpty()) {
9279 MOZ_ASSERT(contentType == nsIContentPolicy::TYPE_DOCUMENT,
9280 "opening a new window requires type to be TYPE_DOCUMENT");
9282 nsISupports* requestingContext = nullptr;
9283 if (XRE_IsContentProcess()) {
9284 // In e10s the child process doesn't have access to the element that
9285 // contains the browsing context (because that element is in the chrome
9286 // process). So we just pass mScriptGlobal.
9287 requestingContext = ToSupports(mScriptGlobal);
9288 } else {
9289 // This is for loading non-e10s tabs and toplevel windows of various
9290 // sorts.
9291 // For the toplevel window cases, requestingElement will be null.
9292 nsCOMPtr<Element> requestingElement =
9293 mScriptGlobal->AsOuter()->GetFrameElementInternal();
9294 requestingContext = requestingElement;
9297 // Ideally we should use the same loadinfo as within DoURILoad which
9298 // should match this one when both are applicable.
9299 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow = mScriptGlobal->AsOuter();
9300 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
9301 new LoadInfo(loadingWindow,
9302 aTriggeringPrincipal,
9303 requestingContext,
9304 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK);
9306 // Since Content Policy checks are performed within docShell as well as
9307 // the ContentSecurityManager we need a reliable way to let certain
9308 // nsIContentPolicy consumers ignore duplicate calls.
9309 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
9311 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
9312 rv = NS_CheckContentLoadPolicy(aURI,
9313 secCheckLoadInfo,
9314 EmptyCString(), // mime guess
9315 &shouldLoad);
9317 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
9318 if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
9319 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
9322 return NS_ERROR_CONTENT_BLOCKED;
9326 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
9328 // Get a principal from the current document if necessary. Note that we only
9329 // do this for URIs that inherit a security context and local file URIs;
9330 // in particular we do NOT do this for about:blank. This way, random
9331 // about:blank loads that have no principal (which basically means they were
9332 // done by someone from chrome manually messing with our nsIWebNavigation
9333 // or by C++ setting document.location) don't get a funky principal. If
9334 // callers want something interesting to happen with the about:blank
9335 // principal in this case, they should pass aPrincipalToInherit in.
9338 bool inherits;
9339 // One more twist: Don't inherit the principal for external loads.
9340 if (aLoadType != LOAD_NORMAL_EXTERNAL && !principalToInherit &&
9341 (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
9342 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
9343 &inherits)) &&
9344 inherits) {
9345 principalToInherit = GetInheritedPrincipal(true);
9349 nsIDocument* doc = mContentViewer ? mContentViewer->GetDocument()
9350 : nullptr;
9352 const bool isDocumentAuxSandboxed = doc &&
9353 (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
9355 if (aURI && mLoadURIDelegate &&
9356 (!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
9357 // Dispatch only load requests for the current or a new window to the
9358 // delegate, e.g., to allow for GeckoView apps to handle the load event
9359 // outside of Gecko.
9360 const int where = (aWindowTarget.IsEmpty() || targetDocShell)
9361 ? nsIBrowserDOMWindow::OPEN_CURRENTWINDOW
9362 : nsIBrowserDOMWindow::OPEN_NEWWINDOW;
9364 if (where == nsIBrowserDOMWindow::OPEN_NEWWINDOW && isDocumentAuxSandboxed) {
9365 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9368 bool loadURIHandled = false;
9369 rv = mLoadURIDelegate->LoadURI(aURI, where, aFlags, aTriggeringPrincipal,
9370 &loadURIHandled);
9371 if (NS_SUCCEEDED(rv) && loadURIHandled) {
9372 // The request has been handled, nothing to do here.
9373 return NS_OK;
9378 // Resolve the window target before going any further...
9379 // If the load has been targeted to another DocShell, then transfer the
9380 // load to it...
9382 if (!aWindowTarget.IsEmpty()) {
9383 // We've already done our owner-inheriting. Mask out that bit, so we
9384 // don't try inheriting an owner from the target window if we came up
9385 // with a null owner above.
9386 aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
9388 bool isNewWindow = false;
9389 if (!targetDocShell) {
9390 // If the docshell's document is sandboxed, only open a new window
9391 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
9392 // (i.e. if allow-popups is specified)
9393 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
9394 if (isDocumentAuxSandboxed) {
9395 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9398 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
9399 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
9401 nsCOMPtr<nsPIDOMWindowOuter> newWin;
9402 nsAutoCString spec;
9403 if (aURI) {
9404 aURI->GetSpec(spec);
9406 // If we are a noopener load, we just hand the whole thing over to our
9407 // window.
9408 if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
9409 // Various asserts that we know to hold because NO_OPENER loads can only
9410 // happen for links.
9411 MOZ_ASSERT(!aLoadReplace);
9412 MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal);
9413 MOZ_ASSERT((aFlags & ~INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED) ==
9414 INTERNAL_LOAD_FLAGS_NO_OPENER ||
9415 (aFlags & ~INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED) ==
9416 (INTERNAL_LOAD_FLAGS_NO_OPENER |
9417 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
9418 MOZ_ASSERT(!aPostData);
9419 MOZ_ASSERT(!aHeadersData);
9420 // If OnLinkClickSync was invoked inside the onload handler, the load
9421 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
9422 // LOAD_LINK.
9423 MOZ_ASSERT(aLoadType == LOAD_LINK ||
9424 aLoadType == LOAD_NORMAL_REPLACE);
9425 MOZ_ASSERT(!aSHEntry);
9426 MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
9428 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
9429 rv = CreateLoadInfo(getter_AddRefs(loadInfo));
9430 if (NS_FAILED(rv)) {
9431 return rv;
9434 // Set up our loadinfo so it will do the load as much like we would have
9435 // as possible.
9436 loadInfo->SetReferrer(aReferrer);
9437 loadInfo->SetReferrerPolicy(aReferrerPolicy);
9438 loadInfo->SetSendReferrer(!(aFlags &
9439 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
9440 loadInfo->SetOriginalURI(aOriginalURI);
9441 SetMaybeResultPrincipalURI(loadInfo, aResultPrincipalURI);
9442 loadInfo->SetLoadReplace(aLoadReplace);
9443 loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
9444 loadInfo->SetInheritPrincipal(
9445 aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
9446 // Explicit principal because we do not want any guesses as to what the
9447 // principal to inherit is: it should be aTriggeringPrincipal.
9448 loadInfo->SetPrincipalIsExplicit(true);
9449 loadInfo->SetLoadType(ConvertLoadTypeToDocShellInfoLoadType(LOAD_LINK));
9450 loadInfo->SetForceAllowDataURI(aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
9452 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
9453 aWindowTarget, // window name
9454 EmptyString(), // Features
9455 loadInfo,
9456 true, // aForceNoOpener
9457 getter_AddRefs(newWin));
9458 MOZ_ASSERT(!newWin);
9459 return rv;
9462 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
9463 aWindowTarget, // window name
9464 EmptyString(), // Features
9465 getter_AddRefs(newWin));
9467 // In some cases the Open call doesn't actually result in a new
9468 // window being opened. We can detect these cases by examining the
9469 // document in |newWin|, if any.
9470 nsCOMPtr<nsPIDOMWindowOuter> piNewWin = do_QueryInterface(newWin);
9471 if (piNewWin) {
9472 nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
9473 if (!newDoc || newDoc->IsInitialDocument()) {
9474 isNewWindow = true;
9475 aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
9479 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
9480 targetDocShell = do_QueryInterface(webNav);
9484 // Transfer the load to the target DocShell... Pass nullptr as the
9485 // window target name from to prevent recursive retargeting!
9487 if (NS_SUCCEEDED(rv) && targetDocShell) {
9488 rv = targetDocShell->InternalLoad(aURI,
9489 aOriginalURI,
9490 aResultPrincipalURI,
9491 aLoadReplace,
9492 aReferrer,
9493 aReferrerPolicy,
9494 aTriggeringPrincipal,
9495 principalToInherit,
9496 aFlags,
9497 EmptyString(), // No window target
9498 aTypeHint,
9499 VoidString(), // No forced download
9500 aPostData,
9501 aHeadersData,
9502 aLoadType,
9503 aSHEntry,
9504 aFirstParty,
9505 aSrcdoc,
9506 aSourceDocShell,
9507 aBaseURI,
9508 aDocShell,
9509 aRequest);
9510 if (rv == NS_ERROR_NO_CONTENT) {
9511 // XXXbz except we never reach this code!
9512 if (isNewWindow) {
9514 // At this point, a new window has been created, but the
9515 // URI did not have any data associated with it...
9517 // So, the best we can do, is to tear down the new window
9518 // that was just created!
9520 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = targetDocShell->GetWindow()) {
9521 domWin->Close();
9525 // NS_ERROR_NO_CONTENT should not be returned to the
9526 // caller... This is an internal error code indicating that
9527 // the URI had no data associated with it - probably a
9528 // helper-app style protocol (ie. mailto://)
9530 rv = NS_OK;
9531 } else if (isNewWindow) {
9532 // XXX: Once new windows are created hidden, the new
9533 // window will need to be made visible... For now,
9534 // do nothing.
9537 if (NS_SUCCEEDED(rv)) {
9538 // Switch to target tab if we're currently focused window.
9539 // Take loadDivertedInBackground into account so the behavior would be
9540 // the same as how the tab first opened.
9541 bool isTargetActive = false;
9542 targetDocShell->GetIsActive(&isTargetActive);
9543 nsCOMPtr<nsPIDOMWindowOuter> domWin = targetDocShell->GetWindow();
9544 if (mIsActive && !isTargetActive && domWin &&
9545 !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false)) {
9546 if (NS_FAILED(nsContentUtils::DispatchFocusChromeEvent(domWin))) {
9547 return NS_ERROR_FAILURE;
9553 // Else we ran out of memory, or were a popup and got blocked,
9554 // or something.
9556 return rv;
9560 // Load is being targetted at this docshell so return an error if the
9561 // docshell is in the process of being destroyed.
9563 if (mIsBeingDestroyed) {
9564 return NS_ERROR_FAILURE;
9567 NS_ENSURE_STATE(!HasUnloadedParent());
9569 rv = CheckLoadingPermissions();
9570 if (NS_FAILED(rv)) {
9571 return rv;
9574 if (mFiredUnloadEvent) {
9575 if (IsOKToLoadURI(aURI)) {
9576 MOZ_ASSERT(aWindowTarget.IsEmpty(),
9577 "Shouldn't have a window target here!");
9579 // If this is a replace load, make whatever load triggered
9580 // the unload event also a replace load, so we don't
9581 // create extra history entries.
9582 if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9583 mLoadType = LOAD_NORMAL_REPLACE;
9586 // Do this asynchronously
9587 nsCOMPtr<nsIRunnable> ev =
9588 new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
9589 aLoadReplace, aReferrer, aReferrerPolicy,
9590 aTriggeringPrincipal, principalToInherit,
9591 aFlags, aTypeHint, aPostData,
9592 aHeadersData, aLoadType, aSHEntry, aFirstParty,
9593 aSrcdoc, aSourceDocShell, aBaseURI);
9594 return DispatchToTabGroup(TaskCategory::Other, ev.forget());
9597 // Just ignore this load attempt
9598 return NS_OK;
9601 // If a source docshell has been passed, check to see if we are sandboxed
9602 // from it as the result of an iframe or CSP sandbox.
9603 if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
9604 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
9607 // If this docshell is owned by a frameloader, make sure to cancel
9608 // possible frameloader initialization before loading a new page.
9609 nsCOMPtr<nsIDocShellTreeItem> parent = GetParentDocshell();
9610 if (parent) {
9611 nsCOMPtr<nsIDocument> doc = parent->GetDocument();
9612 if (doc) {
9613 doc->TryCancelFrameLoaderInitialization(this);
9617 bool loadFromExternal = false;
9619 // Before going any further vet loads initiated by external programs.
9620 if (aLoadType == LOAD_NORMAL_EXTERNAL) {
9621 loadFromExternal = true;
9622 // Disallow external chrome: loads targetted at content windows
9623 bool isChrome = false;
9624 if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
9625 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9626 return NS_ERROR_FAILURE;
9629 // clear the decks to prevent context bleed-through (bug 298255)
9630 rv = CreateAboutBlankContentViewer(nullptr, nullptr);
9631 if (NS_FAILED(rv)) {
9632 return NS_ERROR_FAILURE;
9635 // reset loadType so we don't have to add lots of tests for
9636 // LOAD_NORMAL_EXTERNAL after this point
9637 aLoadType = LOAD_NORMAL;
9640 mAllowKeywordFixup =
9641 (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
9642 mURIResultedInDocument = false; // reset the clock...
9644 if (aLoadType == LOAD_NORMAL ||
9645 aLoadType == LOAD_STOP_CONTENT ||
9646 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
9647 aLoadType == LOAD_HISTORY ||
9648 aLoadType == LOAD_LINK) {
9649 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
9651 nsAutoCString curHash, newHash;
9652 bool curURIHasRef = false, newURIHasRef = false;
9654 nsresult rvURINew = aURI->GetRef(newHash);
9655 if (NS_SUCCEEDED(rvURINew)) {
9656 rvURINew = aURI->GetHasRef(&newURIHasRef);
9659 bool sameExceptHashes = false;
9660 if (currentURI && NS_SUCCEEDED(rvURINew)) {
9661 nsresult rvURIOld = currentURI->GetRef(curHash);
9662 if (NS_SUCCEEDED(rvURIOld)) {
9663 rvURIOld = currentURI->GetHasRef(&curURIHasRef);
9665 if (NS_SUCCEEDED(rvURIOld)) {
9666 if (NS_FAILED(currentURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
9667 sameExceptHashes = false;
9672 if (!sameExceptHashes && sURIFixup && currentURI &&
9673 NS_SUCCEEDED(rvURINew)) {
9674 // Maybe aURI came from the exposable form of currentURI?
9675 nsCOMPtr<nsIURI> currentExposableURI;
9676 rv = sURIFixup->CreateExposableURI(currentURI,
9677 getter_AddRefs(currentExposableURI));
9678 NS_ENSURE_SUCCESS(rv, rv);
9679 nsresult rvURIOld = currentExposableURI->GetRef(curHash);
9680 if (NS_SUCCEEDED(rvURIOld)) {
9681 rvURIOld = currentExposableURI->GetHasRef(&curURIHasRef);
9683 if (NS_SUCCEEDED(rvURIOld)) {
9684 if (NS_FAILED(currentExposableURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
9685 sameExceptHashes = false;
9690 bool historyNavBetweenSameDoc = false;
9691 if (mOSHE && aSHEntry) {
9692 // We're doing a history load.
9694 mOSHE->SharesDocumentWith(aSHEntry, &historyNavBetweenSameDoc);
9696 #ifdef DEBUG
9697 if (historyNavBetweenSameDoc) {
9698 nsCOMPtr<nsIInputStream> currentPostData;
9699 mOSHE->GetPostData(getter_AddRefs(currentPostData));
9700 NS_ASSERTION(currentPostData == aPostData,
9701 "Different POST data for entries for the same page?");
9703 #endif
9706 // A short-circuited load happens when we navigate between two SHEntries
9707 // for the same document. We do a short-circuited load under two
9708 // circumstances. Either
9710 // a) we're navigating between two different SHEntries which share a
9711 // document, or
9713 // b) we're navigating to a new shentry whose URI differs from the
9714 // current URI only in its hash, the new hash is non-empty, and
9715 // we're not doing a POST.
9717 // The restriction tha the SHEntries in (a) must be different ensures
9718 // that history.go(0) and the like trigger full refreshes, rather than
9719 // short-circuited loads.
9720 bool doShortCircuitedLoad =
9721 (historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
9722 (!aSHEntry && !aPostData &&
9723 sameExceptHashes && newURIHasRef);
9725 if (doShortCircuitedLoad) {
9726 // Save the position of the scrollers.
9727 nscoord cx = 0, cy = 0;
9728 GetCurScrollPos(ScrollOrientation_X, &cx);
9729 GetCurScrollPos(ScrollOrientation_Y, &cy);
9731 // Reset mLoadType to its original value once we exit this block,
9732 // because this short-circuited load might have started after a
9733 // normal, network load, and we don't want to clobber its load type.
9734 // See bug 737307.
9735 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
9737 // If a non-short-circuit load (i.e., a network load) is pending,
9738 // make this a replacement load, so that we don't add a SHEntry here
9739 // and the network load goes into the SHEntry it expects to.
9740 if (JustStartedNetworkLoad() && (aLoadType & LOAD_CMD_NORMAL)) {
9741 mLoadType = LOAD_NORMAL_REPLACE;
9742 } else {
9743 mLoadType = aLoadType;
9746 mURIResultedInDocument = true;
9748 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
9750 /* we need to assign mLSHE to aSHEntry right here, so that on History
9751 * loads, SetCurrentURI() called from OnNewURI() will send proper
9752 * onLocationChange() notifications to the browser to update
9753 * back/forward buttons.
9755 SetHistoryEntry(&mLSHE, aSHEntry);
9757 // Set the doc's URI according to the new history entry's URI.
9758 nsCOMPtr<nsIDocument> doc = GetDocument();
9759 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
9760 doc->SetDocumentURI(aURI);
9762 /* This is a anchor traversal with in the same page.
9763 * call OnNewURI() so that, this traversal will be
9764 * recorded in session and global history.
9766 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit;
9767 if (mOSHE) {
9768 mOSHE->GetTriggeringPrincipal(getter_AddRefs(newURITriggeringPrincipal));
9769 mOSHE->GetPrincipalToInherit(getter_AddRefs(newURIPrincipalToInherit));
9770 } else {
9771 newURITriggeringPrincipal = aTriggeringPrincipal;
9772 newURIPrincipalToInherit = doc->NodePrincipal();
9774 // Pass true for aCloneSHChildren, since we're not
9775 // changing documents here, so all of our subframes are
9776 // still relevant to the new session history entry.
9778 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
9779 // flag on firing onLocationChange(...).
9780 // Anyway, aCloneSHChildren param is simply reflecting
9781 // doShortCircuitedLoad in this scope.
9782 OnNewURI(aURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
9783 mLoadType, true, true, true);
9785 nsCOMPtr<nsIInputStream> postData;
9786 uint32_t cacheKey = 0;
9788 bool scrollRestorationIsManual = false;
9789 if (mOSHE) {
9790 /* save current position of scroller(s) (bug 59774) */
9791 mOSHE->SetScrollPosition(cx, cy);
9792 mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
9793 // Get the postdata and page ident from the current page, if
9794 // the new load is being done via normal means. Note that
9795 // "normal means" can be checked for just by checking for
9796 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
9797 // above -- it filters out some LOAD_CMD_NORMAL cases that we
9798 // wouldn't want here.
9799 if (aLoadType & LOAD_CMD_NORMAL) {
9800 mOSHE->GetPostData(getter_AddRefs(postData));
9801 mOSHE->GetCacheKey(&cacheKey);
9803 // Link our new SHEntry to the old SHEntry's back/forward
9804 // cache data, since the two SHEntries correspond to the
9805 // same document.
9806 if (mLSHE) {
9807 if (!aSHEntry) {
9808 // If we're not doing a history load, scroll restoration
9809 // should be inherited from the previous session history entry.
9810 mLSHE->SetScrollRestorationIsManual(scrollRestorationIsManual);
9812 mLSHE->AdoptBFCacheEntry(mOSHE);
9817 // If we're doing a history load, use its scroll restoration state.
9818 if (aSHEntry) {
9819 aSHEntry->GetScrollRestorationIsManual(&scrollRestorationIsManual);
9822 /* Assign mOSHE to mLSHE. This will either be a new entry created
9823 * by OnNewURI() for normal loads or aSHEntry for history loads.
9825 if (mLSHE) {
9826 SetHistoryEntry(&mOSHE, mLSHE);
9827 // Save the postData obtained from the previous page
9828 // in to the session history entry created for the
9829 // anchor page, so that any history load of the anchor
9830 // page will restore the appropriate postData.
9831 if (postData) {
9832 mOSHE->SetPostData(postData);
9835 // Make sure we won't just repost without hitting the
9836 // cache first
9837 if (cacheKey != 0) {
9838 mOSHE->SetCacheKey(cacheKey);
9842 /* Restore the original LSHE if we were loading something
9843 * while short-circuited load was initiated.
9845 SetHistoryEntry(&mLSHE, oldLSHE);
9846 /* Set the title for the SH entry for this target url. so that
9847 * SH menus in go/back/forward buttons won't be empty for this.
9849 if (mSessionHistory) {
9850 int32_t index = mSessionHistory->Index();
9851 nsCOMPtr<nsISHEntry> shEntry;
9852 mSessionHistory->LegacySHistory()->GetEntryAtIndex(
9853 index, false, getter_AddRefs(shEntry));
9854 NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
9855 shEntry->SetTitle(mTitle);
9858 /* Set the title for the Global History entry for this anchor url.
9860 UpdateGlobalHistoryTitle(aURI);
9862 SetDocCurrentStateObj(mOSHE);
9864 // Inform the favicon service that the favicon for oldURI also
9865 // applies to aURI.
9866 CopyFavicon(currentURI, aURI, doc->NodePrincipal(), UsePrivateBrowsing());
9868 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9869 RefPtr<nsGlobalWindowInner> win = scriptGlobal ?
9870 scriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
9872 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9873 // the function decides whether a scroll is appropriate based on the
9874 // arguments it receives. But even if we don't end up scrolling,
9875 // ScrollToAnchor performs other important tasks, such as informing
9876 // the presShell that we have a new hash. See bug 680257.
9877 rv = ScrollToAnchor(curURIHasRef, newURIHasRef, newHash, aLoadType);
9878 NS_ENSURE_SUCCESS(rv, rv);
9880 /* restore previous position of scroller(s), if we're moving
9881 * back in history (bug 59774)
9883 nscoord bx = 0;
9884 nscoord by = 0;
9885 bool needsScrollPosUpdate = false;
9886 if (mOSHE && (aLoadType == LOAD_HISTORY ||
9887 aLoadType == LOAD_RELOAD_NORMAL) &&
9888 !scrollRestorationIsManual) {
9889 needsScrollPosUpdate = true;
9890 mOSHE->GetScrollPosition(&bx, &by);
9893 // Dispatch the popstate and hashchange events, as appropriate.
9895 // The event dispatch below can cause us to re-enter script and
9896 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9897 // reference to avoid null derefs. See bug 914521.
9898 if (win) {
9899 // Fire a hashchange event URIs differ, and only in their hashes.
9900 bool doHashchange = sameExceptHashes &&
9901 (curURIHasRef != newURIHasRef || !curHash.Equals(newHash));
9903 if (historyNavBetweenSameDoc || doHashchange) {
9904 win->DispatchSyncPopState();
9907 if (needsScrollPosUpdate && win->AsInner()->HasActiveDocument()) {
9908 SetCurScrollPosEx(bx, by);
9911 if (doHashchange) {
9912 // Note that currentURI hasn't changed because it's on the
9913 // stack, so we can just use it directly as the old URI.
9914 win->DispatchAsyncHashchange(currentURI, aURI);
9918 return NS_OK;
9922 // mContentViewer->PermitUnload can destroy |this| docShell, which
9923 // causes the next call of CanSavePresentation to crash.
9924 // Hold onto |this| until we return, to prevent a crash from happening.
9925 // (bug#331040)
9926 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9928 // Don't init timing for javascript:, since it generally doesn't
9929 // actually start a load or anything. If it does, we'll init
9930 // timing then, from OnStateChange.
9932 // XXXbz mTiming should know what channel it's for, so we don't
9933 // need this hackery.
9934 bool toBeReset = false;
9935 if (!isJavaScript) {
9936 toBeReset = MaybeInitTiming();
9938 bool timeBeforeUnload = aFileName.IsVoid();
9939 if (mTiming && timeBeforeUnload) {
9940 mTiming->NotifyBeforeUnload();
9942 // Check if the page doesn't want to be unloaded. The javascript:
9943 // protocol handler deals with this for javascript: URLs.
9944 if (!isJavaScript && aFileName.IsVoid() && mContentViewer) {
9945 bool okToUnload;
9946 rv = mContentViewer->PermitUnload(&okToUnload);
9948 if (NS_SUCCEEDED(rv) && !okToUnload) {
9949 // The user chose not to unload the page, interrupt the
9950 // load.
9951 MaybeResetInitTiming(toBeReset);
9952 return NS_OK;
9956 if (mTiming && timeBeforeUnload) {
9957 mTiming->NotifyUnloadAccepted(mCurrentURI);
9960 // Check if the webbrowser chrome wants the load to proceed; this can be
9961 // used to cancel attempts to load URIs in the wrong process.
9962 nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
9963 if (browserChrome3) {
9964 bool shouldLoad;
9965 rv = browserChrome3->ShouldLoadURI(this, aURI, aReferrer, !!aPostData,
9966 aTriggeringPrincipal, &shouldLoad);
9967 if (NS_SUCCEEDED(rv) && !shouldLoad) {
9968 return NS_OK;
9972 // Whenever a top-level browsing context is navigated, the user agent MUST
9973 // lock the orientation of the document to the document's default
9974 // orientation. We don't explicitly check for a top-level browsing context
9975 // here because orientation is only set on top-level browsing contexts.
9976 if (OrientationLock() != eScreenOrientation_None) {
9977 #ifdef DEBUG
9978 nsCOMPtr<nsIDocShellTreeItem> parent;
9979 GetSameTypeParent(getter_AddRefs(parent));
9980 MOZ_ASSERT(!parent);
9981 #endif
9982 SetOrientationLock(eScreenOrientation_None);
9983 if (mIsActive) {
9984 ScreenOrientation::UpdateActiveOrientationLock(eScreenOrientation_None);
9988 // Check for saving the presentation here, before calling Stop().
9989 // This is necessary so that we can catch any pending requests.
9990 // Since the new request has not been created yet, we pass null for the
9991 // new request parameter.
9992 // Also pass nullptr for the document, since it doesn't affect the return
9993 // value for our purposes here.
9994 bool savePresentation = CanSavePresentation(aLoadType, nullptr, nullptr);
9996 // Don't stop current network activity for javascript: URL's since
9997 // they might not result in any data, and thus nothing should be
9998 // stopped in those cases. In the case where they do result in
9999 // data, the javascript: URL channel takes care of stopping
10000 // current network activity.
10001 if (!isJavaScript && aFileName.IsVoid()) {
10002 // Stop any current network activity.
10003 // Also stop content if this is a zombie doc. otherwise
10004 // the onload will be delayed by other loads initiated in the
10005 // background by the first document that
10006 // didn't fully load before the next load was initiated.
10007 // If not a zombie, don't stop content until data
10008 // starts arriving from the new URI...
10010 nsCOMPtr<nsIContentViewer> zombieViewer;
10011 if (mContentViewer) {
10012 mContentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
10015 if (zombieViewer ||
10016 LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_STOP_CONTENT)) {
10017 rv = Stop(nsIWebNavigation::STOP_ALL);
10018 } else {
10019 rv = Stop(nsIWebNavigation::STOP_NETWORK);
10022 if (NS_FAILED(rv)) {
10023 return rv;
10027 mLoadType = aLoadType;
10029 // mLSHE should be assigned to aSHEntry, only after Stop() has
10030 // been called. But when loading an error page, do not clear the
10031 // mLSHE for the real page.
10032 if (mLoadType != LOAD_ERROR_PAGE) {
10033 SetHistoryEntry(&mLSHE, aSHEntry);
10034 if (aSHEntry) {
10035 // We're making history navigation or a reload. Make sure our history ID
10036 // points to the same ID as SHEntry's docshell ID.
10037 mHistoryID = aSHEntry->DocshellID();
10041 mSavingOldViewer = savePresentation;
10043 // If we have a saved content viewer in history, restore and show it now.
10044 if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
10045 // It's possible that the previous viewer of mContentViewer is the
10046 // viewer that will end up in aSHEntry when it gets closed. If that's
10047 // the case, we need to go ahead and force it into its shentry so we
10048 // can restore it.
10049 if (mContentViewer) {
10050 nsCOMPtr<nsIContentViewer> prevViewer;
10051 mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
10052 if (prevViewer) {
10053 #ifdef DEBUG
10054 nsCOMPtr<nsIContentViewer> prevPrevViewer;
10055 prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
10056 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
10057 #endif
10058 nsCOMPtr<nsISHEntry> viewerEntry;
10059 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
10060 if (viewerEntry == aSHEntry) {
10061 // Make sure this viewer ends up in the right place
10062 mContentViewer->SetPreviousViewer(nullptr);
10063 prevViewer->Destroy();
10067 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
10068 bool restoring;
10069 rv = RestorePresentation(aSHEntry, &restoring);
10070 if (restoring) {
10071 return rv;
10074 // We failed to restore the presentation, so clean up.
10075 // Both the old and new history entries could potentially be in
10076 // an inconsistent state.
10077 if (NS_FAILED(rv)) {
10078 if (oldEntry) {
10079 oldEntry->SyncPresentationState();
10082 aSHEntry->SyncPresentationState();
10086 nsAutoString srcdoc;
10087 if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) {
10088 srcdoc = aSrcdoc;
10089 } else {
10090 srcdoc = VoidString();
10093 bool isTopLevelDoc = mItemType == typeContent &&
10094 (isTargetTopLevelDocShell ||
10095 GetIsMozBrowser());
10097 OriginAttributes attrs = GetOriginAttributes();
10098 attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
10100 PredictorLearn(aURI, nullptr,
10101 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
10102 PredictorPredict(aURI, nullptr,
10103 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
10105 nsCOMPtr<nsIRequest> req;
10106 rv = DoURILoad(aURI, aOriginalURI, aResultPrincipalURI, aLoadReplace,
10107 loadFromExternal,
10108 (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI),
10109 (aFlags & INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC),
10110 aReferrer,
10111 !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
10112 aReferrerPolicy,
10113 aTriggeringPrincipal, principalToInherit, aTypeHint,
10114 aFileName, aPostData, aHeadersData,
10115 aFirstParty, aDocShell, getter_AddRefs(req),
10116 (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
10117 (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
10118 (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
10119 srcdoc, aBaseURI, contentType);
10120 if (req && aRequest) {
10121 NS_ADDREF(*aRequest = req);
10124 if (NS_FAILED(rv)) {
10125 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
10126 if (DisplayLoadError(rv, aURI, nullptr, chan) &&
10127 (aFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
10128 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
10132 return rv;
10135 nsIPrincipal*
10136 nsDocShell::GetInheritedPrincipal(bool aConsiderCurrentDocument)
10138 nsCOMPtr<nsIDocument> document;
10139 bool inheritedFromCurrent = false;
10141 if (aConsiderCurrentDocument && mContentViewer) {
10142 document = mContentViewer->GetDocument();
10143 inheritedFromCurrent = true;
10146 if (!document) {
10147 nsCOMPtr<nsIDocShellTreeItem> parentItem;
10148 GetSameTypeParent(getter_AddRefs(parentItem));
10149 if (parentItem) {
10150 document = parentItem->GetDocument();
10154 if (!document) {
10155 if (!aConsiderCurrentDocument) {
10156 return nullptr;
10159 // Make sure we end up with _something_ as the principal no matter
10160 // what.If this fails, we'll just get a null docViewer and bail.
10161 EnsureContentViewer();
10162 if (!mContentViewer) {
10163 return nullptr;
10165 document = mContentViewer->GetDocument();
10168 //-- Get the document's principal
10169 if (document) {
10170 nsIPrincipal* docPrincipal = document->NodePrincipal();
10172 // Don't allow loads in typeContent docShells to inherit the system
10173 // principal from existing documents.
10174 if (inheritedFromCurrent &&
10175 mItemType == typeContent &&
10176 nsContentUtils::IsSystemPrincipal(docPrincipal)) {
10177 return nullptr;
10180 return docPrincipal;
10183 return nullptr;
10186 // CSPs upgrade-insecure-requests directive applies to same origin top level
10187 // navigations. Using the SOP would return false for the case when an https
10188 // page triggers and http page to load, even though that http page would be
10189 // upgraded to https later. Hence we have to use that custom function instead
10190 // of simply calling aTriggeringPrincipal->Equals(aResultPrincipal).
10191 static bool
10192 IsConsideredSameOriginForUIR(nsIPrincipal* aTriggeringPrincipal,
10193 nsIPrincipal* aResultPrincipal)
10195 MOZ_ASSERT(aTriggeringPrincipal);
10196 MOZ_ASSERT(aResultPrincipal);
10198 // we only have to make sure that the following truth table holds:
10199 // aTriggeringPrincipal | aResultPrincipal | Result
10200 // ----------------------------------------------------------------
10201 // http://example.com/foo.html | http://example.com/bar.html | true
10202 // https://example.com/foo.html | https://example.com/bar.html | true
10203 // https://example.com/foo.html | http://example.com/bar.html | true
10204 if (aTriggeringPrincipal->Equals(aResultPrincipal)) {
10205 return true;
10208 if (!aResultPrincipal->GetIsCodebasePrincipal()) {
10209 return false;
10212 nsCOMPtr<nsIURI> resultURI;
10213 nsresult rv = aResultPrincipal->GetURI(getter_AddRefs(resultURI));
10214 NS_ENSURE_SUCCESS(rv, false);
10216 nsAutoCString resultScheme;
10217 rv = resultURI->GetScheme(resultScheme);
10218 NS_ENSURE_SUCCESS(rv, false);
10219 if (!resultScheme.EqualsLiteral("http")) {
10220 return false;
10223 nsAutoCString tmpResultSpec;
10224 rv = resultURI->GetSpec(tmpResultSpec);
10225 NS_ENSURE_SUCCESS(rv, false);
10226 // replace http with https
10227 tmpResultSpec.ReplaceLiteral(0, 4, "https");
10229 nsCOMPtr<nsIURI> tmpResultURI;
10230 rv = NS_NewURI(getter_AddRefs(tmpResultURI), tmpResultSpec);
10231 NS_ENSURE_SUCCESS(rv, false);
10233 mozilla::OriginAttributes tmpOA =
10234 BasePrincipal::Cast(aResultPrincipal)->OriginAttributesRef();
10236 nsCOMPtr<nsIPrincipal> tmpResultPrincipal =
10237 BasePrincipal::CreateCodebasePrincipal(tmpResultURI, tmpOA);
10239 return aTriggeringPrincipal->Equals(tmpResultPrincipal);
10242 nsresult
10243 nsDocShell::DoURILoad(nsIURI* aURI,
10244 nsIURI* aOriginalURI,
10245 Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
10246 bool aLoadReplace,
10247 bool aLoadFromExternal,
10248 bool aForceAllowDataURI,
10249 bool aOriginalFrameSrc,
10250 nsIURI* aReferrerURI,
10251 bool aSendReferrer,
10252 uint32_t aReferrerPolicy,
10253 nsIPrincipal* aTriggeringPrincipal,
10254 nsIPrincipal* aPrincipalToInherit,
10255 const char* aTypeHint,
10256 const nsAString& aFileName,
10257 nsIInputStream* aPostData,
10258 nsIInputStream* aHeadersData,
10259 bool aFirstParty,
10260 nsIDocShell** aDocShell,
10261 nsIRequest** aRequest,
10262 bool aIsNewWindowTarget,
10263 bool aBypassClassifier,
10264 bool aForceAllowCookies,
10265 const nsAString& aSrcdoc,
10266 nsIURI* aBaseURI,
10267 nsContentPolicyType aContentPolicyType)
10269 // Double-check that we're still around to load this URI.
10270 if (mIsBeingDestroyed) {
10271 // Return NS_OK despite not doing anything to avoid throwing exceptions from
10272 // nsLocation::SetHref if the unload handler of the existing page tears us
10273 // down.
10274 return NS_OK;
10277 nsresult rv;
10278 nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
10279 if (NS_FAILED(rv)) {
10280 return rv;
10283 if (IsFrame()) {
10285 MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10286 aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10287 "DoURILoad thinks this is a frame and InternalLoad does not");
10289 // Only allow view-source scheme in top-level docshells. view-source is
10290 // the only scheme to which this applies at the moment due to potential
10291 // timing attacks to read data from cross-origin iframes. If this widens
10292 // we should add a protocol flag for whether the scheme is allowed in
10293 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10294 nsCOMPtr<nsIURI> tempURI = aURI;
10295 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10296 while (nestedURI) {
10297 // view-source should always be an nsINestedURI, loop and check the
10298 // scheme on this and all inner URIs that are also nested URIs.
10299 bool isViewSource = false;
10300 rv = tempURI->SchemeIs("view-source", &isViewSource);
10301 if (NS_FAILED(rv) || isViewSource) {
10302 return NS_ERROR_UNKNOWN_PROTOCOL;
10304 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10305 nestedURI = do_QueryInterface(tempURI);
10307 } else {
10308 MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10309 "DoURILoad thinks this is a document and InternalLoad does not");
10312 // open a channel for the url
10313 nsCOMPtr<nsIChannel> channel;
10315 bool isSrcdoc = !aSrcdoc.IsVoid();
10317 // There are two cases we care about:
10318 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10319 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10320 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10321 // element for the load. loadingPrincipal is the NodePrincipal of the frame
10322 // element.
10323 nsCOMPtr<nsINode> loadingNode;
10324 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10325 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10326 nsCOMPtr<nsISupports> topLevelLoadingContext;
10328 if (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10329 loadingNode = nullptr;
10330 loadingPrincipal = nullptr;
10331 loadingWindow = mScriptGlobal->AsOuter();
10332 if (XRE_IsContentProcess()) {
10333 // In e10s the child process doesn't have access to the element that
10334 // contains the browsing context (because that element is in the chrome
10335 // process).
10336 nsCOMPtr<nsITabChild> tabChild = GetTabChild();
10337 topLevelLoadingContext = ToSupports(tabChild);
10338 } else {
10339 // This is for loading non-e10s tabs and toplevel windows of various
10340 // sorts.
10341 // For the toplevel window cases, requestingElement will be null.
10342 nsCOMPtr<Element> requestingElement =
10343 loadingWindow->GetFrameElementInternal();
10344 topLevelLoadingContext = requestingElement;
10346 } else {
10347 loadingWindow = nullptr;
10348 loadingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
10349 if (loadingNode) {
10350 // If we have a loading node, then use that as our loadingPrincipal.
10351 loadingPrincipal = loadingNode->NodePrincipal();
10352 #ifdef DEBUG
10353 // Get the docshell type for requestingElement.
10354 nsCOMPtr<nsIDocument> requestingDoc = loadingNode->OwnerDoc();
10355 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10356 // requestingElement docshell type = current docshell type.
10357 MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
10358 "subframes should have the same docshell type as their parent");
10359 #endif
10360 } else {
10361 // If this isn't a top-level load and mScriptGlobal's frame element is
10362 // null, then the element got removed from the DOM while we were trying
10363 // to load this resource. This docshell is scheduled for destruction
10364 // already, so bail out here.
10365 return NS_OK;
10369 // Getting the right triggeringPrincipal needs to be updated and is only
10370 // ready for use once bug 1182569 landed. Until then, we cannot rely on
10371 // the triggeringPrincipal for TYPE_DOCUMENT loads.
10372 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
10374 bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN;
10376 // We want to inherit aPrincipalToInherit when:
10377 // 1. ChannelShouldInheritPrincipal returns true.
10378 // 2. aURI is not data: URI, or data: URI is not configured as unique opaque
10379 // origin.
10380 bool inheritAttrs = false, inheritPrincipal = false;
10382 if (aPrincipalToInherit) {
10383 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10384 aPrincipalToInherit,
10385 aURI,
10386 true, // aInheritForAboutBlank
10387 isSrcdoc);
10389 bool isData;
10390 bool isURIUniqueOrigin = nsIOService::IsDataURIUniqueOpaqueOrigin() &&
10391 NS_SUCCEEDED(aURI->SchemeIs("data", &isData)) &&
10392 isData;
10393 inheritPrincipal = inheritAttrs && !isURIUniqueOrigin;
10396 nsLoadFlags loadFlags = mDefaultLoadFlags;
10397 nsSecurityFlags securityFlags =
10398 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
10400 if (aFirstParty) {
10401 // tag first party URL loads
10402 loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
10405 if (mLoadType == LOAD_ERROR_PAGE) {
10406 // Error pages are LOAD_BACKGROUND
10407 loadFlags |= nsIChannel::LOAD_BACKGROUND;
10408 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10411 if (inheritPrincipal) {
10412 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10414 if (isSandBoxed) {
10415 securityFlags |= nsILoadInfo::SEC_SANDBOXED;
10418 nsCOMPtr<nsILoadInfo> loadInfo =
10419 (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
10420 new LoadInfo(loadingWindow, aTriggeringPrincipal, topLevelLoadingContext,
10421 securityFlags) :
10422 new LoadInfo(loadingPrincipal, aTriggeringPrincipal, loadingNode,
10423 securityFlags, aContentPolicyType);
10425 if (aPrincipalToInherit) {
10426 loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
10428 loadInfo->SetLoadTriggeredFromExternal(aLoadFromExternal);
10429 loadInfo->SetForceAllowDataURI(aForceAllowDataURI);
10430 loadInfo->SetOriginalFrameSrcLoad(aOriginalFrameSrc);
10432 // We have to do this in case our OriginAttributes are different from the
10433 // OriginAttributes of the parent document. Or in case there isn't a
10434 // parent document.
10435 bool isTopLevelDoc = mItemType == typeContent &&
10436 (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
10437 GetIsMozBrowser());
10439 OriginAttributes attrs;
10441 // Inherit origin attributes from aPrincipalToInherit if inheritAttrs is true.
10442 // Otherwise we just use the origin attributes from docshell.
10443 if (inheritAttrs) {
10444 MOZ_ASSERT(aPrincipalToInherit, "We should have aPrincipalToInherit here.");
10445 attrs = aPrincipalToInherit->OriginAttributesRef();
10446 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
10447 // have the same origin attributes with docshell.
10448 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(), attrs == GetOriginAttributes());
10449 } else {
10450 attrs = GetOriginAttributes();
10451 attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
10454 rv = loadInfo->SetOriginAttributes(attrs);
10455 if (NS_WARN_IF(NS_FAILED(rv))) {
10456 return rv;
10459 // Document loads should set the reload flag on the channel so that it
10460 // can be exposed on the service worker FetchEvent.
10461 rv = loadInfo->SetIsDocshellReload(mLoadType & LOAD_CMD_RELOAD);
10462 NS_ENSURE_SUCCESS(rv, rv);
10464 if (!isSrcdoc) {
10465 rv = NS_NewChannelInternal(getter_AddRefs(channel),
10466 aURI,
10467 loadInfo,
10468 nullptr, // PerformanceStorage
10469 nullptr, // loadGroup
10470 static_cast<nsIInterfaceRequestor*>(this),
10471 loadFlags);
10473 if (NS_FAILED(rv)) {
10474 if (rv == NS_ERROR_UNKNOWN_PROTOCOL) {
10475 // This is a uri with a protocol scheme we don't know how
10476 // to handle. Embedders might still be interested in
10477 // handling the load, though, so we fire a notification
10478 // before throwing the load away.
10479 bool abort = false;
10480 nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
10481 if (NS_SUCCEEDED(rv2) && abort) {
10482 // Hey, they're handling the load for us! How convenient!
10483 return NS_OK;
10486 return rv;
10489 if (aBaseURI) {
10490 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
10491 if (vsc) {
10492 rv = vsc->SetBaseURI(aBaseURI);
10493 MOZ_ASSERT(NS_SUCCEEDED(rv));
10496 } else {
10497 nsAutoCString scheme;
10498 rv = aURI->GetScheme(scheme);
10499 NS_ENSURE_SUCCESS(rv, rv);
10500 bool isViewSource;
10501 aURI->SchemeIs("view-source", &isViewSource);
10503 if (isViewSource) {
10504 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
10505 NS_ENSURE_TRUE(vsh, NS_ERROR_FAILURE);
10507 rv = vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc,
10508 loadInfo, getter_AddRefs(channel));
10509 } else {
10510 rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
10511 aURI,
10512 aSrcdoc,
10513 NS_LITERAL_CSTRING("text/html"),
10514 loadInfo,
10515 true);
10516 NS_ENSURE_SUCCESS(rv, rv);
10517 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
10518 MOZ_ASSERT(isc);
10519 isc->SetBaseURI(aBaseURI);
10523 // Navigational requests that are same origin need to be upgraded in case
10524 // upgrade-insecure-requests is present. Please note that in that case
10525 // the triggeringPrincipal is holding the CSP that potentially
10526 // holds upgrade-insecure-requests.
10527 nsCOMPtr<nsIContentSecurityPolicy> csp;
10528 aTriggeringPrincipal->GetCsp(getter_AddRefs(csp));
10529 if (csp) {
10530 bool upgradeInsecureRequests = false;
10531 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10532 if (upgradeInsecureRequests) {
10533 // only upgrade if the navigation is same origin
10534 nsCOMPtr<nsIPrincipal> resultPrincipal;
10535 rv = nsContentUtils::GetSecurityManager()->
10536 GetChannelResultPrincipal(channel,
10537 getter_AddRefs(resultPrincipal));
10538 NS_ENSURE_SUCCESS(rv, rv);
10539 if (IsConsideredSameOriginForUIR(aTriggeringPrincipal, resultPrincipal)) {
10540 static_cast<LoadInfo*>(loadInfo.get())->SetUpgradeInsecureRequests();
10546 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
10547 do_QueryInterface(channel);
10548 if (appCacheChannel) {
10549 // Any document load should not inherit application cache.
10550 appCacheChannel->SetInheritApplicationCache(false);
10552 // Loads with the correct permissions should check for a matching
10553 // application cache.
10554 if (GeckoProcessType_Default != XRE_GetProcessType()) {
10555 // Permission will be checked in the parent process
10556 appCacheChannel->SetChooseApplicationCache(true);
10557 } else {
10558 nsCOMPtr<nsIScriptSecurityManager> secMan =
10559 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
10561 if (secMan) {
10562 nsCOMPtr<nsIPrincipal> principal;
10563 secMan->GetDocShellCodebasePrincipal(aURI, this,
10564 getter_AddRefs(principal));
10565 appCacheChannel->SetChooseApplicationCache(
10566 NS_ShouldCheckAppCache(principal));
10571 // Make sure to give the caller a channel if we managed to create one
10572 // This is important for correct error page/session history interaction
10573 if (aRequest) {
10574 NS_ADDREF(*aRequest = channel);
10577 if (aOriginalURI) {
10578 channel->SetOriginalURI(aOriginalURI);
10579 // The LOAD_REPLACE flag and its handling here will be removed as part
10580 // of bug 1319110. For now preserve its restoration here to not break
10581 // any code expecting it being set specially on redirected channels.
10582 // If the flag has originally been set to change result of
10583 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10584 // any harm.
10585 if (aLoadReplace) {
10586 uint32_t loadFlags;
10587 channel->GetLoadFlags(&loadFlags);
10588 NS_ENSURE_SUCCESS(rv, rv);
10589 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10591 } else {
10592 channel->SetOriginalURI(aURI);
10595 if (aResultPrincipalURI) {
10596 // Unconditionally override, we want the replay to be equal to what has
10597 // been captured.
10598 loadInfo->SetResultPrincipalURI(aResultPrincipalURI.ref());
10601 if (aTypeHint && *aTypeHint) {
10602 channel->SetContentType(nsDependentCString(aTypeHint));
10603 mContentTypeHint = aTypeHint;
10604 } else {
10605 mContentTypeHint.Truncate();
10608 if (!aFileName.IsVoid()) {
10609 rv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
10610 NS_ENSURE_SUCCESS(rv, rv);
10611 if (!aFileName.IsEmpty()) {
10612 rv = channel->SetContentDispositionFilename(aFileName);
10613 NS_ENSURE_SUCCESS(rv, rv);
10617 if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
10618 mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
10619 rv = SetMixedContentChannel(channel);
10620 NS_ENSURE_SUCCESS(rv, rv);
10621 } else if (mMixedContentChannel) {
10623 * If the user "Disables Protection on This Page", we call
10624 * SetMixedContentChannel for the first time, otherwise
10625 * mMixedContentChannel is still null.
10626 * Later, if the new channel passes a same orign check, we remember the
10627 * users decision by calling SetMixedContentChannel using the new channel.
10628 * This way, the user does not have to click the disable protection button
10629 * over and over for browsing the same site.
10631 rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, channel);
10632 if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(channel))) {
10633 SetMixedContentChannel(nullptr);
10637 // hack
10638 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
10639 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
10640 do_QueryInterface(channel));
10641 if (httpChannelInternal) {
10642 if (aForceAllowCookies) {
10643 rv = httpChannelInternal->SetThirdPartyFlags(
10644 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
10645 MOZ_ASSERT(NS_SUCCEEDED(rv));
10647 if (aFirstParty) {
10648 rv = httpChannelInternal->SetDocumentURI(aURI);
10649 MOZ_ASSERT(NS_SUCCEEDED(rv));
10650 } else {
10651 rv = httpChannelInternal->SetDocumentURI(aReferrerURI);
10652 MOZ_ASSERT(NS_SUCCEEDED(rv));
10654 rv = httpChannelInternal->SetRedirectMode(
10655 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
10656 MOZ_ASSERT(NS_SUCCEEDED(rv));
10659 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
10660 if (props) {
10661 // save true referrer for those who need it (e.g. xpinstall whitelisting)
10662 // Currently only http and ftp channels support this.
10663 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.internalReferrer"),
10664 aReferrerURI);
10667 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
10668 /* Get the cache Key from SH */
10669 uint32_t cacheKey = 0;
10670 if (cacheChannel) {
10671 if (mLSHE) {
10672 mLSHE->GetCacheKey(&cacheKey);
10673 } else if (mOSHE) { // for reload cases
10674 mOSHE->GetCacheKey(&cacheKey);
10678 // figure out if we need to set the post data stream on the channel...
10679 if (aPostData) {
10680 nsCOMPtr<nsIFormPOSTActionChannel> postChannel(do_QueryInterface(channel));
10681 if (postChannel) {
10682 // XXX it's a bit of a hack to rewind the postdata stream here but
10683 // it has to be done in case the post data is being reused multiple
10684 // times.
10685 nsCOMPtr<nsISeekableStream> postDataSeekable =
10686 do_QueryInterface(aPostData);
10687 if (postDataSeekable) {
10688 rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
10689 NS_ENSURE_SUCCESS(rv, rv);
10692 // we really need to have a content type associated with this stream!!
10693 postChannel->SetUploadStream(aPostData, EmptyCString(), -1);
10696 /* If there is a valid postdata *and* it is a History Load,
10697 * set up the cache key on the channel, to retrieve the
10698 * data *only* from the cache. If it is a normal reload, the
10699 * cache is free to go to the server for updated postdata.
10701 if (cacheChannel && cacheKey != 0) {
10702 if (mLoadType == LOAD_HISTORY ||
10703 mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10704 cacheChannel->SetCacheKey(cacheKey);
10705 uint32_t loadFlags;
10706 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
10707 channel->SetLoadFlags(
10708 loadFlags | nsICachingChannel::LOAD_ONLY_FROM_CACHE);
10710 } else if (mLoadType == LOAD_RELOAD_NORMAL) {
10711 cacheChannel->SetCacheKey(cacheKey);
10714 } else {
10715 /* If there is no postdata, set the cache key on the channel, and
10716 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10717 * will be free to get it from net if it is not found in cache.
10718 * New cache may use it creatively on CGI pages with GET
10719 * method and even on those that say "no-cache"
10721 if (mLoadType == LOAD_HISTORY ||
10722 mLoadType == LOAD_RELOAD_NORMAL ||
10723 mLoadType == LOAD_RELOAD_CHARSET_CHANGE ||
10724 mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
10725 mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
10726 if (cacheChannel && cacheKey != 0) {
10727 cacheChannel->SetCacheKey(cacheKey);
10732 if (httpChannel) {
10733 if (aHeadersData) {
10734 rv = AddHeadersToChannel(aHeadersData, httpChannel);
10736 // Set the referrer explicitly
10737 if (aReferrerURI && aSendReferrer) {
10738 // Referrer is currenly only set for link clicks here.
10739 rv = httpChannel->SetReferrerWithPolicy(aReferrerURI, aReferrerPolicy);
10740 MOZ_ASSERT(NS_SUCCEEDED(rv));
10744 nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel);
10745 if (scriptChannel) {
10746 // Allow execution against our context if the principals match
10747 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10750 if (aIsNewWindowTarget) {
10751 nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
10752 if (props) {
10753 props->SetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
10754 true);
10758 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel));
10759 if (timedChannel) {
10760 timedChannel->SetTimingEnabled(true);
10762 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10763 if (IsFrame() && win) {
10764 nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
10765 if (frameElement) {
10766 timedChannel->SetInitiatorType(frameElement->LocalName());
10771 // Mark the http channel as UrgentStart for top level document loading
10772 // in active tab.
10773 if (mIsActive || (mLoadType & (LOAD_CMD_NORMAL | LOAD_CMD_HISTORY))) {
10774 if (httpChannel && isTopLevelDoc) {
10775 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
10776 if (cos) {
10777 cos->AddClassFlags(nsIClassOfService::UrgentStart);
10782 rv = DoChannelLoad(channel, uriLoader, aBypassClassifier);
10785 // If the channel load failed, we failed and nsIWebProgress just ain't
10786 // gonna happen.
10788 if (NS_SUCCEEDED(rv)) {
10789 if (aDocShell) {
10790 *aDocShell = this;
10791 NS_ADDREF(*aDocShell);
10795 return rv;
10798 static nsresult
10799 AppendSegmentToString(nsIInputStream* aIn,
10800 void* aClosure,
10801 const char* aFromRawSegment,
10802 uint32_t aToOffset,
10803 uint32_t aCount,
10804 uint32_t* aWriteCount)
10806 // aFromSegment now contains aCount bytes of data.
10808 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10809 buf->Append(aFromRawSegment, aCount);
10811 // Indicate that we have consumed all of aFromSegment
10812 *aWriteCount = aCount;
10813 return NS_OK;
10816 nsresult
10817 nsDocShell::AddHeadersToChannel(nsIInputStream* aHeadersData,
10818 nsIChannel* aGenericChannel)
10820 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10821 NS_ENSURE_STATE(httpChannel);
10823 uint32_t numRead;
10824 nsAutoCString headersString;
10825 nsresult rv = aHeadersData->ReadSegments(AppendSegmentToString,
10826 &headersString,
10827 UINT32_MAX,
10828 &numRead);
10829 NS_ENSURE_SUCCESS(rv, rv);
10831 // used during the manipulation of the String from the InputStream
10832 nsAutoCString headerName;
10833 nsAutoCString headerValue;
10834 int32_t crlf;
10835 int32_t colon;
10838 // Iterate over the headersString: for each "\r\n" delimited chunk,
10839 // add the value as a header to the nsIHttpChannel
10842 static const char kWhitespace[] = "\b\t\r\n ";
10843 while (true) {
10844 crlf = headersString.Find("\r\n");
10845 if (crlf == kNotFound) {
10846 return NS_OK;
10849 const nsACString& oneHeader = StringHead(headersString, crlf);
10851 colon = oneHeader.FindChar(':');
10852 if (colon == kNotFound) {
10853 return NS_ERROR_UNEXPECTED;
10856 headerName = StringHead(oneHeader, colon);
10857 headerValue = Substring(oneHeader, colon + 1);
10859 headerName.Trim(kWhitespace);
10860 headerValue.Trim(kWhitespace);
10862 headersString.Cut(0, crlf + 2);
10865 // FINALLY: we can set the header!
10868 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10869 NS_ENSURE_SUCCESS(rv, rv);
10872 MOZ_ASSERT_UNREACHABLE("oops");
10873 return NS_ERROR_UNEXPECTED;
10876 nsresult
10877 nsDocShell::DoChannelLoad(nsIChannel* aChannel,
10878 nsIURILoader* aURILoader,
10879 bool aBypassClassifier)
10881 nsresult rv;
10882 // Mark the channel as being a document URI and allow content sniffing...
10883 nsLoadFlags loadFlags = 0;
10884 (void)aChannel->GetLoadFlags(&loadFlags);
10885 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
10886 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
10888 if (SandboxFlagsImplyCookies(mSandboxFlags)) {
10889 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
10891 // Load attributes depend on load type...
10892 switch (mLoadType) {
10893 case LOAD_HISTORY: {
10894 // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
10895 // push/replaceState (bug 669671).
10896 bool uriModified = false;
10897 if (mLSHE) {
10898 mLSHE->GetURIWasModified(&uriModified);
10901 if (!uriModified) {
10902 loadFlags |= nsIRequest::VALIDATE_NEVER;
10904 break;
10907 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
10908 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
10909 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
10910 nsIRequest::LOAD_FRESH_CONNECTION;
10911 MOZ_FALLTHROUGH;
10913 case LOAD_RELOAD_CHARSET_CHANGE: {
10914 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we only want
10915 // to force cache load for this channel, not the whole loadGroup.
10916 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(aChannel);
10917 if (cachingChannel) {
10918 cachingChannel->SetAllowStaleCacheContent(true);
10920 break;
10923 case LOAD_RELOAD_NORMAL:
10924 case LOAD_REFRESH:
10925 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10926 break;
10928 case LOAD_NORMAL_BYPASS_CACHE:
10929 case LOAD_NORMAL_BYPASS_PROXY:
10930 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
10931 case LOAD_NORMAL_ALLOW_MIXED_CONTENT:
10932 case LOAD_RELOAD_BYPASS_CACHE:
10933 case LOAD_RELOAD_BYPASS_PROXY:
10934 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
10935 case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
10936 case LOAD_REPLACE_BYPASS_CACHE:
10937 loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
10938 nsIRequest::LOAD_FRESH_CONNECTION;
10939 break;
10941 case LOAD_NORMAL:
10942 case LOAD_LINK:
10943 // Set cache checking flags
10944 switch (Preferences::GetInt("browser.cache.check_doc_frequency", -1)) {
10945 case 0:
10946 loadFlags |= nsIRequest::VALIDATE_ONCE_PER_SESSION;
10947 break;
10948 case 1:
10949 loadFlags |= nsIRequest::VALIDATE_ALWAYS;
10950 break;
10951 case 2:
10952 loadFlags |= nsIRequest::VALIDATE_NEVER;
10953 break;
10955 break;
10958 if (!aBypassClassifier) {
10959 loadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
10962 // If the user pressed shift-reload, then do not allow ServiceWorker
10963 // interception to occur. See step 12.1 of the SW HandleFetch algorithm.
10964 if (IsForceReloadType(mLoadType)) {
10965 loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
10968 (void)aChannel->SetLoadFlags(loadFlags);
10970 uint32_t openFlags = 0;
10971 if (mLoadType == LOAD_LINK) {
10972 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10974 if (!mAllowContentRetargeting) {
10975 openFlags |= nsIURILoader::DONT_RETARGET;
10978 // If anything fails here, make sure to clear our initial ClientSource.
10979 auto cleanupInitialClient = MakeScopeExit([&] {
10980 mInitialClientSource.reset();
10983 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10984 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10986 MaybeCreateInitialClientSource();
10988 // Since we are loading a document we need to make sure the proper reserved
10989 // and initial client data is stored on the nsILoadInfo. The
10990 // ClientChannelHelper does this and ensures that it is propagated properly
10991 // on redirects. We pass no reserved client here so that the helper will
10992 // create the reserved ClientSource if necessary.
10993 Maybe<ClientInfo> noReservedClient;
10994 rv = AddClientChannelHelper(aChannel,
10995 std::move(noReservedClient),
10996 GetInitialClientInfo(),
10997 win->EventTargetFor(TaskCategory::Other));
10998 NS_ENSURE_SUCCESS(rv, rv);
11000 rv = aURILoader->OpenURI(aChannel, openFlags, this);
11001 NS_ENSURE_SUCCESS(rv, rv);
11003 // We're about to load a new page and it may take time before necko
11004 // gives back any data, so main thread might have a chance to process a
11005 // collector slice
11006 nsJSContext::MaybeRunNextCollectorSlice(this, JS::gcreason::DOCSHELL);
11008 // Success. Keep the initial ClientSource if it exists.
11009 cleanupInitialClient.release();
11011 return NS_OK;
11014 nsresult
11015 nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
11016 nsACString& aNewHash, uint32_t aLoadType)
11018 if (!mCurrentURI) {
11019 return NS_OK;
11022 nsCOMPtr<nsIPresShell> shell = GetPresShell();
11023 if (!shell) {
11024 // If we failed to get the shell, or if there is no shell,
11025 // nothing left to do here.
11026 return NS_OK;
11029 nsIScrollableFrame* rootScroll = shell->GetRootScrollFrameAsScrollable();
11030 if (rootScroll) {
11031 rootScroll->ClearDidHistoryRestore();
11034 // If we have no new anchor, we do not want to scroll, unless there is a
11035 // current anchor and we are doing a history load. So return if we have no
11036 // new anchor, and there is no current anchor or the load is not a history
11037 // load.
11038 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
11039 return NS_OK;
11042 // Both the new and current URIs refer to the same page. We can now
11043 // browse to the hash stored in the new URI.
11045 if (!aNewHash.IsEmpty()) {
11046 // anchor is there, but if it's a load from history,
11047 // we don't have any anchor jumping to do
11048 bool scroll = aLoadType != LOAD_HISTORY &&
11049 aLoadType != LOAD_RELOAD_NORMAL;
11051 // We assume that the bytes are in UTF-8, as it says in the
11052 // spec:
11053 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
11055 // We try the UTF-8 string first, and then try the document's
11056 // charset (see below). If the string is not UTF-8,
11057 // conversion will fail and give us an empty Unicode string.
11058 // In that case, we should just fall through to using the
11059 // page's charset.
11060 nsresult rv = NS_ERROR_FAILURE;
11061 NS_ConvertUTF8toUTF16 uStr(aNewHash);
11062 if (!uStr.IsEmpty()) {
11063 rv = shell->GoToAnchor(uStr, scroll,
11064 nsIPresShell::SCROLL_SMOOTH_AUTO);
11067 if (NS_FAILED(rv)) {
11068 char* str = ToNewCString(aNewHash);
11069 if (!str) {
11070 return NS_ERROR_OUT_OF_MEMORY;
11072 nsUnescape(str);
11073 NS_ConvertUTF8toUTF16 utf16Str(str);
11074 if (!utf16Str.IsEmpty()) {
11075 rv = shell->GoToAnchor(utf16Str, scroll,
11076 nsIPresShell::SCROLL_SMOOTH_AUTO);
11078 free(str);
11081 // Above will fail if the anchor name is not UTF-8. Need to
11082 // convert from document charset to unicode.
11083 if (NS_FAILED(rv)) {
11084 // Get a document charset
11085 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
11086 nsIDocument* doc = mContentViewer->GetDocument();
11087 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
11088 nsAutoCString charset;
11089 doc->GetDocumentCharacterSet()->Name(charset);
11091 nsCOMPtr<nsITextToSubURI> textToSubURI =
11092 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
11093 NS_ENSURE_SUCCESS(rv, rv);
11095 // Unescape and convert to unicode
11096 nsAutoString uStr;
11098 rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
11099 NS_ENSURE_SUCCESS(rv, rv);
11101 // Ignore return value of GoToAnchor, since it will return an error
11102 // if there is no such anchor in the document, which is actually a
11103 // success condition for us (we want to update the session history
11104 // with the new URI no matter whether we actually scrolled
11105 // somewhere).
11107 // When aNewHash contains "%00", unescaped string may be empty.
11108 // And GoToAnchor asserts if we ask it to scroll to an empty ref.
11109 shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
11110 nsIPresShell::SCROLL_SMOOTH_AUTO);
11112 } else {
11113 // Tell the shell it's at an anchor, without scrolling.
11114 shell->GoToAnchor(EmptyString(), false);
11116 // An empty anchor was found, but if it's a load from history,
11117 // we don't have to jump to the top of the page. Scrollbar
11118 // position will be restored by the caller, based on positions
11119 // stored in session history.
11120 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
11121 return NS_OK;
11123 // An empty anchor. Scroll to the top of the page. Ignore the
11124 // return value; failure to scroll here (e.g. if there is no
11125 // root scrollframe) is not grounds for canceling the load!
11126 SetCurScrollPosEx(0, 0);
11129 return NS_OK;
11132 void
11133 nsDocShell::SetupReferrerFromChannel(nsIChannel* aChannel)
11135 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11136 if (httpChannel) {
11137 nsCOMPtr<nsIURI> referrer;
11138 nsresult rv = httpChannel->GetReferrer(getter_AddRefs(referrer));
11139 if (NS_SUCCEEDED(rv)) {
11140 SetReferrerURI(referrer);
11142 uint32_t referrerPolicy;
11143 rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
11144 if (NS_SUCCEEDED(rv)) {
11145 SetReferrerPolicy(referrerPolicy);
11150 bool
11151 nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
11152 nsIPrincipal* aTriggeringPrincipal,
11153 nsIPrincipal* aPrincipalToInherit,
11154 uint32_t aLoadType, bool aFireOnLocationChange,
11155 bool aAddToGlobalHistory, bool aCloneSHChildren)
11157 MOZ_ASSERT(aURI, "uri is null");
11158 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11160 MOZ_ASSERT(!aPrincipalToInherit || (aPrincipalToInherit && aTriggeringPrincipal));
11162 #if defined(DEBUG)
11163 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11164 nsAutoCString chanName;
11165 if (aChannel) {
11166 aChannel->GetName(chanName);
11167 } else {
11168 chanName.AssignLiteral("<no channel>");
11171 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11172 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n",
11173 this, aURI->GetSpecOrDefault().get(), chanName.get(), aLoadType));
11175 #endif
11177 bool equalUri = false;
11179 // Get the post data and the HTTP response code from the channel.
11180 uint32_t responseStatus = 0;
11181 nsCOMPtr<nsIInputStream> inputStream;
11182 if (aChannel) {
11183 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11185 // Check if the HTTPChannel is hiding under a multiPartChannel
11186 if (!httpChannel) {
11187 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11190 if (httpChannel) {
11191 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11192 if (uploadChannel) {
11193 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11196 // If the response status indicates an error, unlink this session
11197 // history entry from any entries sharing its document.
11198 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
11199 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
11200 mLSHE->AbandonBFCacheEntry();
11205 // Determine if this type of load should update history.
11206 bool updateGHistory = !(aLoadType == LOAD_BYPASS_HISTORY ||
11207 aLoadType == LOAD_ERROR_PAGE ||
11208 aLoadType & LOAD_CMD_HISTORY);
11210 // We don't update session history on reload unless we're loading
11211 // an iframe in shift-reload case.
11212 bool updateSHistory = updateGHistory &&
11213 (!(aLoadType & LOAD_CMD_RELOAD) ||
11214 (IsForceReloadType(aLoadType) && IsFrame()));
11216 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
11217 // current frame or in the root docshell.
11218 RefPtr<ChildSHistory> rootSH = mSessionHistory;
11219 if (!rootSH) {
11220 // Get the handle to SH from the root docshell
11221 rootSH = GetRootSessionHistory();
11223 if (!rootSH) {
11224 updateSHistory = false;
11225 updateGHistory = false; // XXX Why global history too?
11228 // Check if the url to be loaded is the same as the one already loaded.
11229 if (mCurrentURI) {
11230 aURI->Equals(mCurrentURI, &equalUri);
11233 #ifdef DEBUG
11234 bool shAvailable = (rootSH != nullptr);
11236 // XXX This log message is almost useless because |updateSHistory|
11237 // and |updateGHistory| are not correct at this point.
11239 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11240 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
11241 " equalURI=%i\n",
11242 shAvailable, updateSHistory, updateGHistory, equalUri));
11244 if (shAvailable && mCurrentURI && !mOSHE && aLoadType != LOAD_ERROR_PAGE) {
11245 // XXX mCurrentURI can be changed from any caller regardless what actual
11246 // loaded document is, so testing mCurrentURI isn't really a reliable way.
11247 // Session restore is one example which changes current URI in order to
11248 // show address before loading. See bug 1301399.
11249 NS_ASSERTION(NS_IsAboutBlank(mCurrentURI),
11250 "no SHEntry for a non-transient viewer?");
11252 #endif
11254 /* If the url to be loaded is the same as the one already there,
11255 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
11256 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
11257 * AddToSessionHistory() won't mess with the current SHEntry and
11258 * if this page has any frame children, it also will be handled
11259 * properly. see bug 83684
11261 * NB: If mOSHE is null but we have a current URI, then it means
11262 * that we must be at the transient about:blank content viewer
11263 * (asserted above) and we should let the normal load continue,
11264 * since there's nothing to replace.
11266 * XXX Hopefully changing the loadType at this time will not hurt
11267 * anywhere. The other way to take care of sequentially repeating
11268 * frameset pages is to add new methods to nsIDocShellTreeItem.
11269 * Hopefully I don't have to do that.
11271 if (equalUri &&
11272 mOSHE &&
11273 (mLoadType == LOAD_NORMAL ||
11274 mLoadType == LOAD_LINK ||
11275 mLoadType == LOAD_STOP_CONTENT) &&
11276 !inputStream) {
11277 mLoadType = LOAD_NORMAL_REPLACE;
11280 // If this is a refresh to the currently loaded url, we don't
11281 // have to update session or global history.
11282 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
11283 SetHistoryEntry(&mLSHE, mOSHE);
11286 /* If the user pressed shift-reload, cache will create a new cache key
11287 * for the page. Save the new cacheKey in Session History.
11288 * see bug 90098
11290 if (aChannel && IsForceReloadType(aLoadType)) {
11291 MOZ_ASSERT(!updateSHistory || IsFrame(),
11292 "We shouldn't be updating session history for forced"
11293 " reloads unless we're in a newly created iframe!");
11295 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
11296 uint32_t cacheKey = 0;
11297 // Get the Cache Key and store it in SH.
11298 if (cacheChannel) {
11299 cacheChannel->GetCacheKey(&cacheKey);
11301 // If we already have a loading history entry, store the new cache key
11302 // in it. Otherwise, since we're doing a reload and won't be updating
11303 // our history entry, store the cache key in our current history entry.
11304 if (mLSHE) {
11305 mLSHE->SetCacheKey(cacheKey);
11306 } else if (mOSHE) {
11307 mOSHE->SetCacheKey(cacheKey);
11310 // Since we're force-reloading, clear all the sub frame history.
11311 ClearFrameHistory(mLSHE);
11312 ClearFrameHistory(mOSHE);
11315 // Clear subframe history on refresh.
11316 // XXX: history.go(0) won't go this path as aLoadType is LOAD_HISTORY in this
11317 // case. One should re-validate after bug 1331865 fixed.
11318 if (aLoadType == LOAD_REFRESH) {
11319 ClearFrameHistory(mLSHE);
11320 ClearFrameHistory(mOSHE);
11323 if (updateSHistory) {
11324 // Update session history if necessary...
11325 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11326 /* This is a fresh page getting loaded for the first time
11327 *.Create a Entry for it and add it to SH, if this is the
11328 * rootDocShell
11330 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11331 aPrincipalToInherit, aCloneSHChildren,
11332 getter_AddRefs(mLSHE));
11334 } else if (mSessionHistory && mLSHE && mURIResultedInDocument) {
11335 // Even if we don't add anything to SHistory, ensure the current index
11336 // points to the same SHEntry as our mLSHE.
11337 int32_t index = 0;
11338 mSessionHistory->LegacySHistory()->GetRequestedIndex(&index);
11339 if (index == -1) {
11340 index = mSessionHistory->Index();
11342 nsCOMPtr<nsISHEntry> currentSH;
11343 mSessionHistory->LegacySHistory()->GetEntryAtIndex(
11344 index, false, getter_AddRefs(currentSH));
11345 if (currentSH != mLSHE) {
11346 mSessionHistory->LegacySHistoryInternal()->ReplaceEntry(index, mLSHE);
11350 // If this is a POST request, we do not want to include this in global
11351 // history.
11352 if (updateGHistory && aAddToGlobalHistory && !ChannelIsPost(aChannel)) {
11353 nsCOMPtr<nsIURI> previousURI;
11354 uint32_t previousFlags = 0;
11356 if (aLoadType & LOAD_CMD_RELOAD) {
11357 // On a reload request, we don't set redirecting flags.
11358 previousURI = aURI;
11359 } else {
11360 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11363 // Note: We don't use |referrer| when our global history is
11364 // based on IHistory.
11365 nsCOMPtr<nsIURI> referrer;
11366 // Treat referrer as null if there is an error getting it.
11367 (void)NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
11369 AddURIVisit(aURI, referrer, previousURI, previousFlags, responseStatus);
11372 // If this was a history load or a refresh, or it was a history load but
11373 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11374 // in session history.
11375 if (rootSH &&
11376 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11377 mLoadType == LOAD_NORMAL_REPLACE)) {
11378 mPreviousTransIndex = rootSH->Index();
11379 rootSH->LegacySHistoryInternal()->UpdateIndex();
11380 mLoadedTransIndex = rootSH->Index();
11381 #ifdef DEBUG_PAGE_CACHE
11382 printf("Previous index: %d, Loaded index: %d\n\n",
11383 mPreviousTransIndex, mLoadedTransIndex);
11384 #endif
11387 // aCloneSHChildren exactly means "we are not loading a new document".
11388 uint32_t locationFlags =
11389 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11391 bool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
11392 aFireOnLocationChange,
11393 locationFlags);
11394 // Make sure to store the referrer from the channel, if any
11395 SetupReferrerFromChannel(aChannel);
11396 return onLocationChangeNeeded;
11399 bool
11400 nsDocShell::OnLoadingSite(nsIChannel* aChannel, bool aFireOnLocationChange,
11401 bool aAddToGlobalHistory)
11403 nsCOMPtr<nsIURI> uri;
11404 // If this a redirect, use the final url (uri)
11405 // else use the original url
11407 // Note that this should match what documents do (see nsDocument::Reset).
11408 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
11409 NS_ENSURE_TRUE(uri, false);
11411 // Pass false for aCloneSHChildren, since we're loading a new page here.
11412 return OnNewURI(uri, aChannel, nullptr, nullptr, mLoadType, aFireOnLocationChange,
11413 aAddToGlobalHistory, false);
11416 void
11417 nsDocShell::SetReferrerURI(nsIURI* aURI)
11419 mReferrerURI = aURI; // This assigment addrefs
11422 void
11423 nsDocShell::SetReferrerPolicy(uint32_t aReferrerPolicy)
11425 mReferrerPolicy = aReferrerPolicy;
11428 //*****************************************************************************
11429 // nsDocShell: Session History
11430 //*****************************************************************************
11432 NS_IMETHODIMP
11433 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11434 const nsAString& aURL, bool aReplace, JSContext* aCx)
11436 // Implements History.pushState and History.replaceState
11438 // Here's what we do, roughly in the order specified by HTML5:
11439 // 1. Serialize aData using structured clone.
11440 // 2. If the third argument is present,
11441 // a. Resolve the url, relative to the first script's base URL
11442 // b. If (a) fails, raise a SECURITY_ERR
11443 // c. Compare the resulting absolute URL to the document's address. If
11444 // any part of the URLs difer other than the <path>, <query>, and
11445 // <fragment> components, raise a SECURITY_ERR and abort.
11446 // 3. If !aReplace:
11447 // Remove from the session history all entries after the current entry,
11448 // as we would after a regular navigation, and save the current
11449 // entry's scroll position (bug 590573).
11450 // 4. As apropriate, either add a state object entry to the session history
11451 // after the current entry with the following properties, or modify the
11452 // current session history entry to set
11453 // a. cloned data as the state object,
11454 // b. if the third argument was present, the absolute URL found in
11455 // step 2
11456 // Also clear the new history entry's POST data (see bug 580069).
11457 // 5. If aReplace is false (i.e. we're doing a pushState instead of a
11458 // replaceState), notify bfcache that we've navigated to a new page.
11459 // 6. If the third argument is present, set the document's current address
11460 // to the absolute URL found in step 2.
11462 // It's important that this function not run arbitrary scripts after step 1
11463 // and before completing step 5. For example, if a script called
11464 // history.back() before we completed step 5, bfcache might destroy an
11465 // active content viewer. Since EvictOutOfRangeContentViewers at the end of
11466 // step 5 might run script, we can't just put a script blocker around the
11467 // critical section.
11469 // Note that we completely ignore the aTitle parameter.
11471 nsresult rv;
11473 // Don't clobber the load type of an existing network load.
11474 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11476 // pushState effectively becomes replaceState when we've started a network
11477 // load but haven't adopted its document yet. This mirrors what we do with
11478 // changes to the hash at this stage of the game.
11479 if (JustStartedNetworkLoad()) {
11480 aReplace = true;
11483 nsCOMPtr<nsIDocument> document = GetDocument();
11484 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11486 // Step 1: Serialize aData using structured clone.
11487 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11489 // scContainer->Init might cause arbitrary JS to run, and this code might
11490 // navigate the page we're on, potentially to a different origin! (bug
11491 // 634834) To protect against this, we abort if our principal changes due
11492 // to the InitFromJSVal() call.
11494 nsCOMPtr<nsIDocument> origDocument = GetDocument();
11495 if (!origDocument) {
11496 return NS_ERROR_DOM_SECURITY_ERR;
11498 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11500 scContainer = new nsStructuredCloneContainer();
11501 rv = scContainer->InitFromJSVal(aData, aCx);
11502 NS_ENSURE_SUCCESS(rv, rv);
11504 nsCOMPtr<nsIDocument> newDocument = GetDocument();
11505 if (!newDocument) {
11506 return NS_ERROR_DOM_SECURITY_ERR;
11508 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11510 bool principalsEqual = false;
11511 origPrincipal->Equals(newPrincipal, &principalsEqual);
11512 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11515 // Check that the state object isn't too long.
11516 // Default max length: 640k bytes.
11517 int32_t maxStateObjSize =
11518 Preferences::GetInt("browser.history.maxStateObjectSize", 0xA0000);
11519 if (maxStateObjSize < 0) {
11520 maxStateObjSize = 0;
11523 uint64_t scSize;
11524 rv = scContainer->GetSerializedNBytes(&scSize);
11525 NS_ENSURE_SUCCESS(rv, rv);
11527 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11529 // Step 2: Resolve aURL
11530 bool equalURIs = true;
11531 nsCOMPtr<nsIURI> currentURI;
11532 if (sURIFixup && mCurrentURI) {
11533 rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(currentURI));
11534 NS_ENSURE_SUCCESS(rv, rv);
11535 } else {
11536 currentURI = mCurrentURI;
11538 nsCOMPtr<nsIURI> oldURI = currentURI;
11539 nsCOMPtr<nsIURI> newURI;
11540 if (aURL.Length() == 0) {
11541 newURI = currentURI;
11542 } else {
11543 // 2a: Resolve aURL relative to mURI
11545 nsIURI* docBaseURI = document->GetDocBaseURI();
11546 if (!docBaseURI) {
11547 return NS_ERROR_FAILURE;
11550 nsAutoCString spec;
11551 docBaseURI->GetSpec(spec);
11553 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11554 document->GetDocumentCharacterSet(), docBaseURI);
11556 // 2b: If 2a fails, raise a SECURITY_ERR
11557 if (NS_FAILED(rv)) {
11558 return NS_ERROR_DOM_SECURITY_ERR;
11561 // 2c: Same-origin check.
11562 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11563 // In addition to checking that the security manager says that
11564 // the new URI has the same origin as our current URI, we also
11565 // check that the two URIs have the same userpass. (The
11566 // security manager says that |http://foo.com| and
11567 // |http://me@foo.com| have the same origin.) currentURI
11568 // won't contain the password part of the userpass, so this
11569 // means that it's never valid to specify a password in a
11570 // pushState or replaceState URI.
11572 nsCOMPtr<nsIScriptSecurityManager> secMan =
11573 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11574 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11576 // It's very important that we check that newURI is of the same
11577 // origin as currentURI, not docBaseURI, because a page can
11578 // set docBaseURI arbitrarily to any domain.
11579 nsAutoCString currentUserPass, newUserPass;
11580 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11581 NS_ERROR_FAILURE);
11582 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11583 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true)) ||
11584 !currentUserPass.Equals(newUserPass)) {
11585 return NS_ERROR_DOM_SECURITY_ERR;
11587 } else {
11588 // It's a file:// URI
11589 nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
11590 do_QueryInterface(document);
11592 if (!docScriptObj) {
11593 return NS_ERROR_DOM_SECURITY_ERR;
11596 nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
11598 if (!principal ||
11599 NS_FAILED(principal->CheckMayLoad(newURI, true, false))) {
11600 return NS_ERROR_DOM_SECURITY_ERR;
11604 if (currentURI) {
11605 currentURI->Equals(newURI, &equalURIs);
11606 } else {
11607 equalURIs = false;
11610 } // end of same-origin check
11612 // Step 3: Create a new entry in the session history. This will erase
11613 // all SHEntries after the new entry and make this entry the current
11614 // one. This operation may modify mOSHE, which we need later, so we
11615 // keep a reference here.
11616 NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
11617 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11619 mLoadType = LOAD_PUSHSTATE;
11621 nsCOMPtr<nsISHEntry> newSHEntry;
11622 if (!aReplace) {
11623 // Save the current scroll position (bug 590573).
11624 nscoord cx = 0, cy = 0;
11625 GetCurScrollPos(ScrollOrientation_X, &cx);
11626 GetCurScrollPos(ScrollOrientation_Y, &cy);
11627 mOSHE->SetScrollPosition(cx, cy);
11629 bool scrollRestorationIsManual = false;
11630 mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
11632 // Since we're not changing which page we have loaded, pass
11633 // true for aCloneChildren.
11634 rv = AddToSessionHistory(newURI, nullptr,
11635 document->NodePrincipal(), // triggeringPrincipal
11636 nullptr, true,
11637 getter_AddRefs(newSHEntry));
11638 NS_ENSURE_SUCCESS(rv, rv);
11640 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11642 // Session history entries created by pushState inherit scroll restoration
11643 // mode from the current entry.
11644 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11646 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11647 // two entries correspond to the same document.
11648 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), NS_ERROR_FAILURE);
11650 // Set the new SHEntry's title (bug 655273).
11651 nsString title;
11652 mOSHE->GetTitle(getter_Copies(title));
11653 newSHEntry->SetTitle(title);
11655 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11656 // we'll just set mOSHE here.
11657 mOSHE = newSHEntry;
11659 } else {
11660 newSHEntry = mOSHE;
11661 newSHEntry->SetURI(newURI);
11662 newSHEntry->SetOriginalURI(newURI);
11663 newSHEntry->SetLoadReplace(false);
11666 // Step 4: Modify new/original session history entry and clear its POST
11667 // data, if there is any.
11668 newSHEntry->SetStateData(scContainer);
11669 newSHEntry->SetPostData(nullptr);
11671 // If this push/replaceState changed the document's current URI and the new
11672 // URI differs from the old URI in more than the hash, or if the old
11673 // SHEntry's URI was modified in this way by a push/replaceState call
11674 // set URIWasModified to true for the current SHEntry (bug 669671).
11675 bool sameExceptHashes = true, oldURIWasModified = false;
11676 newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
11677 oldOSHE->GetURIWasModified(&oldURIWasModified);
11678 newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
11680 // Step 5: If aReplace is false, indicating that we're doing a pushState
11681 // rather than a replaceState, notify bfcache that we've added a page to
11682 // the history so it can evict content viewers if appropriate. Otherwise
11683 // call ReplaceEntry so that we notify nsIHistoryListeners that an entry
11684 // was replaced.
11685 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11686 NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
11688 if (!aReplace) {
11689 int32_t curIndex = rootSH->Index();
11690 if (curIndex > -1) {
11691 rootSH->LegacySHistoryInternal()->EvictOutOfRangeContentViewers(curIndex);
11693 } else {
11694 nsCOMPtr<nsISHEntry> rootSHEntry = nsSHistory::GetRootSHEntry(newSHEntry);
11696 int32_t index = -1;
11697 rv = rootSH->LegacySHistory()->GetIndexOfEntry(rootSHEntry, &index);
11698 if (NS_SUCCEEDED(rv) && index > -1) {
11699 rootSH->LegacySHistoryInternal()->ReplaceEntry(index, rootSHEntry);
11703 // Step 6: If the document's URI changed, update document's URI and update
11704 // global history.
11706 // We need to call FireOnLocationChange so that the browser's address bar
11707 // gets updated and the back button is enabled, but we only need to
11708 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11709 // since SetCurrentURI will call FireOnLocationChange for us.
11711 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11712 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11713 // notification is allowed only when we know docshell is not loading a new
11714 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11715 // FireOnLocationChange(...) breaks security UI.
11716 if (!equalURIs) {
11717 document->SetDocumentURI(newURI);
11718 // We can't trust SetCurrentURI to do always fire locationchange events
11719 // when we expect it to, so we hack around that by doing it ourselves...
11720 SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
11721 if (mLoadType != LOAD_ERROR_PAGE) {
11722 FireDummyOnLocationChange();
11725 AddURIVisit(newURI, oldURI, oldURI, 0);
11727 // AddURIVisit doesn't set the title for the new URI in global history,
11728 // so do that here.
11729 UpdateGlobalHistoryTitle(newURI);
11731 // Inform the favicon service that our old favicon applies to this new
11732 // URI.
11733 CopyFavicon(oldURI, newURI, document->NodePrincipal(), UsePrivateBrowsing());
11734 } else {
11735 FireDummyOnLocationChange();
11737 document->SetStateObject(scContainer);
11739 return NS_OK;
11742 NS_IMETHODIMP
11743 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual)
11745 *aIsManual = false;
11746 if (mOSHE) {
11747 mOSHE->GetScrollRestorationIsManual(aIsManual);
11750 return NS_OK;
11753 NS_IMETHODIMP
11754 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual)
11756 if (mOSHE) {
11757 mOSHE->SetScrollRestorationIsManual(aIsManual);
11760 return NS_OK;
11763 bool
11764 nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel)
11766 // I believe none of the about: urls should go in the history. But then
11767 // that could just be me... If the intent is only deny about:blank then we
11768 // should just do a spec compare, rather than two gets of the scheme and
11769 // then the path. -Gagan
11770 nsresult rv;
11771 nsAutoCString buf;
11773 rv = aURI->GetScheme(buf);
11774 if (NS_FAILED(rv)) {
11775 return false;
11778 if (buf.EqualsLiteral("about")) {
11779 rv = aURI->GetPathQueryRef(buf);
11780 if (NS_FAILED(rv)) {
11781 return false;
11784 if (buf.EqualsLiteral("blank")) {
11785 return false;
11787 // We only want to add about:newtab if it's not privileged:
11788 if (buf.EqualsLiteral("newtab")) {
11789 NS_ENSURE_TRUE(aChannel, false);
11790 nsCOMPtr<nsIPrincipal> resultPrincipal;
11791 rv = nsContentUtils::GetSecurityManager()->
11792 GetChannelResultPrincipal(aChannel,
11793 getter_AddRefs(resultPrincipal));
11794 NS_ENSURE_SUCCESS(rv, false);
11795 return !nsContentUtils::IsSystemPrincipal(resultPrincipal);
11799 return true;
11802 nsresult
11803 nsDocShell::AddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel,
11804 nsIPrincipal* aTriggeringPrincipal,
11805 nsIPrincipal* aPrincipalToInherit,
11806 bool aCloneChildren,
11807 nsISHEntry** aNewEntry)
11809 MOZ_ASSERT(aURI, "uri is null");
11810 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11812 #if defined(DEBUG)
11813 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11814 nsAutoCString chanName;
11815 if (aChannel) {
11816 aChannel->GetName(chanName);
11817 } else {
11818 chanName.AssignLiteral("<no channel>");
11821 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11822 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n",
11823 this, aURI->GetSpecOrDefault().get(), chanName.get()));
11825 #endif
11827 nsresult rv = NS_OK;
11828 nsCOMPtr<nsISHEntry> entry;
11830 // Get a handle to the root docshell
11831 nsCOMPtr<nsIDocShellTreeItem> root;
11832 GetSameTypeRootTreeItem(getter_AddRefs(root));
11834 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11835 * the existing SH entry in the page and replace the url and
11836 * other vitalities.
11838 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11839 root != static_cast<nsIDocShellTreeItem*>(this)) {
11840 // This is a subframe
11841 entry = mOSHE;
11842 nsCOMPtr<nsISHContainer> shContainer(do_QueryInterface(entry));
11843 if (shContainer) {
11844 int32_t childCount = 0;
11845 shContainer->GetChildCount(&childCount);
11846 // Remove all children of this entry
11847 for (int32_t i = childCount - 1; i >= 0; i--) {
11848 nsCOMPtr<nsISHEntry> child;
11849 shContainer->GetChildAt(i, getter_AddRefs(child));
11850 shContainer->RemoveChild(child);
11852 entry->AbandonBFCacheEntry();
11856 // Create a new entry if necessary.
11857 if (!entry) {
11858 entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);
11860 if (!entry) {
11861 return NS_ERROR_OUT_OF_MEMORY;
11865 // Get the post data & referrer
11866 nsCOMPtr<nsIInputStream> inputStream;
11867 nsCOMPtr<nsIURI> originalURI;
11868 nsCOMPtr<nsIURI> resultPrincipalURI;
11869 bool loadReplace = false;
11870 nsCOMPtr<nsIURI> referrerURI;
11871 uint32_t referrerPolicy = RP_Unset;
11872 uint32_t cacheKey = 0;
11873 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11874 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11875 bool expired = false;
11876 bool discardLayoutState = false;
11877 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11878 if (aChannel) {
11879 cacheChannel = do_QueryInterface(aChannel);
11881 /* If there is a caching channel, get the Cache Key and store it
11882 * in SH.
11884 if (cacheChannel) {
11885 cacheChannel->GetCacheKey(&cacheKey);
11887 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11889 // Check if the httpChannel is hiding under a multipartChannel
11890 if (!httpChannel) {
11891 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11893 if (httpChannel) {
11894 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11895 if (uploadChannel) {
11896 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11898 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11899 uint32_t loadFlags;
11900 aChannel->GetLoadFlags(&loadFlags);
11901 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11902 rv = httpChannel->GetReferrer(getter_AddRefs(referrerURI));
11903 MOZ_ASSERT(NS_SUCCEEDED(rv));
11904 rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
11905 MOZ_ASSERT(NS_SUCCEEDED(rv));
11907 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11910 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
11911 if (loadInfo) {
11912 if (!triggeringPrincipal) {
11913 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11916 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11918 // For now keep storing just the principal in the SHEntry.
11919 if (!principalToInherit) {
11920 if (loadInfo->GetLoadingSandboxed()) {
11921 if (loadInfo->LoadingPrincipal()) {
11922 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11923 loadInfo->LoadingPrincipal());
11924 } else {
11925 // get the OriginAttributes
11926 OriginAttributes attrs;
11927 loadInfo->GetOriginAttributes(&attrs);
11928 principalToInherit = NullPrincipal::Create(attrs);
11930 } else {
11931 principalToInherit = loadInfo->PrincipalToInherit();
11937 // Title is set in nsDocShell::SetTitle()
11938 entry->Create(aURI, // uri
11939 EmptyString(), // Title
11940 inputStream, // Post data stream
11941 nullptr, // LayoutHistory state
11942 cacheKey, // CacheKey
11943 mContentTypeHint, // Content-type
11944 triggeringPrincipal, // Channel or provided principal
11945 principalToInherit,
11946 mHistoryID,
11947 mDynamicallyCreated);
11949 entry->SetOriginalURI(originalURI);
11950 entry->SetResultPrincipalURI(resultPrincipalURI);
11951 entry->SetLoadReplace(loadReplace);
11952 entry->SetReferrerURI(referrerURI);
11953 entry->SetReferrerPolicy(referrerPolicy);
11954 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11955 if (inStrmChan) {
11956 bool isSrcdocChannel;
11957 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11958 if (isSrcdocChannel) {
11959 nsAutoString srcdoc;
11960 inStrmChan->GetSrcdocData(srcdoc);
11961 entry->SetSrcdocData(srcdoc);
11962 nsCOMPtr<nsIURI> baseURI;
11963 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11964 entry->SetBaseURI(baseURI);
11967 /* If cache got a 'no-store', ask SH not to store
11968 * HistoryLayoutState. By default, SH will set this
11969 * flag to true and save HistoryLayoutState.
11971 if (discardLayoutState) {
11972 entry->SetSaveLayoutStateFlag(false);
11974 if (cacheChannel) {
11975 // Check if the page has expired from cache
11976 uint32_t expTime = 0;
11977 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11978 uint32_t now = PRTimeToSeconds(PR_Now());
11979 if (expTime <= now) {
11980 expired = true;
11983 if (expired) {
11984 entry->SetExpirationStatus(true);
11987 if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
11988 // If we need to clone our children onto the new session
11989 // history entry, do so now.
11990 if (aCloneChildren && mOSHE) {
11991 uint32_t cloneID;
11992 mOSHE->GetID(&cloneID);
11993 nsCOMPtr<nsISHEntry> newEntry;
11994 nsSHistory::CloneAndReplace(mOSHE, this, cloneID, entry, true,
11995 getter_AddRefs(newEntry));
11996 NS_ASSERTION(entry == newEntry,
11997 "The new session history should be in the new entry");
12000 // This is the root docshell
12001 bool addToSHistory = !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
12002 if (!addToSHistory) {
12003 // Replace current entry in session history; If the requested index is
12004 // valid, it indicates the loading was triggered by a history load, and
12005 // we should replace the entry at requested index instead.
12006 int32_t index = 0;
12007 mSessionHistory->LegacySHistory()->GetRequestedIndex(&index);
12008 if (index == -1) {
12009 index = mSessionHistory->Index();
12012 // Replace the current entry with the new entry
12013 if (index >= 0) {
12014 rv = mSessionHistory->LegacySHistoryInternal()->ReplaceEntry(index,
12015 entry);
12016 } else {
12017 // If we're trying to replace an inexistant shistory entry, append.
12018 addToSHistory = true;
12022 if (addToSHistory) {
12023 // Add to session history
12024 mPreviousTransIndex = mSessionHistory->Index();
12026 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
12027 rv = mSessionHistory->LegacySHistoryInternal()->AddEntry(
12028 entry, shouldPersist);
12029 mLoadedTransIndex = mSessionHistory->Index();
12030 #ifdef DEBUG_PAGE_CACHE
12031 printf("Previous index: %d, Loaded index: %d\n\n",
12032 mPreviousTransIndex, mLoadedTransIndex);
12033 #endif
12035 } else {
12036 // This is a subframe.
12037 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
12038 rv = AddChildSHEntryToParent(entry, mChildOffset, aCloneChildren);
12042 // Return the new SH entry...
12043 if (aNewEntry) {
12044 *aNewEntry = nullptr;
12045 if (NS_SUCCEEDED(rv)) {
12046 entry.forget(aNewEntry);
12050 return rv;
12053 nsresult
12054 nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType)
12056 if (!IsNavigationAllowed()) {
12057 return NS_OK;
12060 nsCOMPtr<nsIURI> uri;
12061 nsCOMPtr<nsIURI> originalURI;
12062 nsCOMPtr<nsIURI> resultPrincipalURI;
12063 bool loadReplace = false;
12064 nsCOMPtr<nsIInputStream> postData;
12065 nsCOMPtr<nsIURI> referrerURI;
12066 uint32_t referrerPolicy;
12067 nsAutoCString contentType;
12068 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
12069 nsCOMPtr<nsIPrincipal> principalToInherit;
12071 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
12073 NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
12074 NS_ENSURE_SUCCESS(aEntry->GetOriginalURI(getter_AddRefs(originalURI)),
12075 NS_ERROR_FAILURE);
12076 NS_ENSURE_SUCCESS(aEntry->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)),
12077 NS_ERROR_FAILURE);
12078 NS_ENSURE_SUCCESS(aEntry->GetLoadReplace(&loadReplace),
12079 NS_ERROR_FAILURE);
12080 NS_ENSURE_SUCCESS(aEntry->GetReferrerURI(getter_AddRefs(referrerURI)),
12081 NS_ERROR_FAILURE);
12082 NS_ENSURE_SUCCESS(aEntry->GetReferrerPolicy(&referrerPolicy),
12083 NS_ERROR_FAILURE);
12084 NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
12085 NS_ERROR_FAILURE);
12086 NS_ENSURE_SUCCESS(aEntry->GetContentType(contentType), NS_ERROR_FAILURE);
12087 NS_ENSURE_SUCCESS(aEntry->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal)),
12088 NS_ERROR_FAILURE);
12089 NS_ENSURE_SUCCESS(aEntry->GetPrincipalToInherit(getter_AddRefs(principalToInherit)),
12090 NS_ERROR_FAILURE);
12092 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
12093 // that's the only thing holding a ref to aEntry that will cause aEntry to
12094 // die while we're loading it. So hold a strong ref to aEntry here, just
12095 // in case.
12096 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
12097 bool isJS;
12098 nsresult rv = uri->SchemeIs("javascript", &isJS);
12099 if (NS_FAILED(rv) || isJS) {
12100 // We're loading a URL that will execute script from inside asyncOpen.
12101 // Replace the current document with about:blank now to prevent
12102 // anything from the current document from leaking into any JavaScript
12103 // code in the URL.
12104 // Don't cache the presentation if we're going to just reload the
12105 // current entry. Caching would lead to trying to save the different
12106 // content viewers in the same nsISHEntry object.
12107 rv = CreateAboutBlankContentViewer(principalToInherit, nullptr,
12108 aEntry != mOSHE);
12110 if (NS_FAILED(rv)) {
12111 // The creation of the intermittent about:blank content
12112 // viewer failed for some reason (potentially because the
12113 // user prevented it). Interrupt the history load.
12114 return NS_OK;
12117 if (!triggeringPrincipal) {
12118 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
12119 // URIs will pick it up from the about:blank page we just loaded,
12120 // and we don't really want even that in this case.
12121 triggeringPrincipal = NullPrincipal::CreateWithInheritedAttributes(this);
12125 /* If there is a valid postdata *and* the user pressed
12126 * reload or shift-reload, take user's permission before we
12127 * repost the data to the server.
12129 if ((aLoadType & LOAD_CMD_RELOAD) && postData) {
12130 bool repost;
12131 rv = ConfirmRepost(&repost);
12132 if (NS_FAILED(rv)) {
12133 return rv;
12136 // If the user pressed cancel in the dialog, return. We're done here.
12137 if (!repost) {
12138 return NS_BINDING_ABORTED;
12142 // Do not inherit principal from document (security-critical!);
12143 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12145 nsAutoString srcdoc;
12146 bool isSrcdoc;
12147 nsCOMPtr<nsIURI> baseURI;
12148 aEntry->GetIsSrcdocEntry(&isSrcdoc);
12149 if (isSrcdoc) {
12150 aEntry->GetSrcdocData(srcdoc);
12151 aEntry->GetBaseURI(getter_AddRefs(baseURI));
12152 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
12153 } else {
12154 srcdoc = VoidString();
12157 // If there is no valid triggeringPrincipal, we deny the load
12158 MOZ_ASSERT(triggeringPrincipal, "need a valid triggeringPrincipal to load from history");
12159 if (!triggeringPrincipal) {
12160 return NS_ERROR_FAILURE;
12163 // Passing nullptr as aSourceDocShell gives the same behaviour as before
12164 // aSourceDocShell was introduced. According to spec we should be passing
12165 // the source browsing context that was used when the history entry was
12166 // first created. bug 947716 has been created to address this issue.
12167 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
12168 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
12169 rv = InternalLoad(uri,
12170 originalURI,
12171 emplacedResultPrincipalURI,
12172 loadReplace,
12173 referrerURI,
12174 referrerPolicy,
12175 triggeringPrincipal,
12176 principalToInherit,
12177 flags,
12178 EmptyString(), // No window target
12179 contentType.get(), // Type hint
12180 VoidString(), // No forced file download
12181 postData, // Post data stream
12182 nullptr, // No headers stream
12183 aLoadType, // Load type
12184 aEntry, // SHEntry
12185 true,
12186 srcdoc,
12187 nullptr, // Source docshell, see comment above
12188 baseURI,
12189 nullptr, // No nsIDocShell
12190 nullptr); // No nsIRequest
12191 return rv;
12194 NS_IMETHODIMP
12195 nsDocShell::GetShouldSaveLayoutState(bool* aShould)
12197 *aShould = false;
12198 if (mOSHE) {
12199 // Don't capture historystate and save it in history
12200 // if the page asked not to do so.
12201 mOSHE->GetSaveLayoutStateFlag(aShould);
12204 return NS_OK;
12207 nsresult
12208 nsDocShell::PersistLayoutHistoryState()
12210 nsresult rv = NS_OK;
12212 if (mOSHE) {
12213 bool scrollRestorationIsManual = false;
12214 mOSHE->GetScrollRestorationIsManual(&scrollRestorationIsManual);
12216 nsCOMPtr<nsIPresShell> shell = GetPresShell();
12217 nsCOMPtr<nsILayoutHistoryState> layoutState;
12218 if (shell) {
12219 rv = shell->CaptureHistoryState(getter_AddRefs(layoutState));
12220 } else if (scrollRestorationIsManual) {
12221 // Even if we don't have layout anymore, we may want to reset the current
12222 // scroll state in layout history.
12223 GetLayoutHistoryState(getter_AddRefs(layoutState));
12226 if (scrollRestorationIsManual && layoutState) {
12227 layoutState->ResetScrollState();
12231 return rv;
12234 void
12235 nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry)
12237 if (aOldEntry == mOSHE) {
12238 mOSHE = aNewEntry;
12241 if (aOldEntry == mLSHE) {
12242 mLSHE = aNewEntry;
12246 void
12247 nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry)
12249 // We need to sync up the docshell and session history trees for
12250 // subframe navigation. If the load was in a subframe, we forward up to
12251 // the root docshell, which will then recursively sync up all docshells
12252 // to their corresponding entries in the new session history tree.
12253 // If we don't do this, then we can cache a content viewer on the wrong
12254 // cloned entry, and subsequently restore it at the wrong time.
12256 nsISHEntry* newRootEntry = nsSHistory::GetRootSHEntry(aEntry);
12257 if (newRootEntry) {
12258 // newRootEntry is now the new root entry.
12259 // Find the old root entry as well.
12261 // Need a strong ref. on |oldRootEntry| so it isn't destroyed when
12262 // SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
12263 nsCOMPtr<nsISHEntry> oldRootEntry = nsSHistory::GetRootSHEntry(*aPtr);
12264 if (oldRootEntry) {
12265 nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
12266 GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
12267 nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
12268 if (rootShell) { // if we're the root just set it, nothing to swap
12269 nsSHistory::SwapEntriesData data = { this, newRootEntry };
12270 nsIDocShell* rootIDocShell = static_cast<nsIDocShell*>(rootShell);
12271 nsDocShell* rootDocShell = static_cast<nsDocShell*>(rootIDocShell);
12273 #ifdef DEBUG
12274 nsresult rv =
12275 #endif
12276 nsSHistory::SetChildHistoryEntry(oldRootEntry, rootDocShell, 0, &data);
12277 NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
12282 *aPtr = aEntry;
12285 already_AddRefed<ChildSHistory>
12286 nsDocShell::GetRootSessionHistory()
12288 nsCOMPtr<nsIDocShellTreeItem> root;
12289 nsresult rv = GetSameTypeRootTreeItem(getter_AddRefs(root));
12290 if (NS_WARN_IF(NS_FAILED(rv))) {
12291 return nullptr;
12293 nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root);
12294 if (!webnav) {
12295 return nullptr;
12297 return webnav->GetSessionHistory();
12300 nsresult
12301 nsDocShell::GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn)
12303 NS_ENSURE_ARG_POINTER(aReturn);
12304 if (!aChannel) {
12305 return NS_ERROR_FAILURE;
12308 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12309 if (multiPartChannel) {
12310 nsCOMPtr<nsIChannel> baseChannel;
12311 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12312 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12313 *aReturn = httpChannel;
12314 NS_IF_ADDREF(*aReturn);
12316 return NS_OK;
12319 bool
12320 nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel)
12322 // By default layout State will be saved.
12323 if (!aChannel) {
12324 return false;
12327 // figure out if SH should be saving layout state
12328 bool noStore = false;
12329 Unused << aChannel->IsNoStoreResponse(&noStore);
12330 return noStore;
12333 NS_IMETHODIMP
12334 nsDocShell::GetEditor(nsIEditor** aEditor)
12336 NS_ENSURE_ARG_POINTER(aEditor);
12337 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12338 htmlEditor.forget(aEditor);
12339 return NS_OK;
12342 NS_IMETHODIMP
12343 nsDocShell::SetEditor(nsIEditor* aEditor)
12345 HTMLEditor* htmlEditor = aEditor ? aEditor->AsHTMLEditor() : nullptr;
12346 // If TextEditor comes, throw an error.
12347 if (aEditor && !htmlEditor) {
12348 return NS_ERROR_INVALID_ARG;
12350 return SetHTMLEditorInternal(htmlEditor);
12353 HTMLEditor*
12354 nsDocShell::GetHTMLEditorInternal()
12356 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12359 nsresult
12360 nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor)
12362 if (!aHTMLEditor && !mEditorData) {
12363 return NS_OK;
12366 nsresult rv = EnsureEditorData();
12367 if (NS_FAILED(rv)) {
12368 return rv;
12371 return mEditorData->SetHTMLEditor(aHTMLEditor);
12374 NS_IMETHODIMP
12375 nsDocShell::GetEditable(bool* aEditable)
12377 NS_ENSURE_ARG_POINTER(aEditable);
12378 *aEditable = mEditorData && mEditorData->GetEditable();
12379 return NS_OK;
12382 NS_IMETHODIMP
12383 nsDocShell::GetHasEditingSession(bool* aHasEditingSession)
12385 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12387 if (mEditorData) {
12388 nsCOMPtr<nsIEditingSession> editingSession;
12389 mEditorData->GetEditingSession(getter_AddRefs(editingSession));
12390 *aHasEditingSession = (editingSession.get() != nullptr);
12391 } else {
12392 *aHasEditingSession = false;
12395 return NS_OK;
12398 NS_IMETHODIMP
12399 nsDocShell::MakeEditable(bool aInWaitForUriLoad)
12401 nsresult rv = EnsureEditorData();
12402 if (NS_FAILED(rv)) {
12403 return rv;
12406 return mEditorData->MakeEditable(aInWaitForUriLoad);
12409 bool
12410 nsDocShell::ChannelIsPost(nsIChannel* aChannel)
12412 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
12413 if (!httpChannel) {
12414 return false;
12417 nsAutoCString method;
12418 Unused << httpChannel->GetRequestMethod(method);
12419 return method.EqualsLiteral("POST");
12422 void
12423 nsDocShell::ExtractLastVisit(nsIChannel* aChannel,
12424 nsIURI** aURI,
12425 uint32_t* aChannelRedirectFlags)
12427 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12428 if (!props) {
12429 return;
12432 nsresult rv = props->GetPropertyAsInterface(
12433 NS_LITERAL_STRING("docshell.previousURI"),
12434 NS_GET_IID(nsIURI),
12435 reinterpret_cast<void**>(aURI));
12437 if (NS_FAILED(rv)) {
12438 // There is no last visit for this channel, so this must be the first
12439 // link. Link the visit to the referrer of this request, if any.
12440 // Treat referrer as null if there is an error getting it.
12441 (void)NS_GetReferrerFromChannel(aChannel, aURI);
12442 } else {
12443 rv = props->GetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
12444 aChannelRedirectFlags);
12446 NS_WARNING_ASSERTION(
12447 NS_SUCCEEDED(rv),
12448 "Could not fetch previous flags, URI will be treated like referrer");
12452 void
12453 nsDocShell::SaveLastVisit(nsIChannel* aChannel,
12454 nsIURI* aURI,
12455 uint32_t aChannelRedirectFlags)
12457 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12458 if (!props || !aURI) {
12459 return;
12462 props->SetPropertyAsInterface(NS_LITERAL_STRING("docshell.previousURI"),
12463 aURI);
12464 props->SetPropertyAsUint32(NS_LITERAL_STRING("docshell.previousFlags"),
12465 aChannelRedirectFlags);
12468 void
12469 nsDocShell::AddURIVisit(nsIURI* aURI,
12470 nsIURI* aReferrerURI,
12471 nsIURI* aPreviousURI,
12472 uint32_t aChannelRedirectFlags,
12473 uint32_t aResponseStatus)
12475 MOZ_ASSERT(aURI, "Visited URI is null!");
12476 MOZ_ASSERT(mLoadType != LOAD_ERROR_PAGE &&
12477 mLoadType != LOAD_BYPASS_HISTORY,
12478 "Do not add error or bypass pages to global history");
12480 // Only content-type docshells save URI visits. Also don't do
12481 // anything here if we're not supposed to use global history.
12482 if (mItemType != typeContent || !mUseGlobalHistory || UsePrivateBrowsing()) {
12483 return;
12486 nsCOMPtr<IHistory> history = services::GetHistoryService();
12488 if (history) {
12489 uint32_t visitURIFlags = 0;
12491 if (!IsFrame()) {
12492 visitURIFlags |= IHistory::TOP_LEVEL;
12495 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12496 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12497 } else if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) {
12498 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12499 } else {
12500 MOZ_ASSERT(!aChannelRedirectFlags,
12501 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12502 "if any flags in aChannelRedirectFlags is set.");
12505 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12506 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12507 if (aResponseStatus == 301 || aResponseStatus == 308) {
12508 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12511 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12512 // simple retry attempt by the user is unlikely to solve them.
12513 // 408 is special cased, since may actually indicate a temporary
12514 // connection problem.
12515 else if (aResponseStatus != 408 &&
12516 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12517 aResponseStatus == 505)) {
12518 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12521 (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
12525 //*****************************************************************************
12526 // nsDocShell: Helper Routines
12527 //*****************************************************************************
12529 NS_IMETHODIMP
12530 nsDocShell::SetLoadType(uint32_t aLoadType)
12532 mLoadType = aLoadType;
12533 return NS_OK;
12536 NS_IMETHODIMP
12537 nsDocShell::GetLoadType(uint32_t* aLoadType)
12539 *aLoadType = mLoadType;
12540 return NS_OK;
12543 nsresult
12544 nsDocShell::ConfirmRepost(bool* aRepost)
12546 nsCOMPtr<nsIPrompt> prompter;
12547 CallGetInterface(this, static_cast<nsIPrompt**>(getter_AddRefs(prompter)));
12548 if (!prompter) {
12549 return NS_ERROR_NOT_AVAILABLE;
12552 nsCOMPtr<nsIStringBundleService> stringBundleService =
12553 mozilla::services::GetStringBundleService();
12554 if (!stringBundleService) {
12555 return NS_ERROR_FAILURE;
12558 nsCOMPtr<nsIStringBundle> appBundle;
12559 nsresult rv = stringBundleService->CreateBundle(kAppstringsBundleURL,
12560 getter_AddRefs(appBundle));
12561 NS_ENSURE_SUCCESS(rv, rv);
12563 nsCOMPtr<nsIStringBundle> brandBundle;
12564 rv = stringBundleService->CreateBundle(kBrandBundleURL,
12565 getter_AddRefs(brandBundle));
12566 NS_ENSURE_SUCCESS(rv, rv);
12568 NS_ASSERTION(prompter && brandBundle && appBundle,
12569 "Unable to set up repost prompter.");
12571 nsAutoString brandName;
12572 rv = brandBundle->GetStringFromName("brandShortName", brandName);
12574 nsAutoString msgString, button0Title;
12575 if (NS_FAILED(rv)) { // No brand, use the generic version.
12576 rv = appBundle->GetStringFromName("confirmRepostPrompt", msgString);
12577 } else {
12578 // Brand available - if the app has an override file with formatting, the
12579 // app name will be included. Without an override, the prompt will look
12580 // like the generic version.
12581 const char16_t* formatStrings[] = { brandName.get() };
12582 rv = appBundle->FormatStringFromName("confirmRepostPrompt",
12583 formatStrings,
12584 ArrayLength(formatStrings),
12585 msgString);
12587 if (NS_FAILED(rv)) {
12588 return rv;
12591 rv = appBundle->GetStringFromName("resendButton.label", button0Title);
12592 if (NS_FAILED(rv)) {
12593 return rv;
12596 // Make the repost prompt tab modal to prevent malicious pages from locking
12597 // up the browser, see bug 1412559 for an example.
12598 if (nsCOMPtr<nsIWritablePropertyBag2> promptBag = do_QueryInterface(prompter)) {
12599 promptBag->SetPropertyAsBool(NS_LITERAL_STRING("allowTabModal"), true);
12602 int32_t buttonPressed;
12603 // The actual value here is irrelevant, but we can't pass an invalid
12604 // bool through XPConnect.
12605 bool checkState = false;
12606 rv = prompter->ConfirmEx(
12607 nullptr, msgString.get(),
12608 (nsIPrompt::BUTTON_POS_0 * nsIPrompt::BUTTON_TITLE_IS_STRING) +
12609 (nsIPrompt::BUTTON_POS_1 * nsIPrompt::BUTTON_TITLE_CANCEL),
12610 button0Title.get(), nullptr, nullptr, nullptr, &checkState, &buttonPressed);
12611 if (NS_FAILED(rv)) {
12612 return rv;
12615 *aRepost = (buttonPressed == 0);
12616 return NS_OK;
12619 nsresult
12620 nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12621 nsIStringBundle** aStringBundle)
12623 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12624 NS_ERROR_FAILURE);
12626 nsCOMPtr<nsIStringBundleService> stringBundleService =
12627 mozilla::services::GetStringBundleService();
12628 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12630 NS_ENSURE_SUCCESS(
12631 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12632 NS_ERROR_FAILURE);
12634 return NS_OK;
12637 nsIScrollableFrame*
12638 nsDocShell::GetRootScrollFrame()
12640 nsCOMPtr<nsIPresShell> shell = GetPresShell();
12641 NS_ENSURE_TRUE(shell, nullptr);
12643 return shell->GetRootScrollFrameAsScrollable();
12646 nsresult
12647 nsDocShell::EnsureScriptEnvironment()
12649 if (mScriptGlobal) {
12650 return NS_OK;
12653 if (mIsBeingDestroyed) {
12654 return NS_ERROR_NOT_AVAILABLE;
12657 #ifdef DEBUG
12658 NS_ASSERTION(!mInEnsureScriptEnv,
12659 "Infinite loop! Calling EnsureScriptEnvironment() from "
12660 "within EnsureScriptEnvironment()!");
12662 // Yeah, this isn't re-entrant safe, but that's ok since if we
12663 // re-enter this method, we'll infinitely loop...
12664 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12665 mInEnsureScriptEnv = true;
12666 #endif
12668 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12669 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12671 uint32_t chromeFlags;
12672 browserChrome->GetChromeFlags(&chromeFlags);
12674 // If our window is modal and we're not opened as chrome, make
12675 // this window a modal content window.
12676 mScriptGlobal = NS_NewScriptGlobalObject(mItemType == typeChrome);
12677 MOZ_ASSERT(mScriptGlobal);
12679 mScriptGlobal->SetDocShell(this);
12681 // Ensure the script object is set up to run script.
12682 return mScriptGlobal->EnsureScriptEnvironment();
12685 nsresult
12686 nsDocShell::EnsureEditorData()
12688 MOZ_ASSERT(!mIsBeingDestroyed);
12690 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12691 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12692 // We shouldn't recreate the editor data if it already exists, or
12693 // we're shutting down, or we already have a detached editor data
12694 // stored in the session history. We should only have one editordata
12695 // per docshell.
12696 mEditorData = new nsDocShellEditorData(this);
12699 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12702 nsresult
12703 nsDocShell::EnsureFind()
12705 nsresult rv;
12706 if (!mFind) {
12707 mFind = do_CreateInstance("@mozilla.org/embedcomp/find;1", &rv);
12708 if (NS_FAILED(rv)) {
12709 return rv;
12713 // we promise that the nsIWebBrowserFind that we return has been set
12714 // up to point to the focused, or content window, so we have to
12715 // set that up each time.
12717 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12718 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12720 // default to our window
12721 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12722 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12723 nsFocusManager::GetFocusedDescendant(ourWindow,
12724 nsFocusManager::eIncludeAllDescendants,
12725 getter_AddRefs(windowToSearch));
12727 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12728 if (!findInFrames) {
12729 return NS_ERROR_NO_INTERFACE;
12732 rv = findInFrames->SetRootSearchFrame(ourWindow);
12733 if (NS_FAILED(rv)) {
12734 return rv;
12736 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12737 if (NS_FAILED(rv)) {
12738 return rv;
12741 return NS_OK;
12744 bool
12745 nsDocShell::IsFrame()
12747 nsCOMPtr<nsIDocShellTreeItem> parent;
12748 GetSameTypeParent(getter_AddRefs(parent));
12749 return !!parent;
12752 NS_IMETHODIMP
12753 nsDocShell::IsBeingDestroyed(bool* aDoomed)
12755 NS_ENSURE_ARG(aDoomed);
12756 *aDoomed = mIsBeingDestroyed;
12757 return NS_OK;
12760 NS_IMETHODIMP
12761 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult)
12763 NS_ENSURE_ARG(aResult);
12764 *aResult = mIsExecutingOnLoadHandler;
12765 return NS_OK;
12768 NS_IMETHODIMP
12769 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState)
12771 if (mOSHE) {
12772 mOSHE->GetLayoutHistoryState(aLayoutHistoryState);
12774 return NS_OK;
12777 NS_IMETHODIMP
12778 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
12780 if (mOSHE) {
12781 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12783 return NS_OK;
12786 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12787 nsIInterfaceRequestor* aRequestor)
12789 if (aRequestor) {
12790 mWeakPtr = do_GetWeakReference(aRequestor);
12794 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy()
12796 mWeakPtr = nullptr;
12799 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12801 NS_IMETHODIMP
12802 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12803 void** aSink)
12805 NS_ENSURE_ARG_POINTER(aSink);
12806 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12807 if (ifReq) {
12808 return ifReq->GetInterface(aIID, aSink);
12810 *aSink = nullptr;
12811 return NS_NOINTERFACE;
12814 nsresult
12815 nsDocShell::SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer)
12817 if (!aContentViewer) {
12818 return NS_ERROR_FAILURE;
12821 nsCOMPtr<nsIURI> baseURI;
12822 nsresult rv = NS_ERROR_NOT_AVAILABLE;
12824 if (sURIFixup) {
12825 rv = sURIFixup->CreateExposableURI(mCurrentURI, getter_AddRefs(baseURI));
12828 // Get the current document and set the base uri
12829 if (baseURI) {
12830 nsIDocument* document = aContentViewer->GetDocument();
12831 if (document) {
12832 document->SetBaseURI(baseURI);
12835 return rv;
12838 //*****************************************************************************
12839 // nsDocShell::nsIAuthPromptProvider
12840 //*****************************************************************************
12842 NS_IMETHODIMP
12843 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12844 void** aResult)
12846 // a priority prompt request will override a false mAllowAuth setting
12847 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12849 if (!mAllowAuth && !priorityPrompt) {
12850 return NS_ERROR_NOT_AVAILABLE;
12853 // we're either allowing auth, or it's a proxy request
12854 nsresult rv;
12855 nsCOMPtr<nsIPromptFactory> wwatch =
12856 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12857 NS_ENSURE_SUCCESS(rv, rv);
12859 rv = EnsureScriptEnvironment();
12860 NS_ENSURE_SUCCESS(rv, rv);
12862 // Get the an auth prompter for our window so that the parenting
12863 // of the dialogs works as it should when using tabs.
12865 return wwatch->GetPrompt(mScriptGlobal->AsOuter(), aIID,
12866 reinterpret_cast<void**>(aResult));
12869 //*****************************************************************************
12870 // nsDocShell::nsILoadContext
12871 //*****************************************************************************
12873 NS_IMETHODIMP
12874 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow)
12876 CallGetInterface(this, aWindow);
12877 return NS_OK;
12880 NS_IMETHODIMP
12881 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow)
12883 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
12884 if (win) {
12885 win = win->GetTop();
12887 win.forget(aWindow);
12888 return NS_OK;
12891 NS_IMETHODIMP
12892 nsDocShell::GetTopFrameElement(Element** aElement)
12894 *aElement = nullptr;
12895 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
12896 if (!win) {
12897 return NS_OK;
12900 nsCOMPtr<nsPIDOMWindowOuter> top = win->GetScriptableTop();
12901 NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
12903 // GetFrameElementInternal, /not/ GetScriptableFrameElement -- if |top| is
12904 // inside <iframe mozbrowser>, we want to return the iframe, not null.
12905 // And we want to cross the content/chrome boundary.
12906 RefPtr<Element> elt = top->GetFrameElementInternal();
12907 elt.forget(aElement);
12908 return NS_OK;
12911 NS_IMETHODIMP
12912 nsDocShell::GetNestedFrameId(uint64_t* aId)
12914 *aId = 0;
12915 return NS_OK;
12918 NS_IMETHODIMP
12919 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection)
12921 *aUseTrackingProtection = false;
12923 static bool sTPEnabled = false;
12924 static bool sTPInPBEnabled = false;
12925 static bool sPrefsInit = false;
12927 if (!sPrefsInit) {
12928 sPrefsInit = true;
12929 Preferences::AddBoolVarCache(&sTPEnabled,
12930 "privacy.trackingprotection.enabled", false);
12931 Preferences::AddBoolVarCache(&sTPInPBEnabled,
12932 "privacy.trackingprotection.pbmode.enabled", false);
12935 if (mUseTrackingProtection || sTPEnabled ||
12936 (UsePrivateBrowsing() && sTPInPBEnabled)) {
12937 *aUseTrackingProtection = true;
12938 return NS_OK;
12941 RefPtr<nsDocShell> parent = GetParentDocshell();
12942 if (parent) {
12943 return parent->GetUseTrackingProtection(aUseTrackingProtection);
12946 return NS_OK;
12949 NS_IMETHODIMP
12950 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection)
12952 mUseTrackingProtection = aUseTrackingProtection;
12953 return NS_OK;
12956 NS_IMETHODIMP
12957 nsDocShell::GetIsContent(bool* aIsContent)
12959 *aIsContent = (mItemType == typeContent);
12960 return NS_OK;
12963 bool
12964 nsDocShell::IsOKToLoadURI(nsIURI* aURI)
12966 MOZ_ASSERT(aURI, "Must have a URI!");
12968 if (!mFiredUnloadEvent) {
12969 return true;
12972 if (!mLoadingURI) {
12973 return false;
12976 nsCOMPtr<nsIScriptSecurityManager> secMan =
12977 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12978 return secMan &&
12979 NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, false));
12983 // Routines for selection and clipboard
12985 nsresult
12986 nsDocShell::GetControllerForCommand(const char* aCommand,
12987 nsIController** aResult)
12989 NS_ENSURE_ARG_POINTER(aResult);
12990 *aResult = nullptr;
12992 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12994 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12995 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12997 return root->GetControllerForCommand(aCommand, false /* for any window */,
12998 aResult);
13001 NS_IMETHODIMP
13002 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult)
13004 NS_ENSURE_ARG_POINTER(aResult);
13005 *aResult = false;
13007 nsresult rv = NS_ERROR_FAILURE;
13009 nsCOMPtr<nsIController> controller;
13010 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
13011 if (controller) {
13012 rv = controller->IsCommandEnabled(aCommand, aResult);
13015 return rv;
13018 NS_IMETHODIMP
13019 nsDocShell::DoCommand(const char* aCommand)
13021 nsresult rv = NS_ERROR_FAILURE;
13023 nsCOMPtr<nsIController> controller;
13024 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
13025 if (controller) {
13026 rv = controller->DoCommand(aCommand);
13029 return rv;
13032 NS_IMETHODIMP
13033 nsDocShell::DoCommandWithParams(const char* aCommand, nsICommandParams* aParams)
13035 nsCOMPtr<nsIController> controller;
13036 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
13037 if (NS_WARN_IF(NS_FAILED(rv))) {
13038 return rv;
13041 nsCOMPtr<nsICommandController> commandController =
13042 do_QueryInterface(controller, &rv);
13043 if (NS_WARN_IF(NS_FAILED(rv))) {
13044 return rv;
13047 return commandController->DoCommandWithParams(aCommand, aParams);
13050 nsresult
13051 nsDocShell::EnsureCommandHandler()
13053 if (!mCommandManager) {
13054 nsCOMPtr<nsPICommandUpdater> commandUpdater =
13055 do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
13056 if (!commandUpdater) {
13057 return NS_ERROR_OUT_OF_MEMORY;
13060 nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow();
13061 nsresult rv = commandUpdater->Init(domWindow);
13062 if (NS_SUCCEEDED(rv)) {
13063 mCommandManager = do_QueryInterface(commandUpdater);
13067 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
13070 NS_IMETHODIMP
13071 nsDocShell::CanCutSelection(bool* aResult)
13073 return IsCommandEnabled("cmd_cut", aResult);
13076 NS_IMETHODIMP
13077 nsDocShell::CanCopySelection(bool* aResult)
13079 return IsCommandEnabled("cmd_copy", aResult);
13082 NS_IMETHODIMP
13083 nsDocShell::CanCopyLinkLocation(bool* aResult)
13085 return IsCommandEnabled("cmd_copyLink", aResult);
13088 NS_IMETHODIMP
13089 nsDocShell::CanCopyImageLocation(bool* aResult)
13091 return IsCommandEnabled("cmd_copyImageLocation", aResult);
13094 NS_IMETHODIMP
13095 nsDocShell::CanCopyImageContents(bool* aResult)
13097 return IsCommandEnabled("cmd_copyImageContents", aResult);
13100 NS_IMETHODIMP
13101 nsDocShell::CanPaste(bool* aResult)
13103 return IsCommandEnabled("cmd_paste", aResult);
13106 NS_IMETHODIMP
13107 nsDocShell::CutSelection(void)
13109 return DoCommand("cmd_cut");
13112 NS_IMETHODIMP
13113 nsDocShell::CopySelection(void)
13115 return DoCommand("cmd_copy");
13118 NS_IMETHODIMP
13119 nsDocShell::CopyLinkLocation(void)
13121 return DoCommand("cmd_copyLink");
13124 NS_IMETHODIMP
13125 nsDocShell::CopyImageLocation(void)
13127 return DoCommand("cmd_copyImageLocation");
13130 NS_IMETHODIMP
13131 nsDocShell::CopyImageContents(void)
13133 return DoCommand("cmd_copyImageContents");
13136 NS_IMETHODIMP
13137 nsDocShell::Paste(void)
13139 return DoCommand("cmd_paste");
13142 NS_IMETHODIMP
13143 nsDocShell::SelectAll(void)
13145 return DoCommand("cmd_selectAll");
13149 // SelectNone
13151 // Collapses the current selection, insertion point ends up at beginning
13152 // of previous selection.
13154 NS_IMETHODIMP
13155 nsDocShell::SelectNone(void)
13157 return DoCommand("cmd_selectNone");
13160 // link handling
13162 class OnLinkClickEvent : public Runnable
13164 public:
13165 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
13166 nsIURI* aURI,
13167 const char16_t* aTargetSpec,
13168 const nsAString& aFileName,
13169 nsIInputStream* aPostDataStream,
13170 nsIInputStream* aHeadersDataStream,
13171 bool aNoOpenerImplied,
13172 bool aIsUserTriggered,
13173 bool aIsTrusted,
13174 nsIPrincipal* aTriggeringPrincipal);
13176 NS_IMETHOD Run() override
13178 nsAutoPopupStatePusher popupStatePusher(mPopupState);
13180 // We need to set up an AutoJSAPI here for the following reason: When we do
13181 // OnLinkClickSync we'll eventually end up in nsGlobalWindow::OpenInternal
13182 // which only does popup blocking if !LegacyIsCallerChromeOrNativeCode().
13183 // So we need to fake things so that we don't look like native code as far
13184 // as LegacyIsCallerNativeCode() is concerned.
13185 AutoJSAPI jsapi;
13186 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
13187 mHandler->OnLinkClickSync(mContent, mURI,
13188 mTargetSpec.get(), mFileName,
13189 mPostDataStream,
13190 mHeadersDataStream, mNoOpenerImplied,
13191 nullptr, nullptr, mIsUserTriggered,
13192 mTriggeringPrincipal);
13194 return NS_OK;
13197 private:
13198 RefPtr<nsDocShell> mHandler;
13199 nsCOMPtr<nsIURI> mURI;
13200 nsString mTargetSpec;
13201 nsString mFileName;
13202 nsCOMPtr<nsIInputStream> mPostDataStream;
13203 nsCOMPtr<nsIInputStream> mHeadersDataStream;
13204 nsCOMPtr<nsIContent> mContent;
13205 PopupControlState mPopupState;
13206 bool mNoOpenerImplied;
13207 bool mIsUserTriggered;
13208 bool mIsTrusted;
13209 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
13212 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
13213 nsIContent* aContent,
13214 nsIURI* aURI,
13215 const char16_t* aTargetSpec,
13216 const nsAString& aFileName,
13217 nsIInputStream* aPostDataStream,
13218 nsIInputStream* aHeadersDataStream,
13219 bool aNoOpenerImplied,
13220 bool aIsUserTriggered,
13221 bool aIsTrusted,
13222 nsIPrincipal* aTriggeringPrincipal)
13223 : mozilla::Runnable("OnLinkClickEvent")
13224 , mHandler(aHandler)
13225 , mURI(aURI)
13226 , mTargetSpec(aTargetSpec)
13227 , mFileName(aFileName)
13228 , mPostDataStream(aPostDataStream)
13229 , mHeadersDataStream(aHeadersDataStream)
13230 , mContent(aContent)
13231 , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
13232 , mNoOpenerImplied(aNoOpenerImplied)
13233 , mIsUserTriggered(aIsUserTriggered)
13234 , mIsTrusted(aIsTrusted)
13235 , mTriggeringPrincipal(aTriggeringPrincipal)
13239 NS_IMETHODIMP
13240 nsDocShell::OnLinkClick(nsIContent* aContent,
13241 nsIURI* aURI,
13242 const char16_t* aTargetSpec,
13243 const nsAString& aFileName,
13244 nsIInputStream* aPostDataStream,
13245 nsIInputStream* aHeadersDataStream,
13246 bool aIsUserTriggered,
13247 bool aIsTrusted,
13248 nsIPrincipal* aTriggeringPrincipal)
13250 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
13252 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
13253 return NS_OK;
13256 // On history navigation through Back/Forward buttons, don't execute
13257 // automatic JavaScript redirection such as |anchorElement.click()| or
13258 // |formElement.submit()|.
13260 // XXX |formElement.submit()| bypasses this checkpoint because it calls
13261 // nsDocShell::OnLinkClickSync(...) instead.
13262 if (ShouldBlockLoadingForBackButton()) {
13263 return NS_OK;
13266 if (aContent->IsEditable()) {
13267 return NS_OK;
13270 nsresult rv = NS_ERROR_FAILURE;
13271 nsAutoString target;
13273 nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
13274 bool noOpenerImplied = false;
13275 if (browserChrome3) {
13276 nsAutoString oldTarget(aTargetSpec);
13277 rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
13278 aContent, mIsAppTab, target);
13279 if (!oldTarget.Equals(target)) {
13280 noOpenerImplied = true;
13284 if (NS_FAILED(rv)) {
13285 target = aTargetSpec;
13288 nsCOMPtr<nsIRunnable> ev =
13289 new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
13290 aPostDataStream, aHeadersDataStream, noOpenerImplied,
13291 aIsUserTriggered, aIsTrusted, aTriggeringPrincipal);
13292 return DispatchToTabGroup(TaskCategory::UI, ev.forget());
13295 static bool
13296 IsElementAnchorOrArea(nsIContent* aContent)
13298 // Make sure we are dealing with either an <A> or <AREA> element in the HTML
13299 // or XHTML namespace.
13300 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area);
13303 NS_IMETHODIMP
13304 nsDocShell::OnLinkClickSync(nsIContent* aContent,
13305 nsIURI* aURI,
13306 const char16_t* aTargetSpec,
13307 const nsAString& aFileName,
13308 nsIInputStream* aPostDataStream,
13309 nsIInputStream* aHeadersDataStream,
13310 bool aNoOpenerImplied,
13311 nsIDocShell** aDocShell,
13312 nsIRequest** aRequest,
13313 bool aIsUserTriggered,
13314 nsIPrincipal* aTriggeringPrincipal)
13316 // Initialize the DocShell / Request
13317 if (aDocShell) {
13318 *aDocShell = nullptr;
13320 if (aRequest) {
13321 *aRequest = nullptr;
13324 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
13325 return NS_OK;
13328 // XXX When the linking node was HTMLFormElement, it is synchronous event.
13329 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
13330 // but |HTMLFormElement::SubmitSubmission(...)|.
13331 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
13332 ShouldBlockLoadingForBackButton()) {
13333 return NS_OK;
13336 if (aContent->IsEditable()) {
13337 return NS_OK;
13341 // defer to an external protocol handler if necessary...
13342 nsCOMPtr<nsIExternalProtocolService> extProtService =
13343 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
13344 if (extProtService) {
13345 nsAutoCString scheme;
13346 aURI->GetScheme(scheme);
13347 if (!scheme.IsEmpty()) {
13348 // if the URL scheme does not correspond to an exposed protocol, then we
13349 // need to hand this link click over to the external protocol handler.
13350 bool isExposed;
13351 nsresult rv =
13352 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
13353 if (NS_SUCCEEDED(rv) && !isExposed) {
13354 return extProtService->LoadURI(aURI, this);
13360 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
13361 if (IsElementAnchorOrArea(aContent)) {
13362 MOZ_ASSERT(aContent->IsHTMLElement());
13363 nsAutoString referrer;
13364 aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
13365 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);
13366 while (tok.hasMoreTokens()) {
13367 const nsAString& token = tok.nextToken();
13368 if (token.LowerCaseEqualsLiteral("noreferrer")) {
13369 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
13370 INTERNAL_LOAD_FLAGS_NO_OPENER;
13371 // We now have all the flags we could possibly have, so just stop.
13372 break;
13374 if (token.LowerCaseEqualsLiteral("noopener")) {
13375 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13378 if (aNoOpenerImplied) {
13379 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13383 // Get the owner document of the link that was clicked, this will be
13384 // the document that the link is in, or the last document that the
13385 // link was in. From that document, we'll get the URI to use as the
13386 // referer, since the current URI in this docshell may be a
13387 // new document that we're in the process of loading.
13388 nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
13389 NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
13391 // Now check that the refererDoc's inner window is the current inner
13392 // window for mScriptGlobal. If it's not, then we don't want to
13393 // follow this link.
13394 nsPIDOMWindowInner* refererInner = refererDoc->GetInnerWindow();
13395 NS_ENSURE_TRUE(refererInner, NS_ERROR_UNEXPECTED);
13396 if (!mScriptGlobal ||
13397 mScriptGlobal->AsOuter()->GetCurrentInnerWindow() != refererInner) {
13398 // We're no longer the current inner window
13399 return NS_OK;
13402 nsCOMPtr<nsIURI> referer = refererDoc->GetDocumentURI();
13403 uint32_t refererPolicy = refererDoc->GetReferrerPolicy();
13405 // get referrer attribute from clicked link and parse it
13406 // if per element referrer is enabled, the element referrer overrules
13407 // the document wide referrer
13408 if (IsElementAnchorOrArea(aContent)) {
13409 net::ReferrerPolicy refPolEnum =
13410 aContent->AsElement()->GetReferrerPolicyAsEnum();
13411 if (refPolEnum != RP_Unset) {
13412 refererPolicy = refPolEnum;
13416 // referer could be null here in some odd cases, but that's ok,
13417 // we'll just load the link w/o sending a referer in those cases.
13419 nsAutoString target(aTargetSpec);
13421 // If this is an anchor element, grab its type property to use as a hint
13422 nsAutoString typeHint;
13423 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13424 if (anchor) {
13425 anchor->GetType(typeHint);
13426 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13427 nsAutoCString type, dummy;
13428 NS_ParseRequestContentType(utf8Hint, type, dummy);
13429 CopyUTF8toUTF16(type, typeHint);
13432 // if the triggeringPrincipal is not passed explicitly, then we
13433 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
13434 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
13435 aTriggeringPrincipal ? aTriggeringPrincipal
13436 : aContent->NodePrincipal();
13438 // Link click (or form submission) can be triggered inside an onload handler,
13439 // and we don't want to add history entry in this case.
13440 bool inOnLoadHandler = false;
13441 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13442 uint32_t loadType = inOnLoadHandler ? LOAD_NORMAL_REPLACE : LOAD_LINK;
13444 if (aIsUserTriggered) {
13445 flags |= INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED;
13448 nsresult rv = InternalLoad(aURI, // New URI
13449 nullptr, // Original URI
13450 Nothing(), // Let the protocol handler assign it
13451 false, // LoadReplace
13452 referer, // Referer URI
13453 refererPolicy, // Referer policy
13454 triggeringPrincipal,
13455 aContent->NodePrincipal(),
13456 flags,
13457 target, // Window target
13458 NS_LossyConvertUTF16toASCII(typeHint).get(),
13459 aFileName, // Download as file
13460 aPostDataStream, // Post data stream
13461 aHeadersDataStream, // Headers stream
13462 loadType, // Load type
13463 nullptr, // No SHEntry
13464 true, // first party site
13465 VoidString(), // No srcdoc
13466 this, // We are the source
13467 nullptr, // baseURI not needed
13468 aDocShell, // DocShell out-param
13469 aRequest); // Request out-param
13470 if (NS_SUCCEEDED(rv)) {
13471 nsPingListener::DispatchPings(this, aContent, aURI, referer, refererPolicy);
13473 return rv;
13476 NS_IMETHODIMP
13477 nsDocShell::OnOverLink(nsIContent* aContent,
13478 nsIURI* aURI,
13479 const char16_t* aTargetSpec)
13481 if (aContent->IsEditable()) {
13482 return NS_OK;
13485 nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
13486 nsresult rv = NS_ERROR_FAILURE;
13488 nsCOMPtr<nsIWebBrowserChrome> browserChrome;
13489 if (!browserChrome2) {
13490 browserChrome = do_GetInterface(mTreeOwner);
13491 if (!browserChrome) {
13492 return rv;
13496 nsAutoCString spec;
13497 rv = aURI->GetDisplaySpec(spec);
13498 NS_ENSURE_SUCCESS(rv, rv);
13500 NS_ConvertUTF8toUTF16 uStr(spec);
13502 PredictorPredict(aURI, mCurrentURI,
13503 nsINetworkPredictor::PREDICT_LINK,
13504 aContent->NodePrincipal()->OriginAttributesRef(),
13505 nullptr);
13507 if (browserChrome2) {
13508 rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
13509 uStr, aContent);
13510 } else {
13511 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
13513 return rv;
13516 NS_IMETHODIMP
13517 nsDocShell::OnLeaveLink()
13519 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13520 nsresult rv = NS_ERROR_FAILURE;
13522 if (browserChrome) {
13523 rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK,
13524 EmptyString().get());
13526 return rv;
13529 bool
13530 nsDocShell::ShouldBlockLoadingForBackButton()
13532 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13533 EventStateManager::IsHandlingUserInput() ||
13534 !Preferences::GetBool("accessibility.blockjsredirection")) {
13535 return false;
13538 bool canGoForward = false;
13539 GetCanGoForward(&canGoForward);
13540 return canGoForward;
13543 bool
13544 nsDocShell::PluginsAllowedInCurrentDoc()
13547 if (!mContentViewer) {
13548 return false;
13551 nsIDocument* doc = mContentViewer->GetDocument();
13552 if (!doc) {
13553 return false;
13556 return doc->GetAllowPlugins();
13559 //----------------------------------------------------------------------
13560 // Web Shell Services API
13562 // This functions is only called when a new charset is detected in loading a
13563 // document. Its name should be changed to "CharsetReloadDocument"
13564 NS_IMETHODIMP
13565 nsDocShell::ReloadDocument(const char* aCharset, int32_t aSource)
13567 // XXX hack. keep the aCharset and aSource wait to pick it up
13568 nsCOMPtr<nsIContentViewer> cv;
13569 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
13570 if (cv) {
13571 int32_t hint;
13572 cv->GetHintCharacterSetSource(&hint);
13573 if (aSource > hint) {
13574 nsCString charset(aCharset);
13575 cv->SetHintCharacterSet(charset);
13576 cv->SetHintCharacterSetSource(aSource);
13577 if (eCharsetReloadRequested != mCharsetReloadState) {
13578 mCharsetReloadState = eCharsetReloadRequested;
13579 switch (mLoadType) {
13580 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13581 return Reload(LOAD_FLAGS_CHARSET_CHANGE |
13582 LOAD_FLAGS_BYPASS_CACHE |
13583 LOAD_FLAGS_BYPASS_PROXY);
13584 case LOAD_RELOAD_BYPASS_CACHE:
13585 return Reload(LOAD_FLAGS_CHARSET_CHANGE |
13586 LOAD_FLAGS_BYPASS_CACHE);
13587 default:
13588 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13593 // return failure if this request is not accepted due to mCharsetReloadState
13594 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13597 NS_IMETHODIMP
13598 nsDocShell::StopDocumentLoad(void)
13600 if (eCharsetReloadRequested != mCharsetReloadState) {
13601 Stop(nsIWebNavigation::STOP_ALL);
13602 return NS_OK;
13604 // return failer if this request is not accepted due to mCharsetReloadState
13605 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13608 NS_IMETHODIMP
13609 nsDocShell::SetIsPrinting(bool aIsPrinting)
13611 mIsPrintingOrPP = aIsPrinting;
13612 return NS_OK;
13615 NS_IMETHODIMP
13616 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
13618 *aPrintPreview = nullptr;
13619 #if NS_PRINT_PREVIEW
13620 nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
13621 if (!print || !print->IsInitializedForPrintPreview()) {
13622 // XXX: Creating a brand new content viewer to host preview every
13623 // time we enter here seems overwork. We could skip ahead to where
13624 // we QI the mContentViewer if the current URI is either about:blank
13625 // or about:printpreview.
13626 Stop(nsIWebNavigation::STOP_ALL);
13627 nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithInheritedAttributes(this);
13628 nsCOMPtr<nsIURI> uri;
13629 NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview"));
13630 nsresult rv = CreateAboutBlankContentViewer(principal, uri);
13631 NS_ENSURE_SUCCESS(rv, rv);
13632 // Here we manually set current URI since we have just created a
13633 // brand new content viewer (about:blank) to host preview.
13634 SetCurrentURI(uri, nullptr, true, 0);
13635 print = do_QueryInterface(mContentViewer);
13636 NS_ENSURE_STATE(print);
13637 print->InitializeForPrintPreview();
13639 nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
13640 result.forget(aPrintPreview);
13641 return NS_OK;
13642 #else
13643 return NS_ERROR_NOT_IMPLEMENTED;
13644 #endif
13647 #ifdef DEBUG
13648 unsigned long nsDocShell::gNumberOfDocShells = 0;
13649 #endif
13651 NS_IMETHODIMP
13652 nsDocShell::GetCanExecuteScripts(bool* aResult)
13654 *aResult = mCanExecuteScripts;
13655 return NS_OK;
13658 /* [infallible] */ NS_IMETHODIMP
13659 nsDocShell::SetFrameType(uint32_t aFrameType)
13661 mFrameType = aFrameType;
13662 return NS_OK;
13665 /* [infallible] */ NS_IMETHODIMP
13666 nsDocShell::GetFrameType(uint32_t* aFrameType)
13668 *aFrameType = mFrameType;
13669 return NS_OK;
13672 /* [infallible] */ NS_IMETHODIMP
13673 nsDocShell::GetIsMozBrowser(bool* aIsMozBrowser)
13675 *aIsMozBrowser = (mFrameType == FRAME_TYPE_BROWSER);
13676 return NS_OK;
13679 uint32_t
13680 nsDocShell::GetInheritedFrameType()
13682 if (mFrameType != FRAME_TYPE_REGULAR) {
13683 return mFrameType;
13686 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
13687 GetSameTypeParent(getter_AddRefs(parentAsItem));
13689 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
13690 if (!parent) {
13691 return FRAME_TYPE_REGULAR;
13694 return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
13697 /* [infallible] */ NS_IMETHODIMP
13698 nsDocShell::GetIsIsolatedMozBrowserElement(bool* aIsIsolatedMozBrowserElement)
13700 bool result = mFrameType == FRAME_TYPE_BROWSER &&
13701 mOriginAttributes.mInIsolatedMozBrowser;
13702 *aIsIsolatedMozBrowserElement = result;
13703 return NS_OK;
13706 /* [infallible] */ NS_IMETHODIMP
13707 nsDocShell::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
13709 MOZ_ASSERT(!mOriginAttributes.mInIsolatedMozBrowser ||
13710 (GetInheritedFrameType() == FRAME_TYPE_BROWSER),
13711 "Isolated mozbrowser should only be true inside browser frames");
13712 bool result = (GetInheritedFrameType() == FRAME_TYPE_BROWSER) &&
13713 mOriginAttributes.mInIsolatedMozBrowser;
13714 *aIsInIsolatedMozBrowserElement = result;
13715 return NS_OK;
13718 /* [infallible] */ NS_IMETHODIMP
13719 nsDocShell::GetIsInMozBrowser(bool* aIsInMozBrowser)
13721 *aIsInMozBrowser = (GetInheritedFrameType() == FRAME_TYPE_BROWSER);
13722 return NS_OK;
13725 /* [infallible] */ NS_IMETHODIMP
13726 nsDocShell::GetIsTopLevelContentDocShell(bool* aIsTopLevelContentDocShell)
13728 *aIsTopLevelContentDocShell = false;
13730 if (mItemType == typeContent) {
13731 nsCOMPtr<nsIDocShellTreeItem> root;
13732 GetSameTypeRootTreeItem(getter_AddRefs(root));
13733 *aIsTopLevelContentDocShell = root.get() == static_cast<nsIDocShellTreeItem*>(this);
13736 return NS_OK;
13739 // Implements nsILoadContext.originAttributes
13740 NS_IMETHODIMP
13741 nsDocShell::GetScriptableOriginAttributes(JS::MutableHandle<JS::Value> aVal)
13743 JSContext* cx = nsContentUtils::GetCurrentJSContext();
13744 MOZ_ASSERT(cx);
13746 return GetOriginAttributes(cx, aVal);
13749 // Implements nsIDocShell.GetOriginAttributes()
13750 NS_IMETHODIMP
13751 nsDocShell::GetOriginAttributes(JSContext* aCx,
13752 JS::MutableHandle<JS::Value> aVal)
13754 bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
13755 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
13756 return NS_OK;
13759 bool
13760 nsDocShell::CanSetOriginAttributes()
13762 MOZ_ASSERT(mChildList.IsEmpty());
13763 if (!mChildList.IsEmpty()) {
13764 return false;
13767 // TODO: Bug 1273058 - mContentViewer should be null when setting origin
13768 // attributes.
13769 if (mContentViewer) {
13770 nsIDocument* doc = mContentViewer->GetDocument();
13771 if (doc) {
13772 nsIURI* uri = doc->GetDocumentURI();
13773 if (!uri) {
13774 return false;
13776 nsCString uriSpec = uri->GetSpecOrDefault();
13777 MOZ_ASSERT(uriSpec.EqualsLiteral("about:blank"));
13778 if (!uriSpec.EqualsLiteral("about:blank")) {
13779 return false;
13784 return true;
13787 bool
13788 nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13789 nsIURI* aURI)
13791 MOZ_ASSERT(aPrincipal);
13792 MOZ_ASSERT(aURI);
13794 if (UsePrivateBrowsing() || mSandboxFlags) {
13795 return false;
13798 nsCOMPtr<nsIDocShellTreeItem> parent;
13799 GetSameTypeParent(getter_AddRefs(parent));
13800 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13801 nsPIDOMWindowInner* parentInner =
13802 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13804 nsContentUtils::StorageAccess storage =
13805 nsContentUtils::StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13807 return storage == nsContentUtils::StorageAccess::eAllow;
13810 nsresult
13811 nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs)
13813 MOZ_ASSERT(!mIsBeingDestroyed);
13815 if (!CanSetOriginAttributes()) {
13816 return NS_ERROR_FAILURE;
13819 AssertOriginAttributesMatchPrivateBrowsing();
13820 mOriginAttributes = aAttrs;
13822 bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0;
13823 // Chrome docshell can not contain OriginAttributes.mPrivateBrowsingId
13824 if (mItemType == typeChrome && isPrivate) {
13825 mOriginAttributes.mPrivateBrowsingId = 0;
13828 SetPrivateBrowsing(isPrivate);
13829 AssertOriginAttributesMatchPrivateBrowsing();
13831 return NS_OK;
13834 NS_IMETHODIMP
13835 nsDocShell::SetOriginAttributesBeforeLoading(JS::Handle<JS::Value> aOriginAttributes)
13837 if (!aOriginAttributes.isObject()) {
13838 return NS_ERROR_INVALID_ARG;
13841 AutoJSAPI jsapi;
13842 if (!jsapi.Init(&aOriginAttributes.toObject())) {
13843 return NS_ERROR_UNEXPECTED;
13846 JSContext* cx = jsapi.cx();
13847 if (NS_WARN_IF(!cx)) {
13848 return NS_ERROR_FAILURE;
13851 OriginAttributes attrs;
13852 if (!aOriginAttributes.isObject() || !attrs.Init(cx, aOriginAttributes)) {
13853 return NS_ERROR_INVALID_ARG;
13856 return SetOriginAttributes(attrs);
13859 NS_IMETHODIMP
13860 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13861 JSContext* aCx)
13863 OriginAttributes attrs;
13864 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13865 return NS_ERROR_INVALID_ARG;
13868 return SetOriginAttributes(attrs);
13871 NS_IMETHODIMP
13872 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
13874 if (nsIPresShell* presShell = GetPresShell()) {
13875 *aOut = presShell->AsyncPanZoomEnabled();
13876 return NS_OK;
13879 // If we don't have a presShell, fall back to the default platform value of
13880 // whether or not APZ is enabled.
13881 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13882 return NS_OK;
13885 bool
13886 nsDocShell::HasUnloadedParent()
13888 RefPtr<nsDocShell> parent = GetParentDocshell();
13889 while (parent) {
13890 bool inUnload = false;
13891 parent->GetIsInUnload(&inUnload);
13892 if (inUnload) {
13893 return true;
13895 parent = parent->GetParentDocshell();
13897 return false;
13900 void
13901 nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI)
13903 if (mUseGlobalHistory && !UsePrivateBrowsing()) {
13904 nsCOMPtr<IHistory> history = services::GetHistoryService();
13905 if (history) {
13906 history->SetURITitle(aURI, mTitle);
13911 bool
13912 nsDocShell::IsInvisible()
13914 return mInvisible;
13917 void
13918 nsDocShell::SetInvisible(bool aInvisible)
13920 mInvisible = aInvisible;
13923 void
13924 nsDocShell::SetOpener(nsITabParent* aOpener)
13926 mOpener = do_GetWeakReference(aOpener);
13929 nsITabParent*
13930 nsDocShell::GetOpener()
13932 nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
13933 return opener;
13936 // The caller owns |aAsyncCause| here.
13937 void
13938 nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
13939 const char16_t* aFunctionName,
13940 const char16_t* aFilename,
13941 const uint32_t aLineNumber,
13942 JS::Handle<JS::Value> aAsyncStack,
13943 const char* aAsyncCause)
13945 // If first start, mark interval start.
13946 if (mJSRunToCompletionDepth == 0) {
13947 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
13948 if (timelines && timelines->HasConsumer(this)) {
13949 timelines->AddMarkerForDocShell(this,
13950 mozilla::MakeUnique<JavascriptTimelineMarker>(
13951 aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START,
13952 aAsyncStack, aAsyncCause));
13956 mJSRunToCompletionDepth++;
13959 void
13960 nsDocShell::NotifyJSRunToCompletionStop()
13962 mJSRunToCompletionDepth--;
13964 // If last stop, mark interval end.
13965 if (mJSRunToCompletionDepth == 0) {
13966 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
13967 if (timelines && timelines->HasConsumer(this)) {
13968 timelines->AddMarkerForDocShell(this, "Javascript", MarkerTracingType::END);
13973 void
13974 nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13975 const nsString& aKeyword)
13977 if (aProvider.IsEmpty()) {
13978 return;
13981 if (XRE_IsContentProcess()) {
13982 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
13983 if (contentChild) {
13984 contentChild->SendNotifyKeywordSearchLoading(aProvider, aKeyword);
13986 return;
13989 #ifdef MOZ_TOOLKIT_SEARCH
13990 nsCOMPtr<nsIBrowserSearchService> searchSvc =
13991 do_GetService("@mozilla.org/browser/search-service;1");
13992 if (searchSvc) {
13993 nsCOMPtr<nsISearchEngine> searchEngine;
13994 searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
13995 if (searchEngine) {
13996 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13997 if (obsSvc) {
13998 // Note that "keyword-search" refers to a search via the url
13999 // bar, not a bookmarks keyword search.
14000 obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
14004 #endif
14007 NS_IMETHODIMP
14008 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
14009 bool* aShouldIntercept)
14011 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
14012 aShouldIntercept);
14015 NS_IMETHODIMP
14016 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
14018 return mInterceptController->ChannelIntercepted(aChannel);
14021 bool
14022 nsDocShell::InFrameSwap()
14024 RefPtr<nsDocShell> shell = this;
14025 do {
14026 if (shell->mInFrameSwap) {
14027 return true;
14029 shell = shell->GetParentDocshell();
14030 } while (shell);
14031 return false;
14034 UniquePtr<ClientSource>
14035 nsDocShell::TakeInitialClientSource()
14037 return std::move(mInitialClientSource);
14040 NS_IMETHODIMP
14041 nsDocShell::IssueWarning(uint32_t aWarning, bool aAsError)
14043 if (mContentViewer) {
14044 nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
14045 if (doc) {
14046 doc->WarnOnceAbout(nsIDocument::DeprecatedOperations(aWarning), aAsError);
14049 return NS_OK;
14052 NS_IMETHODIMP
14053 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession)
14055 if (!NS_SUCCEEDED(EnsureEditorData())) {
14056 return NS_ERROR_FAILURE;
14059 mEditorData->GetEditingSession(aEditSession);
14060 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
14063 NS_IMETHODIMP
14064 nsDocShell::GetScriptableTabChild(nsITabChild** aTabChild)
14066 *aTabChild = GetTabChild().take();
14067 return *aTabChild ? NS_OK : NS_ERROR_FAILURE;
14070 already_AddRefed<nsITabChild>
14071 nsDocShell::GetTabChild()
14073 nsCOMPtr<nsIDocShellTreeOwner> owner(mTreeOwner);
14074 nsCOMPtr<nsITabChild> tc = do_GetInterface(owner);
14075 return tc.forget();
14078 nsICommandManager*
14079 nsDocShell::GetCommandManager()
14081 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
14082 return mCommandManager;
14085 NS_IMETHODIMP
14086 nsDocShell::GetIsOnlyToplevelInTabGroup(bool* aResult)
14088 MOZ_ASSERT(aResult);
14090 nsPIDOMWindowOuter* outer = GetWindow();
14091 MOZ_ASSERT(outer);
14093 // If we are not toplevel then we are not the only toplevel window in the tab
14094 // group.
14095 if (outer->GetScriptableParentOrNull()) {
14096 *aResult = false;
14097 return NS_OK;
14100 // If we have any other toplevel windows in our tab group, then we are not the
14101 // only toplevel window in the tab group.
14102 nsTArray<nsPIDOMWindowOuter*> toplevelWindows =
14103 outer->TabGroup()->GetTopLevelWindows();
14104 if (toplevelWindows.Length() > 1) {
14105 *aResult = false;
14106 return NS_OK;
14108 MOZ_ASSERT(toplevelWindows.Length() == 1);
14109 MOZ_ASSERT(toplevelWindows[0] == outer);
14111 *aResult = true;
14112 return NS_OK;
14115 NS_IMETHODIMP
14116 nsDocShell::GetAwaitingLargeAlloc(bool* aResult)
14118 MOZ_ASSERT(aResult);
14119 nsCOMPtr<nsITabChild> tabChild = GetTabChild();
14120 if (!tabChild) {
14121 *aResult = false;
14122 return NS_OK;
14124 *aResult = static_cast<TabChild*>(tabChild.get())->IsAwaitingLargeAlloc();
14125 return NS_OK;
14128 NS_IMETHODIMP_(void)
14129 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs)
14131 aAttrs = mOriginAttributes;
14134 HTMLEditor*
14135 nsIDocShell::GetHTMLEditor()
14137 nsDocShell* docShell = static_cast<nsDocShell*>(this);
14138 return docShell->GetHTMLEditorInternal();
14141 nsresult
14142 nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor)
14144 nsDocShell* docShell = static_cast<nsDocShell*>(this);
14145 return docShell->SetHTMLEditorInternal(aHTMLEditor);
14148 NS_IMETHODIMP
14149 nsDocShell::GetDisplayMode(uint32_t* aDisplayMode)
14151 NS_ENSURE_ARG_POINTER(aDisplayMode);
14152 *aDisplayMode = mDisplayMode;
14153 return NS_OK;
14156 NS_IMETHODIMP
14157 nsDocShell::SetDisplayMode(uint32_t aDisplayMode)
14159 if (!(aDisplayMode == nsIDocShell::DISPLAY_MODE_BROWSER ||
14160 aDisplayMode == nsIDocShell::DISPLAY_MODE_STANDALONE ||
14161 aDisplayMode == nsIDocShell::DISPLAY_MODE_FULLSCREEN ||
14162 aDisplayMode == nsIDocShell::DISPLAY_MODE_MINIMAL_UI)) {
14163 return NS_ERROR_INVALID_ARG;
14166 if (aDisplayMode != mDisplayMode) {
14167 mDisplayMode = aDisplayMode;
14169 RefPtr<nsPresContext> presContext;
14170 if (NS_SUCCEEDED(GetPresContext(getter_AddRefs(presContext)))) {
14171 presContext->MediaFeatureValuesChangedAllDocuments({
14172 MediaFeatureChangeReason::DisplayModeChange });
14176 return NS_OK;
14179 NS_IMETHODIMP
14180 nsDocShell::SetColorMatrix(float* aMatrix, uint32_t aMatrixLen)
14182 if (aMatrixLen == 20) {
14183 mColorMatrix.reset(new gfx::Matrix5x4());
14184 MOZ_ASSERT(aMatrixLen * sizeof(*aMatrix) == sizeof(mColorMatrix->components));
14185 memcpy(mColorMatrix->components, aMatrix, sizeof(mColorMatrix->components));
14186 } else if (aMatrixLen == 0) {
14187 mColorMatrix.reset();
14188 } else {
14189 return NS_ERROR_INVALID_ARG;
14192 nsIPresShell* presShell = GetPresShell();
14193 if (!presShell) {
14194 return NS_ERROR_FAILURE;
14197 nsIFrame* frame = presShell->GetRootFrame();
14198 if (!frame) {
14199 return NS_ERROR_FAILURE;
14202 frame->SchedulePaint();
14204 return NS_OK;
14207 NS_IMETHODIMP
14208 nsDocShell::GetColorMatrix(uint32_t* aMatrixLen, float** aMatrix)
14210 NS_ENSURE_ARG_POINTER(aMatrixLen);
14211 *aMatrixLen = 0;
14213 NS_ENSURE_ARG_POINTER(aMatrix);
14214 *aMatrix = nullptr;
14216 if (mColorMatrix) {
14217 *aMatrix = (float*)moz_xmalloc(20 * sizeof(float));
14218 if (!*aMatrix) {
14219 return NS_ERROR_OUT_OF_MEMORY;
14222 MOZ_ASSERT(20 * sizeof(float) == sizeof(mColorMatrix->components));
14223 *aMatrixLen = 20;
14224 memcpy(*aMatrix, mColorMatrix->components, 20 * sizeof(float));
14227 return NS_OK;