content/base/src/ThirdPartyUtil.cpp

changeset 0
6474c204b198
     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 +}

mercurial