/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 tw=80 et: */ /* 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/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" #ifdef MOZ_LOGGING // so we can get logging even in release builds (but only for some things) #define FORCE_PR_LOG 1 #endif #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 "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 "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" // 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 "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 "nsIChannelPolicy.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" 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 perfOverStarvation) { nsCOMPtr appShell = do_GetService(kAppShellCID); if (appShell) { appShell->FavorPerformanceHint(perfOverStarvation, 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 *maxPerLink, bool *requireSameHost) { bool allow = Preferences::GetBool(PREF_PINGS_ENABLED, false); *maxPerLink = 1; *requireSameHost = true; if (allow) { Preferences::GetInt(PREF_PINGS_MAX_PER_LINK, maxPerLink); Preferences::GetBool(PREF_PINGS_REQUIRE_SAME_HOST, requireSameHost); } return allow; } static bool CheckPingURI(nsIURI* uri, nsIContent* content) { if (!uri) return false; // Check with nsIScriptSecurityManager nsCOMPtr ssmgr = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); NS_ENSURE_TRUE(ssmgr, false); nsresult rv = ssmgr->CheckLoadURIWithPrincipal(content->NodePrincipal(), uri, nsIScriptSecurityManager::STANDARD); if (NS_FAILED(rv)) { return false; } // Ignore non-HTTP(S) bool match; if ((NS_FAILED(uri->SchemeIs("http", &match)) || !match) && (NS_FAILED(uri->SchemeIs("https", &match)) || !match)) { return false; } // Check with contentpolicy int16_t shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_PING, uri, content->NodePrincipal(), content, 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 *content, ForEachPingCallback callback, void *closure) { // 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(content)) return; nsCOMPtr pingAtom = do_GetAtom("ping"); if (!pingAtom) return; nsAutoString value; content->GetAttr(kNameSpaceID_None, pingAtom, value); if (value.IsEmpty()) return; nsCOMPtr ios = do_GetIOService(); if (!ios) return; nsIDocument *doc = content->OwnerDoc(); nsWhitespaceTokenizer tokenizer(value); while (tokenizer.hasMoreTokens()) { nsCOMPtr uri, baseURI = content->GetBaseURI(); ios->NewURI(NS_ConvertUTF16toUTF8(tokenizer.nextToken()), doc->GetDocumentCharacterSet().get(), baseURI, getter_AddRefs(uri)); if (CheckPingURI(uri, content)) { callback(closure, content, uri, ios); } } } //---------------------------------------------------------------------- // We wait this many milliseconds before killing the ping channel... #define PING_TIMEOUT 10000 static void OnPingTimeout(nsITimer *timer, void *closure) { nsILoadGroup *loadGroup = static_cast(closure); if (loadGroup) loadGroup->Cancel(NS_ERROR_ABORT); } // Check to see if two URIs have the same host or not static bool IsSameHost(nsIURI *uri1, nsIURI *uri2) { nsAutoCString host1, host2; uri1->GetAsciiHost(host1); uri2->GetAsciiHost(host2); return host1.Equals(host2); } class nsPingListener MOZ_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 requireSameHost, nsIContent* content, nsILoadGroup* loadGroup) : mRequireSameHost(requireSameHost), mContent(content), mLoadGroup(loadGroup) {} 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 *request, nsISupports *context) { return NS_OK; } NS_IMETHODIMP nsPingListener::OnDataAvailable(nsIRequest *request, nsISupports *context, nsIInputStream *stream, uint64_t offset, uint32_t count) { uint32_t result; return stream->ReadSegments(NS_DiscardSegment, nullptr, count, &result); } NS_IMETHODIMP nsPingListener::OnStopRequest(nsIRequest *request, nsISupports *context, nsresult status) { mLoadGroup = nullptr; if (mTimer) { mTimer->Cancel(); mTimer = nullptr; } return NS_OK; } NS_IMETHODIMP nsPingListener::GetInterface(const nsIID &iid, void **result) { if (iid.Equals(NS_GET_IID(nsIChannelEventSink))) { NS_ADDREF_THIS(); *result = (nsIChannelEventSink *) this; return NS_OK; } return NS_ERROR_NO_INTERFACE; } NS_IMETHODIMP nsPingListener::AsyncOnChannelRedirect(nsIChannel *oldChan, nsIChannel *newChan, uint32_t flags, nsIAsyncVerifyRedirectCallback *callback) { nsCOMPtr newURI; newChan->GetURI(getter_AddRefs(newURI)); if (!CheckPingURI(newURI, mContent)) return NS_ERROR_ABORT; if (!mRequireSameHost) { callback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } // XXXbz should this be using something more like the nsContentUtils // same-origin checker? nsCOMPtr oldURI; oldChan->GetURI(getter_AddRefs(oldURI)); NS_ENSURE_STATE(oldURI && newURI); if (!IsSameHost(oldURI, newURI)) return NS_ERROR_ABORT; callback->OnRedirectVerifyCallback(NS_OK); return NS_OK; } struct SendPingInfo { int32_t numPings; int32_t maxPings; bool requireSameHost; nsIURI *target; nsIURI *referrer; }; static void SendPing(void *closure, nsIContent *content, nsIURI *uri, nsIIOService *ios) { SendPingInfo *info = static_cast(closure); 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(uri, info->referrer)) return; } nsIDocument *doc = content->OwnerDoc(); nsCOMPtr chan; ios->NewChannelFromURI(uri, getter_AddRefs(chan)); 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, uri, 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->SetReferrer(info->referrer); } 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, content, loadGroup); if (!pingListener) return; 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 *content, nsIURI *target, nsIURI *referrer) { SendPingInfo info; if (!PingsEnabled(&info.maxPings, &info.requireSameHost)) return; if (info.maxPings == 0) return; info.numPings = 0; info.target = target; info.referrer = referrer; ForEachPing(content, SendPing, &info); } static nsDOMPerformanceNavigationType ConvertLoadTypeToNavigationType(uint32_t aLoadType) { // Not initialized, assume it's normal load. if (aLoadType == 0) { aLoadType = LOAD_NORMAL; } nsDOMPerformanceNavigationType 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 *entry); static void IncreasePrivateDocShellCount() { gNumberOfPrivateDocShells++; if (gNumberOfPrivateDocShells > 1 || XRE_GetProcessType() != GeckoProcessType_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() == GeckoProcessType_Content) { mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); cc->SendPrivateDocShellsExist(false); return; } nsCOMPtr obsvc = mozilla::services::GetObserverService(); if (obsvc) { obsvc->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); } } } //***************************************************************************** //*** nsDocShell: Object Management //***************************************************************************** static uint64_t gDocshellIDCounter = 0; // Note: operator new zeros our memory 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), mCreatingDocument(false), mUseErrorPages(false), mObserveErrorPages(true), mAllowAuth(true), mAllowKeywordFixup(false), mIsOffScreenBrowser(false), mIsActive(true), mIsAppTab(false), mUseGlobalHistory(false), mInPrivateBrowsing(false), mUseRemoteTabs(false), mDeviceSizeIsPageSize(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), mDefaultLoadFlags(nsIRequest::LOAD_NORMAL), mFrameType(eFrameTypeRegular), mOwnOrContainingAppId(nsIScriptSecurityManager::UNKNOWN_APP_ID), mParentCharsetSource(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 (nullptr == 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(), SafeCast(mHistoryID)); } #endif } nsDocShell::~nsDocShell() { 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(), SafeCast(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) 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_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. if (IsPrintingOrPP()) { return NS_OK; // JS may not handle returning of an error code } nsCOMPtr referrer; nsCOMPtr postStream; nsCOMPtr headersStream; nsCOMPtr owner; bool inheritOwner = false; bool ownerIsExplicit = false; bool sendReferrer = true; 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)); 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->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 == nullptr) { // 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::IsSystemOrExpandedPrincipal(ownerPrincipal)) { if (ownerIsExplicit) { return NS_ERROR_DOM_SECURITY_ERR; } owner = nullptr; inheritOwner = true; } } 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 InternalLoad(aURI, referrer, 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; // build up a channel for this stream. nsCOMPtr channel; NS_ENSURE_SUCCESS(NS_NewInputStreamChannel (getter_AddRefs(channel), uri, aStream, aContentType, aContentCharset), 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) { return; } 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; } NS_IMETHODIMP 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); 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::GetUseRemoteTabs(bool* aUseRemoteTabs) { NS_ENSURE_ARG_POINTER(aUseRemoteTabs); *aUseRemoteTabs = mUseRemoteTabs; return NS_OK; } NS_IMETHODIMP nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) { #ifdef MOZ_CRASHREPORTER if (aUseRemoteTabs) { CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("DOMIPCEnabled"), NS_LITERAL_CSTRING("1")); } #endif 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) { mAllowContentRetargeting = aAllowContentRetargeting; 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 **outEnum) { NS_ENSURE_ARG_POINTER(outEnum); *outEnum = 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 **)outEnum); 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 *zoom) { NS_ENSURE_ARG_POINTER(zoom); *zoom = 1.0f; return NS_OK; } NS_IMETHODIMP nsDocShell::SetZoom(float zoom) { 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; } 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; } 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 *_retval) { NS_ENSURE_ARG_POINTER(aName); NS_ENSURE_ARG_POINTER(_retval); *_retval = 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->GetAllowContentRetargeting()); if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) { SetIsActive(value); } 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 ** _retval) { NS_ENSURE_ARG(aName); NS_ENSURE_ARG_POINTER(_retval); // If we don't find one, we return NS_OK and a null result *_retval = 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, _retval); } 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(*_retval); } return NS_OK; } } nsresult nsDocShell::DoFindItemWithName(const char16_t* aName, nsISupports* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** _retval) { // First we check our name. if (mName.Equals(aName) && ItemIsActive(this) && CanAccessItem(this, aOriginalRequestor)) { NS_ADDREF(*_retval = 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, _retval); NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not be failing here."); if (*_retval) 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, _retval); } } // 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, _retval); } 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;iGetChildCount(&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 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) { nsCOMPtrtmp; 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 ** _retval) { NS_ENSURE_ARG(aName); NS_ENSURE_ARG_POINTER(_retval); *_retval = nullptr; // if we don't find one, we return NS_OK and a null result 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(*_retval); break; } if (childType != mItemType) //Only ask it to check children if it is same type continue; if (aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor { // See if child contains the shell with the given name #ifdef DEBUG nsresult rv = #endif child->FindChildWithName(aName, true, aSameType, static_cast (this), aOriginalRequestor, _retval); NS_ASSERTION(NS_SUCCEEDED(rv), "FindChildWithName should not fail here"); if (*_retval) // 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 loadType, bool aCloneChildren) { nsresult rv; if (mLSHE && loadType != 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) { 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 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 = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset, loadType, aCloneChildren); } } return rv; } nsresult nsDocShell::DoAddChildSHEntry(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(presContext->eAlwaysRebuildStyle); } } 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 isAllowed = !IsPrintingOrPP(aDisplayPrintErrorDialog) && !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 LoadURIWithBase(aURI, aLoadFlags, aReferringURI, aPostStream, aHeaderStream, nullptr); } NS_IMETHODIMP nsDocShell::LoadURIWithBase(const char16_t * aURI, uint32_t aLoadFlags, nsIURI * aReferringURI, 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; } 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; nsCOMPtr fixupInfo; 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) { // CreateFixupURI 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->SetHeadersStream(aHeaderStream); loadInfo->SetBaseURI(aBaseURI); 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) { // CSP error cssClass.AssignLiteral("neterror"); error.AssignLiteral("cspFrameAncestorBlocked"); } 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) { // 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 a Strict-Transport-Security host and the cert // is bad, don't allow overrides (STS Spec section 7.3). uint32_t type = nsISiteSecurityService::HEADER_HSTS; uint32_t flags = mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; bool isStsHost = false; if (XRE_GetProcessType() == GeckoProcessType_Default) { nsCOMPtr sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = sss->IsSecureURI(type, aURI, flags, &isStsHost); NS_ENSURE_SUCCESS(rv, rv); } else { mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); mozilla::ipc::URIParams uri; SerializeURI(aURI, uri); cc->SendIsSecureURI(type, uri, flags, &isStsHost); } if (Preferences::GetBool( "browser.xul.error_pages.expert_bad_cert", false)) { cssClass.AssignLiteral("expertBadCert"); } // HSTS takes precedence over the expert bad cert pref. We // never want to show the "Add Exception" button for HSTS sites. uint32_t bucketId; if (isStsHost) { cssClass.AssignLiteral("badStsCert"); //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")) mozilla::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")) mozilla::Telemetry::Accumulate(mozilla::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 (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 InternalLoad(errorPageURI, nullptr, 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; if (doc) { principal = doc->NodePrincipal(); doc->GetContentType(contentTypeHint); if (doc->IsSrcdocDocument()) { doc->GetSrcdocData(srcdoc); flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; baseURI = doc->GetBaseURI(); } } rv = InternalLoad(mCurrentURI, mReferrerURI, 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); } 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 parentNativeWindow, nsIWidget * parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy) { SetParentWidget(parentWidget); SetPositionAndSize(x, y, cx, cy, 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; // 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 x, int32_t y) { mBounds.x = x; mBounds.y = y; if (mContentViewer) NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsDocShell::GetPosition(int32_t * aX, int32_t * aY) { int32_t dummyHolder; return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder); } NS_IMETHODIMP nsDocShell::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) { int32_t x = 0, y = 0; GetPosition(&x, &y); return SetPositionAndSize(x, y, aCX, aCY, aRepaint); } NS_IMETHODIMP nsDocShell::GetSize(int32_t * aCX, int32_t * aCY) { int32_t dummyHolder; return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY); } NS_IMETHODIMP nsDocShell::SetPositionAndSize(int32_t x, int32_t y, int32_t cx, int32_t cy, bool fRepaint) { mBounds.x = x; mBounds.y = y; mBounds.width = cx; mBounds.height = cy; // 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 * x, int32_t * y, int32_t * cx, int32_t * cy) { 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 (cx || cy) { // 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(x, y, cx, cy); return NS_OK; } void nsDocShell::DoGetPositionAndSize(int32_t * x, int32_t * y, int32_t * cx, int32_t * cy) { if (x) *x = mBounds.x; if (y) *y = mBounds.y; if (cx) *cx = mBounds.width; if (cy) *cy = 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 ** parentWidget) { NS_ENSURE_ARG_POINTER(parentWidget); *parentWidget = mParentWidget; NS_IF_ADDREF(*parentWidget); return NS_OK; } NS_IMETHODIMP nsDocShell::SetParentWidget(nsIWidget * aParentWidget) { mParentWidget = aParentWidget; return NS_OK; } NS_IMETHODIMP nsDocShell::GetParentNativeWindow(nativeWindow * parentNativeWindow) { NS_ENSURE_ARG_POINTER(parentNativeWindow); if (mParentWidget) *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET); else *parentNativeWindow = nullptr; return NS_OK; } NS_IMETHODIMP nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow) { 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