michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ThirdPartyUtil.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIHttpChannelInternal.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsICookiePermission.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsILoadContext.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsContentUtils.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil) michael@0: michael@0: nsresult michael@0: ThirdPartyUtil::Init() michael@0: { michael@0: NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: nsresult rv; michael@0: mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv); michael@0: mCookiePermissions = do_GetService(NS_COOKIEPERMISSION_CONTRACTID); michael@0: return rv; michael@0: } michael@0: michael@0: // Determine if aFirstDomain is a different base domain to aSecondURI; or, if michael@0: // the concept of base domain does not apply, determine if the two hosts are not michael@0: // string-identical. michael@0: nsresult michael@0: ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain, michael@0: nsIURI* aSecondURI, michael@0: bool* aResult) michael@0: { michael@0: NS_ASSERTION(aSecondURI, "null URI!"); michael@0: michael@0: // Get the base domain for aSecondURI. michael@0: nsCString secondDomain; michael@0: nsresult rv = GetBaseDomain(aSecondURI, secondDomain); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Check strict equality. michael@0: *aResult = aFirstDomain != secondDomain; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Return true if aURI's scheme is white listed, in which case michael@0: // getFirstPartyURI() will not require that the firstPartyURI contains a host. michael@0: bool ThirdPartyUtil::SchemeIsWhiteListed(nsIURI *aURI) michael@0: { michael@0: if (!aURI) michael@0: return false; michael@0: michael@0: nsAutoCString scheme; michael@0: nsresult rv = aURI->GetScheme(scheme); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: return (scheme.Equals("about") || scheme.Equals("moz-safe-about") michael@0: || scheme.Equals("chrome")); michael@0: } michael@0: michael@0: // Get the URI associated with a window. michael@0: already_AddRefed michael@0: ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin) michael@0: { michael@0: nsCOMPtr scriptObjPrin = do_QueryInterface(aWin); michael@0: NS_ENSURE_TRUE(scriptObjPrin, nullptr); michael@0: michael@0: nsIPrincipal* prin = scriptObjPrin->GetPrincipal(); michael@0: NS_ENSURE_TRUE(prin, nullptr); michael@0: michael@0: nsCOMPtr result; michael@0: prin->GetURI(getter_AddRefs(result)); michael@0: return result.forget(); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: ThirdPartyUtil::GetOriginatingURI(nsIChannel *aChannel, nsIURI **aURI) michael@0: { michael@0: /* to find the originating URI, we use the loadgroup of the channel to obtain michael@0: * the window owning the load, and from there, we find the top same-type michael@0: * window and its URI. there are several possible cases: michael@0: * michael@0: * 1) no channel. michael@0: * michael@0: * 2) a channel with the "force allow third party cookies" option set. michael@0: * since we may not have a window, we return the channel URI in this case. michael@0: * michael@0: * 3) a channel, but no window. this can occur when the consumer kicking michael@0: * off the load doesn't provide one to the channel, and should be limited michael@0: * to loads of certain types of resources. michael@0: * michael@0: * 4) a window equal to the top window of same type, with the channel its michael@0: * document channel. this covers the case of a freshly kicked-off load michael@0: * (e.g. the user typing something in the location bar, or clicking on a michael@0: * bookmark), where the window's URI hasn't yet been set, and will be michael@0: * bogus. we return the channel URI in this case. michael@0: * michael@0: * 5) Anything else. this covers most cases for an ordinary page load from michael@0: * the location bar, and will catch nested frames within a page, image michael@0: * loads, etc. we return the URI of the root window's document's principal michael@0: * in this case. michael@0: */ michael@0: michael@0: *aURI = nullptr; michael@0: michael@0: // case 1) michael@0: if (!aChannel) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: // case 2) michael@0: nsCOMPtr httpChannelInternal = do_QueryInterface(aChannel); michael@0: if (httpChannelInternal) michael@0: { michael@0: bool doForce = false; michael@0: if (NS_SUCCEEDED(httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce)) && doForce) michael@0: { michael@0: // return the channel's URI (we may not have a window) michael@0: aChannel->GetURI(aURI); michael@0: if (!*aURI) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // TODO: Why don't we just use this here: michael@0: // httpChannelInternal->GetDocumentURI(aURI); michael@0: } michael@0: michael@0: // find the associated window and its top window michael@0: nsCOMPtr ctx; michael@0: NS_QueryNotificationCallbacks(aChannel, ctx); michael@0: nsCOMPtr topWin, ourWin; michael@0: if (ctx) { michael@0: ctx->GetTopWindow(getter_AddRefs(topWin)); michael@0: ctx->GetAssociatedWindow(getter_AddRefs(ourWin)); michael@0: } michael@0: michael@0: // case 3) michael@0: if (!topWin) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // case 4) michael@0: if (ourWin == topWin) { michael@0: // Check whether this is the document channel for this window (representing michael@0: // a load of a new page). This is a bit of a nasty hack, but we will michael@0: // hopefully flag these channels better later. michael@0: nsLoadFlags flags; michael@0: aChannel->GetLoadFlags(&flags); michael@0: michael@0: if (flags & nsIChannel::LOAD_DOCUMENT_URI) { michael@0: // get the channel URI - the window's will be bogus michael@0: aChannel->GetURI(aURI); michael@0: if (!*aURI) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // case 5) - get the originating URI from the top window's principal michael@0: nsCOMPtr scriptObjPrin = do_QueryInterface(topWin); michael@0: NS_ENSURE_TRUE(scriptObjPrin, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsIPrincipal* prin = scriptObjPrin->GetPrincipal(); michael@0: NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED); michael@0: michael@0: prin->GetURI(aURI); michael@0: michael@0: if (!*aURI) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: // all done! michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // Determine if aFirstURI is third party with respect to aSecondURI. See docs michael@0: // for mozIThirdPartyUtil. michael@0: NS_IMETHODIMP michael@0: ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI, michael@0: nsIURI* aSecondURI, michael@0: bool* aResult) michael@0: { michael@0: NS_ENSURE_ARG(aFirstURI); michael@0: NS_ENSURE_ARG(aSecondURI); michael@0: NS_ASSERTION(aResult, "null outparam pointer"); michael@0: michael@0: nsCString firstHost; michael@0: nsresult rv = GetBaseDomain(aFirstURI, firstHost); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return IsThirdPartyInternal(firstHost, aSecondURI, aResult); michael@0: } michael@0: michael@0: // Determine if any URI of the window hierarchy of aWindow is foreign with michael@0: // respect to aSecondURI. See docs for mozIThirdPartyUtil. michael@0: NS_IMETHODIMP michael@0: ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow* aWindow, michael@0: nsIURI* aURI, michael@0: bool* aResult) michael@0: { michael@0: NS_ENSURE_ARG(aWindow); michael@0: NS_ASSERTION(aResult, "null outparam pointer"); michael@0: michael@0: bool result; michael@0: michael@0: // Get the URI of the window, and its base domain. michael@0: nsCOMPtr currentURI = GetURIFromWindow(aWindow); michael@0: NS_ENSURE_TRUE(currentURI, NS_ERROR_INVALID_ARG); michael@0: michael@0: nsCString bottomDomain; michael@0: nsresult rv = GetBaseDomain(currentURI, bottomDomain); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (aURI) { michael@0: // Determine whether aURI is foreign with respect to currentURI. michael@0: rv = IsThirdPartyInternal(bottomDomain, aURI, &result); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (result) { michael@0: *aResult = true; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr current = aWindow, parent; michael@0: nsCOMPtr parentURI; michael@0: do { michael@0: // We use GetScriptableParent rather than GetParent because we consider michael@0: //