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