/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AutoRestore.h" #include "mozilla/Casting.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/ProfileTimelineMarkerBinding.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/EventStateManager.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StartupTimeline.h" #include "mozilla/Telemetry.h" #include "mozilla/unused.h" #include "mozilla/VisualEventTracer.h" #include "URIUtils.h" #include "nsIContent.h" #include "nsIContentInlines.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIDOMElement.h" #include "nsIDOMStorage.h" #include "nsIContentViewer.h" #include "nsIDocumentLoaderFactory.h" #include "nsCURILoader.h" #include "nsDocShellCID.h" #include "nsDOMCID.h" #include "nsNetUtil.h" #include "mozilla/net/ReferrerPolicy.h" #include "nsRect.h" #include "prenv.h" #include "nsIDOMWindow.h" #include "nsIGlobalObject.h" #include "nsIWebBrowserChrome.h" #include "nsPoint.h" #include "nsIObserverService.h" #include "nsIPrompt.h" #include "nsIAuthPrompt.h" #include "nsIAuthPrompt2.h" #include "nsIChannelEventSink.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "nsIServiceWorkerManager.h" #include "nsIScriptSecurityManager.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScrollableFrame.h" #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...) #include "nsISeekableStream.h" #include "nsAutoPtr.h" #include "nsIWritablePropertyBag2.h" #include "nsIAppShell.h" #include "nsWidgetsCID.h" #include "nsIInterfaceRequestorUtils.h" #include "nsView.h" #include "nsViewManager.h" #include "nsIScriptChannel.h" #include "nsITimedChannel.h" #include "nsIPrivacyTransitionObserver.h" #include "nsIReflowObserver.h" #include "nsIScrollObserver.h" #include "nsIDocShellTreeItem.h" #include "nsIChannel.h" #include "IHistory.h" #include "nsViewSourceHandler.h" #include "nsWhitespaceTokenizer.h" // we want to explore making the document own the load group // so we can associate the document URI with the load group. // until this point, we have an evil hack: #include "nsIHttpChannelInternal.h" #include "nsPILoadGroupInternal.h" // Local Includes #include "nsDocShellLoadInfo.h" #include "nsCDefaultURIFixup.h" #include "nsDocShellEnumerator.h" #include "nsSHistory.h" #include "nsDocShellEditorData.h" #include "GoannaProfiler.h" // Helper Classes #include "nsError.h" #include "nsEscape.h" // Interfaces Needed #include "nsIUploadChannel.h" #include "nsIUploadChannel2.h" #include "nsIWebProgress.h" #include "nsILayoutHistoryState.h" #include "nsITimer.h" #include "nsISHistoryInternal.h" #include "nsIPrincipal.h" #include "nsNullPrincipal.h" #include "nsISHEntry.h" #include "nsIWindowWatcher.h" #include "nsIPromptFactory.h" #include "nsITransportSecurityInfo.h" #include "nsINode.h" #include "nsINSSErrorsService.h" #include "nsIApplicationCacheChannel.h" #include "nsIApplicationCacheContainer.h" #include "nsStreamUtils.h" #include "nsIController.h" #include "nsPICommandUpdater.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIWebBrowserChrome3.h" #include "nsITabChild.h" #include "nsISiteSecurityService.h" #include "nsStructuredCloneContainer.h" #include "nsIStructuredCloneContainer.h" #ifdef MOZ_PLACES #include "nsIFaviconService.h" #include "mozIAsyncFavicons.h" #endif #include "nsINetworkPredictor.h" // Editor-related #include "nsIEditingSession.h" #include "nsPIDOMWindow.h" #include "nsGlobalWindow.h" #include "nsPIWindowRoot.h" #include "nsICachingChannel.h" #include "nsIMultiPartChannel.h" #include "nsIWyciwygChannel.h" // For reporting errors with the console service. // These can go away if error reporting is propagated up past nsDocShell. #include "nsIScriptError.h" // used to dispatch urls to default protocol handlers #include "nsCExternalHandlerService.h" #include "nsIExternalProtocolService.h" #include "nsFocusManager.h" #include "nsITextToSubURI.h" #include "nsIJARChannel.h" #include "prlog.h" #include "nsISelectionDisplay.h" #include "nsIGlobalHistory2.h" #include "nsIFrame.h" #include "nsSubDocumentFrame.h" // for embedding #include "nsIWebBrowserChromeFocus.h" #if NS_PRINT_PREVIEW #include "nsIDocumentViewerPrint.h" #include "nsIWebBrowserPrint.h" #endif #include "nsContentUtils.h" #include "nsIContentSecurityPolicy.h" #include "nsILoadInfo.h" #include "nsSandboxFlags.h" #include "nsXULAppAPI.h" #include "nsDOMNavigationTiming.h" #include "nsISecurityUITelemetry.h" #include "nsIAppsService.h" #include "nsDSURIContentListener.h" #include "nsDocShellLoadTypes.h" #include "nsDocShellTransferableHooks.h" #include "nsICommandManager.h" #include "nsIDOMNode.h" #include "nsIDocShellTreeOwner.h" #include "nsIHttpChannel.h" #include "nsISHContainer.h" #include "nsISHistory.h" #include "nsISecureBrowserUI.h" #include "nsIStringBundle.h" #include "nsISupportsArray.h" #include "nsIURIFixup.h" #include "nsIURILoader.h" #include "nsIWebBrowserFind.h" #include "nsIWidget.h" #include "mozilla/dom/EncodingUtils.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/URLSearchParams.h" #include "nsPerformance.h" #ifdef MOZ_TOOLKIT_SEARCH #include "nsIBrowserSearchService.h" #endif static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #if defined(DEBUG_bryner) || defined(DEBUG_chb) //#define DEBUG_DOCSHELL_FOCUS #define DEBUG_PAGE_CACHE #endif #ifdef XP_WIN #include #define getpid _getpid #else #include // for getpid() #endif using namespace mozilla; using namespace mozilla::dom; // True means sUseErrorPages has been added to preferences var cache. static bool gAddedPreferencesVarCache = false; bool nsDocShell::sUseErrorPages = false; // 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; // Global reference to the URI fixup service. nsIURIFixup* nsDocShell::sURIFixup = 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; // 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 #ifdef PR_LOGGING #ifdef DEBUG static PRLogModuleInfo* gDocShellLog; #endif static PRLogModuleInfo* gDocShellLeakLog; #endif const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties"; const char kAppstringsBundleURL[] = "chrome://global/locale/appstrings.properties"; 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)); } } //***************************************************************************** // support //***************************************************************************** #define PREF_PINGS_ENABLED "browser.send_pings" #define PREF_PINGS_MAX_PER_LINK "browser.send_pings.max_per_link" #define PREF_PINGS_REQUIRE_SAME_HOST "browser.send_pings.require_same_host" // Check prefs to see if pings are enabled and if so what restrictions might // be applied. // // @param maxPerLink // This parameter returns the number of pings that are allowed per link click // // @param requireSameHost // This parameter returns true if pings are restricted to the same host as // the document in which the click occurs. If the same host restriction is // imposed, then we still allow for pings to cross over to different // protocols and ports for flexibility and because it is not possible to send // a ping via FTP. // // @returns // true if pings are enabled and false otherwise. // static bool PingsEnabled(int32_t* aMaxPerLink, bool* aRequireSameHost) { bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false); *aMaxPerLink = 1; *aRequireSameHost = true; if (allow) { Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, aMaxPerLink); Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, aRequireSameHost); } return allow; } static bool CheckPingURI(nsIURI* aURI, nsIContent* aContent) { if (!aURI) { return false; } // Check with nsIScriptSecurityManager nsCOMPtr ssmgr = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); NS_ENSURE_TRUE(ssmgr, false); nsresult rv = ssmgr->CheckLoadURIWithPrincipal( aContent->NodePrincipal(), aURI, nsIScriptSecurityManager::STANDARD); if (NS_FAILED(rv)) { return false; } // Ignore non-HTTP(S) bool match; if ((NS_FAILED(aURI->SchemeIs("http", &match)) || !match) && (NS_FAILED(aURI->SchemeIs("https", &match)) || !match)) { return false; } // Check with contentpolicy int16_t shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING, aURI, aContent->NodePrincipal(), aContent, EmptyCString(), // mime hint nullptr, // extra &shouldLoad); return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); } typedef void (*ForEachPingCallback)(void* closure, nsIContent* content, nsIURI* uri, nsIIOService* ios); static bool IsElementAnchor(nsIContent* aContent) { // Make sure we are dealing with either an or element in the HTML // or XHTML namespace. if (!aContent->IsHTML()) { return false; } nsIAtom* nameAtom = aContent->Tag(); return nameAtom == nsGkAtoms::a || nameAtom == nsGkAtoms::area; } static void ForEachPing(nsIContent* aContent, ForEachPingCallback aCallback, void* aClosure) { // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here // since we'd still need to parse the resulting string. Instead, we // just parse the raw attribute. It might be nice if the content node // implemented an interface that exposed an enumeration of nsIURIs. // Make sure we are dealing with either an or element in the HTML // or XHTML namespace. if (!IsElementAnchor(aContent)) { return; } nsCOMPtr pingAtom = do_GetAtom("ping"); if (!pingAtom) { return; } nsAutoString value; aContent->GetAttr(kNameSpaceID_None, pingAtom, value); if (value.IsEmpty()) { return; } nsCOMPtr ios = do_GetIOService(); if (!ios) { return; } nsIDocument* doc = aContent->OwnerDoc(); nsWhitespaceTokenizer tokenizer(value); while (tokenizer.hasMoreTokens()) { nsCOMPtr uri, baseURI = aContent->GetBaseURI(); ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()), doc->GetDocumentCharacterSet().get(), baseURI, getter_AddRefs(uri)); if (CheckPingURI(uri, aContent)) { aCallback(aClosure, aContent, uri, ios); } } } //---------------------------------------------------------------------- // We wait this many milliseconds before killing the ping channel... #define PING_TIMEOUT 10000 static void OnPingTimeout(nsITimer* aTimer, void* aClosure) { nsILoadGroup* loadGroup = static_cast(aClosure); if (loadGroup) { loadGroup->Cancel(NS_ERROR_ABORT); } } // Check to see if two URIs have the same host or not static bool IsSameHost(nsIURI* aUri1, nsIURI* aUri2) { nsAutoCString host1, host2; aUri1->GetAsciiHost(host1); aUri2->GetAsciiHost(host2); return host1.Equals(host2); } class nsPingListener final : public nsIStreamListener , public nsIInterfaceRequestor , public nsIChannelEventSink { public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSICHANNELEVENTSINK nsPingListener(bool aRequireSameHost, nsIContent* aContent, nsILoadGroup* aLoadGroup) : mRequireSameHost(aRequireSameHost) , mContent(aContent) , mLoadGroup(aLoadGroup) { } nsresult StartTimeout(); private: ~nsPingListener(); bool mRequireSameHost; nsCOMPtr mContent; nsCOMPtr mLoadGroup; nsCOMPtr mTimer; }; NS_IMPL_ISUPPORTS(nsPingListener, nsIStreamListener, nsIRequestObserver, nsIInterfaceRequestor, nsIChannelEventSink) nsPingListener::~nsPingListener() { if (mTimer) { mTimer->Cancel(); mTimer = nullptr; } } nsresult nsPingListener::StartTimeout() { nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID); if (timer) { nsresult rv = timer->InitWithFuncCallback(OnPingTimeout, mLoadGroup, PING_TIMEOUT, nsITimer::TYPE_ONE_SHOT); if (NS_SUCCEEDED(rv)) { mTimer = timer; return NS_OK; } } return NS_ERROR_OUT_OF_MEMORY; } NS_IMETHODIMP nsPingListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { return NS_OK; } NS_IMETHODIMP nsPingListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream, uint64_t aOffset, uint32_t aCount) { uint32_t result; return aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result); } NS_IMETHODIMP nsPingListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus) { mLoadGroup = nullptr; if (mTimer) { mTimer->Cancel(); mTimer = nullptr; } return NS_OK; } NS_IMETHODIMP nsPingListener::GetInterface(const nsIID& aIID, void** aResult) { if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { NS_ADDREF_THIS(); *aResult = (nsIChannelEventSink*)this; return NS_OK; } return NS_ERROR_NO_INTERFACE; } NS_IMETHODIMP nsPingListener::AsyncOnChannelRedirect(nsIChannel* aOldChan, nsIChannel* aNewChan, uint32_t aFlags, nsIAsyncVerifyRedirectCallback* aCallback) { nsCOMPtr newURI; aNewChan->GetURI(getter_AddRefs(newURI)); if (!CheckPingURI(newURI, mContent)) { return NS_ERROR_ABORT; } if (!mRequireSameHost) { aCallback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } // XXXbz should this be using something more like the nsContentUtils // same-origin checker? nsCOMPtr oldURI; aOldChan->GetURI(getter_AddRefs(oldURI)); NS_ENSURE_STATE(oldURI && newURI); if (!IsSameHost(oldURI, newURI)) { return NS_ERROR_ABORT; } aCallback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } struct SendPingInfo { int32_t numPings; int32_t maxPings; bool requireSameHost; nsIURI* target; nsIURI* referrer; uint32_t referrerPolicy; }; static void SendPing(void* aClosure, nsIContent* aContent, nsIURI* aURI, nsIIOService* aIOService) { SendPingInfo* info = static_cast(aClosure); if (info->maxPings > -1 && info->numPings >= info->maxPings) { return; } if (info->requireSameHost) { // Make sure the referrer and the given uri share the same origin. We // only require the same hostname. The scheme and port may differ. if (!IsSameHost(aURI, info->referrer)) { return; } } nsIDocument* doc = aContent->OwnerDoc(); nsCOMPtr chan; NS_NewChannel(getter_AddRefs(chan), aURI, doc, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_PING, nullptr, // aLoadGroup nullptr, // aCallbacks nsIRequest::LOAD_NORMAL, // aLoadFlags, aIOService); if (!chan) { return; } // Don't bother caching the result of this URI load. chan->SetLoadFlags(nsIRequest::INHIBIT_CACHING); nsCOMPtr httpChan = do_QueryInterface(chan); if (!httpChan) { return; } // This is needed in order for 3rd-party cookie blocking to work. nsCOMPtr httpInternal = do_QueryInterface(httpChan); if (httpInternal) { httpInternal->SetDocumentURI(doc->GetDocumentURI()); } httpChan->SetRequestMethod(NS_LITERAL_CSTRING("POST")); // Remove extraneous request headers (to reduce request size) httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept"), EmptyCString(), false); httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-language"), EmptyCString(), false); httpChan->SetRequestHeader(NS_LITERAL_CSTRING("accept-encoding"), EmptyCString(), false); // Always send a Ping-To header. nsAutoCString pingTo; if (NS_SUCCEEDED(info->target->GetSpec(pingTo))) { httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-To"), pingTo, false); } nsCOMPtr sm = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); if (sm && info->referrer) { bool referrerIsSecure; uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; nsresult rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure); // Default to sending less data if NS_URIChainHasFlags() fails. referrerIsSecure = NS_FAILED(rv) || referrerIsSecure; bool sameOrigin = NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false)); // If both the address of the document containing the hyperlink being // audited and "ping URL" have the same origin or the document containing // the hyperlink being audited was not retrieved over an encrypted // connection, send a Ping-From header. if (sameOrigin || !referrerIsSecure) { nsAutoCString pingFrom; if (NS_SUCCEEDED(info->referrer->GetSpec(pingFrom))) { httpChan->SetRequestHeader(NS_LITERAL_CSTRING("Ping-From"), pingFrom, false); } } // If the document containing the hyperlink being audited was not retrieved // over an encrypted connection and its address does not have the same // origin as "ping URL", send a referrer. if (!sameOrigin && !referrerIsSecure) { httpChan->SetReferrerWithPolicy(info->referrer, info->referrerPolicy); } } nsCOMPtr uploadChan = do_QueryInterface(httpChan); if (!uploadChan) { return; } NS_NAMED_LITERAL_CSTRING(uploadData, "PING"); nsCOMPtr uploadStream; NS_NewPostDataStream(getter_AddRefs(uploadStream), false, uploadData); if (!uploadStream) { return; } uploadChan->ExplicitSetUploadStream(uploadStream, NS_LITERAL_CSTRING("text/ping"), uploadData.Length(), NS_LITERAL_CSTRING("POST"), false); // The channel needs to have a loadgroup associated with it, so that we can // cancel the channel and any redirected channels it may create. nsCOMPtr loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); if (!loadGroup) { return; } chan->SetLoadGroup(loadGroup); // Construct a listener that merely discards any response. If successful at // opening the channel, then it is not necessary to hold a reference to the // channel. The networking subsystem will take care of that for us. nsPingListener* pingListener = new nsPingListener(info->requireSameHost, aContent, loadGroup); nsCOMPtr listener(pingListener); // Observe redirects as well: nsCOMPtr callbacks = do_QueryInterface(listener); NS_ASSERTION(callbacks, "oops"); loadGroup->SetNotificationCallbacks(callbacks); chan->AsyncOpen(listener, nullptr); // Even if AsyncOpen failed, we still count this as a successful ping. It's // possible that AsyncOpen may have failed after triggering some background // process that may have written something to the network. info->numPings++; // Prevent ping requests from stalling and never being garbage collected... if (NS_FAILED(pingListener->StartTimeout())) { // If we failed to setup the timer, then we should just cancel the channel // because we won't be able to ensure that it goes away in a timely manner. chan->Cancel(NS_ERROR_ABORT); } } // Spec: http://whatwg.org/specs/web-apps/current-work/#ping static void DispatchPings(nsIContent* aContent, nsIURI* aTarget, nsIURI* aReferrer, uint32_t aReferrerPolicy) { SendPingInfo info; if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) { return; } if (info.maxPings == 0) { return; } info.numPings = 0; info.target = aTarget; info.referrer = aReferrer; info.referrerPolicy = aReferrerPolicy; ForEachPing(aContent, SendPing, &info); } static nsDOMPerformanceNavigationType ConvertLoadTypeToNavigationType(uint32_t aLoadType) { // Not initialized, assume it's normal load. if (aLoadType == 0) { aLoadType = LOAD_NORMAL; } auto result = dom::PerformanceNavigation::TYPE_RESERVED; switch (aLoadType) { case LOAD_NORMAL: case LOAD_NORMAL_EXTERNAL: case LOAD_NORMAL_BYPASS_CACHE: case LOAD_NORMAL_BYPASS_PROXY: case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE: case LOAD_NORMAL_REPLACE: case LOAD_NORMAL_ALLOW_MIXED_CONTENT: case LOAD_LINK: case LOAD_STOP_CONTENT: case LOAD_REPLACE_BYPASS_CACHE: result = dom::PerformanceNavigation::TYPE_NAVIGATE; break; case LOAD_HISTORY: result = dom::PerformanceNavigation::TYPE_BACK_FORWARD; break; case LOAD_RELOAD_NORMAL: case LOAD_RELOAD_CHARSET_CHANGE: case LOAD_RELOAD_BYPASS_CACHE: case LOAD_RELOAD_BYPASS_PROXY: case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: case LOAD_RELOAD_ALLOW_MIXED_CONTENT: result = dom::PerformanceNavigation::TYPE_RELOAD; break; case LOAD_STOP_CONTENT_AND_REPLACE: case LOAD_REFRESH: case LOAD_BYPASS_HISTORY: case LOAD_ERROR_PAGE: case LOAD_PUSHSTATE: result = dom::PerformanceNavigation::TYPE_RESERVED; break; default: // NS_NOTREACHED("Unexpected load type value"); result = dom::PerformanceNavigation::TYPE_RESERVED; break; } return result; } static nsISHEntry* GetRootSHEntry(nsISHEntry* aEntry); static void IncreasePrivateDocShellCount() { gNumberOfPrivateDocShells++; if (gNumberOfPrivateDocShells > 1 || XRE_GetProcessType() != GoannaProcessType_Content) { return; } mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); cc->SendPrivateDocShellsExist(true); } static void DecreasePrivateDocShellCount() { MOZ_ASSERT(gNumberOfPrivateDocShells > 0); gNumberOfPrivateDocShells--; if (!gNumberOfPrivateDocShells) { if (XRE_GetProcessType() == GoannaProcessType_Content) { 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: Object Management //***************************************************************************** static uint64_t gDocshellIDCounter = 0; nsDocShell::nsDocShell() : nsDocLoader() , mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto) , mTreeOwner(nullptr) , mChromeEventHandler(nullptr) , mCharsetReloadState(eCharsetReloadInit) , mChildOffset(0) , mBusyFlags(BUSY_FLAGS_NONE) , mAppType(nsIDocShell::APP_TYPE_UNKNOWN) , mLoadType(0) , mMarginWidth(-1) , mMarginHeight(-1) , mItemType(typeContent) , mPreviousTransIndex(-1) , mLoadedTransIndex(-1) , mSandboxFlags(0) , mFullscreenAllowed(CHECK_ATTRIBUTES) , mCreated(false) , mAllowSubframes(true) , mAllowPlugins(true) , mAllowJavascript(true) , mAllowMetaRedirects(true) , mAllowImages(true) , mAllowMedia(true) , mAllowDNSPrefetch(true) , mAllowWindowControl(true) , mAllowContentRetargeting(true) , mAllowContentRetargetingOnChildren(true) , mCreatingDocument(false) , mUseErrorPages(false) , mObserveErrorPages(true) , mAllowAuth(true) , mAllowKeywordFixup(false) , mIsOffScreenBrowser(false) , mIsActive(true) , mIsPrerendered(false) , mIsAppTab(false) , mUseGlobalHistory(false) , mInPrivateBrowsing(false) , mUseRemoteTabs(false) , mDeviceSizeIsPageSize(false) , mWindowDraggingAllowed(false) , mCanExecuteScripts(false) , mFiredUnloadEvent(false) , mEODForCurrentDocument(false) , mURIResultedInDocument(false) , mIsBeingDestroyed(false) , mIsExecutingOnLoadHandler(false) , mIsPrintingOrPP(false) , mSavingOldViewer(false) #ifdef DEBUG , mInEnsureScriptEnv(false) #endif , mAffectPrivateSessionLifetime(true) , mInvisible(false) , mHasLoadedNonBlankURI(false) , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL) , mBlankTiming(false) , mFrameType(eFrameTypeRegular) , mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID) , mParentCharsetSource(0) , mJSRunToCompletionDepth(0) { mHistoryID = ++gDocshellIDCounter; if (gDocShellCount++ == 0) { NS_ASSERTION(sURIFixup == nullptr, "Huh, sURIFixup not null in first nsDocShell ctor!"); CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup); } #ifdef PR_LOGGING #ifdef DEBUG if (!gDocShellLog) { gDocShellLog = PR_NewLogModule("nsDocShell"); } #endif if (!gDocShellLeakLog) { gDocShellLeakLog = PR_NewLogModule("nsDocShellLeak"); } if (gDocShellLeakLog) { PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p created\n", this)); } #endif #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 = %llu]\n", (void*)this, gNumberOfDocShells, getpid(), AssertedCast(mHistoryID)); } #endif } nsDocShell::~nsDocShell() { MOZ_ASSERT(!mProfileTimelineRecording); Destroy(); nsCOMPtr shPrivate(do_QueryInterface(mSessionHistory)); if (shPrivate) { shPrivate->SetRootDocShell(nullptr); } if (--gDocShellCount == 0) { NS_IF_RELEASE(sURIFixup); } #ifdef PR_LOGGING if (gDocShellLeakLog) { PR_LOG(gDocShellLeakLog, PR_LOG_DEBUG, ("DOCSHELL %p destroyed\n", this)); } #endif #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 = %llu]\n", (void*)this, gNumberOfDocShells, getpid(), AssertedCast(mHistoryID)); } #endif } nsresult nsDocShell::Init() { nsresult rv = nsDocLoader::Init(); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(mLoadGroup, "Something went wrong!"); mContentListener = new nsDSURIContentListener(this); NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY); rv = mContentListener->Init(); NS_ENSURE_SUCCESS(rv, rv); // 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(static_cast(this)); NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY); mLoadGroup->SetNotificationCallbacks(proxy); rv = nsDocLoader::AddDocLoaderAsChildOfRoot(this); NS_ENSURE_SUCCESS(rv, rv); // Add as |this| 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 ourselves, so it's ok. return AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT | nsIWebProgress::NOTIFY_STATE_NETWORK); } 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(); } //***************************************************************************** // nsDocShell::nsISupports //***************************************************************************** NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader) NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader) NS_INTERFACE_MAP_BEGIN(nsDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShell_ESR38_2) NS_INTERFACE_MAP_ENTRY(nsIDocShell_ESR38) NS_INTERFACE_MAP_ENTRY(nsIDocShell) NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem) NS_INTERFACE_MAP_ENTRY(nsIWebNavigation) NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) NS_INTERFACE_MAP_ENTRY(nsIScrollable) NS_INTERFACE_MAP_ENTRY(nsITextScroll) NS_INTERFACE_MAP_ENTRY(nsIDocCharset) NS_INTERFACE_MAP_ENTRY(nsIRefreshURI) NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer) NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor) NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) NS_INTERFACE_MAP_ENTRY(nsILoadContext) NS_INTERFACE_MAP_ENTRY(nsIWebShellServices) NS_INTERFACE_MAP_ENTRY(nsILinkHandler) NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands) NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager) NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController) NS_INTERFACE_MAP_END_INHERITING(nsDocLoader) ///***************************************************************************** // nsDocShell::nsIInterfaceRequestor //***************************************************************************** NS_IMETHODIMP nsDocShell::GetInterface(const nsIID& aIID, void** aSink) { NS_PRECONDITION(aSink, "null out param"); *aSink = nullptr; if (aIID.Equals(NS_GET_IID(nsICommandManager))) { NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE); *aSink = mCommandManager; } 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(nsPIDOMWindow)) || aIID.Equals(NS_GET_IID(nsIDOMWindow)) || aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) && NS_SUCCEEDED(EnsureScriptEnvironment())) { return mScriptGlobal->QueryInterface(aIID, aSink); } else if (aIID.Equals(NS_GET_IID(nsIDOMDocument)) && NS_SUCCEEDED(EnsureContentViewer())) { mContentViewer->GetDOMDocument((nsIDOMDocument**)aSink); return *aSink ? NS_OK : NS_NOINTERFACE; } else if (aIID.Equals(NS_GET_IID(nsIDocument)) && NS_SUCCEEDED(EnsureContentViewer())) { nsCOMPtr 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; } nsCOMPtr domDoc; contentViewer->GetDOMDocument(getter_AddRefs(domDoc)); NS_ASSERTION(domDoc, "Should have a document."); if (!domDoc) { return NS_ERROR_NO_INTERFACE; } #if defined(PR_LOGGING) && defined(DEBUG) PR_LOG(gDocShellLog, PR_LOG_DEBUG, ("nsDocShell[%p]: returning app cache container %p", this, domDoc.get())); #endif return domDoc->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))) { nsCOMPtr shistory; nsresult rv = GetSessionHistory(getter_AddRefs(shistory)); if (NS_SUCCEEDED(rv) && shistory) { *aSink = shistory; NS_ADDREF((nsISupports*)*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(nsIEditingSession)) && NS_SUCCEEDED(EnsureEditorData())) { nsCOMPtr editingSession; mEditorData->GetEditingSession(getter_AddRefs(editingSession)); if (editingSession) { *aSink = editingSession; NS_ADDREF((nsISupports*)*aSink); return NS_OK; } return NS_NOINTERFACE; } else if (aIID.Equals(NS_GET_IID(nsIClipboardDragDropHookList)) && NS_SUCCEEDED(EnsureTransferableHookData())) { *aSink = mTransferableHookData; NS_ADDREF((nsISupports*)*aSink); return NS_OK; } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) { nsIPresShell* shell = GetPresShell(); if (shell) { return shell->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(nsITabChild))) { nsCOMPtr treeOwner; nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner)); if (NS_SUCCEEDED(rv) && treeOwner) { nsCOMPtr ir = do_QueryInterface(treeOwner); if (ir) { return ir->GetInterface(aIID, aSink); } } } else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) { nsCOMPtr tabChild = do_GetInterface(static_cast(this)); nsCOMPtr mm; if (tabChild) { tabChild->GetMessageManager(getter_AddRefs(mm)); } else { nsCOMPtr win = GetWindow(); if (win) { mm = do_QueryInterface(win->GetParentTarget()); } } *aSink = mm.get(); } else { return nsDocLoader::GetInterface(aIID, aSink); } NS_IF_ADDREF(((nsISupports*)*aSink)); return *aSink ? NS_OK : NS_NOINTERFACE; } uint32_t nsDocShell::ConvertDocShellLoadInfoToLoadType( nsDocShellInfoLoadType aDocShellLoadType) { uint32_t loadType = LOAD_NORMAL; switch (aDocShellLoadType) { case nsIDocShellLoadInfo::loadNormal: loadType = LOAD_NORMAL; break; case nsIDocShellLoadInfo::loadNormalReplace: loadType = LOAD_NORMAL_REPLACE; break; case nsIDocShellLoadInfo::loadNormalExternal: loadType = LOAD_NORMAL_EXTERNAL; break; case nsIDocShellLoadInfo::loadHistory: loadType = LOAD_HISTORY; break; case nsIDocShellLoadInfo::loadNormalBypassCache: loadType = LOAD_NORMAL_BYPASS_CACHE; break; case nsIDocShellLoadInfo::loadNormalBypassProxy: loadType = LOAD_NORMAL_BYPASS_PROXY; break; case nsIDocShellLoadInfo::loadNormalBypassProxyAndCache: loadType = LOAD_NORMAL_BYPASS_PROXY_AND_CACHE; break; case nsIDocShellLoadInfo::loadNormalAllowMixedContent: loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT; break; case nsIDocShellLoadInfo::loadReloadNormal: loadType = LOAD_RELOAD_NORMAL; break; case nsIDocShellLoadInfo::loadReloadCharsetChange: loadType = LOAD_RELOAD_CHARSET_CHANGE; break; case nsIDocShellLoadInfo::loadReloadBypassCache: loadType = LOAD_RELOAD_BYPASS_CACHE; break; case nsIDocShellLoadInfo::loadReloadBypassProxy: loadType = LOAD_RELOAD_BYPASS_PROXY; break; case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache: loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE; break; case nsIDocShellLoadInfo::loadLink: loadType = LOAD_LINK; break; case nsIDocShellLoadInfo::loadRefresh: loadType = LOAD_REFRESH; break; case nsIDocShellLoadInfo::loadBypassHistory: loadType = LOAD_BYPASS_HISTORY; break; case nsIDocShellLoadInfo::loadStopContent: loadType = LOAD_STOP_CONTENT; break; case nsIDocShellLoadInfo::loadStopContentAndReplace: loadType = LOAD_STOP_CONTENT_AND_REPLACE; break; case nsIDocShellLoadInfo::loadPushState: loadType = LOAD_PUSHSTATE; break; case nsIDocShellLoadInfo::loadReplaceBypassCache: loadType = LOAD_REPLACE_BYPASS_CACHE; break; case nsIDocShellLoadInfo::loadReloadMixedContent: loadType = LOAD_RELOAD_ALLOW_MIXED_CONTENT; break; default: NS_NOTREACHED("Unexpected nsDocShellInfoLoadType value"); } return loadType; } nsDocShellInfoLoadType nsDocShell::ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType) { nsDocShellInfoLoadType docShellLoadType = nsIDocShellLoadInfo::loadNormal; switch (aLoadType) { case LOAD_NORMAL: docShellLoadType = nsIDocShellLoadInfo::loadNormal; break; case LOAD_NORMAL_REPLACE: docShellLoadType = nsIDocShellLoadInfo::loadNormalReplace; break; case LOAD_NORMAL_EXTERNAL: docShellLoadType = nsIDocShellLoadInfo::loadNormalExternal; break; case LOAD_NORMAL_BYPASS_CACHE: docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassCache; break; case LOAD_NORMAL_BYPASS_PROXY: docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxy; break; case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE: docShellLoadType = nsIDocShellLoadInfo::loadNormalBypassProxyAndCache; break; case LOAD_NORMAL_ALLOW_MIXED_CONTENT: docShellLoadType = nsIDocShellLoadInfo::loadNormalAllowMixedContent; break; case LOAD_HISTORY: docShellLoadType = nsIDocShellLoadInfo::loadHistory; break; case LOAD_RELOAD_NORMAL: docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal; break; case LOAD_RELOAD_CHARSET_CHANGE: docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange; break; case LOAD_RELOAD_BYPASS_CACHE: docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache; break; case LOAD_RELOAD_BYPASS_PROXY: docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy; break; case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache; break; case LOAD_LINK: docShellLoadType = nsIDocShellLoadInfo::loadLink; break; case LOAD_REFRESH: docShellLoadType = nsIDocShellLoadInfo::loadRefresh; break; case LOAD_BYPASS_HISTORY: case LOAD_ERROR_PAGE: docShellLoadType = nsIDocShellLoadInfo::loadBypassHistory; break; case LOAD_STOP_CONTENT: docShellLoadType = nsIDocShellLoadInfo::loadStopContent; break; case LOAD_STOP_CONTENT_AND_REPLACE: docShellLoadType = nsIDocShellLoadInfo::loadStopContentAndReplace; break; case LOAD_PUSHSTATE: docShellLoadType = nsIDocShellLoadInfo::loadPushState; break; case LOAD_REPLACE_BYPASS_CACHE: docShellLoadType = nsIDocShellLoadInfo::loadReplaceBypassCache; break; case LOAD_RELOAD_ALLOW_MIXED_CONTENT: docShellLoadType = nsIDocShellLoadInfo::loadReloadMixedContent; break; default: NS_NOTREACHED("Unexpected load type value"); } return docShellLoadType; } //***************************************************************************** // nsDocShell::nsIDocShell //***************************************************************************** NS_IMETHODIMP nsDocShell::LoadURI(nsIURI* aURI, nsIDocShellLoadInfo* aLoadInfo, uint32_t aLoadFlags, bool aFirstParty) { NS_PRECONDITION(aLoadInfo || (aLoadFlags & EXTRA_LOAD_FLAGS) == 0, "Unexpected flags"); NS_PRECONDITION((aLoadFlags & 0xf) == 0, "Should not have these flags set"); // 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 (DoAppRedirectIfNeeded(aURI, aLoadInfo, aFirstParty)) { return NS_OK; } nsCOMPtr referrer; nsCOMPtr originalURI; bool loadReplace = false; nsCOMPtr postStream; nsCOMPtr headersStream; nsCOMPtr owner; bool inheritOwner = false; bool ownerIsExplicit = false; bool sendReferrer = true; uint32_t referrerPolicy = mozilla::net::RP_Default; bool isSrcdoc = false; nsCOMPtr shEntry; nsXPIDLString target; nsAutoString srcdoc; nsCOMPtr sourceDocShell; nsCOMPtr baseURI; uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags); NS_ENSURE_ARG(aURI); if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) && mItemType == typeContent && !NS_IsAboutBlank(aURI)) { StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI); } // Extract the info from the DocShellLoadInfo struct... if (aLoadInfo) { aLoadInfo->GetReferrer(getter_AddRefs(referrer)); nsCOMPtr liESR38 = do_QueryInterface(aLoadInfo); if (liESR38) { liESR38->GetOriginalURI(getter_AddRefs(originalURI)); liESR38->GetLoadReplace(&loadReplace); } nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; aLoadInfo->GetLoadType(<); // Get the appropriate loadType from nsIDocShellLoadInfo type loadType = ConvertDocShellLoadInfoToLoadType(lt); aLoadInfo->GetOwner(getter_AddRefs(owner)); aLoadInfo->GetInheritOwner(&inheritOwner); aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit); aLoadInfo->GetSHEntry(getter_AddRefs(shEntry)); aLoadInfo->GetTarget(getter_Copies(target)); aLoadInfo->GetPostDataStream(getter_AddRefs(postStream)); aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream)); aLoadInfo->GetSendReferrer(&sendReferrer); aLoadInfo->GetReferrerPolicy(&referrerPolicy); aLoadInfo->GetIsSrcdocLoad(&isSrcdoc); aLoadInfo->GetSrcdocData(srcdoc); aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell)); aLoadInfo->GetBaseURI(getter_AddRefs(baseURI)); } #if defined(PR_LOGGING) && defined(DEBUG) if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) { nsAutoCString uristr; aURI->GetAsciiSpec(uristr); PR_LOG(gDocShellLog, PR_LOG_DEBUG, ("nsDocShell[%p]: loading %s with flags 0x%08x", this, uristr.get(), aLoadFlags)); } #endif if (!shEntry && !LOAD_TYPE_HAS_FLAGS(loadType, LOAD_FLAGS_REPLACE_HISTORY)) { // First verify if this is a subframe. nsCOMPtr parentAsItem; GetSameTypeParent(getter_AddRefs(parentAsItem)); nsCOMPtr parentDS(do_QueryInterface(parentAsItem)); uint32_t parentLoadType; if (parentDS && parentDS != static_cast(this)) { /* OK. It is a subframe. Checkout the * parent's loadtype. If the parent was loaded thro' 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 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. parentDS->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry)); } // Make some decisions on the child frame's loadType based on the // parent's loadType. if (!mCurrentURI) { // This is a newly created frame. Check for exception cases first. // By default the subframe will inherit the parent's loadType. if (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) { loadType = LOAD_NORMAL_REPLACE; shEntry = nullptr; } } else if (parentLoadType == LOAD_REFRESH) { // Clear shEntry. For refresh loads, we have to load // what comes thro' the pipe, not what's in history. shEntry = nullptr; } else if ((parentLoadType == LOAD_BYPASS_HISTORY) || (shEntry && ((parentLoadType & LOAD_CMD_HISTORY) || (parentLoadType == LOAD_RELOAD_NORMAL) || (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) { // 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. loadType = 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. loadType = 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 loadType = parentLoadType; } } else { // This is a pre-existing subframe. If the load was not originally // initiated by session history, (if (!shEntry) condition succeeded) and // mCurrentURI is not null, 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. uint32_t parentBusy = BUSY_FLAGS_NONE; uint32_t selfBusy = BUSY_FLAGS_NONE; parentDS->GetBusyFlags(&parentBusy); GetBusyFlags(&selfBusy); if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) { loadType = LOAD_NORMAL_REPLACE; shEntry = nullptr; } } } // parentDS else { // 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) { loadType = LOAD_NORMAL_REPLACE; } } } // !shEntry if (shEntry) { #ifdef DEBUG PR_LOG(gDocShellLog, PR_LOG_DEBUG, ("nsDocShell[%p]: loading from session history", this)); #endif return LoadHistoryEntry(shEntry, 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 ((loadType == LOAD_NORMAL || loadType == LOAD_STOP_CONTENT) && ShouldBlockLoadingForBackButton()) { return NS_OK; } // Perform the load... // We need an owner (a referring principal). // // If ownerIsExplicit is not set there are 4 possibilities: // (1) If the system principal or an expanded principal was passed // in and we're a typeContent docshell, inherit the principal // from the current document instead. // (2) In all other cases when the principal passed in is not null, // use that principal. // (3) If the caller has allowed inheriting from the current document, // or if we're being called from system code (eg chrome JS or pure // C++) then inheritOwner should be true and InternalLoad will get // an owner from the current document. If none of these things are // true, then // (4) we pass a null owner into the channel, and an owner will be // created later from the channel's internal data. // // If ownerIsExplicit *is* set, there are 4 possibilities // (1) If the system principal or an expanded principal was passed in // and we're a typeContent docshell, return an error. // (2) In all other cases when the principal passed in is not null, // use that principal. // (3) If the caller has allowed inheriting from the current document, // then inheritOwner should be true and InternalLoad will get an owner // from the current document. If none of these things are true, then // (4) we pass a null owner into the channel, and an owner will be // created later from the channel's internal data. // // NOTE: This all only works because the only thing the owner is used // for in InternalLoad is data:, javascript:, and about:blank // URIs. For other URIs this would all be dead wrong! if (owner && mItemType != typeChrome) { nsCOMPtr ownerPrincipal = do_QueryInterface(owner); if (nsContentUtils::IsSystemPrincipal(ownerPrincipal)) { if (ownerIsExplicit) { return NS_ERROR_DOM_SECURITY_ERR; } owner = nullptr; inheritOwner = true; } else if (nsContentUtils::IsExpandedPrincipal(ownerPrincipal)) { if (ownerIsExplicit) { return NS_ERROR_DOM_SECURITY_ERR; } // Don't inherit from the current page. Just do the safe thing // and pretend that we were loaded by a nullprincipal. owner = do_CreateInstance("@mozilla.org/nullprincipal;1"); NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); inheritOwner = false; } } if (!owner && !inheritOwner && !ownerIsExplicit) { // See if there's system or chrome JS code running inheritOwner = nsContentUtils::IsCallerChrome(); } if (aLoadFlags & LOAD_FLAGS_DISALLOW_INHERIT_OWNER) { inheritOwner = false; owner = do_CreateInstance("@mozilla.org/nullprincipal;1"); } uint32_t flags = 0; if (inheritOwner) { flags |= INTERNAL_LOAD_FLAGS_INHERIT_OWNER; } if (!sendReferrer) { flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER; } if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { flags |= INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; } if (aLoadFlags & LOAD_FLAGS_FIRST_LOAD) { flags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD; } if (aLoadFlags & LOAD_FLAGS_BYPASS_CLASSIFIER) { flags |= INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER; } if (aLoadFlags & LOAD_FLAGS_FORCE_ALLOW_COOKIES) { flags |= INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES; } if (isSrcdoc) { flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; } return InternalLoad2(aURI, originalURI, loadReplace, referrer, referrerPolicy, owner, flags, target.get(), nullptr, // No type hint NullString(), // No forced download postStream, headersStream, loadType, nullptr, // No SHEntry aFirstParty, srcdoc, sourceDocShell, baseURI, nullptr, // No nsIDocShell nullptr); // No nsIRequest } NS_IMETHODIMP nsDocShell::LoadStream(nsIInputStream* aStream, nsIURI* aURI, const nsACString& aContentType, const nsACString& aContentCharset, nsIDocShellLoadInfo* aLoadInfo) { NS_ENSURE_ARG(aStream); mAllowKeywordFixup = false; // if the caller doesn't pass in a URI we need to create a dummy URI. necko // currently requires a URI in various places during the load. Some consumers // do as well. nsCOMPtr uri = aURI; if (!uri) { // HACK ALERT nsresult rv = NS_OK; uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv); if (NS_FAILED(rv)) { return rv; } // Make sure that the URI spec "looks" like a protocol and path... // For now, just use a bogus protocol called "internal" rv = uri->SetSpec(NS_LITERAL_CSTRING("internal:load-stream")); if (NS_FAILED(rv)) { return rv; } } uint32_t loadType = LOAD_NORMAL; if (aLoadInfo) { nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; (void)aLoadInfo->GetLoadType(<); // Get the appropriate LoadType from nsIDocShellLoadInfo type loadType = ConvertDocShellLoadInfoToLoadType(lt); } NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE); mLoadType = loadType; nsCOMPtr owner; aLoadInfo->GetOwner(getter_AddRefs(owner)); nsCOMPtr requestingPrincipal = do_QueryInterface(owner); if (!requestingPrincipal) { requestingPrincipal = nsContentUtils::GetSystemPrincipal(); } // build up a channel for this stream. nsCOMPtr channel; nsresult rv = NS_NewInputStreamChannel(getter_AddRefs(channel), uri, aStream, requestingPrincipal, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, aContentType, aContentCharset); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); nsCOMPtr uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID)); NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo** aLoadInfo) { nsDocShellLoadInfo* loadInfo = new nsDocShellLoadInfo(); NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY); nsCOMPtr localRef(loadInfo); *aLoadInfo = localRef; NS_ADDREF(*aLoadInfo); return NS_OK; } /* * 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) { if (mContentViewer && !mFiredUnloadEvent) { // Keep an explicit reference since calling PageHide could release // mContentViewer nsCOMPtr kungFuDeathGrip(mContentViewer); mFiredUnloadEvent = true; if (mTiming) { mTiming->NotifyUnloadEventStart(); } mContentViewer->PageHide(aIsUnload); if (mTiming) { mTiming->NotifyUnloadEventEnd(); } nsAutoTArray, 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) { if (kids[i]) { kids[i]->FirePageHideNotification(aIsUnload); } } // Now make sure our editor, if any, is detached before we go // any farther. DetachEditorFromWindow(); } return NS_OK; } void nsDocShell::MaybeInitTiming() { if (mTiming && !mBlankTiming) { return; } if (mScriptGlobal && mBlankTiming) { nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow(); if (innerWin && innerWin->GetPerformance()) { mTiming = innerWin->GetPerformance()->GetDOMTiming(); mBlankTiming = false; } } if (!mTiming) { mTiming = new nsDOMNavigationTiming(); } mTiming->NotifyNavigationStart(); } // // 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 nsCOMPtr originDocument = aOriginTreeItem->GetDocument(); NS_ENSURE_TRUE(originDocument, false); // Get target principal nsCOMPtr 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 bool originIsFile = false; bool targetIsFile = false; 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 && NS_SUCCEEDED(innerOriginURI->SchemeIs("file", &originIsFile)) && NS_SUCCEEDED(innerTargetURI->SchemeIs("file", &targetIsFile)) && originIsFile && targetIsFile; } nsresult nsDocShell::GetEldestPresContext(nsPresContext** aPresContext) { NS_ENSURE_ARG_POINTER(aPresContext); *aPresContext = nullptr; nsCOMPtr viewer = mContentViewer; while (viewer) { nsCOMPtr prevViewer; viewer->GetPreviousViewer(getter_AddRefs(prevViewer)); if (!prevViewer) { return viewer->GetPresContext(aPresContext); } viewer = prevViewer; } return NS_OK; } NS_IMETHODIMP nsDocShell::GetPresContext(nsPresContext** aPresContext) { NS_ENSURE_ARG_POINTER(aPresContext); *aPresContext = nullptr; if (!mContentViewer) { return NS_OK; } return mContentViewer->GetPresContext(aPresContext); } NS_IMETHODIMP_(nsIPresShell*) nsDocShell::GetPresShell() { nsRefPtr presContext; (void)GetPresContext(getter_AddRefs(presContext)); return presContext ? presContext->GetPresShell() : nullptr; } NS_IMETHODIMP nsDocShell::GetEldestPresShell(nsIPresShell** aPresShell) { nsresult rv = NS_OK; NS_ENSURE_ARG_POINTER(aPresShell); *aPresShell = nullptr; nsRefPtr presContext; (void)GetEldestPresContext(getter_AddRefs(presContext)); if (presContext) { NS_IF_ADDREF(*aPresShell = presContext->GetPresShell()); } return rv; } NS_IMETHODIMP nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer) { NS_ENSURE_ARG_POINTER(aContentViewer); *aContentViewer = mContentViewer; NS_IF_ADDREF(*aContentViewer); return NS_OK; } NS_IMETHODIMP nsDocShell::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler) { // Weak reference. Don't addref. nsCOMPtr handler = do_QueryInterface(aChromeEventHandler); mChromeEventHandler = handler.get(); if (mScriptGlobal) { mScriptGlobal->SetChromeEventHandler(mChromeEventHandler); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetChromeEventHandler(nsIDOMEventTarget** aChromeEventHandler) { NS_ENSURE_ARG_POINTER(aChromeEventHandler); nsCOMPtr handler = mChromeEventHandler; handler.forget(aChromeEventHandler); return NS_OK; } /* void setCurrentURI (in nsIURI uri); */ 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) { #ifdef PR_LOGGING if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) { nsAutoCString spec; if (aURI) { aURI->GetSpec(spec); } PR_LogPrint("DOCSHELL %p SetCurrentURI %s\n", this, spec.get()); } #endif // 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; } mCurrentURI = NS_TryToMakeImmutable(aURI); if (!NS_IsAboutBlank(mCurrentURI)) { mHasLoadedNonBlankURI = true; } bool isRoot = false; // Is this the root docshell bool isSubFrame = false; // Is this a subframe navigation? nsCOMPtr root; GetSameTypeRootTreeItem(getter_AddRefs(root)); if (root.get() == static_cast(this)) { // This is the root docshell isRoot = true; } if (mLSHE) { mLSHE->GetIsSubFrame(&isSubFrame); } // nsDocShell owns a URLSearchParams that is used by // window.location.searchParams to be in sync with the current location. if (!mURLSearchParams) { mURLSearchParams = new URLSearchParams(); } nsAutoCString search; nsCOMPtr url(do_QueryInterface(mCurrentURI)); if (url) { nsresult rv = url->GetQuery(search); if (NS_FAILED(rv)) { NS_WARNING("Failed to get the query from a nsIURL."); } } mURLSearchParams->ParseInput(search, nullptr); 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(); nsIPresShell* presShell = GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); nsIDocument* doc = presShell->GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); aCharset = doc->GetDocumentCharacterSet(); return NS_OK; } NS_IMETHODIMP nsDocShell::GatherCharsetMenuTelemetry() { nsCOMPtr viewer; GetContentViewer(getter_AddRefs(viewer)); if (!viewer) { return NS_OK; } nsIDocument* doc = viewer->GetDocument(); if (!doc || doc->WillIgnoreCharsetOverride()) { return NS_OK; } Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_USED, true); bool isFileURL = false; nsIURI* url = doc->GetOriginalURI(); if (url) { url->SchemeIs("file", &isFileURL); } int32_t charsetSource = doc->GetDocumentCharacterSetSource(); switch (charsetSource) { case kCharsetFromTopLevelDomain: // Unlabeled doc on a domain that we map to a fallback encoding Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 7); break; case kCharsetFromFallback: case kCharsetFromDocTypeDefault: case kCharsetFromCache: case kCharsetFromParentFrame: case kCharsetFromHintPrevDoc: // Changing charset on an unlabeled doc. if (isFileURL) { Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 0); } else { Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 1); } break; case kCharsetFromAutoDetection: // Changing charset on unlabeled doc where chardet fired if (isFileURL) { Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 2); } else { Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 3); } break; case kCharsetFromMetaPrescan: case kCharsetFromMetaTag: case kCharsetFromChannel: // Changing charset on a doc that had a charset label. Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 4); break; case kCharsetFromParentForced: case kCharsetFromUserForced: // Changing charset on a document that already had an override. Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 5); break; case kCharsetFromIrreversibleAutoDetection: case kCharsetFromOtherComponent: case kCharsetFromByteOrderMark: case kCharsetUninitialized: default: // Bug. This isn't supposed to happen. Telemetry::Accumulate(Telemetry::CHARSET_OVERRIDE_SITUATION, 6); 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.Truncate(); return NS_OK; } nsAutoCString encoding; if (!EncodingUtils::FindEncodingForLabel(aCharset, encoding)) { // Reject unknown labels return NS_ERROR_INVALID_ARG; } if (!EncodingUtils::IsAsciiCompatible(encoding)) { // Reject XSS hazards return NS_ERROR_INVALID_ARG; } mForcedCharset = encoding; return NS_OK; } NS_IMETHODIMP nsDocShell::GetForcedCharset(nsACString& aResult) { aResult = mForcedCharset; return NS_OK; } void nsDocShell::SetParentCharset(const nsACString& aCharset, int32_t aCharsetSource, nsIPrincipal* aPrincipal) { mParentCharset = aCharset; mParentCharsetSource = aCharsetSource; mParentCharsetPrincipal = aPrincipal; } void nsDocShell::GetParentCharset(nsACString& aCharset, int32_t* aCharsetSource, nsIPrincipal** aPrincipal) { aCharset = mParentCharset; *aCharsetSource = mParentCharsetSource; NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal); } NS_IMETHODIMP nsDocShell::GetChannelIsUnsafe(bool* aUnsafe) { *aUnsafe = false; nsIChannel* channel = GetCurrentDocChannel(); if (!channel) { return NS_OK; } nsCOMPtr jarChannel = do_QueryInterface(channel); if (!jarChannel) { return NS_OK; } return jarChannel->GetIsUnsafe(aUnsafe); } NS_IMETHODIMP nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded) { nsCOMPtr doc(GetDocument()); *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasMixedActiveContentBlocked(bool* aHasMixedActiveContentBlocked) { nsCOMPtr doc(GetDocument()); *aHasMixedActiveContentBlocked = doc && doc->GetHasMixedActiveContentBlocked(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasMixedDisplayContentLoaded(bool* aHasMixedDisplayContentLoaded) { nsCOMPtr doc(GetDocument()); *aHasMixedDisplayContentLoaded = doc && doc->GetHasMixedDisplayContentLoaded(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasMixedDisplayContentBlocked( bool* aHasMixedDisplayContentBlocked) { nsCOMPtr doc(GetDocument()); *aHasMixedDisplayContentBlocked = doc && doc->GetHasMixedDisplayContentBlocked(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked) { nsCOMPtr doc(GetDocument()); *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded) { nsCOMPtr doc(GetDocument()); *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetAllowPlugins(bool* aAllowPlugins) { NS_ENSURE_ARG_POINTER(aAllowPlugins); *aAllowPlugins = mAllowPlugins; if (!mAllowPlugins) { return NS_OK; } bool unsafe; *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; 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::SetAllowJavascript(bool aAllowJavascript) { mAllowJavascript = aAllowJavascript; RecomputeCanExecuteScripts(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) { NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing); *aUsePrivateBrowsing = mInPrivateBrowsing; return NS_OK; } NS_IMETHODIMP nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) { nsContentUtils::ReportToConsoleNonLocalized( NS_LITERAL_STRING("Only internal code is allowed to set the usePrivateBrowsing attribute"), nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Internal API Used"), mContentViewer ? mContentViewer->GetDocument() : nullptr); return SetPrivateBrowsing(aUsePrivateBrowsing); } NS_IMETHODIMP nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) { bool changed = aUsePrivateBrowsing != mInPrivateBrowsing; if (changed) { mInPrivateBrowsing = 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); } } } 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) { mUseRemoteTabs = aUseRemoteTabs; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) { bool change = aAffectLifetime != mAffectPrivateSessionLifetime; if (change && mInPrivateBrowsing) { 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; } return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) { nsWeakPtr weakObs = do_GetWeakReference(aObserver); if (!weakObs) { return NS_ERROR_FAILURE; } return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE; } 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; if (!mAllowMetaRedirects) { return NS_OK; } bool unsafe; *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe; 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) { nsPIDOMWindow* innerWin = mScriptGlobal->GetCurrentInnerWindow(); if (innerWin) { 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::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; // For non-browsers/apps, check that the enclosing iframe element // has the allowfullscreen attribute set to true. If any ancestor // iframe does not have mozallowfullscreen=true, then fullscreen is // prohibited. nsCOMPtr win = GetWindow(); if (!win) { return NS_OK; } nsCOMPtr frameElement = win->GetFrameElementInternal(); if (frameElement && frameElement->IsHTML(nsGkAtoms::iframe) && !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) && !frameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)) { 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. nsRefPtr parent = GetParentDocshell(); 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); } NS_IMETHODIMP nsDocShell::SetFullscreenAllowed(bool aFullscreenAllowed) { if (!nsIDocShell::GetIsBrowserOrApp()) { // Only allow setting of fullscreenAllowed on content/process boundaries. // At non-boundaries the fullscreenAllowed attribute is calculated based on // whether all enclosing frames have the "mozFullscreenAllowed" attribute // set to "true". fullscreenAllowed is set at the process boundaries to // propagate the value of the parent's "mozFullscreenAllowed" attribute // across process boundaries. return NS_ERROR_UNEXPECTED; } mFullscreenAllowed = (aFullscreenAllowed ? PARENT_ALLOWS : PARENT_PROHIBITS); return NS_OK; } NS_IMETHODIMP nsDocShell::GetMayEnableCharacterEncodingMenu( bool* aMayEnableCharacterEncodingMenu) { *aMayEnableCharacterEncodingMenu = false; if (!mContentViewer) { return NS_OK; } nsIDocument* doc = mContentViewer->GetDocument(); if (!doc) { return NS_OK; } if (doc->WillIgnoreCharsetOverride()) { return NS_OK; } *aMayEnableCharacterEncodingMenu = true; return NS_OK; } NS_IMETHODIMP nsDocShell::GetDocShellEnumerator(int32_t aItemType, int32_t aDirection, nsISimpleEnumerator** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; nsRefPtr docShellEnum; if (aDirection == ENUMERATE_FORWARDS) { docShellEnum = new nsDocShellForwardsEnumerator; } else { docShellEnum = new nsDocShellBackwardsEnumerator; } if (!docShellEnum) { return NS_ERROR_OUT_OF_MEMORY; } nsresult rv = docShellEnum->SetEnumDocShellType(aItemType); if (NS_FAILED(rv)) { return rv; } rv = docShellEnum->SetEnumerationRootItem((nsIDocShellTreeItem*)this); if (NS_FAILED(rv)) { return rv; } rv = docShellEnum->First(); if (NS_FAILED(rv)) { return rv; } rv = docShellEnum->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void**)aResult); return rv; } NS_IMETHODIMP nsDocShell::GetAppType(uint32_t* aAppType) { *aAppType = mAppType; return NS_OK; } NS_IMETHODIMP nsDocShell::SetAppType(uint32_t 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::GetMarginWidth(int32_t* aWidth) { NS_ENSURE_ARG_POINTER(aWidth); *aWidth = mMarginWidth; return NS_OK; } NS_IMETHODIMP nsDocShell::SetMarginWidth(int32_t aWidth) { mMarginWidth = aWidth; return NS_OK; } NS_IMETHODIMP nsDocShell::GetMarginHeight(int32_t* aHeight) { NS_ENSURE_ARG_POINTER(aHeight); *aHeight = mMarginHeight; return NS_OK; } NS_IMETHODIMP nsDocShell::SetMarginHeight(int32_t aHeight) { mMarginHeight = aHeight; return NS_OK; } NS_IMETHODIMP nsDocShell::GetBusyFlags(uint32_t* aBusyFlags) { NS_ENSURE_ARG_POINTER(aBusyFlags); *aBusyFlags = mBusyFlags; return NS_OK; } NS_IMETHODIMP nsDocShell::TabToTreeOwner(bool aForward, bool* aTookFocus) { NS_ENSURE_ARG_POINTER(aTookFocus); nsCOMPtr chromeFocus = do_GetInterface(mTreeOwner); if (chromeFocus) { if (aForward) { *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusNextElement()); } else { *aTookFocus = NS_SUCCEEDED(chromeFocus->FocusPrevElement()); } } 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) { mSecurityUI = aSecurityUI; mSecurityUI->SetDocShell(this); 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 sUseErrorPages. if (mObserveErrorPages) { mObserveErrorPages = false; } mUseErrorPages = aUseErrorPages; return NS_OK; } NS_IMETHODIMP nsDocShell::GetPreviousTransIndex(int32_t* aPreviousTransIndex) { *aPreviousTransIndex = mPreviousTransIndex; return NS_OK; } NS_IMETHODIMP nsDocShell::GetLoadedTransIndex(int32_t* aLoadedTransIndex) { *aLoadedTransIndex = mLoadedTransIndex; 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. mPreviousTransIndex = std::max(-1, mPreviousTransIndex - aNumEntries); mLoadedTransIndex = std::max(0, mLoadedTransIndex - aNumEntries); nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shell = do_QueryObject(iter.GetNext()); if (shell) { shell->HistoryPurged(aNumEntries); } } return NS_OK; } nsresult nsDocShell::HistoryTransactionRemoved(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 == mPreviousTransIndex) { mPreviousTransIndex = -1; } else if (aIndex < mPreviousTransIndex) { --mPreviousTransIndex; } if (mLoadedTransIndex == aIndex) { mLoadedTransIndex = 0; } else if (aIndex < mLoadedTransIndex) { --mLoadedTransIndex; } nsTObserverArray::ForwardIterator iter(mChildList); while (iter.HasMore()) { nsCOMPtr shell = do_QueryObject(iter.GetNext()); if (shell) { static_cast(shell.get())->HistoryTransactionRemoved(aIndex); } } return NS_OK; } unsigned long nsDocShell::gProfileTimelineRecordingsCount = 0; NS_IMETHODIMP nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) { bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers(); if (currentValue != aValue) { if (aValue) { ++gProfileTimelineRecordingsCount; UseEntryScriptProfiling(); mProfileTimelineRecording = true; } else { --gProfileTimelineRecordingsCount; UnuseEntryScriptProfiling(); mProfileTimelineRecording = false; ClearProfileTimelineMarkers(); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) { *aValue = mProfileTimelineRecording; return NS_OK; } nsresult nsDocShell::PopProfileTimelineMarkers( JSContext* aCx, JS::MutableHandle aProfileTimelineMarkers) { // Looping over all markers gathered so far at the docShell level, whenever a // START marker is found, look for the corresponding END marker and build a // {name,start,end} JS object. // Paint markers are different because paint is handled at root docShell level // in the information that a paint was done is then stored at each sub // docShell level but we can only be sure that a paint did happen in a // docShell if an Layer marker type was recorded too. nsTArray profileTimelineMarkers; SequenceRooter rooter( aCx, &profileTimelineMarkers); // If we see an unpaired START, we keep it around for the next call // to PopProfileTimelineMarkers. We store the kept START objects in // this array. nsTArray keptMarkers; for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) { TimelineMarker* startPayload = mProfileTimelineMarkers[i]; const char* startMarkerName = startPayload->GetName(); bool hasSeenPaintedLayer = false; bool isPaint = strcmp(startMarkerName, "Paint") == 0; // If we are processing a Paint marker, we append information from // all the embedded Layer markers to this array. dom::Sequence layerRectangles; if (startPayload->GetMetaData() == TRACING_INTERVAL_START) { bool hasSeenEnd = false; // DOM events can be nested, so we must take care when searching // for the matching end. It doesn't hurt to apply this logic to // all event types. uint32_t markerDepth = 0; // The assumption is that the devtools timeline flushes markers frequently // enough for the amount of markers to always be small enough that the // nested for loop isn't going to be a performance problem. for (uint32_t j = i + 1; j < mProfileTimelineMarkers.Length(); ++j) { TimelineMarker* endPayload = mProfileTimelineMarkers[j]; const char* endMarkerName = endPayload->GetName(); // Look for Layer markers to stream out paint markers. if (isPaint && strcmp(endMarkerName, "Layer") == 0) { hasSeenPaintedLayer = true; endPayload->AddLayerRectangles(layerRectangles); } if (!startPayload->Equals(endPayload)) { continue; } // Pair start and end markers. if (endPayload->GetMetaData() == TRACING_INTERVAL_START) { ++markerDepth; } else if (endPayload->GetMetaData() == TRACING_INTERVAL_END) { if (markerDepth > 0) { --markerDepth; } else { // But ignore paint start/end if no layer has been painted. if (!isPaint || (isPaint && hasSeenPaintedLayer)) { mozilla::dom::ProfileTimelineMarker* marker = profileTimelineMarkers.AppendElement(); marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName()); marker->mStart = startPayload->GetTime(); marker->mEnd = endPayload->GetTime(); marker->mStack = startPayload->GetStack(); if (isPaint) { marker->mRectangles.Construct(layerRectangles); } startPayload->AddDetails(*marker); endPayload->AddDetails(*marker); } // We want the start to be dropped either way. hasSeenEnd = true; break; } } } // If we did not see the corresponding END, keep the START. if (!hasSeenEnd) { keptMarkers.AppendElement(mProfileTimelineMarkers[i]); mProfileTimelineMarkers.RemoveElementAt(i); --i; } } } ClearProfileTimelineMarkers(); mProfileTimelineMarkers.SwapElements(keptMarkers); if (!ToJSValue(aCx, profileTimelineMarkers, aProfileTimelineMarkers)) { JS_ClearPendingException(aCx); return NS_ERROR_UNEXPECTED; } return NS_OK; } nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) { bool ignore; *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds(); return NS_OK; } void nsDocShell::AddProfileTimelineMarker(const char* aName, TracingMetadata aMetaData) { if (mProfileTimelineRecording) { TimelineMarker* marker = new TimelineMarker(this, aName, aMetaData); mProfileTimelineMarkers.AppendElement(marker); } } void nsDocShell::AddProfileTimelineMarker(UniquePtr& aMarker) { if (mProfileTimelineRecording) { mProfileTimelineMarkers.AppendElement(aMarker.release()); } } NS_IMETHODIMP nsDocShell::SetWindowDraggingAllowed(bool aValue) { nsRefPtr parent = GetParentDocshell(); 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. nsRefPtr parent = GetParentDocshell(); if (mItemType == typeChrome && !parent) { // Top level chrome window *aValue = true; } else { *aValue = mWindowDraggingAllowed; } return NS_OK; } void nsDocShell::ClearProfileTimelineMarkers() { for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) { delete mProfileTimelineMarkers[i]; } mProfileTimelineMarkers.Clear(); } nsIDOMStorageManager* nsDocShell::TopSessionStorageManager() { nsresult rv; nsCOMPtr topItem; rv = GetSameTypeRootTreeItem(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 = do_CreateInstance("@mozilla.org/dom/sessionStorage-manager;1"); } return mSessionStorageManager; } NS_IMETHODIMP nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aDocumentURI, bool aCreate, nsIDOMStorage** aStorage) { nsCOMPtr manager = TopSessionStorageManager(); if (!manager) { return NS_ERROR_UNEXPECTED; } nsCOMPtr domWin = do_GetInterface(GetAsSupports(this)); if (aCreate) { return manager->CreateStorage(domWin, aPrincipal, aDocumentURI, mInPrivateBrowsing, aStorage); } return manager->GetStorage(domWin, aPrincipal, mInPrivateBrowsing, aStorage); } nsresult nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, nsIDOMStorage* aStorage) { nsRefPtr storage = static_cast(aStorage); if (!storage) { return NS_ERROR_UNEXPECTED; } nsIPrincipal* storagePrincipal = storage->GetPrincipal(); if (storagePrincipal != aPrincipal) { NS_ERROR("Wanting to add a sessionStorage for different principal"); return NS_ERROR_DOM_SECURITY_ERR; } nsCOMPtr manager = TopSessionStorageManager(); if (!manager) { return NS_ERROR_UNEXPECTED; } return manager->CloneStorage(aStorage); } NS_IMETHODIMP nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) { NS_IF_ADDREF(*aResult = GetCurrentDocChannel()); return NS_OK; } nsIChannel* nsDocShell::GetCurrentDocChannel() { if (mContentViewer) { nsIDocument* 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; } return mScrollObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) { nsWeakPtr obs = do_GetWeakReference(aObserver); return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; } void nsDocShell::NotifyAsyncPanZoomStarted(const mozilla::CSSIntPoint aScrollPos) { nsTObserverArray::ForwardIterator iter(mScrollObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (obs) { obs->AsyncPanZoomStarted(aScrollPos); } else { mScrollObservers.RemoveElement(ref); } } // Also notify child docshell for (uint32_t i = 0; i < mChildList.Length(); ++i) { nsCOMPtr kid = do_QueryInterface(ChildAt(i)); if (kid) { nsDocShell* docShell = static_cast(kid.get()); docShell->NotifyAsyncPanZoomStarted(aScrollPos); } } } void nsDocShell::NotifyAsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos) { nsTObserverArray::ForwardIterator iter(mScrollObservers); while (iter.HasMore()) { nsWeakPtr ref = iter.GetNext(); nsCOMPtr obs = do_QueryReferent(ref); if (obs) { obs->AsyncPanZoomStopped(aScrollPos); } else { mScrollObservers.RemoveElement(ref); } } // Also notify child docshell for (uint32_t i = 0; i < mChildList.Length(); ++i) { nsCOMPtr kid = do_QueryInterface(ChildAt(i)); if (kid) { nsDocShell* docShell = static_cast(kid.get()); docShell->NotifyAsyncPanZoomStopped(aScrollPos); } } } 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 = mName; return NS_OK; } NS_IMETHODIMP nsDocShell::SetName(const nsAString& aName) { mName = aName; return NS_OK; } NS_IMETHODIMP nsDocShell::NameEquals(const char16_t* aName, bool* aResult) { NS_ENSURE_ARG_POINTER(aName); NS_ENSURE_ARG_POINTER(aResult); *aResult = mName.Equals(aName); return NS_OK; } /* virtual */ int32_t nsDocShell::ItemType() { return mItemType; } NS_IMETHODIMP nsDocShell::GetItemType(int32_t* aItemType) { NS_ENSURE_ARG_POINTER(aItemType); *aItemType = ItemType(); return NS_OK; } NS_IMETHODIMP nsDocShell::SetItemType(int32_t aItemType) { NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType)); // Only allow setting the type on root docshells. Those would be the ones // that have the docloader service as mParent or have no mParent at all. nsCOMPtr docLoaderService = do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); NS_ENSURE_TRUE(docLoaderService, NS_ERROR_UNEXPECTED); NS_ENSURE_STATE(!mParent || mParent == docLoaderService); mItemType = aItemType; // disable auth prompting for anything but content mAllowAuth = mItemType == typeContent; nsRefPtr presContext = nullptr; GetPresContext(getter_AddRefs(presContext)); if (presContext) { presContext->UpdateIsChrome(); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetParent(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; } already_AddRefed nsDocShell::GetParentDocshell() { nsCOMPtr docshell = do_QueryInterface(GetAsSupports(mParent)); return docshell.forget().downcast(); } void nsDocShell::RecomputeCanExecuteScripts() { bool old = mCanExecuteScripts; nsRefPtr parent = GetParentDocshell(); // If we have no tree owner, that means that we've been detached from the // docshell tree (this is distinct from having no parent dochshell, 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(); nsDocLoader::SetDocLoaderParent(aParent); 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; nsCOMPtr parentAsDocShell(do_QueryInterface(parent)); if (parentAsDocShell) { if (NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) { SetAllowPlugins(value); } if (NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) { SetAllowJavascript(value); } if (NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) { SetAllowMetaRedirects(value); } if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) { SetAllowSubframes(value); } if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) { SetAllowImages(value); } SetAllowMedia(parentAsDocShell->GetAllowMedia()); if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) { SetAllowWindowControl(value); } SetAllowContentRetargeting(parentAsDocShell->GetAllowContentRetargetingOnChildren()); if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) { SetIsActive(value); } if (parentAsDocShell->GetIsPrerendered()) { SetIsPrerendered(true); } if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { value = false; } SetAllowDNSPrefetch(value); value = parentAsDocShell->GetAffectPrivateSessionLifetime(); SetAffectPrivateSessionLifetime(value); uint32_t flags; if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) { SetDefaultLoadFlags(flags); } } nsCOMPtr parentAsLoadContext(do_QueryInterface(parent)); if (parentAsLoadContext && NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) { SetPrivateBrowsing(value); } nsCOMPtr parentURIListener(do_GetInterface(parent)); if (parentURIListener) { mContentListener->SetParentContentListener(parentURIListener); } // Our parent has changed. Recompute scriptability. RecomputeCanExecuteScripts(); return NS_OK; } NS_IMETHODIMP nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent) { NS_ENSURE_ARG_POINTER(aParent); *aParent = nullptr; if (nsIDocShell::GetIsBrowserOrApp()) { return NS_OK; } nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent)); if (!parent) { return NS_OK; } if (parent->ItemType() == mItemType) { parent.swap(*aParent); } return NS_OK; } NS_IMETHODIMP nsDocShell::GetSameTypeParentIgnoreBrowserAndAppBoundaries(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::GetRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) { NS_ENSURE_ARG_POINTER(aRootTreeItem); nsRefPtr root = this; nsRefPtr parent = root->GetParentDocshell(); while (parent) { root = parent; parent = root->GetParentDocshell(); } root.forget(aRootTreeItem); return NS_OK; } NS_IMETHODIMP nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) { NS_ENSURE_ARG_POINTER(aRootTreeItem); *aRootTreeItem = static_cast(this); nsCOMPtr parent; NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); while (parent) { *aRootTreeItem = parent; NS_ENSURE_SUCCESS( (*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)), NS_ERROR_FAILURE); } NS_ADDREF(*aRootTreeItem); return NS_OK; } /* static */ bool nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem, nsIDocShellTreeItem* aAccessingItem, bool aConsiderOpener) { NS_PRECONDITION(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 or neither to nsIDocShell. return false; } if (targetDS && accessingDS && (targetDS->GetIsInBrowserElement() != accessingDS->GetIsInBrowserElement() || targetDS->GetAppId() != accessingDS->GetAppId())) { return false; } nsCOMPtr accessingRoot; aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot)); 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->GetSameTypeParent(getter_AddRefs(parent)); parent.swap(target); } while (target); nsCOMPtr targetRoot; aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot)); 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(getter_AddRefs(targetOpener)); nsCOMPtr openerWebNav(do_GetInterface(targetOpener)); nsCOMPtr openerItem(do_QueryInterface(openerWebNav)); if (!openerItem) { return false; } return CanAccessItem(openerItem, aAccessingItem, false); } static bool ItemIsActive(nsIDocShellTreeItem* aItem) { nsCOMPtr window = aItem->GetWindow(); if (window) { bool isClosed; if (NS_SUCCEEDED(window->GetClosed(&isClosed)) && !isClosed) { return true; } } return false; } NS_IMETHODIMP nsDocShell::FindItemWithName(const char16_t* aName, nsISupports* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aResult) { NS_ENSURE_ARG(aName); NS_ENSURE_ARG_POINTER(aResult); // If we don't find one, we return NS_OK and a null result *aResult = nullptr; if (!*aName) { 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, 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; nsDependentString name(aName); if (name.LowerCaseEqualsLiteral("_self")) { foundItem = this; } else if (name.LowerCaseEqualsLiteral("_blank")) { // Just return null. Caller must handle creating a new window with // a blank name himself. return NS_OK; } else if (name.LowerCaseEqualsLiteral("_parent")) { GetSameTypeParent(getter_AddRefs(foundItem)); if (!foundItem) { foundItem = this; } } else if (name.LowerCaseEqualsLiteral("_top")) { GetSameTypeRootTreeItem(getter_AddRefs(foundItem)); NS_ASSERTION(foundItem, "Must have this; worst case it's us!"); } // _main is an IE target which should be case-insensitive but isn't // see bug 217886 for details else if (name.LowerCaseEqualsLiteral("_content") || name.EqualsLiteral("_main")) { // Must pass our same type root as requestor to the // treeowner to make sure things work right. nsCOMPtr root; GetSameTypeRootTreeItem(getter_AddRefs(root)); if (mTreeOwner) { NS_ASSERTION(root, "Must have this; worst case it's us!"); mTreeOwner->FindItemWithName(aName, root, aOriginalRequestor, getter_AddRefs(foundItem)); } #ifdef DEBUG else { NS_ERROR("Someone isn't setting up the tree owner. " "You might like to try that. " "Things will.....you know, work."); // Note: _content should always exist. If we don't have one // hanging off the treeowner, just create a named window.... // so don't return here, in case we did that and can now find // it. // XXXbz should we be using |root| instead of creating // a new window? } #endif } else { // Do the search for item by an actual name. DoFindItemWithName(aName, aRequestor, aOriginalRequestor, 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; } } nsresult nsDocShell::DoFindItemWithName(const char16_t* aName, nsISupports* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aResult) { // First we check our name. if (mName.Equals(aName) && ItemIsActive(this) && CanAccessItem(this, aOriginalRequestor)) { NS_ADDREF(*aResult = this); return NS_OK; } // This QI may fail, but the places where we want to compare, comparing // against nullptr serves the same purpose. nsCOMPtr reqAsTreeItem(do_QueryInterface(aRequestor)); // 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, reqAsTreeItem, 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 == reqAsTreeItem) { return NS_OK; } if (parentAsTreeItem->ItemType() == mItemType) { return parentAsTreeItem->FindItemWithName( aName, static_cast(this), aOriginalRequestor, aResult); } } // If the parent is null or not of the same type fall through and ask tree // owner. // This may fail, but comparing against null serves the same purpose nsCOMPtr reqAsTreeOwner(do_QueryInterface(aRequestor)); if (mTreeOwner && mTreeOwner != reqAsTreeOwner) { return mTreeOwner->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) { nsCOMPtr 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->GetSameTypeParent(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->GetSameTypeParent(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; GetSameTypeRootTreeItem(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; } #ifdef DEBUG_DOCSHELL_FOCUS static void PrintDocTree(nsIDocShellTreeItem* aParentNode, int aLevel) { for (int32_t i = 0; i < aLevel; i++) { printf(" "); } int32_t childWebshellCount; aParentNode->GetChildCount(&childWebshellCount); nsCOMPtr parentAsDocShell(do_QueryInterface(aParentNode)); int32_t type = aParentNode->ItemType(); nsCOMPtr presShell = parentAsDocShell->GetPresShell(); nsRefPtr presContext; parentAsDocShell->GetPresContext(getter_AddRefs(presContext)); nsIDocument* doc = presShell->GetDocument(); nsCOMPtr domwin(doc->GetWindow()); nsCOMPtr widget; nsViewManager* vm = presShell->GetViewManager(); if (vm) { vm->GetWidget(getter_AddRefs(widget)); } dom::Element* rootElement = doc->GetRootElement(); printf("DS %p Ty %s Doc %p DW %p EM %p CN %p\n", (void*)parentAsDocShell.get(), type == nsIDocShellTreeItem::typeChrome ? "Chr" : "Con", (void*)doc, (void*)domwin.get(), (void*)presContext->EventStateManager(), (void*)rootElement); if (childWebshellCount > 0) { for (int32_t i = 0; i < childWebshellCount; i++) { nsCOMPtr child; aParentNode->GetChildAt(i, getter_AddRefs(child)); PrintDocTree(child, aLevel + 1); } } } static void PrintDocTree(nsIDocShellTreeItem* aParentNode) { NS_ASSERTION(aParentNode, "Pointer is null!"); nsCOMPtr parentItem; aParentNode->GetParent(getter_AddRefs(parentItem)); while (parentItem) { nsCOMPtr tmp; parentItem->GetParent(getter_AddRefs(tmp)); if (!tmp) { break; } parentItem = tmp; } if (!parentItem) { parentItem = aParentNode; } PrintDocTree(parentItem, 0); } #endif NS_IMETHODIMP nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) { #ifdef DEBUG_DOCSHELL_FOCUS nsCOMPtr item(do_QueryInterface(aTreeOwner)); if (item) { PrintDocTree(item); } #endif // 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); } } // 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; } NS_IMETHODIMP nsDocShell::SetChildOffset(uint32_t aChildOffset) { mChildOffset = aChildOffset; return NS_OK; } NS_IMETHODIMP nsDocShell::GetHistoryID(uint64_t* aID) { *aID = mHistoryID; return NS_OK; } NS_IMETHODIMP nsDocShell::GetIsInUnload(bool* aIsInUnload) { *aIsInUnload = mFiredUnloadEvent; return NS_OK; } NS_IMETHODIMP nsDocShell::GetChildCount(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); nsRefPtr 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) { childsParent->RemoveChildLoader(childAsDocLoader); } // 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; } nsIDocument* doc = mContentViewer->GetDocument(); if (!doc) { return NS_OK; } bool isWyciwyg = false; if (mCurrentURI) { // Check if the url is wyciwyg mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg); } if (!isWyciwyg) { // If this docshell is loaded from a wyciwyg: URI, don't // advertise our charset since it does not in any way reflect // the actual source charset, which is what we're trying to // expose here. const nsACString& 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); nsRefPtr 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::GetChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) { NS_ENSURE_ARG_POINTER(aChild); #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); NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); return CallQueryInterface(child, aChild); } NS_IMETHODIMP nsDocShell::FindChildWithName(const char16_t* aName, bool aRecurse, bool aSameType, nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aResult) { NS_ENSURE_ARG(aName); NS_ENSURE_ARG_POINTER(aResult); // if we don't find one, we return NS_OK and a null result *aResult = nullptr; if (!*aName) { return NS_OK; } nsXPIDLString childName; 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 = false; mLSHE->GetExpirationStatus(&parentExpired); /* 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 = nsIDocShellLoadInfo::loadHistory; mLSHE->GetLoadType(&loadType); // If the user did a shift-reload on this frameset page, // we don't want to load the subframes from history. if (loadType == nsIDocShellLoadInfo::loadReloadBypassCache || loadType == nsIDocShellLoadInfo::loadReloadBypassProxy || loadType == nsIDocShellLoadInfo::loadReloadBypassProxyAndCache || loadType == nsIDocShellLoadInfo::loadRefresh) { 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 == nsIDocShellLoadInfo::loadReloadNormal)) { // The parent has expired. Return null. *aResult = nullptr; return rv; } nsCOMPtr container(do_QueryInterface(mLSHE)); if (container) { // Get the child subframe from session history. rv = container->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 */ nsCOMPtr container(do_QueryInterface(mLSHE, &rv)); if (container) { if (NS_FAILED(container->ReplaceChild(aNewEntry))) { rv = container->AddChild(aNewEntry, aChildOffset); } } } else if (!aCloneRef) { /* This is an initial load in some subframe. Just append it if we can */ nsCOMPtr container(do_QueryInterface(mOSHE, &rv)); if (container) { rv = container->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. */ int32_t index = -1; nsCOMPtr currentHE; mSessionHistory->GetIndex(&index); if (index < 0) { return NS_ERROR_FAILURE; } rv = mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(currentHE)); NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE); nsCOMPtr currentEntry(do_QueryInterface(currentHE)); if (currentEntry) { uint32_t cloneID = 0; nsCOMPtr nextEntry; aCloneRef->GetID(&cloneID); rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry, aCloneChildren, getter_AddRefs(nextEntry)); if (NS_SUCCEEDED(rv)) { nsCOMPtr shPrivate = do_QueryInterface(mSessionHistory); NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE); rv = shPrivate->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 nsCOMPtr rootSH; GetRootSessionHistory(getter_AddRefs(rootSH)); if (rootSH) { rootSH->GetIndex(&mPreviousTransIndex); } nsresult rv; nsCOMPtr parent = do_QueryInterface(GetAsSupports(mParent), &rv); if (parent) { rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren); } if (rootSH) { rootSH->GetIndex(&mLoadedTransIndex); #ifdef DEBUG_PAGE_CACHE printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex, mLoadedTransIndex); #endif } return rv; } NS_IMETHODIMP nsDocShell::SetUseGlobalHistory(bool aUseGlobalHistory) { nsresult rv; mUseGlobalHistory = aUseGlobalHistory; if (!aUseGlobalHistory) { mGlobalHistory = nullptr; return NS_OK; } // No need to initialize mGlobalHistory if IHistory is available. nsCOMPtr history = services::GetHistoryService(); if (history) { return NS_OK; } if (mGlobalHistory) { return NS_OK; } mGlobalHistory = do_GetService(NS_GLOBALHISTORY2_CONTRACTID, &rv); return rv; } NS_IMETHODIMP nsDocShell::GetUseGlobalHistory(bool* aUseGlobalHistory) { *aUseGlobalHistory = mUseGlobalHistory; return NS_OK; } NS_IMETHODIMP nsDocShell::RemoveFromSessionHistory() { nsCOMPtr internalHistory; nsCOMPtr sessionHistory; nsCOMPtr root; GetSameTypeRootTreeItem(getter_AddRefs(root)); if (root) { nsCOMPtr rootAsWebnav = do_QueryInterface(root); if (rootAsWebnav) { rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory)); internalHistory = do_QueryInterface(sessionHistory); } } if (!internalHistory) { return NS_OK; } int32_t index = 0; sessionHistory->GetIndex(&index); nsAutoTArray ids; ids.AppendElement(mHistoryID); internalHistory->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; } nsIDocument* nsDocShell::GetDocument() { NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr); return mContentViewer->GetDocument(); } nsPIDOMWindow* nsDocShell::GetWindow() { NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr); return mScriptGlobal; } NS_IMETHODIMP nsDocShell::SetDeviceSizeIsPageSize(bool aValue) { if (mDeviceSizeIsPageSize != aValue) { mDeviceSizeIsPageSize = aValue; nsRefPtr presContext; GetPresContext(getter_AddRefs(presContext)); if (presContext) { presContext->MediaFeatureValuesChanged(nsRestyleHint(0)); } } return NS_OK; } NS_IMETHODIMP nsDocShell::GetDeviceSizeIsPageSize(bool* aValue) { *aValue = mDeviceSizeIsPageSize; return NS_OK; } void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) { nsCOMPtr shcontainer = do_QueryInterface(aEntry); nsCOMPtr rootSH; GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr history = do_QueryInterface(rootSH); if (!history || !shcontainer) { return; } int32_t count = 0; shcontainer->GetChildCount(&count); nsAutoTArray ids; for (int32_t i = 0; i < count; ++i) { nsCOMPtr child; shcontainer->GetChildAt(i, getter_AddRefs(child)); if (child) { uint64_t id = 0; child->GetDocshellID(&id); ids.AppendElement(id); } } int32_t index = 0; rootSH->GetIndex(&index); history->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) { if (!IsNavigationAllowed(false)) { *aCanGoBack = false; return NS_OK; // JS may not handle returning of an error code } nsresult rv; nsCOMPtr rootSH; rv = GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr webnav(do_QueryInterface(rootSH)); NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); rv = webnav->GetCanGoBack(aCanGoBack); return rv; } NS_IMETHODIMP nsDocShell::GetCanGoForward(bool* aCanGoForward) { if (!IsNavigationAllowed(false)) { *aCanGoForward = false; return NS_OK; // JS may not handle returning of an error code } nsresult rv; nsCOMPtr rootSH; rv = GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr webnav(do_QueryInterface(rootSH)); NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); rv = webnav->GetCanGoForward(aCanGoForward); return rv; } NS_IMETHODIMP nsDocShell::GoBack() { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } nsresult rv; nsCOMPtr rootSH; rv = GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr webnav(do_QueryInterface(rootSH)); NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); rv = webnav->GoBack(); return rv; } NS_IMETHODIMP nsDocShell::GoForward() { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } nsresult rv; nsCOMPtr rootSH; rv = GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr webnav(do_QueryInterface(rootSH)); NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); rv = webnav->GoForward(); return rv; } NS_IMETHODIMP nsDocShell::GotoIndex(int32_t aIndex) { if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } nsresult rv; nsCOMPtr rootSH; rv = GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr webnav(do_QueryInterface(rootSH)); NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); rv = webnav->GotoIndex(aIndex); return rv; } NS_IMETHODIMP nsDocShell::LoadURI(const char16_t* aURI, uint32_t aLoadFlags, nsIURI* aReferringURI, nsIInputStream* aPostStream, nsIInputStream* aHeaderStream) { return LoadURIWithOptions(aURI, aLoadFlags, aReferringURI, mozilla::net::RP_Default, aPostStream, aHeaderStream, nullptr); } NS_IMETHODIMP nsDocShell::LoadURIWithOptions(const char16_t* aURI, uint32_t aLoadFlags, nsIURI* aReferringURI, uint32_t aReferrerPolicy, nsIInputStream* aPostStream, nsIInputStream* aHeaderStream, nsIURI* aBaseURI) { NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags"); if (!IsNavigationAllowed()) { return NS_OK; // JS may not handle returning of an error code } nsCOMPtr uri; nsCOMPtr postStream(aPostStream); nsresult rv = NS_OK; // Create a URI from our string; if that succeeds, we want to // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP // flag. 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.StripChars("\r\n"); NS_ENSURE_TRUE(!uriString.IsEmpty(), NS_ERROR_FAILURE); rv = NS_NewURI(getter_AddRefs(uri), uriString); if (uri) { aLoadFlags &= ~LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; } nsCOMPtr fixupInfo; if (sURIFixup) { // Call the fixup object. This will clobber the rv from NS_NewURI // above, but that's fine with us. Note that we need to do this even // if NS_NewURI returned a URI, because fixup handles nested URIs, etc // (things like view-source:mozilla.org for example). uint32_t fixupFlags = 0; if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { fixupFlags |= nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; } if (aLoadFlags & LOAD_FLAGS_FIXUP_SCHEME_TYPOS) { fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS; } 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. postStream = fixupStream; } if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { nsCOMPtr serv = services::GetObserverService(); if (serv) { serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI); } } } // else no fixup service so just use the URI we created and see // what happens if (NS_ERROR_MALFORMED_URI == rv) { DisplayLoadError(rv, uri, aURI, nullptr); } if (NS_FAILED(rv) || !uri) { return NS_ERROR_FAILURE; } PopupControlState popupState; if (aLoadFlags & LOAD_FLAGS_ALLOW_POPUPS) { popupState = openAllowed; aLoadFlags &= ~LOAD_FLAGS_ALLOW_POPUPS; } else { popupState = openOverridden; } nsAutoPopupStatePusher statePusher(popupState); // Don't pass certain flags that aren't needed and end up confusing // ConvertLoadTypeToDocShellLoadInfo. We do need to ensure that they are // passed to LoadURI though, since it uses them. uint32_t extraFlags = (aLoadFlags & EXTRA_LOAD_FLAGS); aLoadFlags &= ~EXTRA_LOAD_FLAGS; nsCOMPtr loadInfo; rv = CreateLoadInfo(getter_AddRefs(loadInfo)); if (NS_FAILED(rv)) { return rv; } /* * 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] */ uint32_t loadType; if (aLoadFlags & LOAD_FLAGS_ALLOW_MIXED_CONTENT) { loadType = MAKE_LOAD_TYPE(LOAD_NORMAL_ALLOW_MIXED_CONTENT, aLoadFlags); } else { loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags); } loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType)); loadInfo->SetPostDataStream(postStream); loadInfo->SetReferrer(aReferringURI); loadInfo->SetReferrerPolicy(aReferrerPolicy); loadInfo->SetHeadersStream(aHeaderStream); loadInfo->SetBaseURI(aBaseURI); if (fixupInfo) { nsAutoString searchProvider, keyword; fixupInfo->GetKeywordProviderName(searchProvider); fixupInfo->GetKeywordAsSent(keyword); MaybeNotifyKeywordSearchLoading(searchProvider, keyword); } rv = LoadURI(uri, loadInfo, extraFlags, true); // Save URI string in case it's needed later when // sending to search engine service in EndPageLoad() mOriginalUriString = uriString; return rv; } NS_IMETHODIMP nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, const char16_t* aURL, nsIChannel* aFailedChannel) { // Get prompt and string bundle servcies 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); nsAutoString error; const uint32_t kMaxFormatStrArgs = 3; nsAutoString formatStrs[kMaxFormatStrArgs]; uint32_t formatStrCount = 0; 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[0]); 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); } formatStrCount = 1; error.AssignLiteral("unknownProtocolFound"); } else if (NS_ERROR_FILE_NOT_FOUND == aError) { NS_ENSURE_ARG_POINTER(aURI); error.AssignLiteral("fileNotFound"); } 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[0]); formatStrCount = 1; error.AssignLiteral("dnsNotFound"); } else if (NS_ERROR_CONNECTION_REFUSED == aError) { NS_ENSURE_ARG_POINTER(aURI); addHostPort = true; error.AssignLiteral("connectionFailure"); } else if (NS_ERROR_NET_INTERRUPT == aError) { NS_ENSURE_ARG_POINTER(aURI); addHostPort = true; error.AssignLiteral("netInterrupt"); } else if (NS_ERROR_NET_TIMEOUT == aError) { NS_ENSURE_ARG_POINTER(aURI); // Get the host nsAutoCString host; aURI->GetHost(host); CopyUTF8toUTF16(host, formatStrs[0]); formatStrCount = 1; error.AssignLiteral("netTimeout"); } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError || NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) { // CSP error cssClass.AssignLiteral("neterror"); error.AssignLiteral("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.AssignLiteral("sslv3Used"); addHostPort = true; } else { // Usually we should have aFailedChannel and get a detailed message tsi->GetErrorMessage(getter_Copies(messageStr)); } } else { // No channel, let's obtain the generic error message if (nsserr) { nsserr->GetErrorMessage(aError, messageStr); } } if (!messageStr.IsEmpty()) { if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) { error.AssignLiteral("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 = mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; bool isStsHost = false; bool isPinnedHost = false; if (XRE_GetProcessType() == GoannaProcessType_Default) { nsCOMPtr sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags, &isStsHost); NS_ENSURE_SUCCESS(rv, rv); rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI, flags, &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, &isStsHost); cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, uri, flags, &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"); } uint32_t bucketId; if (isStsHost) { // measuring STS separately allows us to measure click through // rates easily bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP_STS; } else { bucketId = nsISecurityUITelemetry::WARNING_BAD_CERT_TOP; } // See if an alternate cert error page is registered nsAdoptingCString alternateErrorPage = Preferences::GetCString("security.alternate_certificate_error_page"); if (alternateErrorPage) { errorPage.Assign(alternateErrorPage); } if (!IsFrame() && errorPage.EqualsIgnoreCase("certerror")) { Telemetry::Accumulate(mozilla::Telemetry::SECURITY_UI, bucketId); } } else { error.AssignLiteral("nssFailure2"); } } } else if (NS_ERROR_PHISHING_URI == aError || NS_ERROR_MALWARE_URI == aError) { nsAutoCString host; aURI->GetHost(host); CopyUTF8toUTF16(host, formatStrs[0]); formatStrCount = 1; // 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 nsAdoptingCString alternateErrorPage = Preferences::GetCString("urlclassifier.alternate_error_page"); if (alternateErrorPage) { errorPage.Assign(alternateErrorPage); } uint32_t bucketId; if (NS_ERROR_PHISHING_URI == aError) { error.AssignLiteral("phishingBlocked"); bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME : nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP; } else { error.AssignLiteral("malwareBlocked"); bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME : nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP; } if (errorPage.EqualsIgnoreCase("blocked")) Telemetry::Accumulate(Telemetry::SECURITY_UI, bucketId); cssClass.AssignLiteral("blacklist"); } else if (NS_ERROR_CONTENT_CRASHED == aError) { errorPage.AssignLiteral("tabcrashed"); error.AssignLiteral("tabcrashed"); nsCOMPtr handler = mChromeEventHandler; if (handler) { nsCOMPtr element = do_QueryInterface(handler); element->GetAttribute(NS_LITERAL_STRING("crashedPageTitle"), 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(MOZ_UTF16(" ")); } } else { // Errors requiring simple formatting switch (aError) { case NS_ERROR_MALFORMED_URI: // URI is malformed error.AssignLiteral("malformedURI"); break; case NS_ERROR_REDIRECT_LOOP: // Doc failed to load because the server generated too many redirects error.AssignLiteral("redirectLoop"); break; case NS_ERROR_UNKNOWN_SOCKET_TYPE: // Doc failed to load because PSM is not installed error.AssignLiteral("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.AssignLiteral("netReset"); break; case NS_ERROR_DOCUMENT_NOT_CACHED: // Doc failed to load because the cache does not contain a copy of // the document. error.AssignLiteral("notCached"); break; case NS_ERROR_OFFLINE: // Doc failed to load because we are offline. error.AssignLiteral("netOffline"); break; case NS_ERROR_DOCUMENT_IS_PRINTMODE: // Doc navigation attempted while Printing or Print Preview error.AssignLiteral("isprinting"); break; case NS_ERROR_PORT_ACCESS_NOT_ALLOWED: // Port blocked for security reasons addHostPort = true; error.AssignLiteral("deniedPortAccess"); break; case NS_ERROR_UNKNOWN_PROXY_HOST: // Proxy hostname could not be resolved. error.AssignLiteral("proxyResolveFailure"); break; case NS_ERROR_PROXY_CONNECTION_REFUSED: // Proxy connection was refused. error.AssignLiteral("proxyConnectFailure"); break; case NS_ERROR_INVALID_CONTENT_ENCODING: // Bad Content Encoding. error.AssignLiteral("contentEncodingError"); break; case NS_ERROR_REMOTE_XUL: error.AssignLiteral("remoteXUL"); break; case NS_ERROR_UNSAFE_CONTENT_TYPE: // Channel refused to load from an unrecognized content type. error.AssignLiteral("unsafeContentType"); break; case NS_ERROR_CORRUPTED_CONTENT: // Broken Content Detected. e.g. Content-MD5 check failure. error.AssignLiteral("corruptedContentError"); break; default: break; } } // Test if the error should be displayed if (error.IsEmpty()) { return NS_OK; } // 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[formatStrCount++]); } nsAutoCString spec; rv = NS_ERROR_NOT_AVAILABLE; if (aURI) { // displaying "file://" is aesthetically unpleasing and could even be // confusing to the user bool isFileURI = false; rv = aURI->SchemeIs("file", &isFileURI); if (NS_SUCCEEDED(rv) && isFileURI) { aURI->GetPath(spec); } else { aURI->GetSpec(spec); } nsAutoCString charset; // unescape and convert from origin charset aURI->GetOriginCharset(charset); nsCOMPtr textToSubURI( do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { rv = textToSubURI->UnEscapeURIForUI(charset, spec, formatStrs[formatStrCount]); } } else { spec.Assign('?'); } if (NS_FAILED(rv)) { CopyUTF8toUTF16(spec, formatStrs[formatStrCount]); } rv = NS_OK; ++formatStrCount; const char16_t* strs[kMaxFormatStrArgs]; for (uint32_t i = 0; i < formatStrCount; i++) { strs[i] = formatStrs[i].get(); } nsXPIDLString str; rv = stringBundle->FormatStringFromName(error.get(), strs, formatStrCount, getter_Copies(str)); NS_ENSURE_SUCCESS(rv, rv); messageStr.Assign(str.get()); } // 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) { bool isSecureURI = false; rv = aURI->SchemeIs("https", &isSecureURI); if (NS_SUCCEEDED(rv) && isSecureURI) { // Maybe TLS intolerant. Treat this as an SSL error. error.AssignLiteral("nssFailure2"); } } if (UseErrorPages()) { // Display an error page LoadErrorPage(aURI, aURL, errorPage.get(), error.get(), messageStr.get(), cssClass.get(), aFailedChannel); } 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; } NS_IMETHODIMP nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, const char* aErrorPage, const char16_t* aErrorType, const char16_t* aDescription, const char* aCSSClass, nsIChannel* aFailedChannel) { #if defined(PR_LOGGING) && defined(DEBUG) if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) { nsAutoCString spec; aURI->GetSpec(spec); nsAutoCString chanName; if (aFailedChannel) { aFailedChannel->GetName(chanName); } else { chanName.AssignLiteral(""); } PR_LOG(gDocShellLog, PR_LOG_DEBUG, ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", this, spec.get(), NS_ConvertUTF16toUTF8(aURL).get(), chanName.get())); } #endif mFailedChannel = aFailedChannel; mFailedURI = aURI; 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(); } nsAutoCString url; nsAutoCString charset; if (aURI) { nsresult rv = aURI->GetSpec(url); NS_ENSURE_SUCCESS(rv, rv); rv = aURI->GetOriginCharset(charset); NS_ENSURE_SUCCESS(rv, rv); } else if (aURL) { CopyUTF16toUTF8(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(cstring, escArg1, escArg2) \ { \ char* s = nsEscape(escArg1, escArg2); \ if (!s) \ return NS_ERROR_OUT_OF_MEMORY; \ cstring.Adopt(s); \ } nsCString escapedUrl, escapedCharset, escapedError, escapedDescription, escapedCSSClass; SAFE_ESCAPE(escapedUrl, url.get(), url_Path); SAFE_ESCAPE(escapedCharset, charset.get(), url_Path); SAFE_ESCAPE(escapedError, NS_ConvertUTF16toUTF8(aErrorType).get(), url_Path); SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription).get(), url_Path); if (aCSSClass) { SAFE_ESCAPE(escapedCSSClass, aCSSClass, url_Path); } nsCString errorPageUrl("about:"); errorPageUrl.AppendASCII(aErrorPage); errorPageUrl.AppendLiteral("?e="); errorPageUrl.AppendASCII(escapedError.get()); errorPageUrl.AppendLiteral("&u="); errorPageUrl.AppendASCII(escapedUrl.get()); if (!escapedCSSClass.IsEmpty()) { errorPageUrl.AppendLiteral("&s="); errorPageUrl.AppendASCII(escapedCSSClass.get()); } errorPageUrl.AppendLiteral("&c="); errorPageUrl.AppendASCII(escapedCharset.get()); nsAutoCString frameType(FrameTypeToString(mFrameType)); errorPageUrl.AppendLiteral("&f="); errorPageUrl.AppendASCII(frameType.get()); // Append the manifest URL if the error comes from an app. nsString manifestURL; nsresult rv = GetAppManifestURL(manifestURL); if (manifestURL.Length() > 0) { nsCString manifestParam; SAFE_ESCAPE(manifestParam, NS_ConvertUTF16toUTF8(manifestURL).get(), url_Path); errorPageUrl.AppendLiteral("&m="); errorPageUrl.AppendASCII(manifestParam.get()); } // 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; rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); NS_ENSURE_SUCCESS(rv, rv); return InternalLoad2(errorPageURI, nullptr, false, nullptr, mozilla::net::RP_Default, nullptr, INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE, nullptr, true, NullString(), this, nullptr, 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 & 0xf) == 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 nsCOMPtr rootSH; rv = GetRootSessionHistory(getter_AddRefs(rootSH)); nsCOMPtr shistInt(do_QueryInterface(rootSH)); bool canReload = true; if (rootSH) { shistInt->NotifyOnHistoryReload(mCurrentURI, aReloadFlags, &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 { nsCOMPtr doc(GetDocument()); // Do not inherit owner from document uint32_t flags = INTERNAL_LOAD_FLAGS_NONE; nsAutoString srcdoc; nsIPrincipal* principal = nullptr; nsAutoString contentTypeHint; nsCOMPtr baseURI; nsCOMPtr originalURI; bool loadReplace = false; if (doc) { principal = doc->NodePrincipal(); 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)); } } } rv = InternalLoad2(mCurrentURI, originalURI, loadReplace, mReferrerURI, mReferrerPolicy, principal, flags, nullptr, // No window target NS_LossyConvertUTF16toASCII(contentTypeHint).get(), NullString(), // No forced download nullptr, // No post data nullptr, // No headers data loadType, // Load type nullptr, // No SHEntry true, srcdoc, // srcdoc argument for iframe this, // For reloads we are the source baseURI, nullptr, // No nsIDocShell nullptr); // No nsIRequest } 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(nsIDOMDocument** aDocument) { NS_ENSURE_ARG_POINTER(aDocument); NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE); return mContentViewer->GetDOMDocument(aDocument); } NS_IMETHODIMP nsDocShell::GetCurrentURI(nsIURI** aURI) { NS_ENSURE_ARG_POINTER(aURI); if (mCurrentURI) { return NS_EnsureSafeToReturn(mCurrentURI, aURI); } *aURI = nullptr; return NS_OK; } NS_IMETHODIMP nsDocShell::GetReferringURI(nsIURI** aURI) { NS_ENSURE_ARG_POINTER(aURI); *aURI = mReferrerURI; NS_IF_ADDREF(*aURI); return NS_OK; } NS_IMETHODIMP nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory) { NS_ENSURE_TRUE(aSessionHistory, NS_ERROR_FAILURE); // make sure that we are the root docshell and // set a handle to root docshell in SH. nsCOMPtr root; /* Get the root docshell. If *this* is the root docshell * then save a handle to *this* in SH. SH needs it to do * traversions thro' its entries */ GetSameTypeRootTreeItem(getter_AddRefs(root)); NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); if (root.get() == static_cast(this)) { mSessionHistory = aSessionHistory; nsCOMPtr shPrivate = do_QueryInterface(mSessionHistory); NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE); shPrivate->SetRootDocShell(this); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsDocShell::GetSessionHistory(nsISHistory** aSessionHistory) { NS_ENSURE_ARG_POINTER(aSessionHistory); *aSessionHistory = mSessionHistory; NS_IF_ADDREF(*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) { nsCOMPtr oldUri, newUri; nsCString spec, newSpec; // Create a new view-source URI and replace the original. rv = shEntry->GetURI(getter_AddRefs(oldUri)); if (NS_FAILED(rv)) { return rv; } oldUri->GetSpec(spec); newSpec.AppendLiteral("view-source:"); newSpec.Append(spec); rv = NS_NewURI(getter_AddRefs(newUri), newSpec); if (NS_FAILED(rv)) { return rv; } shEntry->SetURI(newUri); nsCOMPtr entryESR38 = do_QueryInterface(shEntry); if (entryESR38) { entryESR38->SetOriginalURI(nullptr); } } rv = LoadHistoryEntry(shEntry, LOAD_HISTORY); return rv; } NS_IMETHODIMP nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) { NS_PRECONDITION(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, false); 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); } // Should we use XUL error pages instead of alerts if possible? mUseErrorPages = Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages); if (!gAddedPreferencesVarCache) { Preferences::AddBoolVarCache(&sUseErrorPages, "browser.xul.error_pages.enabled", mUseErrorPages); gAddedPreferencesVarCache = true; } 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() { NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, "Unexpected item type in docshell"); if (!mIsBeingDestroyed) { 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; // 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; mTransferableHookData = 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 (mURLSearchParams) { mURLSearchParams->RemoveObservers(); mURLSearchParams = 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) nsCOMPtr shPrivate = do_QueryInterface(mSessionHistory); if (shPrivate) { shPrivate->EvictAllContentViewers(); } mSessionHistory = nullptr; } SetTreeOwner(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 (mInPrivateBrowsing) { mInPrivateBrowsing = 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::SetPosition(int32_t aX, int32_t aY) { mBounds.x = aX; mBounds.y = aY; if (mContentViewer) { NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE); } return NS_OK; } 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); } 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, bool aFRepaint) { mBounds.x = aX; mBounds.y = aY; mBounds.width = aWidth; mBounds.height = aHeight; // Hold strong ref, since SetBounds can make us null out mContentViewer nsCOMPtr viewer = mContentViewer; if (viewer) { // XXX Border figured in here or is that handled elsewhere? NS_ENSURE_SUCCESS(viewer->SetBounds(mBounds), 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 nsIntRect r; mParentWidget->GetClientBounds(r); SetPositionAndSize(mBounds.x, mBounds.y, r.width, r.height, false); } // 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. nsCOMPtr doc(do_GetInterface(GetAsSupports(mParent))); if (doc) { doc->FlushPendingNotifications(Flush_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) { nsCOMPtr presShell = GetPresShell(); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); nsViewManager* 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) { 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 nsIXULWindow return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDocShell::GetVisibility(bool* aVisibility) { NS_ENSURE_ARG_POINTER(aVisibility); *aVisibility = false; if (!mContentViewer) { return NS_OK; } nsCOMPtr 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. nsRefPtr docShell = this; nsRefPtr parentItem = docShell->GetParentDocshell(); while (parentItem) { presShell = docShell->GetPresShell(); nsCOMPtr pPresShell = parentItem->GetPresShell(); // Null-check for crash in bug 267804 if (!pPresShell) { NS_NOTREACHED("parent docshell has null pres shell"); return NS_OK; } vm = presShell->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->GetParentDocshell(); } 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) { // We disallow setting active on chrome docshells. if (mItemType == nsIDocShellTreeItem::typeChrome) { return NS_ERROR_INVALID_ARG; } // Keep track ourselves. mIsActive = aIsActive; // Tell the PresShell about it. nsCOMPtr pshell = GetPresShell(); if (pshell) { pshell->SetIsActive(aIsActive); } // Tell the window about it if (mScriptGlobal) { mScriptGlobal->SetIsBackground(!aIsActive); if (nsCOMPtr doc = mScriptGlobal->GetExtantDoc()) { doc->PostVisibilityUpdateEvent(); } } // Recursively tell all of our children, but don't tell