/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsDocShell.h" #include #ifdef XP_WIN # include # define getpid _getpid #else # include // for getpid() #endif #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AutoRestore.h" #include "mozilla/BasePrincipal.h" #include "mozilla/Casting.h" #include "mozilla/Components.h" #include "mozilla/Encoding.h" #include "mozilla/EventStateManager.h" #include "mozilla/HTMLEditor.h" #include "mozilla/LoadInfo.h" #include "mozilla/Logging.h" #include "mozilla/MediaFeatureChange.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" #include "mozilla/ResultExtensions.h" #include "mozilla/ScrollTypes.h" #include "mozilla/Services.h" #include "mozilla/StaticPrefs_browser.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_extensions.h" #include "mozilla/StaticPrefs_privacy.h" #include "mozilla/StaticPrefs_security.h" #include "mozilla/StaticPrefs_ui.h" #include "mozilla/StartupTimeline.h" #include "mozilla/StorageAccess.h" #include "mozilla/Telemetry.h" #include "mozilla/Unused.h" #include "mozilla/WidgetUtils.h" #include "mozilla/dom/ClientChannelHelper.h" #include "mozilla/dom/ClientHandle.h" #include "mozilla/dom/ClientInfo.h" #include "mozilla/dom/ClientManager.h" #include "mozilla/dom/ClientSource.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentFrameMessageManager.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/HTMLAnchorElement.h" #include "mozilla/dom/PerformanceNavigation.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/PopupBlocker.h" #include "mozilla/dom/ProfileTimelineMarkerBinding.h" #include "mozilla/dom/ScreenOrientation.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ServiceWorkerInterceptController.h" #include "mozilla/dom/ServiceWorkerUtils.h" #include "mozilla/dom/SessionStorageManager.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/TabGroup.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/dom/UserActivation.h" #include "mozilla/dom/ChildSHistory.h" #include "mozilla/dom/nsCSPContext.h" #include "mozilla/dom/LoadURIOptionsBinding.h" #include "mozilla/net/UrlClassifierFeatureFactory.h" #include "ReferrerInfo.h" #include "nsIApplicationCacheChannel.h" #include "nsIApplicationCacheContainer.h" #include "nsIAppShell.h" #include "nsIAuthPrompt.h" #include "nsIAuthPrompt2.h" #include "nsICachingChannel.h" #include "nsICaptivePortalService.h" #include "nsIChannel.h" #include "nsIChannelEventSink.h" #include "nsIClassOfService.h" #include "nsIConsoleReportCollector.h" #include "nsIContent.h" #include "nsIContentInlines.h" #include "nsIContentSecurityPolicy.h" #include "nsIContentViewer.h" #include "nsIController.h" #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeOwner.h" #include "mozilla/dom/Document.h" #include "nsIDocumentLoaderFactory.h" #include "nsIDOMWindow.h" #include "nsIEditingSession.h" #include "nsIExternalProtocolService.h" #include "nsIFormPOSTActionChannel.h" #include "nsIFrame.h" #include "nsIGlobalObject.h" #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIIDNService.h" #include "nsIInputStreamChannel.h" #include "nsIInterfaceRequestorUtils.h" #include "nsILayoutHistoryState.h" #include "nsILoadInfo.h" #include "nsIMultiPartChannel.h" #include "nsINestedURI.h" #include "nsINetworkPredictor.h" #include "nsINode.h" #include "nsINSSErrorsService.h" #include "nsIObserverService.h" #include "nsIOService.h" #include "nsIPrincipal.h" #include "nsIPrivacyTransitionObserver.h" #include "nsIPrompt.h" #include "nsIPromptFactory.h" #include "nsIReflowObserver.h" #include "nsIScriptChannel.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScriptSecurityManager.h" #include "nsIScrollableFrame.h" #include "nsIScrollObserver.h" #include "nsISecureBrowserUI.h" #include "nsISeekableStream.h" #include "nsISelectionDisplay.h" #include "nsISHEntry.h" #include "nsISHistory.h" #include "nsISiteSecurityService.h" #include "nsISocketProvider.h" #include "nsIStringBundle.h" #include "nsIStructuredCloneContainer.h" #include "nsIBrowserChild.h" #include "nsITextToSubURI.h" #include "nsITimedChannel.h" #include "nsITimer.h" #include "nsITransportSecurityInfo.h" #include "nsIUploadChannel.h" #include "nsIURIFixup.h" #include "nsIURILoader.h" #include "nsIViewSourceChannel.h" #include "nsIWebBrowserChrome.h" #include "nsIWebBrowserChrome3.h" #include "nsIWebBrowserChromeFocus.h" #include "nsIWebBrowserFind.h" #include "nsIWebProgress.h" #include "nsIWidget.h" #include "nsIWindowWatcher.h" #include "nsIWritablePropertyBag2.h" #include "nsCommandManager.h" #include "nsPIDOMWindow.h" #include "nsPIWindowRoot.h" #include "IHistory.h" #include "IUrlClassifierUITelemetry.h" #include "nsArray.h" #include "nsArrayUtils.h" #include "nsAutoPtr.h" #include "nsCExternalHandlerService.h" #include "nsContentDLF.h" #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...) #include "nsContentSecurityManager.h" #include "nsContentUtils.h" #include "nsCURILoader.h" #include "nsDocShellCID.h" #include "nsDocShellEditorData.h" #include "nsDocShellEnumerator.h" #include "nsDocShellLoadState.h" #include "nsDocShellLoadTypes.h" #include "nsDOMCID.h" #include "nsDOMNavigationTiming.h" #include "nsDSURIContentListener.h" #include "nsEditingSession.h" #include "nsError.h" #include "nsEscape.h" #include "nsFocusManager.h" #include "nsGlobalWindow.h" #include "nsISearchService.h" #include "nsJSEnvironment.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsObjectLoadingContent.h" #include "nsPingListener.h" #include "nsPoint.h" #include "nsQueryObject.h" #include "nsRect.h" #include "nsRefreshTimer.h" #include "nsSandboxFlags.h" #include "nsSHistory.h" #include "nsStructuredCloneContainer.h" #include "nsSubDocumentFrame.h" #include "nsView.h" #include "nsViewManager.h" #include "nsViewSourceHandler.h" #include "nsWebBrowserFind.h" #include "nsWhitespaceTokenizer.h" #include "nsWidgetsCID.h" #include "nsXULAppAPI.h" #include "GeckoProfiler.h" #include "mozilla/NullPrincipal.h" #include "Navigator.h" #include "prenv.h" #include "URIUtils.h" #include "timeline/JavascriptTimelineMarker.h" #ifdef MOZ_PLACES # include "nsIFaviconService.h" # include "mozIPlacesPendingOperation.h" #endif #if NS_PRINT_PREVIEW # include "nsIDocumentViewerPrint.h" # include "nsIWebBrowserPrint.h" #endif #ifdef MOZ_GECKO_PROFILER # include "ProfilerMarkerPayload.h" #endif using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::net; // Threshold value in ms for META refresh based redirects #define REFRESH_REDIRECT_TIMER 15000 // Hint for native dispatch of events on how long to delay after // all documents have loaded in milliseconds before favoring normal // native event dispatch priorites over performance // Can be overridden with docshell.event_starvation_delay_hint pref. #define NS_EVENT_STARVATION_DELAY_HINT 2000 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); // Number of documents currently loading static int32_t gNumberOfDocumentsLoading = 0; // Global count of existing docshells. static int32_t gDocShellCount = 0; // Global count of docshells with the private attribute set static uint32_t gNumberOfPrivateDocShells = 0; // True means we validate window targets to prevent frameset // spoofing. Initialize this to a non-bolean value so we know to check // the pref on the creation of the first docshell. static uint32_t gValidateOrigin = 0xffffffff; #ifdef DEBUG static mozilla::LazyLogModule gDocShellLog("nsDocShell"); #endif static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak"); extern mozilla::LazyLogModule gPageCacheLog; const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties"; // Global reference to the URI fixup service. nsIURIFixup* nsDocShell::sURIFixup = nullptr; static void FavorPerformanceHint(bool aPerfOverStarvation) { nsCOMPtr appShell = do_GetService(kAppShellCID); if (appShell) { appShell->FavorPerformanceHint( aPerfOverStarvation, Preferences::GetUint("docshell.event_starvation_delay_hint", NS_EVENT_STARVATION_DELAY_HINT)); } } static void IncreasePrivateDocShellCount() { gNumberOfPrivateDocShells++; if (gNumberOfPrivateDocShells > 1 || !XRE_IsContentProcess()) { return; } mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); cc->SendPrivateDocShellsExist(true); } static void DecreasePrivateDocShellCount() { MOZ_ASSERT(gNumberOfPrivateDocShells > 0); gNumberOfPrivateDocShells--; if (!gNumberOfPrivateDocShells) { if (XRE_IsContentProcess()) { dom::ContentChild* cc = dom::ContentChild::GetSingleton(); cc->SendPrivateDocShellsExist(false); return; } nsCOMPtr obsvc = services::GetObserverService(); if (obsvc) { obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); } } } nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext) : nsDocLoader(), mContentWindowID(NextWindowID()), mBrowsingContext(aBrowsingContext), mForcedCharset(nullptr), mParentCharset(nullptr), mTreeOwner(nullptr), mScrollbarPref(ScrollbarPreference::Auto), mCharsetReloadState(eCharsetReloadInit), mOrientationLock(hal::eScreenOrientation_None), mParentCharsetSource(0), mFrameMargins(-1, -1), mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome), mPreviousEntryIndex(-1), mLoadedEntryIndex(-1), mChildOffset(0), mSandboxFlags(0), mBusyFlags(BUSY_FLAGS_NONE), mAppType(nsIDocShell::APP_TYPE_UNKNOWN), mLoadType(0), mDefaultLoadFlags(nsIRequest::LOAD_NORMAL), mFailedLoadType(0), mPrivateBrowsingId(0), mDisplayMode(nsIDocShell::DISPLAY_MODE_BROWSER), mJSRunToCompletionDepth(0), mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE), mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE), mFullscreenAllowed(CHECK_ATTRIBUTES), mCreatingDocument(false), #ifdef DEBUG mInEnsureScriptEnv(false), #endif mCreated(false), mAllowSubframes(true), mAllowPlugins(true), mAllowJavascript(true), mAllowMetaRedirects(true), mAllowImages(true), mAllowMedia(true), mAllowDNSPrefetch(true), mAllowWindowControl(true), mAllowContentRetargeting(true), mAllowContentRetargetingOnChildren(true), mUseErrorPages(false), mUseStrictSecurityChecks(false), mObserveErrorPages(true), mCSSErrorReportingEnabled(false), mAllowAuth(mItemType == typeContent), mAllowKeywordFixup(false), mIsOffScreenBrowser(false), mIsActive(true), mDisableMetaRefreshWhenInactive(false), mIsAppTab(false), mUseGlobalHistory(false), mUseRemoteTabs(false), mUseRemoteSubframes(false), mUseTrackingProtection(false), mDeviceSizeIsPageSize(false), mWindowDraggingAllowed(false), mInFrameSwap(false), mInheritPrivateBrowsingId(true), mCanExecuteScripts(false), mFiredUnloadEvent(false), mEODForCurrentDocument(false), mURIResultedInDocument(false), mIsBeingDestroyed(false), mIsExecutingOnLoadHandler(false), mIsPrintingOrPP(false), mSavingOldViewer(false), mDynamicallyCreated(false), mAffectPrivateSessionLifetime(true), mInvisible(false), mHasLoadedNonBlankURI(false), mBlankTiming(false), mTitleValidForCurrentURI(false), mIsFrame(false), mSkipBrowsingContextDetachOnDestroy(false), mWatchedByDevtools(false), mIsNavigating(false) { mHistoryID.m0 = 0; mHistoryID.m1 = 0; mHistoryID.m2 = 0; AssertOriginAttributesMatchPrivateBrowsing(); nsContentUtils::GenerateUUIDInPlace(mHistoryID); if (gDocShellCount++ == 0) { NS_ASSERTION(sURIFixup == nullptr, "Huh, sURIFixup not null in first nsDocShell ctor!"); nsCOMPtr uriFixup = components::URIFixup::Service(); uriFixup.forget(&sURIFixup); } MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this)); #ifdef DEBUG // We're counting the number of |nsDocShells| to help find leaks ++gNumberOfDocShells; if (!PR_GetEnv("MOZ_QUIET")) { printf_stderr("++DOCSHELL %p == %ld [pid = %d] [id = %s]\n", (void*)this, gNumberOfDocShells, getpid(), nsIDToCString(mHistoryID).get()); } #endif } nsDocShell::~nsDocShell() { MOZ_ASSERT(!mObserved); // Avoid notifying observers while we're in the dtor. mIsBeingDestroyed = true; #ifdef MOZ_GECKO_PROFILER profiler_unregister_pages(mHistoryID); #endif Destroy(); if (mSessionHistory) { mSessionHistory->LegacySHistory()->ClearRootDocShell(); } if (mContentViewer) { mContentViewer->Close(nullptr); mContentViewer->Destroy(); mContentViewer = nullptr; } if (--gDocShellCount == 0) { NS_IF_RELEASE(sURIFixup); } MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this)); #ifdef DEBUG nsAutoCString url; if (mLastOpenedURI) { url = mLastOpenedURI->GetSpecOrDefault(); // Data URLs can be very long, so truncate to avoid flooding the log. const uint32_t maxURLLength = 1000; if (url.Length() > maxURLLength) { url.Truncate(maxURLLength); } } // We're counting the number of |nsDocShells| to help find leaks --gNumberOfDocShells; if (!PR_GetEnv("MOZ_QUIET")) { printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s] [url = %s]\n", (void*)this, gNumberOfDocShells, getpid(), nsIDToCString(mHistoryID).get(), url.get()); } #endif } /* static */ already_AddRefed nsDocShell::Create( BrowsingContext* aBrowsingContext) { MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!"); nsresult rv; RefPtr ds = new nsDocShell(aBrowsingContext); // Initialize the underlying nsDocLoader. rv = ds->nsDocLoader::Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } // Create our ContentListener ds->mContentListener = new nsDSURIContentListener(ds); rv = ds->mContentListener->Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } // If parent intercept is not enabled then we must forward to // the network controller from docshell. We also enable if we're // in the parent process in order to support non-e10s configurations. // Note: This check is duplicated in SharedWorkerInterfaceRequestor's // constructor. if (!ServiceWorkerParentInterceptEnabled() || XRE_IsParentProcess()) { ds->mInterceptController = new ServiceWorkerInterceptController(); } // We want to hold a strong ref to the loadgroup, so it better hold a weak // ref to us... use an InterfaceRequestorProxy to do this. nsCOMPtr proxy = new InterfaceRequestorProxy(ds); ds->mLoadGroup->SetNotificationCallbacks(proxy); // XXX(nika): We have our BrowsingContext, so we might be able to skip this. // It could be nice to directly set up our DocLoader tree? rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } // Add |ds| as a progress listener to itself. A little weird, but simpler // than reproducing all the listener-notification logic in overrides of the // various methods via which nsDocLoader can be notified. Note that this // holds an nsWeakPtr to |ds|, so it's ok. rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT | nsIWebProgress::NOTIFY_STATE_NETWORK); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } // If our parent is present in this process, set up our parent now. RefPtr parent = aBrowsingContext->GetParent(); if (parent && parent->GetDocShell()) { parent->GetDocShell()->AddChild(ds); } // Make |ds| the primary DocShell for the given context. aBrowsingContext->SetDocShell(ds); return ds.forget(); } void nsDocShell::DestroyChildren() { nsCOMPtr shell; nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { shell = do_QueryObject(iter.GetNext()); NS_ASSERTION(shell, "docshell has null child"); if (shell) { shell->SetTreeOwner(nullptr); } } nsDocLoader::DestroyChildren(); } NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell, nsDocLoader, mSessionStorageManager, mScriptGlobal, mInitialClientSource, mSessionHistory, mBrowsingContext, mChromeEventHandler) NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader) NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem) NS_INTERFACE_MAP_ENTRY(nsIWebNavigation) NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) NS_INTERFACE_MAP_ENTRY(nsIRefreshURI) NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor) NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) NS_INTERFACE_MAP_ENTRY(nsILoadContext) NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController, mInterceptController) NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner) NS_INTERFACE_MAP_END_INHERITING(nsDocLoader) NS_IMETHODIMP nsDocShell::GetInterface(const nsIID& aIID, void** aSink) { MOZ_ASSERT(aSink, "null out param"); *aSink = nullptr; if (aIID.Equals(NS_GET_IID(nsICommandManager))) { NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE); *aSink = static_cast(mCommandManager.get()); } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) { *aSink = mContentListener; } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) || aIID.Equals(NS_GET_IID(nsIGlobalObject)) || aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) || aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) || aIID.Equals(NS_GET_IID(nsIDOMWindow))) && NS_SUCCEEDED(EnsureScriptEnvironment())) { return mScriptGlobal->QueryInterface(aIID, aSink); } else if (aIID.Equals(NS_GET_IID(Document)) && NS_SUCCEEDED(EnsureContentViewer())) { RefPtr doc = mContentViewer->GetDocument(); doc.forget(aSink); return *aSink ? NS_OK : NS_NOINTERFACE; } else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) { *aSink = nullptr; // Return application cache associated with this docshell, if any nsCOMPtr contentViewer; GetContentViewer(getter_AddRefs(contentViewer)); if (!contentViewer) { return NS_ERROR_NO_INTERFACE; } RefPtr doc = contentViewer->GetDocument(); NS_ASSERTION(doc, "Should have a document."); if (!doc) { return NS_ERROR_NO_INTERFACE; } #if defined(DEBUG) MOZ_LOG( gDocShellLog, LogLevel::Debug, ("nsDocShell[%p]: returning app cache container %p", this, doc.get())); #endif return doc->QueryInterface(aIID, aSink); } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) && NS_SUCCEEDED(EnsureScriptEnvironment())) { nsresult rv; nsCOMPtr wwatch = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // Get the an auth prompter for our window so that the parenting // of the dialogs works as it should when using tabs. nsIPrompt* prompt; rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt); NS_ENSURE_SUCCESS(rv, rv); *aSink = prompt; return NS_OK; } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) ? NS_OK : NS_NOINTERFACE; } else if (aIID.Equals(NS_GET_IID(nsISHistory))) { RefPtr shistory = GetSessionHistory(); if (shistory) { // XXX(nika): Stop exposing nsISHistory through GetInterface. nsCOMPtr legacy = shistory->LegacySHistory(); legacy.forget(aSink); return NS_OK; } return NS_NOINTERFACE; } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) { nsresult rv = EnsureFind(); if (NS_FAILED(rv)) { return rv; } *aSink = mFind; NS_ADDREF((nsISupports*)*aSink); return NS_OK; } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) { if (PresShell* presShell = GetPresShell()) { return presShell->QueryInterface(aIID, aSink); } } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) { nsCOMPtr treeOwner; nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner)); if (NS_SUCCEEDED(rv) && treeOwner) { return treeOwner->QueryInterface(aIID, aSink); } } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) { *aSink = GetBrowserChild().take(); return *aSink ? NS_OK : NS_ERROR_FAILURE; } else { return nsDocLoader::GetInterface(aIID, aSink); } NS_IF_ADDREF(((nsISupports*)*aSink)); return *aSink ? NS_OK : NS_NOINTERFACE; } NS_IMETHODIMP nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) { // Note: this gets called fairly early (before a pageload actually starts). // We could probably defer this even longer. nsCOMPtr browserChild = GetBrowserChild(); static_cast(browserChild.get()) ->SetCancelContentJSEpoch(aEpoch); return NS_OK; } NS_IMETHODIMP nsDocShell::LoadURI(nsDocShellLoadState* aLoadState) { MOZ_ASSERT(aLoadState, "Must have a valid load state!"); MOZ_ASSERT( (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0, "Should not have these flags set"); if (!aLoadState->TriggeringPrincipal()) { #ifndef ANDROID MOZ_ASSERT(false, "LoadURI must have a triggering principal"); #endif if (mUseStrictSecurityChecks) { return NS_ERROR_FAILURE; } } // Note: we allow loads to get through here even if mFiredUnloadEvent is // true; that case will get handled in LoadInternal or LoadHistoryEntry, // so we pass false as the second parameter to IsNavigationAllowed. // However, we don't allow the page to change location *in the middle of* // firing beforeunload, so we do need to check if *beforeunload* is currently // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP. if (!IsNavigationAllowed(true, false)) { return NS_OK; // JS may not handle returning of an error code } if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) && mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) { StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI); } // LoadType used to be set to a default value here, if no LoadInfo/LoadState // object was passed in. That functionality has been removed as of bug // 1492648. LoadType should now be set up by the caller at the time they // create their nsDocShellLoadState object to pass into LoadURI. MOZ_LOG( gDocShellLeakLog, LogLevel::Debug, ("nsDocShell[%p]: loading %s with flags 0x%08x", this, aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags())); if (!aLoadState->SHEntry() && !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_REPLACE_HISTORY)) { // This is possibly a subframe, so handle it accordingly. // // If history exists, it will be loaded into the aLoadState object, and the // LoadType will be changed. MaybeHandleSubframeHistory(aLoadState); } if (aLoadState->SHEntry()) { #ifdef DEBUG MOZ_LOG(gDocShellLog, LogLevel::Debug, ("nsDocShell[%p]: loading from session history", this)); #endif return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType()); } // On history navigation via Back/Forward buttons, don't execute // automatic JavaScript redirection such as |location.href = ...| or // |window.open()| // // LOAD_NORMAL: window.open(...) etc. // LOAD_STOP_CONTENT: location.href = ..., location.assign(...) if ((aLoadState->LoadType() == LOAD_NORMAL || aLoadState->LoadType() == LOAD_STOP_CONTENT) && ShouldBlockLoadingForBackButton()) { return NS_OK; } // Set up the inheriting principal in LoadState. nsresult rv = aLoadState->SetupInheritingPrincipal(mItemType, mOriginAttributes); NS_ENSURE_SUCCESS(rv, rv); rv = aLoadState->SetupTriggeringPrincipal(mOriginAttributes); NS_ENSURE_SUCCESS(rv, rv); aLoadState->CalculateLoadURIFlags(); MOZ_ASSERT(aLoadState->TypeHint().IsVoid(), "Typehint should be null when calling InternalLoad from LoadURI"); MOZ_ASSERT(aLoadState->FileName().IsVoid(), "FileName should be null when calling InternalLoad from LoadURI"); MOZ_ASSERT(aLoadState->SHEntry() == nullptr, "SHEntry should be null when calling InternalLoad from LoadURI"); return InternalLoad(aLoadState, nullptr, // no nsIDocShell nullptr); // no nsIRequest } void nsDocShell::MaybeHandleSubframeHistory(nsDocShellLoadState* aLoadState) { // First, verify if this is a subframe. nsCOMPtr parentAsItem; GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)); nsCOMPtr parentDS(do_QueryInterface(parentAsItem)); if (!parentDS || parentDS == static_cast(this)) { // This is the root docshell. If we got here while // executing an onLoad Handler,this load will not go // into session history. bool inOnLoadHandler = false; GetIsExecutingOnLoadHandler(&inOnLoadHandler); if (inOnLoadHandler) { aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); } return; } /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was * loaded through a history mechanism, then get the SH entry for the child * from the parent. This is done to restore frameset navigation while going * back/forward. If the parent was loaded through any other loadType, set the * child's loadType too accordingly, so that session history does not get * confused. */ // Get the parent's load type uint32_t parentLoadType; parentDS->GetLoadType(&parentLoadType); // Get the ShEntry for the child from the parent nsCOMPtr currentSH; bool oshe = false; parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe); bool dynamicallyAddedChild = mDynamicallyCreated; if (!dynamicallyAddedChild && !oshe && currentSH) { currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild); } if (!dynamicallyAddedChild) { // Only use the old SHEntry, if we're sure enough that // it wasn't originally for some other frame. nsCOMPtr shEntry; parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry)); aLoadState->SetSHEntry(shEntry); } // Make some decisions on the child frame's loadType based on the // parent's loadType, if the subframe hasn't loaded anything into it. // // In some cases privileged scripts may try to get the DOMWindow // reference of this docshell before the loading starts, causing the // initial about:blank content viewer being created and mCurrentURI being // set. To handle this case we check if mCurrentURI is about:blank and // currentSHEntry is null. nsCOMPtr currentChildEntry; GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe); if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry)) { // This is a pre-existing subframe. If // 1. The load of this frame was not originally initiated by session // history directly (i.e. (!shEntry) condition succeeded, but it can // still be a history load on parent which causes this frame being // loaded), which we checked with the above assert, and // 2. mCurrentURI is not null, nor the initial about:blank, // it is possible that a parent's onLoadHandler or even self's // onLoadHandler is loading a new page in this child. Check parent's and // self's busy flag and if it is set, we don't want this onLoadHandler // load to get in to session history. BusyFlags parentBusy = parentDS->GetBusyFlags(); BusyFlags selfBusy = GetBusyFlags(); if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) { aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); aLoadState->SetSHEntry(nullptr); } return; } // This is a newly created frame. Check for exception cases first. // By default the subframe will inherit the parent's loadType. if (aLoadState->SHEntry() && (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK || parentLoadType == LOAD_NORMAL_EXTERNAL)) { // The parent was loaded normally. In this case, this *brand new* // child really shouldn't have a SHEntry. If it does, it could be // because the parent is replacing an existing frame with a new frame, // in the onLoadHandler. We don't want this url to get into session // history. Clear off shEntry, and set load type to // LOAD_BYPASS_HISTORY. bool inOnLoadHandler = false; parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler); if (inOnLoadHandler) { aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); aLoadState->SetSHEntry(nullptr); } } else if (parentLoadType == LOAD_REFRESH) { // Clear shEntry. For refresh loads, we have to load // what comes through the pipe, not what's in history. aLoadState->SetSHEntry(nullptr); } else if ((parentLoadType == LOAD_BYPASS_HISTORY) || (aLoadState->SHEntry() && ((parentLoadType & LOAD_CMD_HISTORY) || (parentLoadType == LOAD_RELOAD_NORMAL) || (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) || (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) || (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) { // If the parent url, bypassed history or was loaded from // history, pass on the parent's loadType to the new child // frame too, so that the child frame will also // avoid getting into history. aLoadState->SetLoadType(parentLoadType); } else if (parentLoadType == LOAD_ERROR_PAGE) { // If the parent document is an error page, we don't // want to update global/session history. However, // this child frame is not an error page. aLoadState->SetLoadType(LOAD_BYPASS_HISTORY); } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) || (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) || (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) { // the new frame should inherit the parent's load type so that it also // bypasses the cache and/or proxy aLoadState->SetLoadType(parentLoadType); } } /* * Reset state to a new content model within the current document and the * document viewer. Called by the document before initiating an out of band * document.write(). */ NS_IMETHODIMP nsDocShell::PrepareForNewContentModel() { mEODForCurrentDocument = false; return NS_OK; } NS_IMETHODIMP nsDocShell::FirePageHideNotification(bool aIsUnload) { FirePageHideNotificationInternal(aIsUnload, false); return NS_OK; } void nsDocShell::FirePageHideNotificationInternal( bool aIsUnload, bool aSkipCheckingDynEntries) { if (mContentViewer && !mFiredUnloadEvent) { // Keep an explicit reference since calling PageHide could release // mContentViewer nsCOMPtr contentViewer(mContentViewer); mFiredUnloadEvent = true; if (mTiming) { mTiming->NotifyUnloadEventStart(); } contentViewer->PageHide(aIsUnload); if (mTiming) { mTiming->NotifyUnloadEventEnd(); } AutoTArray, 8> kids; uint32_t n = mChildList.Length(); kids.SetCapacity(n); for (uint32_t i = 0; i < n; i++) { kids.AppendElement(do_QueryInterface(ChildAt(i))); } n = kids.Length(); for (uint32_t i = 0; i < n; ++i) { RefPtr child = static_cast(kids[i].get()); if (child) { // Skip checking dynamic subframe entries in our children. child->FirePageHideNotificationInternal(aIsUnload, true); } } // If the document is unloading, remove all dynamic subframe entries. if (aIsUnload && !aSkipCheckingDynEntries) { RefPtr rootSH = GetRootSessionHistory(); if (rootSH && mOSHE) { int32_t index = rootSH->Index(); rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE); } } // Now make sure our editor, if any, is detached before we go // any farther. DetachEditorFromWindow(); } } nsresult nsDocShell::DispatchToTabGroup( TaskCategory aCategory, already_AddRefed&& aRunnable) { // Hold the ref so we won't forget to release it. nsCOMPtr runnable(aRunnable); nsCOMPtr win = GetWindow(); if (!win) { // Window should only be unavailable after destroyed. MOZ_ASSERT(mIsBeingDestroyed); return NS_ERROR_FAILURE; } if (win->GetDocGroup()) { return win->GetDocGroup()->Dispatch(aCategory, runnable.forget()); } RefPtr tabGroup = win->TabGroup(); return tabGroup->Dispatch(aCategory, runnable.forget()); } NS_IMETHODIMP nsDocShell::DispatchLocationChangeEvent() { return DispatchToTabGroup( TaskCategory::Other, NewRunnableMethod("nsDocShell::FireDummyOnLocationChange", this, &nsDocShell::FireDummyOnLocationChange)); } NS_IMETHODIMP nsDocShell::StartDelayedAutoplayMediaComponents() { RefPtr outerWindow = GetWindow(); if (outerWindow) { outerWindow->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED); } return NS_OK; } bool nsDocShell::MaybeInitTiming() { if (mTiming && !mBlankTiming) { return false; } bool canBeReset = false; if (mScriptGlobal && mBlankTiming) { nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow(); if (innerWin && innerWin->GetPerformance()) { mTiming = innerWin->GetPerformance()->GetDOMTiming(); mBlankTiming = false; } } if (!mTiming) { mTiming = new nsDOMNavigationTiming(this); canBeReset = true; } mTiming->NotifyNavigationStart( mIsActive ? nsDOMNavigationTiming::DocShellState::eActive : nsDOMNavigationTiming::DocShellState::eInactive); return canBeReset; } void nsDocShell::MaybeResetInitTiming(bool aReset) { if (aReset) { mTiming = nullptr; } } nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const { return mTiming; } // // Bug 13871: Prevent frameset spoofing // // This routine answers: 'Is origin's document from same domain as // target's document?' // // file: uris are considered the same domain for the purpose of // frame navigation regardless of script accessibility (bug 420425) // /* static */ bool nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem, nsIDocShellTreeItem* aTargetTreeItem) { // We want to bypass this check for chrome callers, but only if there's // JS on the stack. System callers still need to do it. if (nsContentUtils::GetCurrentJSContext() && nsContentUtils::IsCallerChrome()) { return true; } MOZ_ASSERT(aOriginTreeItem && aTargetTreeItem, "need two docshells"); // Get origin document principal RefPtr originDocument = aOriginTreeItem->GetDocument(); NS_ENSURE_TRUE(originDocument, false); // Get target principal RefPtr targetDocument = aTargetTreeItem->GetDocument(); NS_ENSURE_TRUE(targetDocument, false); bool equal; nsresult rv = originDocument->NodePrincipal()->Equals( targetDocument->NodePrincipal(), &equal); if (NS_SUCCEEDED(rv) && equal) { return true; } // Not strictly equal, special case if both are file: uris nsCOMPtr originURI; nsCOMPtr targetURI; nsCOMPtr innerOriginURI; nsCOMPtr innerTargetURI; rv = originDocument->NodePrincipal()->GetURI(getter_AddRefs(originURI)); if (NS_SUCCEEDED(rv) && originURI) { innerOriginURI = NS_GetInnermostURI(originURI); } rv = targetDocument->NodePrincipal()->GetURI(getter_AddRefs(targetURI)); if (NS_SUCCEEDED(rv) && targetURI) { innerTargetURI = NS_GetInnermostURI(targetURI); } return innerOriginURI && innerTargetURI && SchemeIsFile(innerOriginURI) && SchemeIsFile(innerTargetURI); } nsPresContext* nsDocShell::GetEldestPresContext() { nsIContentViewer* viewer = mContentViewer; while (viewer) { nsIContentViewer* prevViewer = viewer->GetPreviousViewer(); if (!prevViewer) { return viewer->GetPresContext(); } viewer = prevViewer; } return nullptr; } nsPresContext* nsDocShell::GetPresContext() { if (!mContentViewer) { return nullptr; } return mContentViewer->GetPresContext(); } PresShell* nsDocShell::GetPresShell() { nsPresContext* presContext = GetPresContext(); return presContext ? presContext->GetPresShell() : nullptr; } PresShell* nsDocShell::GetEldestPresShell() { nsPresContext* presContext = GetEldestPresContext(); if (presContext) { return presContext->GetPresShell(); } return nullptr; } NS_IMETHODIMP nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer) { NS_ENSURE_ARG_POINTER(aContentViewer); *aContentViewer = mContentViewer; NS_IF_ADDREF(*aContentViewer); return NS_OK; } NS_IMETHODIMP nsDocShell::GetOuterWindowID(uint64_t* aWindowID) { *aWindowID = mContentWindowID; return NS_OK; } NS_IMETHODIMP nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) { mChromeEventHandler = aChromeEventHandler; if (mScriptGlobal) { mScriptGlobal->SetChromeEventHandler(mChromeEventHandler); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) { NS_ENSURE_ARG_POINTER(aChromeEventHandler); RefPtr handler = mChromeEventHandler; handler.forget(aChromeEventHandler); return NS_OK; } NS_IMETHODIMP nsDocShell::SetCurrentURI(nsIURI* aURI) { // Note that securityUI will set STATE_IS_INSECURE, even if // the scheme of |aURI| is "https". SetCurrentURI(aURI, nullptr, true, 0); return NS_OK; } bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest, bool aFireOnLocationChange, uint32_t aLocationFlags) { MOZ_ASSERT(!mIsBeingDestroyed); MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p SetCurrentURI %s\n", this, aURI ? aURI->GetSpecOrDefault().get() : "")); // We don't want to send a location change when we're displaying an error // page, and we don't want to change our idea of "current URI" either if (mLoadType == LOAD_ERROR_PAGE) { return false; } bool uriIsEqual = false; if (!mCurrentURI || !aURI || NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) { mTitleValidForCurrentURI = false; } mCurrentURI = aURI; #ifdef DEBUG mLastOpenedURI = aURI; #endif if (!NS_IsAboutBlank(mCurrentURI)) { mHasLoadedNonBlankURI = true; } bool isRoot = false; // Is this the root docshell bool isSubFrame = false; // Is this a subframe navigation? nsCOMPtr root; GetInProcessSameTypeRootTreeItem(getter_AddRefs(root)); if (root.get() == static_cast(this)) { // This is the root docshell isRoot = true; } if (mLSHE) { isSubFrame = mLSHE->GetIsSubFrame(); } if (!isSubFrame && !isRoot) { /* * We don't want to send OnLocationChange notifications when * a subframe is being loaded for the first time, while * visiting a frameset page */ return false; } if (aFireOnLocationChange) { FireOnLocationChange(this, aRequest, aURI, aLocationFlags); } return !aFireOnLocationChange; } NS_IMETHODIMP nsDocShell::GetCharset(nsACString& aCharset) { aCharset.Truncate(); PresShell* presShell = GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); Document* doc = presShell->GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); doc->GetDocumentCharacterSet()->Name(aCharset); return NS_OK; } NS_IMETHODIMP nsDocShell::GatherCharsetMenuTelemetry() { nsCOMPtr viewer; GetContentViewer(getter_AddRefs(viewer)); if (!viewer) { return NS_OK; } Document* doc = viewer->GetDocument(); if (!doc || doc->WillIgnoreCharsetOverride()) { return NS_OK; } Telemetry::ScalarSet(Telemetry::ScalarID::ENCODING_OVERRIDE_USED, true); nsIURI* url = doc->GetOriginalURI(); bool isFileURL = url && SchemeIsFile(url); int32_t charsetSource = doc->GetDocumentCharacterSetSource(); switch (charsetSource) { case kCharsetFromTopLevelDomain: // Unlabeled doc on a domain that we map to a fallback encoding Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::RemoteTld); break; case kCharsetFromFallback: case kCharsetFromDocTypeDefault: case kCharsetFromCache: case kCharsetFromParentFrame: case kCharsetFromHintPrevDoc: // Changing charset on an unlabeled doc. if (isFileURL) { Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::Local); } else { Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::RemoteNonTld); } break; case kCharsetFromAutoDetection: // Changing charset on unlabeled doc where chardet fired if (isFileURL) { Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::LocalChardet); } else { Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::RemoteChardet); } break; case kCharsetFromMetaPrescan: case kCharsetFromMetaTag: case kCharsetFromChannel: // Changing charset on a doc that had a charset label. Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::Labeled); break; case kCharsetFromParentForced: case kCharsetFromUserForced: // Changing charset on a document that already had an override. Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::AlreadyOverridden); break; case kCharsetFromIrreversibleAutoDetection: case kCharsetFromOtherComponent: case kCharsetFromByteOrderMark: case kCharsetUninitialized: default: // Bug. This isn't supposed to happen. Telemetry::AccumulateCategorical( Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION::Bug); break; } return NS_OK; } NS_IMETHODIMP nsDocShell::SetCharset(const nsACString& aCharset) { // set the charset override return SetForcedCharset(aCharset); } NS_IMETHODIMP nsDocShell::SetForcedCharset(const nsACString& aCharset) { if (aCharset.IsEmpty()) { mForcedCharset = nullptr; return NS_OK; } const Encoding* encoding = Encoding::ForLabel(aCharset); if (!encoding) { // Reject unknown labels return NS_ERROR_INVALID_ARG; } if (!encoding->IsAsciiCompatible() && encoding != ISO_2022_JP_ENCODING) { // Reject XSS hazards return NS_ERROR_INVALID_ARG; } mForcedCharset = encoding; return NS_OK; } NS_IMETHODIMP nsDocShell::GetForcedCharset(nsACString& aResult) { if (mForcedCharset) { mForcedCharset->Name(aResult); } else { aResult.Truncate(); } return NS_OK; } void nsDocShell::SetParentCharset(const Encoding*& aCharset, int32_t aCharsetSource, nsIPrincipal* aPrincipal) { mParentCharset = aCharset; mParentCharsetSource = aCharsetSource; mParentCharsetPrincipal = aPrincipal; } void nsDocShell::GetParentCharset(const Encoding*& aCharset, int32_t* aCharsetSource, nsIPrincipal** aPrincipal) { aCharset = mParentCharset; *aCharsetSource = mParentCharsetSource; NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal); } NS_IMETHODIMP nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded) { RefPtr doc(GetDocument()); *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasMixedActiveContentBlocked( bool* aHasMixedActiveContentBlocked) { RefPtr doc(GetDocument()); *aHasMixedActiveContentBlocked = doc && doc->GetHasMixedActiveContentBlocked(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasMixedDisplayContentLoaded( bool* aHasMixedDisplayContentLoaded) { RefPtr doc(GetDocument()); *aHasMixedDisplayContentLoaded = doc && doc->GetHasMixedDisplayContentLoaded(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasMixedDisplayContentBlocked( bool* aHasMixedDisplayContentBlocked) { RefPtr doc(GetDocument()); *aHasMixedDisplayContentBlocked = doc && doc->GetHasMixedDisplayContentBlocked(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked) { RefPtr doc(GetDocument()); *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowPlugins(bool* aAllowPlugins) { NS_ENSURE_ARG_POINTER(aAllowPlugins); *aAllowPlugins = mAllowPlugins; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowPlugins(bool aAllowPlugins) { mAllowPlugins = aAllowPlugins; // XXX should enable or disable a plugin host return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowJavascript(bool* aAllowJavascript) { NS_ENSURE_ARG_POINTER(aAllowJavascript); *aAllowJavascript = mAllowJavascript; return NS_OK; } NS_IMETHODIMP nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) { MOZ_ASSERT(aEnabled); *aEnabled = mCSSErrorReportingEnabled; return NS_OK; } NS_IMETHODIMP nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) { mCSSErrorReportingEnabled = aEnabled; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowJavascript(bool aAllowJavascript) { mAllowJavascript = aAllowJavascript; RecomputeCanExecuteScripts(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) { NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing); AssertOriginAttributesMatchPrivateBrowsing(); *aUsePrivateBrowsing = mPrivateBrowsingId > 0; return NS_OK; } NS_IMETHODIMP nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) { if (!CanSetOriginAttributes()) { bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0); return changed ? NS_ERROR_FAILURE : NS_OK; } return SetPrivateBrowsing(aUsePrivateBrowsing); } NS_IMETHODIMP nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) { MOZ_ASSERT(!mIsBeingDestroyed); bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0); if (changed) { mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0; if (mItemType != typeChrome) { mOriginAttributes.SyncAttributesWithPrivateBrowsing(aUsePrivateBrowsing); } if (mAffectPrivateSessionLifetime) { if (aUsePrivateBrowsing) { IncreasePrivateDocShellCount(); } else { DecreasePrivateDocShellCount(); } } } nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shell = do_QueryObject(iter.GetNext()); if (shell) { shell->SetPrivateBrowsing(aUsePrivateBrowsing); } } if (changed) { nsTObserverArray::ForwardIterator iter(mPrivacyObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (!obs) { mPrivacyObservers.RemoveElement(ref); } else { obs->PrivateModeChanged(aUsePrivateBrowsing); } } } AssertOriginAttributesMatchPrivateBrowsing(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = mHasLoadedNonBlankURI; return NS_OK; } NS_IMETHODIMP nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) { NS_ENSURE_ARG_POINTER(aUseRemoteTabs); *aUseRemoteTabs = mUseRemoteTabs; return NS_OK; } NS_IMETHODIMP nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) { if (aUseRemoteTabs) { CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled, true); } // Don't allow non-remote tabs with remote subframes. if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) { return NS_ERROR_UNEXPECTED; } mUseRemoteTabs = aUseRemoteTabs; return NS_OK; } NS_IMETHODIMP nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) { NS_ENSURE_ARG_POINTER(aUseRemoteSubframes); *aUseRemoteSubframes = mUseRemoteSubframes; return NS_OK; } NS_IMETHODIMP nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) { // Should we annotate crash reports like in aUseRemoteTabs? // Don't allow non-remote tabs with remote subframes. if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) { return NS_ERROR_UNEXPECTED; } mUseRemoteSubframes = aUseRemoteSubframes; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) { MOZ_ASSERT(!mIsBeingDestroyed); bool change = aAffectLifetime != mAffectPrivateSessionLifetime; if (change && UsePrivateBrowsing()) { AssertOriginAttributesMatchPrivateBrowsing(); if (aAffectLifetime) { IncreasePrivateDocShellCount(); } else { DecreasePrivateDocShellCount(); } } mAffectPrivateSessionLifetime = aAffectLifetime; nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shell = do_QueryObject(iter.GetNext()); if (shell) { shell->SetAffectPrivateSessionLifetime(aAffectLifetime); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetAffectPrivateSessionLifetime(bool* aAffectLifetime) { *aAffectLifetime = mAffectPrivateSessionLifetime; return NS_OK; } NS_IMETHODIMP nsDocShell::AddWeakPrivacyTransitionObserver( nsIPrivacyTransitionObserver* aObserver) { nsWeakPtr weakObs = do_GetWeakReference(aObserver); if (!weakObs) { return NS_ERROR_NOT_AVAILABLE; } mPrivacyObservers.AppendElement(weakObs); return NS_OK; } NS_IMETHODIMP nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) { nsWeakPtr weakObs = do_GetWeakReference(aObserver); if (!weakObs) { return NS_ERROR_FAILURE; } mReflowObservers.AppendElement(weakObs); return NS_OK; } NS_IMETHODIMP nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) { nsWeakPtr obs = do_GetWeakReference(aObserver); return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::NotifyReflowObservers(bool aInterruptible, DOMHighResTimeStamp aStart, DOMHighResTimeStamp aEnd) { nsTObserverArray::ForwardIterator iter(mReflowObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (!obs) { mReflowObservers.RemoveElement(ref); } else if (aInterruptible) { obs->ReflowInterruptible(aStart, aEnd); } else { obs->Reflow(aStart, aEnd); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool* aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = mAllowMetaRedirects; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowMetaRedirects(bool aValue) { mAllowMetaRedirects = aValue; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowSubframes(bool* aAllowSubframes) { NS_ENSURE_ARG_POINTER(aAllowSubframes); *aAllowSubframes = mAllowSubframes; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowSubframes(bool aAllowSubframes) { mAllowSubframes = aAllowSubframes; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowImages(bool* aAllowImages) { NS_ENSURE_ARG_POINTER(aAllowImages); *aAllowImages = mAllowImages; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowImages(bool aAllowImages) { mAllowImages = aAllowImages; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowMedia(bool* aAllowMedia) { *aAllowMedia = mAllowMedia; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia) { mAllowMedia = aAllowMedia; // Mute or unmute audio contexts attached to the inner window. if (mScriptGlobal) { if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) { if (aAllowMedia) { innerWin->UnmuteAudioContexts(); } else { innerWin->MuteAudioContexts(); } } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) { *aAllowDNSPrefetch = mAllowDNSPrefetch; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) { mAllowDNSPrefetch = aAllowDNSPrefetch; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) { *aAllowWindowControl = mAllowWindowControl; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) { mAllowWindowControl = aAllowWindowControl; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) { *aAllowContentRetargeting = mAllowContentRetargeting; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) { mAllowContentRetargetingOnChildren = aAllowContentRetargeting; mAllowContentRetargeting = aAllowContentRetargeting; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowContentRetargetingOnChildren( bool* aAllowContentRetargetingOnChildren) { *aAllowContentRetargetingOnChildren = mAllowContentRetargetingOnChildren; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowContentRetargetingOnChildren( bool aAllowContentRetargetingOnChildren) { mAllowContentRetargetingOnChildren = aAllowContentRetargetingOnChildren; return NS_OK; } NS_IMETHODIMP nsDocShell::GetInheritPrivateBrowsingId(bool* aInheritPrivateBrowsingId) { *aInheritPrivateBrowsingId = mInheritPrivateBrowsingId; return NS_OK; } NS_IMETHODIMP nsDocShell::SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId) { mInheritPrivateBrowsingId = aInheritPrivateBrowsingId; return NS_OK; } NS_IMETHODIMP nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed) { NS_ENSURE_ARG_POINTER(aFullscreenAllowed); // Browsers and apps have their mFullscreenAllowed retrieved from their // corresponding iframe in their parent upon creation. if (mFullscreenAllowed != CHECK_ATTRIBUTES) { *aFullscreenAllowed = (mFullscreenAllowed == PARENT_ALLOWS); return NS_OK; } // Assume false until we determine otherwise... *aFullscreenAllowed = false; nsCOMPtr win = GetWindow(); if (!win) { return NS_OK; } if (nsCOMPtr frameElement = win->GetFrameElementInternal()) { if (frameElement->IsXULElement()) { if (frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disablefullscreen)) { // Document inside this frame is explicitly disabled. return NS_OK; } } else { // We do not allow document inside any containing element other // than iframe to enter fullscreen. if (frameElement->IsHTMLElement(nsGkAtoms::iframe)) { // If any ancestor iframe does not have allowfullscreen attribute // set, then fullscreen is not allowed. if (!frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) && !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) { return NS_OK; } } else if (frameElement->IsHTMLElement(nsGkAtoms::embed)) { // Respect allowfullscreen only if this is a rewritten YouTube embed. nsCOMPtr objectLoadingContent = do_QueryInterface(frameElement); if (!objectLoadingContent) { return NS_OK; } nsObjectLoadingContent* olc = static_cast(objectLoadingContent.get()); if (!olc->IsRewrittenYoutubeEmbed()) { return NS_OK; } // We don't have to check prefixed attributes because Flash does not // support them. if (!frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen)) { return NS_OK; } } else { // neither iframe nor embed return NS_OK; } } } // If we have no parent then we're the root docshell; no ancestor of the // original docshell doesn't have a allowfullscreen attribute, so // report fullscreen as allowed. RefPtr parent = GetInProcessParentDocshell(); if (!parent) { *aFullscreenAllowed = true; return NS_OK; } // Otherwise, we have a parent, continue the checking for // mozFullscreenAllowed in the parent docshell's ancestors. return parent->GetFullscreenAllowed(aFullscreenAllowed); } hal::ScreenOrientation nsDocShell::OrientationLock() { return mOrientationLock; } void nsDocShell::SetOrientationLock(hal::ScreenOrientation aOrientationLock) { mOrientationLock = aOrientationLock; } NS_IMETHODIMP nsDocShell::GetMayEnableCharacterEncodingMenu( bool* aMayEnableCharacterEncodingMenu) { *aMayEnableCharacterEncodingMenu = false; if (!mContentViewer) { return NS_OK; } Document* doc = mContentViewer->GetDocument(); if (!doc) { return NS_OK; } if (doc->WillIgnoreCharsetOverride()) { return NS_OK; } *aMayEnableCharacterEncodingMenu = true; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType, DocShellEnumeratorDirection aDirection, nsTArray>& aResult) { aResult.Clear(); nsDocShellEnumerator docShellEnum( (aDirection == ENUMERATE_FORWARDS) ? nsDocShellEnumerator::EnumerationDirection::Forwards : nsDocShellEnumerator::EnumerationDirection::Backwards, aItemType, *this); nsresult rv = docShellEnum.BuildDocShellArray(aResult); if (NS_FAILED(rv)) { return rv; } return NS_OK; } NS_IMETHODIMP nsDocShell::GetAppType(AppType* aAppType) { *aAppType = mAppType; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAppType(AppType aAppType) { mAppType = aAppType; return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowAuth(bool* aAllowAuth) { *aAllowAuth = mAllowAuth; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAllowAuth(bool aAllowAuth) { mAllowAuth = aAllowAuth; return NS_OK; } NS_IMETHODIMP nsDocShell::GetZoom(float* aZoom) { NS_ENSURE_ARG_POINTER(aZoom); *aZoom = 1.0f; return NS_OK; } NS_IMETHODIMP nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) { NS_ENSURE_ARG_POINTER(aBusyFlags); *aBusyFlags = mBusyFlags; return NS_OK; } NS_IMETHODIMP nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation, bool* aTookFocus) { NS_ENSURE_ARG_POINTER(aTookFocus); nsCOMPtr chromeFocus = do_GetInterface(mTreeOwner); if (chromeFocus) { if (aForward) { *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation)); } else { *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation)); } } else { *aTookFocus = false; } return NS_OK; } NS_IMETHODIMP nsDocShell::GetSecurityUI(nsISecureBrowserUI** aSecurityUI) { NS_IF_ADDREF(*aSecurityUI = mSecurityUI); return NS_OK; } NS_IMETHODIMP nsDocShell::SetSecurityUI(nsISecureBrowserUI* aSecurityUI) { MOZ_ASSERT(!mIsBeingDestroyed); mSecurityUI = aSecurityUI; return NS_OK; } NS_IMETHODIMP nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) { NS_IF_ADDREF(*aLoadURIDelegate = mLoadURIDelegate); return NS_OK; } NS_IMETHODIMP nsDocShell::SetLoadURIDelegate(nsILoadURIDelegate* aLoadURIDelegate) { mLoadURIDelegate = aLoadURIDelegate; return NS_OK; } NS_IMETHODIMP nsDocShell::GetUseErrorPages(bool* aUseErrorPages) { *aUseErrorPages = UseErrorPages(); return NS_OK; } NS_IMETHODIMP nsDocShell::SetUseErrorPages(bool aUseErrorPages) { // If mUseErrorPages is set explicitly, stop using the // browser.xul.error_pages_enabled pref. if (mObserveErrorPages) { mObserveErrorPages = false; } mUseErrorPages = aUseErrorPages; return NS_OK; } NS_IMETHODIMP nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) { *aPreviousEntryIndex = mPreviousEntryIndex; return NS_OK; } NS_IMETHODIMP nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) { *aLoadedEntryIndex = mLoadedEntryIndex; return NS_OK; } NS_IMETHODIMP nsDocShell::HistoryPurged(int32_t aNumEntries) { // These indices are used for fastback cache eviction, to determine // which session history entries are candidates for content viewer // eviction. We need to adjust by the number of entries that we // just purged from history, so that we look at the right session history // entries during eviction. mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries); mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries); nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shell = do_QueryObject(iter.GetNext()); if (shell) { shell->HistoryPurged(aNumEntries); } } return NS_OK; } nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) { // These indices are used for fastback cache eviction, to determine // which session history entries are candidates for content viewer // eviction. We need to adjust by the number of entries that we // just purged from history, so that we look at the right session history // entries during eviction. if (aIndex == mPreviousEntryIndex) { mPreviousEntryIndex = -1; } else if (aIndex < mPreviousEntryIndex) { --mPreviousEntryIndex; } if (mLoadedEntryIndex == aIndex) { mLoadedEntryIndex = 0; } else if (aIndex < mLoadedEntryIndex) { --mLoadedEntryIndex; } nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shell = do_QueryObject(iter.GetNext()); if (shell) { static_cast(shell.get())->HistoryEntryRemoved(aIndex); } } return NS_OK; } NS_IMETHODIMP nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) { bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers(); if (currentValue == aValue) { return NS_OK; } RefPtr timelines = TimelineConsumers::Get(); if (!timelines) { return NS_OK; } if (aValue) { MOZ_ASSERT(!timelines->HasConsumer(this)); timelines->AddConsumer(this); MOZ_ASSERT(timelines->HasConsumer(this)); UseEntryScriptProfiling(); } else { MOZ_ASSERT(timelines->HasConsumer(this)); timelines->RemoveConsumer(this); MOZ_ASSERT(!timelines->HasConsumer(this)); UnuseEntryScriptProfiling(); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) { *aValue = !!mObserved; return NS_OK; } nsresult nsDocShell::PopProfileTimelineMarkers( JSContext* aCx, JS::MutableHandle aOut) { RefPtr timelines = TimelineConsumers::Get(); if (!timelines) { return NS_OK; } nsTArray store; SequenceRooter rooter(aCx, &store); timelines->PopMarkers(this, aCx, store); if (!ToJSValue(aCx, store, aOut)) { JS_ClearPendingException(aCx); return NS_ERROR_UNEXPECTED; } return NS_OK; } nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) { *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds(); return NS_OK; } NS_IMETHODIMP nsDocShell::SetWindowDraggingAllowed(bool aValue) { RefPtr parent = GetInProcessParentDocshell(); if (!aValue && mItemType == typeChrome && !parent) { // Window dragging is always allowed for top level // chrome docshells. return NS_ERROR_FAILURE; } mWindowDraggingAllowed = aValue; return NS_OK; } NS_IMETHODIMP nsDocShell::GetWindowDraggingAllowed(bool* aValue) { // window dragging regions in CSS (-moz-window-drag:drag) // can be slow. Default behavior is to only allow it for // chrome top level windows. RefPtr parent = GetInProcessParentDocshell(); if (mItemType == typeChrome && !parent) { // Top level chrome window *aValue = true; } else { *aValue = mWindowDraggingAllowed; } return NS_OK; } nsIDOMStorageManager* nsDocShell::TopSessionStorageManager() { nsresult rv; nsCOMPtr topItem; rv = GetInProcessSameTypeRootTreeItem(getter_AddRefs(topItem)); if (NS_FAILED(rv)) { return nullptr; } if (!topItem) { return nullptr; } nsDocShell* topDocShell = static_cast(topItem.get()); if (topDocShell != this) { return topDocShell->TopSessionStorageManager(); } if (!mSessionStorageManager) { mSessionStorageManager = new SessionStorageManager(); } return mSessionStorageManager; } NS_IMETHODIMP nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) { NS_IF_ADDREF(*aResult = GetCurrentDocChannel()); return NS_OK; } nsIChannel* nsDocShell::GetCurrentDocChannel() { if (mContentViewer) { Document* doc = mContentViewer->GetDocument(); if (doc) { return doc->GetChannel(); } } return nullptr; } NS_IMETHODIMP nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) { nsWeakPtr weakObs = do_GetWeakReference(aObserver); if (!weakObs) { return NS_ERROR_FAILURE; } mScrollObservers.AppendElement(weakObs); return NS_OK; } NS_IMETHODIMP nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) { nsWeakPtr obs = do_GetWeakReference(aObserver); return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; } void nsDocShell::NotifyAsyncPanZoomStarted() { nsTObserverArray::ForwardIterator iter(mScrollObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (obs) { obs->AsyncPanZoomStarted(); } else { mScrollObservers.RemoveElement(ref); } } } void nsDocShell::NotifyAsyncPanZoomStopped() { nsTObserverArray::ForwardIterator iter(mScrollObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (obs) { obs->AsyncPanZoomStopped(); } else { mScrollObservers.RemoveElement(ref); } } } NS_IMETHODIMP nsDocShell::NotifyScrollObservers() { nsTObserverArray::ForwardIterator iter(mScrollObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (obs) { obs->ScrollPositionChanged(); } else { mScrollObservers.RemoveElement(ref); } } return NS_OK; } //***************************************************************************** // nsDocShell::nsIDocShellTreeItem //***************************************************************************** NS_IMETHODIMP nsDocShell::GetName(nsAString& aName) { aName = mBrowsingContext->Name(); return NS_OK; } NS_IMETHODIMP nsDocShell::SetName(const nsAString& aName) { mBrowsingContext->SetName(aName); return NS_OK; } NS_IMETHODIMP nsDocShell::NameEquals(const nsAString& aName, bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = mBrowsingContext->NameEquals(aName); return NS_OK; } NS_IMETHODIMP nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) { aCustomUserAgent = mCustomUserAgent; return NS_OK; } NS_IMETHODIMP nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) { mCustomUserAgent = aCustomUserAgent; RefPtr win = mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr; if (win) { Navigator* navigator = win->Navigator(); if (navigator) { navigator->ClearUserAgentCache(); } } uint32_t childCount = mChildList.Length(); for (uint32_t i = 0; i < childCount; ++i) { nsCOMPtr childShell = do_QueryInterface(ChildAt(i)); if (childShell) { childShell->SetCustomUserAgent(aCustomUserAgent); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetTouchEventsOverride(TouchEventsOverride* aTouchEventsOverride) { *aTouchEventsOverride = mTouchEventsOverride; return NS_OK; } NS_IMETHODIMP nsDocShell::SetTouchEventsOverride(TouchEventsOverride aTouchEventsOverride) { // We don't have a way to verify this coming from Javascript, so this check is // still needed. if (!(aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_NONE || aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_ENABLED || aTouchEventsOverride == TOUCHEVENTS_OVERRIDE_DISABLED)) { return NS_ERROR_INVALID_ARG; } mTouchEventsOverride = aTouchEventsOverride; uint32_t childCount = mChildList.Length(); for (uint32_t i = 0; i < childCount; ++i) { nsCOMPtr childShell = do_QueryInterface(ChildAt(i)); if (childShell) { childShell->SetTouchEventsOverride(aTouchEventsOverride); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetMetaViewportOverride( MetaViewportOverride* aMetaViewportOverride) { NS_ENSURE_ARG_POINTER(aMetaViewportOverride); *aMetaViewportOverride = mMetaViewportOverride; return NS_OK; } NS_IMETHODIMP nsDocShell::SetMetaViewportOverride( MetaViewportOverride aMetaViewportOverride) { // We don't have a way to verify this coming from Javascript, so this check is // still needed. if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE || aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED || aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) { return NS_ERROR_INVALID_ARG; } mMetaViewportOverride = aMetaViewportOverride; // Inform our presShell that it needs to re-check its need for a viewport // override. if (RefPtr presShell = GetPresShell()) { presShell->UpdateViewportOverridden(true); } return NS_OK; } /* virtual */ int32_t nsDocShell::ItemType() { return mItemType; } NS_IMETHODIMP nsDocShell::GetItemType(int32_t* aItemType) { NS_ENSURE_ARG_POINTER(aItemType); MOZ_DIAGNOSTIC_ASSERT( (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType); *aItemType = mItemType; return NS_OK; } NS_IMETHODIMP nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) { if (!mParent) { *aParent = nullptr; } else { CallQueryInterface(mParent, aParent); } // Note that in the case when the parent is not an nsIDocShellTreeItem we // don't want to throw; we just want to return null. return NS_OK; } // With Fission, related nsDocShell objects may exist in a different process. In // that case, this method will return `nullptr`, despite a parent nsDocShell // object existing. // // Prefer using `BrowsingContext::Parent()`, which will succeed even if the // parent entry is not in the current process, and handle the case where the // parent nsDocShell is inaccessible. already_AddRefed nsDocShell::GetInProcessParentDocshell() { nsCOMPtr docshell = do_QueryInterface(GetAsSupports(mParent)); return docshell.forget().downcast(); } void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) { MOZ_ASSERT(!mIsBeingDestroyed); // If there is an existing document then there is no need to create // a client for a future initial about:blank document. if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() && mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) { MOZ_DIAGNOSTIC_ASSERT(mScriptGlobal->GetCurrentInnerWindowInternal() ->GetClientInfo() .isSome()); MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource); return; } // Don't recreate the initial client source. We call this multiple times // when DoChannelLoad() is called before CreateAboutBlankContentViewer. if (mInitialClientSource) { return; } // Don't pre-allocate the client when we are sandboxed. The inherited // principal does not take sandboxing into account. // TODO: Refactor sandboxing principal code out so we can use it here. if (!aPrincipal && mSandboxFlags) { return; } nsIPrincipal* principal = aPrincipal ? aPrincipal : GetInheritedPrincipal(false); // Sometimes there is no principal available when we are called from // CreateAboutBlankContentViewer. For example, sometimes the principal // is only extracted from the load context after the document is created // in Document::ResetToURI(). Ideally we would do something similar // here, but for now lets just avoid the issue by not preallocating the // client. if (!principal) { return; } nsCOMPtr win = GetWindow(); if (!win) { return; } mInitialClientSource = ClientManager::CreateSource( ClientType::Window, win->EventTargetFor(TaskCategory::Other), principal); MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource); // Mark the initial client as execution ready, but owned by the docshell. // If the client is actually used this will cause ClientSource to force // the creation of the initial about:blank by calling // nsDocShell::GetDocument(). mInitialClientSource->DocShellExecutionReady(this); // Next, check to see if the parent is controlled. nsCOMPtr parent = GetInProcessParentDocshell(); nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr; nsPIDOMWindowInner* parentInner = parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr; if (!parentInner) { return; } nsCOMPtr uri; MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns)); // We're done if there is no parent controller or if this docshell // is not permitted to control for some reason. Maybe controller(parentInner->GetController()); if (controller.isNothing() || !ServiceWorkerAllowedToControlWindow(principal, uri)) { return; } mInitialClientSource->InheritController(controller.ref()); } Maybe nsDocShell::GetInitialClientInfo() const { if (mInitialClientSource) { Maybe result; result.emplace(mInitialClientSource->Info()); return result; } nsGlobalWindowInner* innerWindow = mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr; Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr; if (!doc || !doc->IsInitialDocument()) { return Maybe(); } return innerWindow->GetClientInfo(); } void nsDocShell::RecomputeCanExecuteScripts() { bool old = mCanExecuteScripts; RefPtr parent = GetInProcessParentDocshell(); // If we have no tree owner, that means that we've been detached from the // docshell tree (this is distinct from having no parent docshell, which // is the case for root docshells). It would be nice to simply disallow // script in detached docshells, but bug 986542 demonstrates that this // behavior breaks at least one website. // // So instead, we use our previous value, unless mAllowJavascript has been // explicitly set to false. if (!mTreeOwner) { mCanExecuteScripts = mCanExecuteScripts && mAllowJavascript; // If scripting has been explicitly disabled on our docshell, we're done. } else if (!mAllowJavascript) { mCanExecuteScripts = false; // If we have a parent, inherit. } else if (parent) { mCanExecuteScripts = parent->mCanExecuteScripts; // Otherwise, we're the root of the tree, and we haven't explicitly disabled // script. Allow. } else { mCanExecuteScripts = true; } // Inform our active DOM window. // // This will pass the outer, which will be in the scope of the active inner. if (mScriptGlobal && mScriptGlobal->GetGlobalJSObject()) { xpc::Scriptability& scriptability = xpc::Scriptability::Get(mScriptGlobal->GetGlobalJSObject()); scriptability.SetDocShellAllowsScript(mCanExecuteScripts); } // If our value has changed, our children might be affected. Recompute their // value as well. if (old != mCanExecuteScripts) { nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { static_cast(iter.GetNext())->RecomputeCanExecuteScripts(); } } } nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) { bool wasFrame = IsFrame(); #ifdef DEBUG bool wasPrivate = UsePrivateBrowsing(); #endif nsresult rv = nsDocLoader::SetDocLoaderParent(aParent); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr priorityGroup = do_QueryInterface(mLoadGroup); if (wasFrame != IsFrame() && priorityGroup) { priorityGroup->AdjustPriority(wasFrame ? -1 : 1); } // Curse ambiguous nsISupports inheritance! nsISupports* parent = GetAsSupports(aParent); // If parent is another docshell, we inherit all their flags for // allowing plugins, scripting etc. bool value; nsString customUserAgent; nsCOMPtr parentAsDocShell(do_QueryInterface(parent)); if (parentAsDocShell) { if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) { SetAllowPlugins(value); } if (mAllowJavascript && NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) { SetAllowJavascript(value); } if (mAllowMetaRedirects && NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) { SetAllowMetaRedirects(value); } if (mAllowSubframes && NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) { SetAllowSubframes(value); } if (mAllowImages && NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) { SetAllowImages(value); } SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia); if (mAllowWindowControl && NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) { SetAllowWindowControl(value); } SetAllowContentRetargeting( mAllowContentRetargeting && parentAsDocShell->GetAllowContentRetargetingOnChildren()); if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) { SetIsActive(value); } if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) && !customUserAgent.IsEmpty()) { SetCustomUserAgent(customUserAgent); } if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { value = false; } SetAllowDNSPrefetch(mAllowDNSPrefetch && value); if (mInheritPrivateBrowsingId) { value = parentAsDocShell->GetAffectPrivateSessionLifetime(); SetAffectPrivateSessionLifetime(value); } uint32_t flags; if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) { SetDefaultLoadFlags(flags); } SetTouchEventsOverride(parentAsDocShell->GetTouchEventsOverride()); // We don't need to inherit metaViewportOverride, because the viewport // is only relevant for the outermost nsDocShell, not for any iframes // like this that might be embedded within it. } nsCOMPtr parentAsLoadContext(do_QueryInterface(parent)); if (parentAsLoadContext && mInheritPrivateBrowsingId && NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) { SetPrivateBrowsing(value); } nsCOMPtr parentURIListener(do_GetInterface(parent)); if (parentURIListener) { mContentListener->SetParentContentListener(parentURIListener); } // Our parent has changed. Recompute scriptability. RecomputeCanExecuteScripts(); // Inform windows when they're being removed from their parent. if (!aParent) { MaybeClearStorageAccessFlag(); } NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(), "Private browsing state changed while inheritance was disabled"); return NS_OK; } void nsDocShell::MaybeClearStorageAccessFlag() { if (mScriptGlobal) { // Tell our window that the parent has now changed. mScriptGlobal->ParentWindowChanged(); // Tell all of our children about the change recursively as well. nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr child = do_QueryObject(iter.GetNext()); if (child) { static_cast(child.get())->MaybeClearStorageAccessFlag(); } } } } NS_IMETHODIMP nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) { if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) { *aParent = do_AddRef(parentBC->GetDocShell()).take(); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetSameTypeParentIgnoreBrowserBoundaries(nsIDocShell** aParent) { NS_ENSURE_ARG_POINTER(aParent); *aParent = nullptr; nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent)); if (!parent) { return NS_OK; } if (parent->ItemType() == mItemType) { nsCOMPtr parentDS = do_QueryInterface(parent); parentDS.forget(aParent); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) { NS_ENSURE_ARG_POINTER(aRootTreeItem); RefPtr root = this; RefPtr parent = root->GetInProcessParentDocshell(); while (parent) { root = parent; parent = root->GetInProcessParentDocshell(); } root.forget(aRootTreeItem); return NS_OK; } NS_IMETHODIMP nsDocShell::GetInProcessSameTypeRootTreeItem( nsIDocShellTreeItem** aRootTreeItem) { NS_ENSURE_ARG_POINTER(aRootTreeItem); *aRootTreeItem = static_cast(this); nsCOMPtr parent; NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); while (parent) { *aRootTreeItem = parent; NS_ENSURE_SUCCESS( (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); } NS_ADDREF(*aRootTreeItem); return NS_OK; } NS_IMETHODIMP nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserBoundaries( nsIDocShell** aRootTreeItem) { NS_ENSURE_ARG_POINTER(aRootTreeItem); *aRootTreeItem = static_cast(this); nsCOMPtr parent; NS_ENSURE_SUCCESS( GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent)), NS_ERROR_FAILURE); while (parent) { *aRootTreeItem = parent; NS_ENSURE_SUCCESS( (*aRootTreeItem) ->GetSameTypeParentIgnoreBrowserBoundaries(getter_AddRefs(parent)), NS_ERROR_FAILURE); } NS_ADDREF(*aRootTreeItem); return NS_OK; } /* static */ bool nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem, nsIDocShellTreeItem* aAccessingItem, bool aConsiderOpener) { MOZ_ASSERT(aTargetItem, "Must have target item!"); if (!gValidateOrigin || !aAccessingItem) { // Good to go return true; } // XXXbz should we care if aAccessingItem or the document therein is // chrome? Should those get extra privileges? // For historical context, see: // // Bug 13871: Prevent frameset spoofing // Bug 103638: Targets with same name in different windows open in wrong // window with javascript // Bug 408052: Adopt "ancestor" frame navigation policy // Now do a security check. // // Disallow navigation if the two frames are not part of the same app, or if // they have different is-in-browser-element states. // // Allow navigation if // 1) aAccessingItem can script aTargetItem or one of its ancestors in // the frame hierarchy or // 2) aTargetItem is a top-level frame and aAccessingItem is its descendant // 3) aTargetItem is a top-level frame and aAccessingItem can target // its opener per rule (1) or (2). if (aTargetItem == aAccessingItem) { // A frame is allowed to navigate itself. return true; } nsCOMPtr targetDS = do_QueryInterface(aTargetItem); nsCOMPtr accessingDS = do_QueryInterface(aAccessingItem); if (!targetDS || !accessingDS) { // We must be able to convert both to nsIDocShell. return false; } nsCOMPtr accessingRoot; aAccessingItem->GetInProcessSameTypeRootTreeItem( getter_AddRefs(accessingRoot)); nsCOMPtr accessingRootDS = do_QueryInterface(accessingRoot); nsCOMPtr targetRoot; aTargetItem->GetInProcessSameTypeRootTreeItem(getter_AddRefs(targetRoot)); nsCOMPtr targetRootDS = do_QueryInterface(targetRoot); OriginAttributes targetOA = static_cast(targetDS.get())->GetOriginAttributes(); OriginAttributes accessingOA = static_cast(accessingDS.get())->GetOriginAttributes(); // When the first party isolation is on, the top-level docShell may not have // the firstPartyDomain in its originAttributes, but its document will have // it. So we get the firstPartyDomain from the nodePrincipal of the document // before we compare the originAttributes. if (OriginAttributes::IsFirstPartyEnabled()) { if (aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent && accessingDS == accessingRootDS) { RefPtr accessingDoc = aAccessingItem->GetDocument(); if (accessingDoc) { nsCOMPtr accessingPrincipal = accessingDoc->NodePrincipal(); accessingOA.mFirstPartyDomain = accessingPrincipal->OriginAttributesRef().mFirstPartyDomain; } } if (aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent && targetDS == targetRootDS) { RefPtr targetDoc = aAccessingItem->GetDocument(); if (targetDoc) { nsCOMPtr targetPrincipal = targetDoc->NodePrincipal(); targetOA.mFirstPartyDomain = targetPrincipal->OriginAttributesRef().mFirstPartyDomain; } } } if (targetOA != accessingOA) { return false; } // A private document can't access a non-private one, and vice versa. if (static_cast(targetDS.get())->UsePrivateBrowsing() != static_cast(accessingDS.get())->UsePrivateBrowsing()) { return false; } if (aTargetItem == accessingRoot) { // A frame can navigate its root. return true; } // Check if aAccessingItem can navigate one of aTargetItem's ancestors. nsCOMPtr target = aTargetItem; do { if (ValidateOrigin(aAccessingItem, target)) { return true; } nsCOMPtr parent; target->GetInProcessSameTypeParent(getter_AddRefs(parent)); parent.swap(target); } while (target); if (aTargetItem != targetRoot) { // target is a subframe, not in accessor's frame hierarchy, and all its // ancestors have origins different from that of the accessor. Don't // allow access. return false; } if (!aConsiderOpener) { // All done here return false; } nsCOMPtr targetWindow = aTargetItem->GetWindow(); if (!targetWindow) { NS_ERROR("This should not happen, really"); return false; } nsCOMPtr targetOpener = targetWindow->GetOpener(); nsCOMPtr openerWebNav(do_GetInterface(targetOpener)); nsCOMPtr openerItem(do_QueryInterface(openerWebNav)); if (!openerItem) { return false; } return CanAccessItem(openerItem, aAccessingItem, false); } static bool ItemIsActive(nsIDocShellTreeItem* aItem) { if (nsCOMPtr window = aItem->GetWindow()) { auto* win = nsGlobalWindowOuter::Cast(window); if (!win->GetClosedOuter()) { return true; } } return false; } NS_IMETHODIMP nsDocShell::FindItemWithName(const nsAString& aName, nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, bool aSkipTabGroup, nsIDocShellTreeItem** aResult) { NS_ENSURE_ARG_POINTER(aResult); // If we don't find one, we return NS_OK and a null result *aResult = nullptr; if (aName.IsEmpty()) { return NS_OK; } if (aRequestor) { // If aRequestor is not null we don't need to check special names, so // just hand straight off to the search by actual name function. return DoFindItemWithName(aName, aRequestor, aOriginalRequestor, aSkipTabGroup, aResult); } else { // This is the entry point into the target-finding algorithm. Check // for special names. This should only be done once, hence the check // for a null aRequestor. nsCOMPtr foundItem; if (aName.LowerCaseEqualsLiteral("_self")) { foundItem = this; } else if (aName.LowerCaseEqualsLiteral("_blank")) { // Just return null. Caller must handle creating a new window with // a blank name himself. return NS_OK; } else if (aName.LowerCaseEqualsLiteral("_parent")) { GetInProcessSameTypeParent(getter_AddRefs(foundItem)); if (!foundItem) { foundItem = this; } } else if (aName.LowerCaseEqualsLiteral("_top")) { GetInProcessSameTypeRootTreeItem(getter_AddRefs(foundItem)); NS_ASSERTION(foundItem, "Must have this; worst case it's us!"); } else { // Do the search for item by an actual name. DoFindItemWithName(aName, aRequestor, aOriginalRequestor, aSkipTabGroup, getter_AddRefs(foundItem)); } if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) { foundItem = nullptr; } // DoFindItemWithName only returns active items and we don't check if // the item is active for the special cases. if (foundItem) { foundItem.swap(*aResult); } return NS_OK; } } void nsDocShell::AssertOriginAttributesMatchPrivateBrowsing() { // Chrome docshells must not have a private browsing OriginAttribute // Content docshells must maintain the equality: // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId if (mItemType == typeChrome) { MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0); } else { MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId); } } nsresult nsDocShell::DoFindItemWithName(const nsAString& aName, nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, bool aSkipTabGroup, nsIDocShellTreeItem** aResult) { // First we check our name. if (mBrowsingContext->NameEquals(aName) && ItemIsActive(this) && CanAccessItem(this, aOriginalRequestor)) { NS_ADDREF(*aResult = this); return NS_OK; } // Second we check our children making sure not to ask a child if // it is the aRequestor. #ifdef DEBUG nsresult rv = #endif FindChildWithName(aName, true, true, aRequestor, aOriginalRequestor, aResult); NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not be failing here."); if (*aResult) { return NS_OK; } // Third if we have a parent and it isn't the requestor then we // should ask it to do the search. If it is the requestor we // should just stop here and let the parent do the rest. If we // don't have a parent, then we should ask the // docShellTreeOwner to do the search. nsCOMPtr parentAsTreeItem = do_QueryInterface(GetAsSupports(mParent)); if (parentAsTreeItem) { if (parentAsTreeItem == aRequestor) { return NS_OK; } // If we have a same-type parent, respecting browser and app boundaries. // NOTE: Could use GetInProcessSameTypeParent if the issues described in // bug 1310344 are fixed. if (parentAsTreeItem->ItemType() == mItemType) { return parentAsTreeItem->FindItemWithName(aName, this, aOriginalRequestor, /* aSkipTabGroup = */ false, aResult); } } // If we have a null parent or the parent is not of the same type, we need to // give up on finding it in our tree, and start looking in our TabGroup. nsCOMPtr window = GetWindow(); if (window && !aSkipTabGroup) { RefPtr tabGroup = window->TabGroup(); tabGroup->FindItemWithName(aName, this, aOriginalRequestor, aResult); } return NS_OK; } bool nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell) { // If no target then not sandboxed. if (!aTargetDocShell) { return false; } // We cannot be sandboxed from ourselves. if (aTargetDocShell == this) { return false; } // Default the sandbox flags to our flags, so that if we can't retrieve the // active document, we will still enforce our own. uint32_t sandboxFlags = mSandboxFlags; if (mContentViewer) { RefPtr doc = mContentViewer->GetDocument(); if (doc) { sandboxFlags = doc->GetSandboxFlags(); } } // If no flags, we are not sandboxed at all. if (!sandboxFlags) { return false; } // If aTargetDocShell has an ancestor, it is not top level. nsCOMPtr ancestorOfTarget; aTargetDocShell->GetInProcessSameTypeParent(getter_AddRefs(ancestorOfTarget)); if (ancestorOfTarget) { do { // We are not sandboxed if we are an ancestor of target. if (ancestorOfTarget == this) { return false; } nsCOMPtr tempTreeItem; ancestorOfTarget->GetInProcessSameTypeParent( getter_AddRefs(tempTreeItem)); tempTreeItem.swap(ancestorOfTarget); } while (ancestorOfTarget); // Otherwise, we are sandboxed from aTargetDocShell. return true; } // aTargetDocShell is top level, are we the "one permitted sandboxed // navigator", i.e. did we open aTargetDocShell? nsCOMPtr permittedNavigator; aTargetDocShell->GetOnePermittedSandboxedNavigator( getter_AddRefs(permittedNavigator)); if (permittedNavigator == this) { return false; } // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed // from our top. if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) { nsCOMPtr rootTreeItem; GetInProcessSameTypeRootTreeItem(getter_AddRefs(rootTreeItem)); if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) { return false; } } // Otherwise, we are sandboxed from aTargetDocShell. return true; } NS_IMETHODIMP nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) { NS_ENSURE_ARG_POINTER(aTreeOwner); *aTreeOwner = mTreeOwner; NS_IF_ADDREF(*aTreeOwner); return NS_OK; } NS_IMETHODIMP nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) { if (mIsBeingDestroyed && aTreeOwner) { return NS_ERROR_FAILURE; } // Don't automatically set the progress based on the tree owner for frames if (!IsFrame()) { nsCOMPtr webProgress = do_QueryInterface(GetAsSupports(this)); if (webProgress) { nsCOMPtr oldListener = do_QueryInterface(mTreeOwner); nsCOMPtr newListener = do_QueryInterface(aTreeOwner); if (oldListener) { webProgress->RemoveProgressListener(oldListener); } if (newListener) { webProgress->AddProgressListener(newListener, nsIWebProgress::NOTIFY_ALL); } } } mTreeOwner = aTreeOwner; // Weak reference per API nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr child = do_QueryObject(iter.GetNext()); NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); if (child->ItemType() == mItemType) { child->SetTreeOwner(aTreeOwner); } } // If we're in the content process and have had a TreeOwner set on us, extract // our BrowserChild actor. If we've already had our BrowserChild set, assert // that it hasn't changed. if (mTreeOwner && XRE_IsContentProcess()) { nsCOMPtr newBrowserChild = do_GetInterface(mTreeOwner); MOZ_ASSERT(newBrowserChild, "No BrowserChild actor for tree owner in Content!"); if (mBrowserChild) { nsCOMPtr oldBrowserChild = do_QueryReferent(mBrowserChild); MOZ_RELEASE_ASSERT( oldBrowserChild == newBrowserChild, "Cannot cahnge BrowserChild during nsDocShell lifetime!"); } else { mBrowserChild = do_GetWeakReference(newBrowserChild); } } // Our tree owner has changed. Recompute scriptability. // // Note that this is near-redundant with the recomputation in // SetDocLoaderParent(), but not so for the root DocShell, where the call to // SetTreeOwner() happens after the initial AddDocLoaderAsChildOfRoot(), // and we never set another parent. Given that this is neither expensive nor // performance-critical, let's be safe and unconditionally recompute this // state whenever dependent state changes. RecomputeCanExecuteScripts(); return NS_OK; } void nsDocShell::SetChildOffset(int32_t aChildOffset) { mChildOffset = aChildOffset; } int32_t nsDocShell::GetChildOffset() { return mChildOffset; } NS_IMETHODIMP nsDocShell::GetHistoryID(nsID** aID) { *aID = mHistoryID.Clone(); return NS_OK; } const nsID nsDocShell::HistoryID() { return mHistoryID; } NS_IMETHODIMP nsDocShell::GetIsInUnload(bool* aIsInUnload) { *aIsInUnload = mFiredUnloadEvent; return NS_OK; } NS_IMETHODIMP nsDocShell::GetInProcessChildCount(int32_t* aChildCount) { NS_ENSURE_ARG_POINTER(aChildCount); *aChildCount = mChildList.Length(); return NS_OK; } NS_IMETHODIMP nsDocShell::AddChild(nsIDocShellTreeItem* aChild) { NS_ENSURE_ARG_POINTER(aChild); RefPtr childAsDocLoader = GetAsDocLoader(aChild); NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED); // Make sure we're not creating a loop in the docshell tree nsDocLoader* ancestor = this; do { if (childAsDocLoader == ancestor) { return NS_ERROR_ILLEGAL_VALUE; } ancestor = ancestor->GetParent(); } while (ancestor); // Make sure to remove the child from its current parent. nsDocLoader* childsParent = childAsDocLoader->GetParent(); if (childsParent) { nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader); NS_ENSURE_SUCCESS(rv, rv); } // Make sure to clear the treeowner in case this child is a different type // from us. aChild->SetTreeOwner(nullptr); nsresult res = AddChildLoader(childAsDocLoader); NS_ENSURE_SUCCESS(res, res); NS_ASSERTION(!mChildList.IsEmpty(), "child list must not be empty after a successful add"); nsCOMPtr childDocShell = do_QueryInterface(aChild); bool dynamic = false; childDocShell->GetCreatedDynamically(&dynamic); if (!dynamic) { nsCOMPtr currentSH; bool oshe = false; GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe); if (currentSH) { currentSH->HasDynamicallyAddedChild(&dynamic); } } childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Length() - 1); /* Set the child's global history if the parent has one */ if (mUseGlobalHistory) { childDocShell->SetUseGlobalHistory(true); } if (aChild->ItemType() != mItemType) { return NS_OK; } aChild->SetTreeOwner(mTreeOwner); nsCOMPtr childAsDocShell(do_QueryInterface(aChild)); if (!childAsDocShell) { return NS_OK; } // charset, style-disabling, and zoom will be inherited in SetupNewViewer() // Now take this document's charset and set the child's parentCharset field // to it. We'll later use that field, in the loading process, for the // charset choosing algorithm. // If we fail, at any point, we just return NS_OK. // This code has some performance impact. But this will be reduced when // the current charset will finally be stored as an Atom, avoiding the // alias resolution extra look-up. // we are NOT going to propagate the charset is this Chrome's docshell if (mItemType == nsIDocShellTreeItem::typeChrome) { return NS_OK; } // get the parent's current charset if (!mContentViewer) { return NS_OK; } Document* doc = mContentViewer->GetDocument(); if (!doc) { return NS_OK; } const Encoding* parentCS = doc->GetDocumentCharacterSet(); int32_t charsetSource = doc->GetDocumentCharacterSetSource(); // set the child's parentCharset childAsDocShell->SetParentCharset(parentCS, charsetSource, doc->NodePrincipal()); // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType); return NS_OK; } NS_IMETHODIMP nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) { NS_ENSURE_ARG_POINTER(aChild); RefPtr childAsDocLoader = GetAsDocLoader(aChild); NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED); nsresult rv = RemoveChildLoader(childAsDocLoader); NS_ENSURE_SUCCESS(rv, rv); aChild->SetTreeOwner(nullptr); return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader); } NS_IMETHODIMP nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) { NS_ENSURE_ARG_POINTER(aChild); RefPtr child = GetInProcessChildAt(aIndex); NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); child.forget(aChild); return NS_OK; } nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) { #ifdef DEBUG if (aIndex < 0) { NS_WARNING("Negative index passed to GetChildAt"); } else if (static_cast(aIndex) >= mChildList.Length()) { NS_WARNING("Too large an index passed to GetChildAt"); } #endif nsIDocumentLoader* child = ChildAt(aIndex); // child may be nullptr here. return static_cast(child); } NS_IMETHODIMP nsDocShell::FindChildWithName(const nsAString& aName, bool aRecurse, bool aSameType, nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aResult) { NS_ENSURE_ARG_POINTER(aResult); // if we don't find one, we return NS_OK and a null result *aResult = nullptr; if (aName.IsEmpty()) { return NS_OK; } nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr child = do_QueryObject(iter.GetNext()); NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); int32_t childType = child->ItemType(); if (aSameType && (childType != mItemType)) { continue; } bool childNameEquals = false; child->NameEquals(aName, &childNameEquals); if (childNameEquals && ItemIsActive(child) && CanAccessItem(child, aOriginalRequestor)) { child.swap(*aResult); break; } // Only ask it to check children if it is same type if (childType != mItemType) { continue; } // Only ask the child if it isn't the requestor if (aRecurse && (aRequestor != child)) { // See if child contains the shell with the given name #ifdef DEBUG nsresult rv = #endif child->FindChildWithName(aName, true, aSameType, static_cast(this), aOriginalRequestor, aResult); NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not fail here"); if (*aResult) { // found it return NS_OK; } } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetChildSHEntry(int32_t aChildOffset, nsISHEntry** aResult) { nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; // A nsISHEntry for a child is *only* available when the parent is in // the progress of loading a document too... if (mLSHE) { /* Before looking for the subframe's url, check * the expiration status of the parent. If the parent * has expired from cache, then subframes will not be * loaded from history in certain situations. */ bool parentExpired = mLSHE->GetExpirationStatus(); /* Get the parent's Load Type so that it can be set on the child too. * By default give a loadHistory value */ uint32_t loadType = mLSHE->GetLoadType(); // If the user did a shift-reload on this frameset page, // we don't want to load the subframes from history. if (IsForceReloadType(loadType) || loadType == LOAD_REFRESH) { return rv; } /* If the user pressed reload and the parent frame has expired * from cache, we do not want to load the child frame from history. */ if (parentExpired && (loadType == LOAD_RELOAD_NORMAL)) { // The parent has expired. Return null. *aResult = nullptr; return rv; } // Get the child subframe from session history. rv = mLSHE->GetChildAt(aChildOffset, aResult); if (*aResult) { (*aResult)->SetLoadType(loadType); } } return rv; } NS_IMETHODIMP nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry, int32_t aChildOffset, uint32_t aLoadType, bool aCloneChildren) { nsresult rv = NS_OK; if (mLSHE && aLoadType != LOAD_PUSHSTATE) { /* You get here if you are currently building a * hierarchy ie.,you just visited a frameset page */ if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) { rv = mLSHE->AddChild(aNewEntry, aChildOffset); } } else if (!aCloneRef) { /* This is an initial load in some subframe. Just append it if we can */ if (mOSHE) { rv = mOSHE->AddChild(aNewEntry, aChildOffset); } } else { rv = AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset, aLoadType, aCloneChildren); } return rv; } nsresult nsDocShell::AddChildSHEntryInternal(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry, int32_t aChildOffset, uint32_t aLoadType, bool aCloneChildren) { nsresult rv = NS_OK; if (mSessionHistory) { /* You are currently in the rootDocShell. * You will get here when a subframe has a new url * to load and you have walked up the tree all the * way to the top to clone the current SHEntry hierarchy * and replace the subframe where a new url was loaded with * a new entry. */ nsCOMPtr currentHE; int32_t index = mSessionHistory->Index(); if (index < 0) { return NS_ERROR_FAILURE; } rv = mSessionHistory->LegacySHistory()->GetEntryAtIndex( index, getter_AddRefs(currentHE)); NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE); nsCOMPtr currentEntry(currentHE); if (currentEntry) { nsCOMPtr nextEntry; uint32_t cloneID = aCloneRef->GetID(); rv = nsSHistory::CloneAndReplace(currentEntry, this, cloneID, aNewEntry, aCloneChildren, getter_AddRefs(nextEntry)); if (NS_SUCCEEDED(rv)) { rv = mSessionHistory->LegacySHistory()->AddEntry(nextEntry, true); } } } else { /* Just pass this along */ nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent), &rv); if (parent) { rv = static_cast(parent.get()) ->AddChildSHEntryInternal(aCloneRef, aNewEntry, aChildOffset, aLoadType, aCloneChildren); } } return rv; } nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry, int32_t aChildOffset, bool aCloneChildren) { /* You will get here when you are in a subframe and * a new url has been loaded on you. * The mOSHE in this subframe will be the previous url's * mOSHE. This mOSHE will be used as the identification * for this subframe in the CloneAndReplace function. */ // In this case, we will end up calling AddEntry, which increases the // current index by 1 RefPtr rootSH = GetRootSessionHistory(); if (rootSH) { mPreviousEntryIndex = rootSH->Index(); } nsresult rv; nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent), &rv); if (parent) { rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren); } if (rootSH) { mLoadedEntryIndex = rootSH->Index(); if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) { MOZ_LOG(gPageCacheLog, LogLevel::Verbose, ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex, mLoadedEntryIndex)); } } return rv; } NS_IMETHODIMP nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory) { mUseGlobalHistory = aUseGlobalHistory; if (!aUseGlobalHistory) { return NS_OK; } nsCOMPtr history = services::GetHistoryService(); return history ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::GetUseGlobalHistory(bool* aUseGlobalHistory) { *aUseGlobalHistory = mUseGlobalHistory; return NS_OK; } NS_IMETHODIMP nsDocShell::RemoveFromSessionHistory() { nsCOMPtr root; GetInProcessSameTypeRootTreeItem(getter_AddRefs(root)); nsCOMPtr rootAsWebnav = do_QueryInterface(root); if (!rootAsWebnav) { return NS_OK; } RefPtr sessionHistory = rootAsWebnav->GetSessionHistory(); if (!sessionHistory) { return NS_OK; } int32_t index = sessionHistory->Index(); AutoTArray ids({mHistoryID}); sessionHistory->LegacySHistory()->RemoveEntries(ids, index); return NS_OK; } NS_IMETHODIMP nsDocShell::SetCreatedDynamically(bool aDynamic) { mDynamicallyCreated = aDynamic; return NS_OK; } NS_IMETHODIMP nsDocShell::GetCreatedDynamically(bool* aDynamic) { *aDynamic = mDynamicallyCreated; return NS_OK; } NS_IMETHODIMP nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) { *aOSHE = false; *aEntry = nullptr; if (mLSHE) { NS_ADDREF(*aEntry = mLSHE); } else if (mOSHE) { NS_ADDREF(*aEntry = mOSHE); *aOSHE = true; } return NS_OK; } nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() { NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr); return mScriptGlobal; } Document* nsDocShell::GetDocument() { NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr); return mContentViewer->GetDocument(); } nsPIDOMWindowOuter* nsDocShell::GetWindow() { if (NS_FAILED(EnsureScriptEnvironment())) { return nullptr; } return mScriptGlobal; } NS_IMETHODIMP nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) { NS_ENSURE_ARG_POINTER(aWindow); nsresult rv = EnsureScriptEnvironment(); NS_ENSURE_SUCCESS(rv, rv); RefPtr window = mScriptGlobal; window.forget(aWindow); return NS_OK; } NS_IMETHODIMP nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { RefPtr mm; if (RefPtr browserChild = BrowserChild::GetFrom(this)) { mm = browserChild->GetMessageManager(); } else if (nsPIDOMWindowOuter* win = GetWindow()) { mm = win->GetMessageManager(); } mm.forget(aMessageManager); return NS_OK; } NS_IMETHODIMP nsDocShell::GetContentBlockingLog(Promise** aPromise) { NS_ENSURE_ARG_POINTER(aPromise); if (!mContentViewer) { *aPromise = nullptr; return NS_ERROR_FAILURE; } Document* doc = mContentViewer->GetDocument(); ErrorResult rv; RefPtr promise = Promise::Create(doc->GetOwnerGlobal(), rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } promise->MaybeResolve( NS_ConvertUTF8toUTF16(doc->GetContentBlockingLog()->Stringify())); promise.forget(aPromise); return NS_OK; } NS_IMETHODIMP nsDocShell::GetIsNavigating(bool* aOut) { *aOut = mIsNavigating; return NS_OK; } NS_IMETHODIMP nsDocShell::SetDeviceSizeIsPageSize(bool aValue) { if (mDeviceSizeIsPageSize != aValue) { mDeviceSizeIsPageSize = aValue; RefPtr presContext = GetPresContext(); if (presContext) { presContext->MediaFeatureValuesChanged( {MediaFeatureChangeReason::DeviceSizeIsPageSizeChange}); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) { *aValue = mDeviceSizeIsPageSize; return NS_OK; } void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) { RefPtr rootSH = GetRootSessionHistory(); if (!rootSH || !aEntry) { return; } int32_t count = aEntry->GetChildCount(); AutoTArray ids; for (int32_t i = 0; i < count; ++i) { nsCOMPtr child; aEntry->GetChildAt(i, getter_AddRefs(child)); if (child) { ids.AppendElement(child->DocshellID()); } } int32_t index = rootSH->Index(); rootSH->LegacySHistory()->RemoveEntries(ids, index); } //------------------------------------- //-- Helper Method for Print discovery //------------------------------------- bool nsDocShell::IsPrintingOrPP(bool aDisplayErrorDialog) { if (mIsPrintingOrPP && aDisplayErrorDialog) { DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr); } return mIsPrintingOrPP; } bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog, bool aCheckIfUnloadFired) { bool isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) && (!aCheckIfUnloadFired || !mFiredUnloadEvent); if (!isAllowed) { return false; } if (!mContentViewer) { return true; } bool firingBeforeUnload; mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload); return !firingBeforeUnload; } //***************************************************************************** // nsDocShell::nsIWebNavigation //***************************************************************************** NS_IMETHODIMP nsDocShell::GetCanGoBack(bool* aCanGoBack) { *aCanGoBack = false; if (!IsNavigationAllowed(false)) { return NS_OK; // JS may not handle returning of an error code } RefPtr rootSH = GetRootSessionHistory(); if (rootSH) { *aCanGoBack = rootSH->CanGo(-1); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::GetCanGoForward(bool* aCanGoForward) { *aCanGoForward = false; if (!IsNavigationAllowed(false)) { return NS_OK; // JS may not handle returning of an error code } RefPtr rootSH = GetRootSessionHistory(); if (rootSH) { *aCanGoForward = rootSH->CanGo(1); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::GoBack() { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); mIsNavigating = true; RefPtr rootSH = GetRootSessionHistory(); NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE); ErrorResult rv; rootSH->Go(-1, rv); return rv.StealNSResult(); } NS_IMETHODIMP nsDocShell::GoForward() { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); mIsNavigating = true; RefPtr rootSH = GetRootSessionHistory(); NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE); ErrorResult rv; rootSH->Go(1, rv); return rv.StealNSResult(); } // XXX(nika): We may want to stop exposing this API in the child process? Going // to a specific index from multiple different processes could definitely race. NS_IMETHODIMP nsDocShell::GotoIndex(int32_t aIndex) { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); mIsNavigating = true; RefPtr rootSH = GetRootSessionHistory(); NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE); return rootSH->LegacySHistory()->GotoIndex(aIndex); } nsresult nsDocShell::LoadURI(const nsAString& aURI, const LoadURIOptions& aLoadURIOptions) { uint32_t loadFlags = aLoadURIOptions.mLoadFlags; NS_ASSERTION((loadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0, "Unexpected flags"); if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); mIsNavigating = true; nsCOMPtr uri; nsCOMPtr postData(aLoadURIOptions.mPostData); nsresult rv = NS_OK; NS_ConvertUTF16toUTF8 uriString(aURI); // Cleanup the empty spaces that might be on each end. uriString.Trim(" "); // Eliminate embedded newlines, which single-line text fields now allow: uriString.StripCRLF(); NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE); if (mUseStrictSecurityChecks && !aLoadURIOptions.mTriggeringPrincipal) { return NS_ERROR_FAILURE; } nsCOMPtr fixupInfo; if (sURIFixup) { uint32_t fixupFlags; rv = sURIFixup->WebNavigationFlagsToFixupFlags(uriString, loadFlags, &fixupFlags); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); // If we don't allow keyword lookups for this URL string, make sure to // update loadFlags to indicate this as well. if (!(fixupFlags & nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) { loadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; } nsCOMPtr fixupStream; rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags, getter_AddRefs(fixupStream), getter_AddRefs(fixupInfo)); if (NS_SUCCEEDED(rv)) { fixupInfo->GetPreferredURI(getter_AddRefs(uri)); fixupInfo->SetConsumer(GetAsSupports(this)); } if (fixupStream) { // GetFixupURIInfo only returns a post data stream if it succeeded // and changed the URI, in which case we should override the // passed-in post data. postData = fixupStream; } if (loadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { nsCOMPtr serv = services::GetObserverService(); if (serv) { serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", PromiseFlatString(aURI).get()); } } } else { // No fixup service so just create a URI and see what happens... rv = NS_NewURI(getter_AddRefs(uri), uriString); loadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; } if (NS_ERROR_MALFORMED_URI == rv) { if (DisplayLoadError(rv, uri, PromiseFlatString(aURI).get(), nullptr) && (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) { return NS_ERROR_LOAD_SHOWED_ERRORPAGE; } } if (NS_FAILED(rv) || !uri) { return NS_ERROR_FAILURE; } PopupBlocker::PopupControlState popupState; if (loadFlags & LOAD_FLAGS_ALLOW_POPUPS) { popupState = PopupBlocker::openAllowed; loadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS; } else { popupState = PopupBlocker::openOverridden; } AutoPopupStatePusher statePusher(popupState); bool forceAllowDataURI = loadFlags & LOAD_FLAGS_FORCE_ALLOW_DATA_URI; // Don't pass certain flags that aren't needed and end up confusing // ConvertLoadTypeToDocShellInfoLoadType. We do need to ensure that they are // passed to LoadURI though, since it uses them. uint32_t extraFlags = (loadFlags & EXTRA_LOAD_FLAGS); loadFlags &= ~EXTRA_LOAD_FLAGS; RefPtr loadState = new nsDocShellLoadState(uri); loadState->SetReferrerInfo(aLoadURIOptions.mReferrerInfo); /* * If the user "Disables Protection on This Page", we have to make sure to * remember the users decision when opening links in child tabs [Bug 906190] */ if (loadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) { loadState->SetLoadType( MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, loadFlags)); } else { loadState->SetLoadType(MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags)); } loadState->SetLoadFlags(extraFlags); loadState->SetFirstParty(true); loadState->SetPostDataStream(postData); loadState->SetHeadersStream(aLoadURIOptions.mHeaders); loadState->SetBaseURI(aLoadURIOptions.mBaseURI); loadState->SetTriggeringPrincipal(aLoadURIOptions.mTriggeringPrincipal); loadState->SetCsp(aLoadURIOptions.mCsp); loadState->SetForceAllowDataURI(forceAllowDataURI); if (fixupInfo) { nsAutoString searchProvider, keyword; fixupInfo->GetKeywordProviderName(searchProvider); fixupInfo->GetKeywordAsSent(keyword); MaybeNotifyKeywordSearchLoading(searchProvider, keyword); } rv = LoadURI(loadState); // Save URI string in case it's needed later when // sending to search engine service in EndPageLoad() mOriginalUriString = uriString; return rv; } NS_IMETHODIMP nsDocShell::LoadURIFromScript(const nsAString& aURI, JS::Handle aLoadURIOptions, JSContext* aCx) { // generate dictionary for aLoadURIOptions and forward call LoadURIOptions loadURIOptions; if (!loadURIOptions.Init(aCx, aLoadURIOptions)) { return NS_ERROR_INVALID_ARG; } return LoadURI(aURI, loadURIOptions); } NS_IMETHODIMP nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL, nsIChannel* aFailedChannel, bool* aDisplayedErrorPage) { *aDisplayedErrorPage = false; // Get prompt and string bundle services nsCOMPtr prompter; nsCOMPtr stringBundle; GetPromptAndStringBundle(getter_AddRefs(prompter), getter_AddRefs(stringBundle)); NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE); NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE); const char* error = nullptr; // The key used to select the appropriate error message from the properties // file. const char* errorDescriptionID = nullptr; AutoTArray formatStrs; bool addHostPort = false; nsresult rv = NS_OK; nsAutoString messageStr; nsAutoCString cssClass; nsAutoCString errorPage; errorPage.AssignLiteral("neterror"); // Turn the error code into a human readable error message. if (NS_ERROR_UNKNOWN_PROTOCOL == aError) { NS_ENSURE_ARG_POINTER(aURI); // Extract the schemes into a comma delimited list. nsAutoCString scheme; aURI->GetScheme(scheme); CopyASCIItoUTF16(scheme, *formatStrs.AppendElement()); nsCOMPtr nestedURI = do_QueryInterface(aURI); while (nestedURI) { nsCOMPtr tempURI; nsresult rv2; rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI)); if (NS_SUCCEEDED(rv2) && tempURI) { tempURI->GetScheme(scheme); formatStrs[0].AppendLiteral(", "); AppendASCIItoUTF16(scheme, formatStrs[0]); } nestedURI = do_QueryInterface(tempURI); } error = "unknownProtocolFound"; } else if (NS_ERROR_FILE_NOT_FOUND == aError) { NS_ENSURE_ARG_POINTER(aURI); error = "fileNotFound"; } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) { NS_ENSURE_ARG_POINTER(aURI); error = "fileAccessDenied"; } else if (NS_ERROR_UNKNOWN_HOST == aError) { NS_ENSURE_ARG_POINTER(aURI); // Get the host nsAutoCString host; nsCOMPtr innermostURI = NS_GetInnermostURI(aURI); innermostURI->GetHost(host); CopyUTF8toUTF16(host, *formatStrs.AppendElement()); errorDescriptionID = "dnsNotFound2"; error = "dnsNotFound"; } else if (NS_ERROR_CONNECTION_REFUSED == aError || NS_ERROR_PROXY_BAD_GATEWAY == aError) { NS_ENSURE_ARG_POINTER(aURI); addHostPort = true; error = "connectionFailure"; } else if (NS_ERROR_NET_INTERRUPT == aError) { NS_ENSURE_ARG_POINTER(aURI); addHostPort = true; error = "netInterrupt"; } else if (NS_ERROR_NET_TIMEOUT == aError || NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError) { NS_ENSURE_ARG_POINTER(aURI); // Get the host nsAutoCString host; aURI->GetHost(host); CopyUTF8toUTF16(host, *formatStrs.AppendElement()); error = "netTimeout"; } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError || NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) { // CSP error cssClass.AssignLiteral("neterror"); error = "cspBlocked"; } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) { nsCOMPtr nsserr = do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID); uint32_t errorClass; if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) { errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL; } nsCOMPtr securityInfo; nsCOMPtr tsi; if (aFailedChannel) { aFailedChannel->GetSecurityInfo(getter_AddRefs(securityInfo)); } tsi = do_QueryInterface(securityInfo); if (tsi) { uint32_t securityState; tsi->GetSecurityState(&securityState); if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) { error = "sslv3Used"; addHostPort = true; } else if (securityState & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) { error = "weakCryptoUsed"; addHostPort = true; } } else { // No channel, let's obtain the generic error message if (nsserr) { nsserr->GetErrorMessage(aError, messageStr); } } // We don't have a message string here anymore but DisplayLoadError // requires a non-empty messageStr. messageStr.Truncate(); messageStr.AssignLiteral(u" "); if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) { error = "nssBadCert"; // If this is an HTTP Strict Transport Security host or a pinned host // and the certificate is bad, don't allow overrides (RFC 6797 section // 12.1, HPKP draft spec section 2.6). uint32_t flags = UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; bool isStsHost = false; bool isPinnedHost = false; if (XRE_IsParentProcess()) { nsCOMPtr sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags, mOriginAttributes, nullptr, nullptr, &isStsHost); NS_ENSURE_SUCCESS(rv, rv); rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI, flags, mOriginAttributes, nullptr, nullptr, &isPinnedHost); NS_ENSURE_SUCCESS(rv, rv); } else { mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); mozilla::ipc::URIParams uri; SerializeURI(aURI, uri); cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags, mOriginAttributes, &isStsHost); cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags, mOriginAttributes, &isPinnedHost); } if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert", false)) { cssClass.AssignLiteral("expertBadCert"); } // HSTS/pinning takes precedence over the expert bad cert pref. We // never want to show the "Add Exception" button for these sites. // In the future we should differentiate between an HSTS host and a // pinned host and display a more informative message to the user. if (isStsHost || isPinnedHost) { cssClass.AssignLiteral("badStsCert"); } // See if an alternate cert error page is registered nsAutoCString alternateErrorPage; nsresult rv = Preferences::GetCString( "security.alternate_certificate_error_page", alternateErrorPage); if (NS_SUCCEEDED(rv)) { errorPage.Assign(alternateErrorPage); } } else { error = "nssFailure2"; } } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError || NS_ERROR_HARMFUL_URI == aError) { nsAutoCString host; aURI->GetHost(host); CopyUTF8toUTF16(host, *formatStrs.AppendElement()); // Malware and phishing detectors may want to use an alternate error // page, but if the pref's not set, we'll fall back on the standard page nsAutoCString alternateErrorPage; nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page", alternateErrorPage); if (NS_SUCCEEDED(rv)) { errorPage.Assign(alternateErrorPage); } uint32_t bucketId; bool sendTelemetry = false; if (NS_ERROR_PHISHING_URI == aError) { sendTelemetry = true; error = "deceptiveBlocked"; bucketId = IsFrame() ? IUrlClassifierUITelemetry::WARNING_PHISHING_PAGE_FRAME : IUrlClassifierUITelemetry::WARNING_PHISHING_PAGE_TOP; } else if (NS_ERROR_MALWARE_URI == aError) { sendTelemetry = true; error = "malwareBlocked"; bucketId = IsFrame() ? IUrlClassifierUITelemetry::WARNING_MALWARE_PAGE_FRAME : IUrlClassifierUITelemetry::WARNING_MALWARE_PAGE_TOP; } else if (NS_ERROR_UNWANTED_URI == aError) { sendTelemetry = true; error = "unwantedBlocked"; bucketId = IsFrame() ? IUrlClassifierUITelemetry::WARNING_UNWANTED_PAGE_FRAME : IUrlClassifierUITelemetry::WARNING_UNWANTED_PAGE_TOP; } else if (NS_ERROR_HARMFUL_URI == aError) { sendTelemetry = true; error = "harmfulBlocked"; bucketId = IsFrame() ? IUrlClassifierUITelemetry::WARNING_HARMFUL_PAGE_FRAME : IUrlClassifierUITelemetry::WARNING_HARMFUL_PAGE_TOP; } if (sendTelemetry && errorPage.EqualsIgnoreCase("blocked")) { Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UI_EVENTS, bucketId); } cssClass.AssignLiteral("blacklist"); } else if (NS_ERROR_CONTENT_CRASHED == aError) { errorPage.AssignLiteral("tabcrashed"); error = "tabcrashed"; RefPtr handler = mChromeEventHandler; if (handler) { nsCOMPtr element = do_QueryInterface(handler); element->GetAttribute(u"crashedPageTitle"_ns, messageStr); } // DisplayLoadError requires a non-empty messageStr to proceed and call // LoadErrorPage. If the page doesn't have a title, we will use a blank // space which will be trimmed and thus treated as empty by the front-end. if (messageStr.IsEmpty()) { messageStr.AssignLiteral(u" "); } } else if (NS_ERROR_FRAME_CRASHED == aError) { errorPage.AssignLiteral("framecrashed"); error = "framecrashed"; messageStr.AssignLiteral(u" "); } else if (NS_ERROR_BUILDID_MISMATCH == aError) { errorPage.AssignLiteral("restartrequired"); error = "restartrequired"; // DisplayLoadError requires a non-empty messageStr to proceed and call // LoadErrorPage. If the page doesn't have a title, we will use a blank // space which will be trimmed and thus treated as empty by the front-end. if (messageStr.IsEmpty()) { messageStr.AssignLiteral(u" "); } } else { // Errors requiring simple formatting switch (aError) { case NS_ERROR_MALFORMED_URI: // URI is malformed error = "malformedURI"; errorDescriptionID = "malformedURI2"; break; case NS_ERROR_REDIRECT_LOOP: // Doc failed to load because the server generated too many redirects error = "redirectLoop"; break; case NS_ERROR_UNKNOWN_SOCKET_TYPE: // Doc failed to load because PSM is not installed error = "unknownSocketType"; break; case NS_ERROR_NET_RESET: // Doc failed to load because the server kept reseting the connection // before we could read any data from it error = "netReset"; break; case NS_ERROR_DOCUMENT_NOT_CACHED: // Doc failed to load because the cache does not contain a copy of // the document. error = "notCached"; break; case NS_ERROR_OFFLINE: // Doc failed to load because we are offline. error = "netOffline"; break; case NS_ERROR_DOCUMENT_IS_PRINTMODE: // Doc navigation attempted while Printing or Print Preview error = "isprinting"; break; case NS_ERROR_PORT_ACCESS_NOT_ALLOWED: // Port blocked for security reasons addHostPort = true; error = "deniedPortAccess"; break; case NS_ERROR_UNKNOWN_PROXY_HOST: // Proxy hostname could not be resolved. error = "proxyResolveFailure"; break; case NS_ERROR_PROXY_CONNECTION_REFUSED: case NS_ERROR_PROXY_AUTHENTICATION_FAILED: case NS_ERROR_TOO_MANY_REQUESTS: // Proxy connection was refused. error = "proxyConnectFailure"; break; case NS_ERROR_INVALID_CONTENT_ENCODING: // Bad Content Encoding. error = "contentEncodingError"; break; case NS_ERROR_REMOTE_XUL: error = "remoteXUL"; break; case NS_ERROR_UNSAFE_CONTENT_TYPE: // Channel refused to load from an unrecognized content type. error = "unsafeContentType"; break; case NS_ERROR_CORRUPTED_CONTENT: // Broken Content Detected. e.g. Content-MD5 check failure. error = "corruptedContentErrorv2"; break; case NS_ERROR_INTERCEPTION_FAILED: // ServiceWorker intercepted request, but something went wrong. error = "corruptedContentErrorv2"; break; case NS_ERROR_NET_INADEQUATE_SECURITY: // Server negotiated bad TLS for HTTP/2. error = "inadequateSecurityError"; addHostPort = true; break; case NS_ERROR_BLOCKED_BY_POLICY: // Page blocked by policy error = "blockedByPolicy"; break; case NS_ERROR_NET_HTTP2_SENT_GOAWAY: // HTTP/2 stack detected a protocol error error = "networkProtocolError"; break; default: break; } } if (mLoadURIDelegate) { nsCOMPtr errorPageURI; rv = mLoadURIDelegate->HandleLoadError(aURI, aError, NS_ERROR_GET_MODULE(aError), getter_AddRefs(errorPageURI)); if (NS_FAILED(rv)) { *aDisplayedErrorPage = false; return NS_OK; } if (errorPageURI) { *aDisplayedErrorPage = NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel)); return NS_OK; } } // Test if the error should be displayed if (!error) { return NS_OK; } if (!errorDescriptionID) { errorDescriptionID = error; } // Test if the error needs to be formatted if (!messageStr.IsEmpty()) { // already obtained message } else { if (addHostPort) { // Build up the host:port string. nsAutoCString hostport; if (aURI) { aURI->GetHostPort(hostport); } else { hostport.Assign('?'); } CopyUTF8toUTF16(hostport, *formatStrs.AppendElement()); } nsAutoCString spec; rv = NS_ERROR_NOT_AVAILABLE; auto& nextFormatStr = *formatStrs.AppendElement(); if (aURI) { // displaying "file://" is aesthetically unpleasing and could even be // confusing to the user if (SchemeIsFile(aURI)) { aURI->GetPathQueryRef(spec); } else { aURI->GetSpec(spec); } nsCOMPtr textToSubURI( do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr); } } else { spec.Assign('?'); } if (NS_FAILED(rv)) { CopyUTF8toUTF16(spec, nextFormatStr); } rv = NS_OK; nsAutoString str; rv = stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str); NS_ENSURE_SUCCESS(rv, rv); messageStr.Assign(str); } // Display the error as a page or an alert prompt NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE); if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) && SchemeIsHTTPS(aURI)) { // Maybe TLS intolerant. Treat this as an SSL error. error = "nssFailure2"; } if (UseErrorPages()) { // Display an error page nsresult loadedPage = LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(), cssClass.get(), aFailedChannel); *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage); } else { // The prompter reqires that our private window has a document (or it // asserts). Satisfy that assertion now since GetDoc will force // creation of one if it hasn't already been created. if (mScriptGlobal) { Unused << mScriptGlobal->GetDoc(); } // Display a message box prompter->Alert(nullptr, messageStr.get()); } return NS_OK; } #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride" nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, const char* aErrorPage, const char* aErrorType, const char16_t* aDescription, const char* aCSSClass, nsIChannel* aFailedChannel) { MOZ_ASSERT(!mIsBeingDestroyed); #if defined(DEBUG) if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) { nsAutoCString chanName; if (aFailedChannel) { aFailedChannel->GetName(chanName); } else { chanName.AssignLiteral(""); } MOZ_LOG(gDocShellLog, LogLevel::Debug, ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this, aURI ? aURI->GetSpecOrDefault().get() : "", NS_ConvertUTF16toUTF8(aURL).get(), chanName.get())); } #endif nsAutoCString url; if (aURI) { nsresult rv = aURI->GetSpec(url); NS_ENSURE_SUCCESS(rv, rv); } else if (aURL) { CopyUTF16toUTF8(MakeStringSpan(aURL), url); } else { return NS_ERROR_INVALID_POINTER; } // Create a URL to pass all the error information through to the page. #undef SAFE_ESCAPE #define SAFE_ESCAPE(output, input, params) \ if (NS_WARN_IF(!NS_Escape(input, output, params))) { \ return NS_ERROR_OUT_OF_MEMORY; \ } nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass; SAFE_ESCAPE(escapedUrl, url, url_Path); SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path); SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription), url_Path); if (aCSSClass) { nsCString cssClass(aCSSClass); SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path); } nsCString errorPageUrl("about:"); errorPageUrl.AppendASCII(aErrorPage); errorPageUrl.AppendLiteral("?e="); errorPageUrl.AppendASCII(escapedError.get()); errorPageUrl.AppendLiteral("&u="); errorPageUrl.AppendASCII(escapedUrl.get()); if ((strcmp(aErrorPage, "blocked") == 0) && Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) { errorPageUrl.AppendLiteral("&o=1"); } if (!escapedCSSClass.IsEmpty()) { errorPageUrl.AppendLiteral("&s="); errorPageUrl.AppendASCII(escapedCSSClass.get()); } errorPageUrl.AppendLiteral("&c=UTF-8"); nsCOMPtr cps = do_GetService(NS_CAPTIVEPORTAL_CID); int32_t cpsState; if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) && cpsState == nsICaptivePortalService::LOCKED_PORTAL) { errorPageUrl.AppendLiteral("&captive=true"); } // netError.xhtml's getDescription only handles the "d" parameter at the // end of the URL, so append it last. errorPageUrl.AppendLiteral("&d="); errorPageUrl.AppendASCII(escapedDescription.get()); nsCOMPtr errorPageURI; nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); NS_ENSURE_SUCCESS(rv, rv); return LoadErrorPage(errorPageURI, aURI, aFailedChannel); } nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI, nsIChannel* aFailedChannel) { mFailedChannel = aFailedChannel; mFailedURI = aFailedURI; mFailedLoadType = mLoadType; if (mLSHE) { // Abandon mLSHE's BFCache entry and create a new one. This way, if // we go back or forward to another SHEntry with the same doc // identifier, the error page won't persist. mLSHE->AbandonBFCacheEntry(); } RefPtr loadState = new nsDocShellLoadState(aErrorURI); loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); loadState->SetLoadType(LOAD_ERROR_PAGE); loadState->SetFirstParty(true); loadState->SetSourceDocShell(this); return InternalLoad(loadState, nullptr, nullptr); } NS_IMETHODIMP nsDocShell::Reload(uint32_t aReloadFlags) { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } nsresult rv; NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0), "Reload command not updated to use load flags!"); NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0, "Don't pass these flags to Reload"); uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags); NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG); // Send notifications to the HistoryListener if any, about the impending // reload RefPtr rootSH = GetRootSessionHistory(); bool canReload = true; if (rootSH) { rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload); } if (!canReload) { return NS_OK; } /* If you change this part of code, make sure bug 45297 does not re-occur */ if (mOSHE) { rv = LoadHistoryEntry(mOSHE, loadType); } else if (mLSHE) { // In case a reload happened before the current load is // done rv = LoadHistoryEntry(mLSHE, loadType); } else { RefPtr doc(GetDocument()); if (!doc) { return NS_OK; } // Do not inherit owner from document uint32_t flags = INTERNAL_LOAD_FLAGS_NONE; nsAutoString srcdoc; nsIURI* baseURI = nullptr; nsCOMPtr originalURI; nsCOMPtr resultPrincipalURI; bool loadReplace = false; nsIPrincipal* triggeringPrincipal = doc->NodePrincipal(); nsCOMPtr csp = doc->GetCsp(); nsAutoString contentTypeHint; doc->GetContentType(contentTypeHint); if (doc->IsSrcdocDocument()) { doc->GetSrcdocData(srcdoc); flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; baseURI = doc->GetBaseURI(); } nsCOMPtr chan = doc->GetChannel(); if (chan) { uint32_t loadFlags; chan->GetLoadFlags(&loadFlags); loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; nsCOMPtr httpChan(do_QueryInterface(chan)); if (httpChan) { httpChan->GetOriginalURI(getter_AddRefs(originalURI)); } nsCOMPtr loadInfo = chan->LoadInfo(); loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)); } MOZ_ASSERT(triggeringPrincipal, "Need a valid triggeringPrincipal"); if (mUseStrictSecurityChecks && !triggeringPrincipal) { return NS_ERROR_FAILURE; } // Stack variables to ensure changes to the member variables don't affect to // the call. nsCOMPtr currentURI = mCurrentURI; // Reload always rewrites result principal URI. Maybe> emplacedResultPrincipalURI; emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI)); RefPtr loadState = new nsDocShellLoadState(currentURI); loadState->SetReferrerInfo(mReferrerInfo); loadState->SetOriginalURI(originalURI); loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI); loadState->SetLoadReplace(loadReplace); loadState->SetTriggeringPrincipal(triggeringPrincipal); loadState->SetPrincipalToInherit(triggeringPrincipal); loadState->SetCsp(csp); loadState->SetLoadFlags(flags); loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint)); loadState->SetLoadType(loadType); loadState->SetFirstParty(true); loadState->SetSrcdocData(srcdoc); loadState->SetSourceDocShell(this); loadState->SetBaseURI(baseURI); rv = InternalLoad(loadState, nullptr, nullptr); } return rv; } NS_IMETHODIMP nsDocShell::Stop(uint32_t aStopFlags) { // Revoke any pending event related to content viewer restoration mRestorePresentationEvent.Revoke(); if (mLoadType == LOAD_ERROR_PAGE) { if (mLSHE) { // Since error page loads never unset mLSHE, do so now SetHistoryEntry(&mOSHE, mLSHE); SetHistoryEntry(&mLSHE, nullptr); } mFailedChannel = nullptr; mFailedURI = nullptr; } if (nsIWebNavigation::STOP_CONTENT & aStopFlags) { // Stop the document loading if (mContentViewer) { nsCOMPtr cv = mContentViewer; cv->Stop(); } } if (nsIWebNavigation::STOP_NETWORK & aStopFlags) { // Suspend any timers that were set for this loader. We'll clear // them out for good in CreateContentViewer. if (mRefreshURIList) { SuspendRefreshURIs(); mSavedRefreshURIList.swap(mRefreshURIList); mRefreshURIList = nullptr; } // XXXbz We could also pass |this| to nsIURILoader::Stop. That will // just call Stop() on us as an nsIDocumentLoader... We need fewer // redundant apis! Stop(); } nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shellAsNav(do_QueryObject(iter.GetNext())); if (shellAsNav) { shellAsNav->Stop(aStopFlags); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetDocument(Document** aDocument) { NS_ENSURE_ARG_POINTER(aDocument); NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE); RefPtr doc = mContentViewer->GetDocument(); if (!doc) { return NS_ERROR_NOT_AVAILABLE; } doc.forget(aDocument); return NS_OK; } NS_IMETHODIMP nsDocShell::GetCurrentURI(nsIURI** aURI) { NS_ENSURE_ARG_POINTER(aURI); nsCOMPtr uri = mCurrentURI; uri.forget(aURI); return NS_OK; } NS_IMETHODIMP nsDocShell::InitSessionHistory() { MOZ_ASSERT(!mIsBeingDestroyed); // Make sure that we are the root DocShell, and set a handle to root docshell // in the session history. nsCOMPtr root; GetInProcessSameTypeRootTreeItem(getter_AddRefs(root)); if (root != this) { return NS_ERROR_FAILURE; } mSessionHistory = new ChildSHistory(this); return NS_OK; } NS_IMETHODIMP nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) { NS_ENSURE_ARG_POINTER(aSessionHistory); RefPtr shistory = mSessionHistory; shistory.forget(aSessionHistory); return NS_OK; } //***************************************************************************** // nsDocShell::nsIWebPageDescriptor //***************************************************************************** NS_IMETHODIMP nsDocShell::LoadPage(nsISupports* aPageDescriptor, uint32_t aDisplayType) { nsCOMPtr shEntryIn(do_QueryInterface(aPageDescriptor)); // Currently, the opaque 'page descriptor' is an nsISHEntry... if (!shEntryIn) { return NS_ERROR_INVALID_POINTER; } // Now clone shEntryIn, since we might end up modifying it later on, and we // want a page descriptor to be reusable. nsCOMPtr shEntry; nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry)); NS_ENSURE_SUCCESS(rv, rv); // Give our cloned shEntry a new bfcache entry so this load is independent // of all other loads. (This is important, in particular, for bugs 582795 // and 585298.) rv = shEntry->AbandonBFCacheEntry(); NS_ENSURE_SUCCESS(rv, rv); // // load the page as view-source // if (nsIWebPageDescriptor::DISPLAY_AS_SOURCE == aDisplayType) { nsCString spec, newSpec; // Create a new view-source URI and replace the original. nsCOMPtr oldUri = shEntry->GetURI(); oldUri->GetSpec(spec); newSpec.AppendLiteral("view-source:"); newSpec.Append(spec); nsCOMPtr newUri; rv = NS_NewURI(getter_AddRefs(newUri), newSpec); if (NS_FAILED(rv)) { return rv; } shEntry->SetURI(newUri); shEntry->SetOriginalURI(nullptr); shEntry->SetResultPrincipalURI(nullptr); // shEntry's current triggering principal is whoever loaded that page // initially. But now we're doing another load of the page, via an API that // is only exposed to system code. The triggering principal for this load // should be the system principal. shEntry->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); } rv = LoadHistoryEntry(shEntry, LOAD_HISTORY); return rv; } NS_IMETHODIMP nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) { MOZ_ASSERT(aPageDescriptor, "Null out param?"); *aPageDescriptor = nullptr; nsISHEntry* src = mOSHE ? mOSHE : mLSHE; if (src) { nsCOMPtr dest; nsresult rv = src->Clone(getter_AddRefs(dest)); if (NS_FAILED(rv)) { return rv; } // null out inappropriate cloned attributes... dest->SetParent(nullptr); dest->SetIsSubFrame(false); return CallQueryInterface(dest, aPageDescriptor); } return NS_ERROR_NOT_AVAILABLE; } //***************************************************************************** // nsDocShell::nsIBaseWindow //***************************************************************************** NS_IMETHODIMP nsDocShell::InitWindow(nativeWindow aParentNativeWindow, nsIWidget* aParentWidget, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight) { SetParentWidget(aParentWidget); SetPositionAndSize(aX, aY, aWidth, aHeight, 0); return NS_OK; } NS_IMETHODIMP nsDocShell::Create() { if (mCreated) { // We've already been created return NS_OK; } NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE); mCreated = true; if (gValidateOrigin == 0xffffffff) { // Check pref to see if we should prevent frameset spoofing gValidateOrigin = Preferences::GetBool("browser.frame.validate_origin", true); } mUseStrictSecurityChecks = Preferences::GetBool( "security.strict_security_checks.enabled", mUseStrictSecurityChecks); // Should we use XUL error pages instead of alerts if possible? mUseErrorPages = StaticPrefs::browser_xul_error_pages_enabled(); mDisableMetaRefreshWhenInactive = Preferences::GetBool("browser.meta_refresh_when_inactive.disabled", mDisableMetaRefreshWhenInactive); mDeviceSizeIsPageSize = Preferences::GetBool( "docshell.device_size_is_page_size", mDeviceSizeIsPageSize); nsCOMPtr serv = services::GetObserverService(); if (serv) { const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE : NS_CHROME_WEBNAVIGATION_CREATE; serv->NotifyObservers(GetAsSupports(this), msg, nullptr); } return NS_OK; } NS_IMETHODIMP nsDocShell::Destroy() { // XXX: We allow this function to be called just once. If you are going to // reset new variables in this function, please make sure the variables will // never be re-initialized. Adding assertions to check |mIsBeingDestroyed| // in the setter functions for the variables would be enough. if (mIsBeingDestroyed) { return NS_ERROR_DOCSHELL_DYING; } NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); AssertOriginAttributesMatchPrivateBrowsing(); nsCOMPtr serv = services::GetObserverService(); if (serv) { const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_DESTROY : NS_CHROME_WEBNAVIGATION_DESTROY; serv->NotifyObservers(GetAsSupports(this), msg, nullptr); } mIsBeingDestroyed = true; // Brak the cycle with the initial client, if present. mInitialClientSource.reset(); // Make sure we don't record profile timeline markers anymore SetRecordProfileTimelineMarkers(false); // Remove our pref observers if (mObserveErrorPages) { mObserveErrorPages = false; } // Make sure to blow away our mLoadingURI just in case. No loads // from inside this pagehide. mLoadingURI = nullptr; // Fire unload event before we blow anything away. (void)FirePageHideNotification(true); // Clear pointers to any detached nsEditorData that's lying // around in shistory entries. Breaks cycle. See bug 430921. if (mOSHE) { mOSHE->SetEditorData(nullptr); } if (mLSHE) { mLSHE->SetEditorData(nullptr); } // Note: mContentListener can be null if Init() failed and we're being // called from the destructor. if (mContentListener) { mContentListener->DropDocShellReference(); mContentListener->SetParentContentListener(nullptr); // Note that we do NOT set mContentListener to null here; that // way if someone tries to do a load in us after this point // the nsDSURIContentListener will block it. All of which // means that we should do this before calling Stop(), of // course. } // Stop any URLs that are currently being loaded... Stop(nsIWebNavigation::STOP_ALL); mEditorData = nullptr; // Save the state of the current document, before destroying the window. // This is needed to capture the state of a frameset when the new document // causes the frameset to be destroyed... PersistLayoutHistoryState(); // Remove this docshell from its parent's child list nsCOMPtr docShellParentAsItem = do_QueryInterface(GetAsSupports(mParent)); if (docShellParentAsItem) { docShellParentAsItem->RemoveChild(this); } if (mContentViewer) { mContentViewer->Close(nullptr); mContentViewer->Destroy(); mContentViewer = nullptr; } nsDocLoader::Destroy(); mParentWidget = nullptr; mCurrentURI = nullptr; if (mScriptGlobal) { mScriptGlobal->DetachFromDocShell(); mScriptGlobal = nullptr; } if (mSessionHistory) { // We want to destroy these content viewers now rather than // letting their destruction wait for the session history // entries to get garbage collected. (Bug 488394) mSessionHistory->EvictLocalContentViewers(); mSessionHistory = nullptr; } // This will be skipped in cases where we want to preserve the browsing // context between loads. if (!mSkipBrowsingContextDetachOnDestroy) { mBrowsingContext->Detach(); } SetTreeOwner(nullptr); mBrowserChild = nullptr; mChromeEventHandler = nullptr; mOnePermittedSandboxedNavigator = nullptr; // required to break ref cycle mSecurityUI = nullptr; // Cancel any timers that were set for this docshell; this is needed // to break the cycle between us and the timers. CancelRefreshURITimers(); if (UsePrivateBrowsing()) { mPrivateBrowsingId = 0; mOriginAttributes.SyncAttributesWithPrivateBrowsing(false); if (mAffectPrivateSessionLifetime) { DecreasePrivateDocShellCount(); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) { if (mParentWidget) { *aScale = mParentWidget->GetDefaultScale().scale; return NS_OK; } nsCOMPtr ownerWindow(do_QueryInterface(mTreeOwner)); if (ownerWindow) { return ownerWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale); } *aScale = 1.0; return NS_OK; } NS_IMETHODIMP nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) { if (mParentWidget) { *aScale = mParentWidget->GetDesktopToDeviceScale().scale; return NS_OK; } nsCOMPtr ownerWindow(do_QueryInterface(mTreeOwner)); if (ownerWindow) { return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale); } *aScale = 1.0; return NS_OK; } NS_IMETHODIMP nsDocShell::SetPosition(int32_t aX, int32_t aY) { mBounds.MoveTo(aX, aY); if (mContentViewer) { NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE); } return NS_OK; } NS_IMETHODIMP nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) { nsCOMPtr ownerWindow(do_QueryInterface(mTreeOwner)); if (ownerWindow) { return ownerWindow->SetPositionDesktopPix(aX, aY); } double scale = 1.0; GetDevicePixelsPerDesktopPixel(&scale); return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale)); } NS_IMETHODIMP nsDocShell::GetPosition(int32_t* aX, int32_t* aY) { return GetPositionAndSize(aX, aY, nullptr, nullptr); } NS_IMETHODIMP nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) { int32_t x = 0, y = 0; GetPosition(&x, &y); return SetPositionAndSize(x, y, aWidth, aHeight, aRepaint ? nsIBaseWindow::eRepaint : 0); } NS_IMETHODIMP nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) { return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight); } NS_IMETHODIMP nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, uint32_t aFlags) { mBounds.SetRect(aX, aY, aWidth, aHeight); // Hold strong ref, since SetBounds can make us null out mContentViewer nsCOMPtr viewer = mContentViewer; if (viewer) { uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize) ? nsIContentViewer::eDelayResize : 0; // XXX Border figured in here or is that handled elsewhere? nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth, int32_t* aHeight) { if (mParentWidget) { // ensure size is up-to-date if window has changed resolution LayoutDeviceIntRect r = mParentWidget->GetClientBounds(); SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0); } // We should really consider just getting this information from // our window instead of duplicating the storage and code... if (aWidth || aHeight) { // Caller wants to know our size; make sure to give them up to // date information. RefPtr doc(do_GetInterface(GetAsSupports(mParent))); if (doc) { doc->FlushPendingNotifications(FlushType::Layout); } } DoGetPositionAndSize(aX, aY, aWidth, aHeight); return NS_OK; } void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth, int32_t* aHeight) { if (aX) { *aX = mBounds.X(); } if (aY) { *aY = mBounds.Y(); } if (aWidth) { *aWidth = mBounds.Width(); } if (aHeight) { *aHeight = mBounds.Height(); } } NS_IMETHODIMP nsDocShell::Repaint(bool aForce) { PresShell* presShell = GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); RefPtr viewManager = presShell->GetViewManager(); NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE); viewManager->InvalidateAllViews(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetParentWidget(nsIWidget** aParentWidget) { NS_ENSURE_ARG_POINTER(aParentWidget); *aParentWidget = mParentWidget; NS_IF_ADDREF(*aParentWidget); return NS_OK; } NS_IMETHODIMP nsDocShell::SetParentWidget(nsIWidget* aParentWidget) { MOZ_ASSERT(!mIsBeingDestroyed); mParentWidget = aParentWidget; return NS_OK; } NS_IMETHODIMP nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) { NS_ENSURE_ARG_POINTER(aParentNativeWindow); if (mParentWidget) { *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET); } else { *aParentNativeWindow = nullptr; } return NS_OK; } NS_IMETHODIMP nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetNativeHandle(nsAString& aNativeHandle) { // the nativeHandle should be accessed from nsIAppWindow return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetVisibility(bool* aVisibility) { NS_ENSURE_ARG_POINTER(aVisibility); *aVisibility = false; if (!mContentViewer) { return NS_OK; } PresShell* presShell = GetPresShell(); if (!presShell) { return NS_OK; } // get the view manager nsViewManager* vm = presShell->GetViewManager(); NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); // get the root view nsView* view = vm->GetRootView(); // views are not ref counted NS_ENSURE_TRUE(view, NS_ERROR_FAILURE); // if our root view is hidden, we are not visible if (view->GetVisibility() == nsViewVisibility_kHide) { return NS_OK; } // otherwise, we must walk up the document and view trees checking // for a hidden view, unless we're an off screen browser, which // would make this test meaningless. RefPtr docShell = this; RefPtr parentItem = docShell->GetInProcessParentDocshell(); while (parentItem) { // Null-check for crash in bug 267804 if (!parentItem->GetPresShell()) { MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell"); return NS_OK; } vm = docShell->GetPresShell()->GetViewManager(); if (vm) { view = vm->GetRootView(); } if (view) { view = view->GetParent(); // anonymous inner view if (view) { view = view->GetParent(); // subdocumentframe's view } } nsIFrame* frame = view ? view->GetFrame() : nullptr; bool isDocShellOffScreen = false; docShell->GetIsOffScreenBrowser(&isDocShellOffScreen); if (frame && !frame->IsVisibleConsideringAncestors( nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) && !isDocShellOffScreen) { return NS_OK; } docShell = parentItem; parentItem = docShell->GetInProcessParentDocshell(); } nsCOMPtr treeOwnerAsWin(do_QueryInterface(mTreeOwner)); if (!treeOwnerAsWin) { *aVisibility = true; return NS_OK; } // Check with the tree owner as well to give embedders a chance to // expose visibility as well. return treeOwnerAsWin->GetVisibility(aVisibility); } NS_IMETHODIMP nsDocShell::SetIsOffScreenBrowser(bool aIsOffScreen) { mIsOffScreenBrowser = aIsOffScreen; return NS_OK; } NS_IMETHODIMP nsDocShell::GetIsOffScreenBrowser(bool* aIsOffScreen) { *aIsOffScreen = mIsOffScreenBrowser; return NS_OK; } NS_IMETHODIMP nsDocShell::SetIsActive(bool aIsActive) { // Keep track ourselves. mIsActive = aIsActive; // Tell the PresShell about it. if (RefPtr presShell = GetPresShell()) { presShell->SetIsActive(aIsActive); } // Tell the window about it if (mScriptGlobal) { mScriptGlobal->SetIsBackground(!aIsActive); if (RefPtr doc = mScriptGlobal->GetExtantDoc()) { // Update orientation when the top-level browsing context becomes active. if (aIsActive) { nsCOMPtr parent; GetInProcessSameTypeParent(getter_AddRefs(parent)); if (!parent) { // We only care about the top-level browsing context. uint16_t orientation = OrientationLock(); ScreenOrientation::UpdateActiveOrientationLock(orientation); } } doc->PostVisibilityUpdateEvent(); } } // Tell the nsDOMNavigationTiming about it RefPtr timing = mTiming; if (!timing && mContentViewer) { Document* doc = mContentViewer->GetDocument(); if (doc) { timing = doc->GetNavigationTiming(); } } if (timing) { timing->NotifyDocShellStateChanged( aIsActive ? nsDOMNavigationTiming::DocShellState::eActive : nsDOMNavigationTiming::DocShellState::eInactive); } // Recursively tell all of our children, but don't tell