1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/ThirdPartyUtil.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,628 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "ThirdPartyUtil.h" 1.9 +#include "mozilla/Preferences.h" 1.10 +#include "nsNetUtil.h" 1.11 +#include "nsIServiceManager.h" 1.12 +#include "nsIHttpChannelInternal.h" 1.13 +#include "nsIDOMWindow.h" 1.14 +#include "nsICookiePermission.h" 1.15 +#include "nsIDOMDocument.h" 1.16 +#include "nsIDocument.h" 1.17 +#include "nsILoadContext.h" 1.18 +#include "nsIPrincipal.h" 1.19 +#include "nsIScriptObjectPrincipal.h" 1.20 +#include "nsThreadUtils.h" 1.21 +#include "nsPrintfCString.h" 1.22 +#include "nsIConsoleService.h" 1.23 +#include "nsContentUtils.h" 1.24 + 1.25 +NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil) 1.26 + 1.27 +nsresult 1.28 +ThirdPartyUtil::Init() 1.29 +{ 1.30 + NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE); 1.31 + 1.32 + nsresult rv; 1.33 + mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv); 1.34 + mCookiePermissions = do_GetService(NS_COOKIEPERMISSION_CONTRACTID); 1.35 + return rv; 1.36 +} 1.37 + 1.38 +// Determine if aFirstDomain is a different base domain to aSecondURI; or, if 1.39 +// the concept of base domain does not apply, determine if the two hosts are not 1.40 +// string-identical. 1.41 +nsresult 1.42 +ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain, 1.43 + nsIURI* aSecondURI, 1.44 + bool* aResult) 1.45 +{ 1.46 + NS_ASSERTION(aSecondURI, "null URI!"); 1.47 + 1.48 + // Get the base domain for aSecondURI. 1.49 + nsCString secondDomain; 1.50 + nsresult rv = GetBaseDomain(aSecondURI, secondDomain); 1.51 + if (NS_FAILED(rv)) 1.52 + return rv; 1.53 + 1.54 + // Check strict equality. 1.55 + *aResult = aFirstDomain != secondDomain; 1.56 + return NS_OK; 1.57 +} 1.58 + 1.59 +// Return true if aURI's scheme is white listed, in which case 1.60 +// getFirstPartyURI() will not require that the firstPartyURI contains a host. 1.61 +bool ThirdPartyUtil::SchemeIsWhiteListed(nsIURI *aURI) 1.62 +{ 1.63 + if (!aURI) 1.64 + return false; 1.65 + 1.66 + nsAutoCString scheme; 1.67 + nsresult rv = aURI->GetScheme(scheme); 1.68 + NS_ENSURE_SUCCESS(rv, false); 1.69 + 1.70 + return (scheme.Equals("about") || scheme.Equals("moz-safe-about") 1.71 + || scheme.Equals("chrome")); 1.72 +} 1.73 + 1.74 +// Get the URI associated with a window. 1.75 +already_AddRefed<nsIURI> 1.76 +ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin) 1.77 +{ 1.78 + nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aWin); 1.79 + NS_ENSURE_TRUE(scriptObjPrin, nullptr); 1.80 + 1.81 + nsIPrincipal* prin = scriptObjPrin->GetPrincipal(); 1.82 + NS_ENSURE_TRUE(prin, nullptr); 1.83 + 1.84 + nsCOMPtr<nsIURI> result; 1.85 + prin->GetURI(getter_AddRefs(result)); 1.86 + return result.forget(); 1.87 +} 1.88 + 1.89 + 1.90 +nsresult 1.91 +ThirdPartyUtil::GetOriginatingURI(nsIChannel *aChannel, nsIURI **aURI) 1.92 +{ 1.93 + /* to find the originating URI, we use the loadgroup of the channel to obtain 1.94 + * the window owning the load, and from there, we find the top same-type 1.95 + * window and its URI. there are several possible cases: 1.96 + * 1.97 + * 1) no channel. 1.98 + * 1.99 + * 2) a channel with the "force allow third party cookies" option set. 1.100 + * since we may not have a window, we return the channel URI in this case. 1.101 + * 1.102 + * 3) a channel, but no window. this can occur when the consumer kicking 1.103 + * off the load doesn't provide one to the channel, and should be limited 1.104 + * to loads of certain types of resources. 1.105 + * 1.106 + * 4) a window equal to the top window of same type, with the channel its 1.107 + * document channel. this covers the case of a freshly kicked-off load 1.108 + * (e.g. the user typing something in the location bar, or clicking on a 1.109 + * bookmark), where the window's URI hasn't yet been set, and will be 1.110 + * bogus. we return the channel URI in this case. 1.111 + * 1.112 + * 5) Anything else. this covers most cases for an ordinary page load from 1.113 + * the location bar, and will catch nested frames within a page, image 1.114 + * loads, etc. we return the URI of the root window's document's principal 1.115 + * in this case. 1.116 + */ 1.117 + 1.118 + *aURI = nullptr; 1.119 + 1.120 + // case 1) 1.121 + if (!aChannel) 1.122 + return NS_ERROR_NULL_POINTER; 1.123 + 1.124 + // case 2) 1.125 + nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(aChannel); 1.126 + if (httpChannelInternal) 1.127 + { 1.128 + bool doForce = false; 1.129 + if (NS_SUCCEEDED(httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce)) && doForce) 1.130 + { 1.131 + // return the channel's URI (we may not have a window) 1.132 + aChannel->GetURI(aURI); 1.133 + if (!*aURI) 1.134 + return NS_ERROR_NULL_POINTER; 1.135 + 1.136 + return NS_OK; 1.137 + } 1.138 + 1.139 + // TODO: Why don't we just use this here: 1.140 + // httpChannelInternal->GetDocumentURI(aURI); 1.141 + } 1.142 + 1.143 + // find the associated window and its top window 1.144 + nsCOMPtr<nsILoadContext> ctx; 1.145 + NS_QueryNotificationCallbacks(aChannel, ctx); 1.146 + nsCOMPtr<nsIDOMWindow> topWin, ourWin; 1.147 + if (ctx) { 1.148 + ctx->GetTopWindow(getter_AddRefs(topWin)); 1.149 + ctx->GetAssociatedWindow(getter_AddRefs(ourWin)); 1.150 + } 1.151 + 1.152 + // case 3) 1.153 + if (!topWin) 1.154 + return NS_ERROR_INVALID_ARG; 1.155 + 1.156 + // case 4) 1.157 + if (ourWin == topWin) { 1.158 + // Check whether this is the document channel for this window (representing 1.159 + // a load of a new page). This is a bit of a nasty hack, but we will 1.160 + // hopefully flag these channels better later. 1.161 + nsLoadFlags flags; 1.162 + aChannel->GetLoadFlags(&flags); 1.163 + 1.164 + if (flags & nsIChannel::LOAD_DOCUMENT_URI) { 1.165 + // get the channel URI - the window's will be bogus 1.166 + aChannel->GetURI(aURI); 1.167 + if (!*aURI) 1.168 + return NS_ERROR_NULL_POINTER; 1.169 + 1.170 + return NS_OK; 1.171 + } 1.172 + } 1.173 + 1.174 + // case 5) - get the originating URI from the top window's principal 1.175 + nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(topWin); 1.176 + NS_ENSURE_TRUE(scriptObjPrin, NS_ERROR_UNEXPECTED); 1.177 + 1.178 + nsIPrincipal* prin = scriptObjPrin->GetPrincipal(); 1.179 + NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED); 1.180 + 1.181 + prin->GetURI(aURI); 1.182 + 1.183 + if (!*aURI) 1.184 + return NS_ERROR_NULL_POINTER; 1.185 + 1.186 + // all done! 1.187 + return NS_OK; 1.188 +} 1.189 + 1.190 + 1.191 +// Determine if aFirstURI is third party with respect to aSecondURI. See docs 1.192 +// for mozIThirdPartyUtil. 1.193 +NS_IMETHODIMP 1.194 +ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI, 1.195 + nsIURI* aSecondURI, 1.196 + bool* aResult) 1.197 +{ 1.198 + NS_ENSURE_ARG(aFirstURI); 1.199 + NS_ENSURE_ARG(aSecondURI); 1.200 + NS_ASSERTION(aResult, "null outparam pointer"); 1.201 + 1.202 + nsCString firstHost; 1.203 + nsresult rv = GetBaseDomain(aFirstURI, firstHost); 1.204 + if (NS_FAILED(rv)) 1.205 + return rv; 1.206 + 1.207 + return IsThirdPartyInternal(firstHost, aSecondURI, aResult); 1.208 +} 1.209 + 1.210 +// Determine if any URI of the window hierarchy of aWindow is foreign with 1.211 +// respect to aSecondURI. See docs for mozIThirdPartyUtil. 1.212 +NS_IMETHODIMP 1.213 +ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow* aWindow, 1.214 + nsIURI* aURI, 1.215 + bool* aResult) 1.216 +{ 1.217 + NS_ENSURE_ARG(aWindow); 1.218 + NS_ASSERTION(aResult, "null outparam pointer"); 1.219 + 1.220 + bool result; 1.221 + 1.222 + // Get the URI of the window, and its base domain. 1.223 + nsCOMPtr<nsIURI> currentURI = GetURIFromWindow(aWindow); 1.224 + NS_ENSURE_TRUE(currentURI, NS_ERROR_INVALID_ARG); 1.225 + 1.226 + nsCString bottomDomain; 1.227 + nsresult rv = GetBaseDomain(currentURI, bottomDomain); 1.228 + if (NS_FAILED(rv)) 1.229 + return rv; 1.230 + 1.231 + if (aURI) { 1.232 + // Determine whether aURI is foreign with respect to currentURI. 1.233 + rv = IsThirdPartyInternal(bottomDomain, aURI, &result); 1.234 + if (NS_FAILED(rv)) 1.235 + return rv; 1.236 + 1.237 + if (result) { 1.238 + *aResult = true; 1.239 + return NS_OK; 1.240 + } 1.241 + } 1.242 + 1.243 + nsCOMPtr<nsIDOMWindow> current = aWindow, parent; 1.244 + nsCOMPtr<nsIURI> parentURI; 1.245 + do { 1.246 + // We use GetScriptableParent rather than GetParent because we consider 1.247 + // <iframe mozbrowser/mozapp> to be a top-level frame. 1.248 + rv = current->GetScriptableParent(getter_AddRefs(parent)); 1.249 + NS_ENSURE_SUCCESS(rv, rv); 1.250 + 1.251 + if (SameCOMIdentity(parent, current)) { 1.252 + // We're at the topmost content window. We already know the answer. 1.253 + *aResult = false; 1.254 + return NS_OK; 1.255 + } 1.256 + 1.257 + parentURI = GetURIFromWindow(parent); 1.258 + NS_ENSURE_TRUE(parentURI, NS_ERROR_INVALID_ARG); 1.259 + 1.260 + rv = IsThirdPartyInternal(bottomDomain, parentURI, &result); 1.261 + if (NS_FAILED(rv)) 1.262 + return rv; 1.263 + 1.264 + if (result) { 1.265 + *aResult = true; 1.266 + return NS_OK; 1.267 + } 1.268 + 1.269 + current = parent; 1.270 + currentURI = parentURI; 1.271 + } while (1); 1.272 + 1.273 + NS_NOTREACHED("should've returned"); 1.274 + return NS_ERROR_UNEXPECTED; 1.275 +} 1.276 + 1.277 +// Determine if the URI associated with aChannel or any URI of the window 1.278 +// hierarchy associated with the channel is foreign with respect to aSecondURI. 1.279 +// See docs for mozIThirdPartyUtil. 1.280 +NS_IMETHODIMP 1.281 +ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel, 1.282 + nsIURI* aURI, 1.283 + bool* aResult) 1.284 +{ 1.285 + NS_ENSURE_ARG(aChannel); 1.286 + NS_ASSERTION(aResult, "null outparam pointer"); 1.287 + 1.288 + nsresult rv; 1.289 + bool doForce = false; 1.290 + nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = 1.291 + do_QueryInterface(aChannel); 1.292 + if (httpChannelInternal) { 1.293 + rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce); 1.294 + NS_ENSURE_SUCCESS(rv, rv); 1.295 + 1.296 + // If aURI was not supplied, and we're forcing, then we're by definition 1.297 + // not foreign. If aURI was supplied, we still want to check whether it's 1.298 + // foreign with respect to the channel URI. (The forcing only applies to 1.299 + // whatever window hierarchy exists above the channel.) 1.300 + if (doForce && !aURI) { 1.301 + *aResult = false; 1.302 + return NS_OK; 1.303 + } 1.304 + } 1.305 + 1.306 + // Obtain the URI from the channel, and its base domain. 1.307 + nsCOMPtr<nsIURI> channelURI; 1.308 + aChannel->GetURI(getter_AddRefs(channelURI)); 1.309 + NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG); 1.310 + 1.311 + nsCString channelDomain; 1.312 + rv = GetBaseDomain(channelURI, channelDomain); 1.313 + if (NS_FAILED(rv)) 1.314 + return rv; 1.315 + 1.316 + if (aURI) { 1.317 + // Determine whether aURI is foreign with respect to channelURI. 1.318 + bool result; 1.319 + rv = IsThirdPartyInternal(channelDomain, aURI, &result); 1.320 + if (NS_FAILED(rv)) 1.321 + return rv; 1.322 + 1.323 + // If it's foreign, or we're forcing, we're done. 1.324 + if (result || doForce) { 1.325 + *aResult = result; 1.326 + return NS_OK; 1.327 + } 1.328 + } 1.329 + 1.330 + // Find the associated window and its parent window. 1.331 + nsCOMPtr<nsILoadContext> ctx; 1.332 + NS_QueryNotificationCallbacks(aChannel, ctx); 1.333 + if (!ctx) return NS_ERROR_INVALID_ARG; 1.334 + 1.335 + // If there is no window, the consumer kicking off the load didn't provide one 1.336 + // to the channel. This is limited to loads of certain types of resources. If 1.337 + // those loads require cookies, the forceAllowThirdPartyCookie property should 1.338 + // be set on the channel. 1.339 + nsCOMPtr<nsIDOMWindow> ourWin, parentWin; 1.340 + ctx->GetAssociatedWindow(getter_AddRefs(ourWin)); 1.341 + if (!ourWin) return NS_ERROR_INVALID_ARG; 1.342 + 1.343 + // We use GetScriptableParent rather than GetParent because we consider 1.344 + // <iframe mozbrowser/mozapp> to be a top-level frame. 1.345 + ourWin->GetScriptableParent(getter_AddRefs(parentWin)); 1.346 + NS_ENSURE_TRUE(parentWin, NS_ERROR_INVALID_ARG); 1.347 + 1.348 + // Check whether this is the document channel for this window (representing a 1.349 + // load of a new page). In that situation we want to avoid comparing 1.350 + // channelURI to ourWin, since what's in ourWin right now will be replaced as 1.351 + // the channel loads. This covers the case of a freshly kicked-off load 1.352 + // (e.g. the user typing something in the location bar, or clicking on a 1.353 + // bookmark), where the window's URI hasn't yet been set, and will be bogus. 1.354 + // It also covers situations where a subframe is navigated to someting that 1.355 + // is same-origin with all its ancestors. This is a bit of a nasty hack, but 1.356 + // we will hopefully flag these channels better later. 1.357 + nsLoadFlags flags; 1.358 + rv = aChannel->GetLoadFlags(&flags); 1.359 + NS_ENSURE_SUCCESS(rv, rv); 1.360 + 1.361 + if (flags & nsIChannel::LOAD_DOCUMENT_URI) { 1.362 + if (SameCOMIdentity(ourWin, parentWin)) { 1.363 + // We only need to compare aURI to the channel URI -- the window's will be 1.364 + // bogus. We already know the answer. 1.365 + *aResult = false; 1.366 + return NS_OK; 1.367 + } 1.368 + 1.369 + // Make sure to still compare to ourWin's ancestors 1.370 + ourWin = parentWin; 1.371 + } 1.372 + 1.373 + // Check the window hierarchy. This covers most cases for an ordinary page 1.374 + // load from the location bar. 1.375 + return IsThirdPartyWindow(ourWin, channelURI, aResult); 1.376 +} 1.377 + 1.378 +// Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be 1.379 +// "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing 1.380 +// dot may be present. If aHostURI is an IP address, an alias such as 1.381 +// 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will 1.382 +// be the exact host. The result of this function should only be used in exact 1.383 +// string comparisons, since substring comparisons will not be valid for the 1.384 +// special cases elided above. 1.385 +NS_IMETHODIMP 1.386 +ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI, 1.387 + nsACString& aBaseDomain) 1.388 +{ 1.389 + // Get the base domain. this will fail if the host contains a leading dot, 1.390 + // more than one trailing dot, or is otherwise malformed. 1.391 + nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain); 1.392 + if (rv == NS_ERROR_HOST_IS_IP_ADDRESS || 1.393 + rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { 1.394 + // aHostURI is either an IP address, an alias such as 'localhost', an eTLD 1.395 + // such as 'co.uk', or the empty string. Uses the normalized host in such 1.396 + // cases. 1.397 + rv = aHostURI->GetAsciiHost(aBaseDomain); 1.398 + } 1.399 + NS_ENSURE_SUCCESS(rv, rv); 1.400 + 1.401 + // aHostURI (and thus aBaseDomain) may be the string '.'. If so, fail. 1.402 + if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.') 1.403 + return NS_ERROR_INVALID_ARG; 1.404 + 1.405 + // Reject any URIs without a host that aren't file:// URIs. This makes it the 1.406 + // only way we can get a base domain consisting of the empty string, which 1.407 + // means we can safely perform foreign tests on such URIs where "not foreign" 1.408 + // means "the involved URIs are all file://". 1.409 + if (aBaseDomain.IsEmpty()) { 1.410 + bool isFileURI = false; 1.411 + aHostURI->SchemeIs("file", &isFileURI); 1.412 + NS_ENSURE_TRUE(isFileURI, NS_ERROR_INVALID_ARG); 1.413 + } 1.414 + 1.415 + return NS_OK; 1.416 +} 1.417 + 1.418 +// Returns true if First Party Isolation is currently active for the given nsIChannel. 1.419 +// Depends on Preference setting and possibly the state of Private Browsing mode. 1.420 +bool ThirdPartyUtil::IsFirstPartyIsolationActive(nsIChannel *aChannel, nsIDocument *aDoc) 1.421 +{ 1.422 + int32_t isolationState = mozilla::Preferences::GetInt("privacy.thirdparty.isolate"); 1.423 + if (isolationState == 1) { 1.424 + if (!aChannel && aDoc) { 1.425 + // No channel passed directly. Can we get a channel from aDoc? 1.426 + aChannel = aDoc->GetChannel(); 1.427 + } 1.428 + return aChannel && NS_UsePrivateBrowsing(aChannel); 1.429 + } else { // (isolationState == 0) || (isolationState == 2) 1.430 + return (isolationState == 2); 1.431 + } 1.432 +} 1.433 + 1.434 +// Produces a URI that uniquely identifies the first party to which 1.435 +// image cache and dom storage objects should be isolated. If isolation 1.436 +// is deactivated, then aOutput will return null. 1.437 +// Not scriptable due to the use of an nsIDocument parameter. 1.438 +NS_IMETHODIMP 1.439 +ThirdPartyUtil::GetFirstPartyIsolationURI(nsIChannel *aChannel, nsIDocument *aDoc, nsIURI **aOutput) 1.440 +{ 1.441 + bool isolationActive = IsFirstPartyIsolationActive(aChannel, aDoc); 1.442 + if (isolationActive) { 1.443 + return GetFirstPartyURI(aChannel, aDoc, aOutput); 1.444 + } else { 1.445 + // We return a null pointer when isolation is off. 1.446 + *aOutput = nullptr; 1.447 + return NS_OK; 1.448 + } 1.449 +} 1.450 + 1.451 +// Not scriptable due to the use of an nsIDocument parameter. 1.452 +NS_IMETHODIMP 1.453 +ThirdPartyUtil::GetFirstPartyURI(nsIChannel *aChannel, 1.454 + nsIDocument *aDoc, 1.455 + nsIURI **aOutput) 1.456 +{ 1.457 + return GetFirstPartyURIInternal(aChannel, aDoc, true, aOutput); 1.458 +} 1.459 + 1.460 +nsresult 1.461 +ThirdPartyUtil::GetFirstPartyURIInternal(nsIChannel *aChannel, 1.462 + nsIDocument *aDoc, 1.463 + bool aLogErrors, 1.464 + nsIURI **aOutput) 1.465 +{ 1.466 + nsresult rv = NS_ERROR_NULL_POINTER; 1.467 + nsCOMPtr<nsIURI> srcURI; 1.468 + 1.469 + if (!aOutput) 1.470 + return rv; 1.471 + 1.472 + *aOutput = nullptr; 1.473 + 1.474 + if (!aChannel && aDoc) { 1.475 + aChannel = aDoc->GetChannel(); 1.476 + } 1.477 + 1.478 + // If aChannel is specified or available, use the official route 1.479 + // for sure 1.480 + if (aChannel) { 1.481 + rv = GetOriginatingURI(aChannel, aOutput); 1.482 + aChannel->GetURI(getter_AddRefs(srcURI)); 1.483 + if (NS_SUCCEEDED(rv) && *aOutput) { 1.484 + // At this point, about: and chrome: URLs have been mapped to file: or 1.485 + // jar: URLs. Try to recover the original URL. 1.486 + nsAutoCString scheme; 1.487 + nsresult rv2 = (*aOutput)->GetScheme(scheme); 1.488 + NS_ENSURE_SUCCESS(rv2, rv2); 1.489 + if (scheme.Equals("file") || scheme.Equals("jar")) { 1.490 + nsCOMPtr<nsIURI> originalURI; 1.491 + rv2 = aChannel->GetOriginalURI(getter_AddRefs(originalURI)); 1.492 + if (NS_SUCCEEDED(rv2) && originalURI) { 1.493 + NS_RELEASE(*aOutput); 1.494 + NS_ADDREF(*aOutput = originalURI); 1.495 + } 1.496 + } 1.497 + } 1.498 + } 1.499 + 1.500 + // If the channel was missing, closed or broken, try the 1.501 + // window hierarchy directly. 1.502 + // 1.503 + // This might fail to work for first-party loads themselves, but 1.504 + // we don't need this codepath for that case. 1.505 + if (NS_FAILED(rv) && aDoc) { 1.506 + nsCOMPtr<nsIDOMWindow> top; 1.507 + nsCOMPtr<nsIDOMDocument> topDDoc; 1.508 + nsIURI *docURI = nullptr; 1.509 + srcURI = aDoc->GetDocumentURI(); 1.510 + 1.511 + if (aDoc->GetWindow()) { 1.512 + aDoc->GetWindow()->GetTop(getter_AddRefs(top)); 1.513 + top->GetDocument(getter_AddRefs(topDDoc)); 1.514 + 1.515 + nsCOMPtr<nsIDocument> topDoc(do_QueryInterface(topDDoc)); 1.516 + docURI = topDoc->GetOriginalURI(); 1.517 + if (docURI) { 1.518 + // Give us a mutable URI and also addref 1.519 + rv = NS_EnsureSafeToReturn(docURI, aOutput); 1.520 + } 1.521 + } else { 1.522 + // XXX: Chrome callers (such as NoScript) can end up here 1.523 + // through getImageData/canvas usage with no document state 1.524 + // (no Window and a document URI of about:blank). Propogate 1.525 + // rv fail (by doing nothing), and hope caller recovers. 1.526 + } 1.527 + 1.528 + if (*aOutput) 1.529 + rv = NS_OK; 1.530 + } 1.531 + 1.532 + if (*aOutput && !SchemeIsWhiteListed(*aOutput)) { 1.533 + // If URI scheme is not whitelisted and the URI lacks a hostname, force a 1.534 + // failure. 1.535 + nsAutoCString host; 1.536 + rv = (*aOutput)->GetHost(host); 1.537 + if (NS_SUCCEEDED(rv) && (host.Length() == 0)) { 1.538 + rv = NS_ERROR_FAILURE; 1.539 + } 1.540 + } 1.541 + 1.542 + // Log failure to error console. 1.543 + if (aLogErrors && NS_FAILED(rv)) { 1.544 + nsCOMPtr<nsIConsoleService> console 1.545 + (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); 1.546 + if (console) { 1.547 + nsCString spec; 1.548 + nsCString srcSpec("unknown"); 1.549 + 1.550 + if (srcURI) 1.551 + srcURI->GetSpec(srcSpec); 1.552 + 1.553 + if (*aOutput) 1.554 + (*aOutput)->GetSpec(spec); 1.555 + if (spec.Length() > 0) { 1.556 + nsPrintfCString msg("getFirstPartyURI failed for %s: no host in first party URI %s", 1.557 + srcSpec.get(), spec.get()); // TODO: L10N 1.558 + console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get()); 1.559 + } else { 1.560 + nsPrintfCString msg("getFirstPartyURI failed for %s: 0x%x", srcSpec.get(), rv); 1.561 + console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get()); 1.562 + } 1.563 + } 1.564 + 1.565 + if (*aOutput) { 1.566 + // discard return object. 1.567 + (*aOutput)->Release(); 1.568 + *aOutput = nullptr; 1.569 + } 1.570 + } 1.571 + 1.572 + // TODO: We could provide a route through the loadgroup + notification 1.573 + // callbacks too, but either channel or document was always available 1.574 + // in the cases where this function was originally needed (the image cache). 1.575 + // The notification callbacks also appear to suffers from the same limitation 1.576 + // as the document path. See nsICookiePermissions.GetOriginatingURI() for 1.577 + // details. 1.578 + 1.579 + return rv; 1.580 +} 1.581 + 1.582 +NS_IMETHODIMP 1.583 +ThirdPartyUtil::GetFirstPartyURIFromChannel(nsIChannel *aChannel, 1.584 + bool aLogErrors, 1.585 + nsIURI **aOutput) 1.586 +{ 1.587 + return GetFirstPartyURIInternal(aChannel, nullptr, aLogErrors, aOutput); 1.588 +} 1.589 + 1.590 +NS_IMETHODIMP 1.591 +ThirdPartyUtil::GetFirstPartyHostForIsolation(nsIURI *aFirstPartyURI, 1.592 + nsACString& aHost) 1.593 +{ 1.594 + if (!aFirstPartyURI) 1.595 + return NS_ERROR_INVALID_ARG; 1.596 + 1.597 + if (!SchemeIsWhiteListed(aFirstPartyURI)) { 1.598 + nsresult rv = aFirstPartyURI->GetHost(aHost); 1.599 + return (aHost.Length() > 0) ? NS_OK : rv; 1.600 + } 1.601 + 1.602 + // This URI lacks a host, so construct and return a pseudo-host. 1.603 + aHost = "--NoFirstPartyHost-"; 1.604 + 1.605 + // Append the scheme. To ensure that the pseudo-hosts are consistent 1.606 + // when the hacky "moz-safe-about" scheme is used, map it back to "about". 1.607 + nsAutoCString scheme; 1.608 + nsresult rv = aFirstPartyURI->GetScheme(scheme); 1.609 + NS_ENSURE_SUCCESS(rv, rv); 1.610 + if (scheme.Equals("moz-safe-about")) 1.611 + aHost.Append("about"); 1.612 + else 1.613 + aHost.Append(scheme); 1.614 + 1.615 + // Append the URL's file name (e.g., -browser.xul) or its path (e.g., 1.616 + // -home for about:home) 1.617 + nsAutoCString s; 1.618 + nsCOMPtr<nsIURL> url = do_QueryInterface(aFirstPartyURI); 1.619 + if (url) 1.620 + url->GetFileName(s); 1.621 + else 1.622 + aFirstPartyURI->GetPath(s); 1.623 + 1.624 + if (s.Length() > 0) { 1.625 + aHost.Append("-"); 1.626 + aHost.Append(s); 1.627 + } 1.628 + 1.629 + aHost.Append("--"); 1.630 + return NS_OK; 1.631 +}