content/base/src/ThirdPartyUtil.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "ThirdPartyUtil.h"
michael@0 6 #include "mozilla/Preferences.h"
michael@0 7 #include "nsNetUtil.h"
michael@0 8 #include "nsIServiceManager.h"
michael@0 9 #include "nsIHttpChannelInternal.h"
michael@0 10 #include "nsIDOMWindow.h"
michael@0 11 #include "nsICookiePermission.h"
michael@0 12 #include "nsIDOMDocument.h"
michael@0 13 #include "nsIDocument.h"
michael@0 14 #include "nsILoadContext.h"
michael@0 15 #include "nsIPrincipal.h"
michael@0 16 #include "nsIScriptObjectPrincipal.h"
michael@0 17 #include "nsThreadUtils.h"
michael@0 18 #include "nsPrintfCString.h"
michael@0 19 #include "nsIConsoleService.h"
michael@0 20 #include "nsContentUtils.h"
michael@0 21
michael@0 22 NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil)
michael@0 23
michael@0 24 nsresult
michael@0 25 ThirdPartyUtil::Init()
michael@0 26 {
michael@0 27 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE);
michael@0 28
michael@0 29 nsresult rv;
michael@0 30 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
michael@0 31 mCookiePermissions = do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
michael@0 32 return rv;
michael@0 33 }
michael@0 34
michael@0 35 // Determine if aFirstDomain is a different base domain to aSecondURI; or, if
michael@0 36 // the concept of base domain does not apply, determine if the two hosts are not
michael@0 37 // string-identical.
michael@0 38 nsresult
michael@0 39 ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain,
michael@0 40 nsIURI* aSecondURI,
michael@0 41 bool* aResult)
michael@0 42 {
michael@0 43 NS_ASSERTION(aSecondURI, "null URI!");
michael@0 44
michael@0 45 // Get the base domain for aSecondURI.
michael@0 46 nsCString secondDomain;
michael@0 47 nsresult rv = GetBaseDomain(aSecondURI, secondDomain);
michael@0 48 if (NS_FAILED(rv))
michael@0 49 return rv;
michael@0 50
michael@0 51 // Check strict equality.
michael@0 52 *aResult = aFirstDomain != secondDomain;
michael@0 53 return NS_OK;
michael@0 54 }
michael@0 55
michael@0 56 // Return true if aURI's scheme is white listed, in which case
michael@0 57 // getFirstPartyURI() will not require that the firstPartyURI contains a host.
michael@0 58 bool ThirdPartyUtil::SchemeIsWhiteListed(nsIURI *aURI)
michael@0 59 {
michael@0 60 if (!aURI)
michael@0 61 return false;
michael@0 62
michael@0 63 nsAutoCString scheme;
michael@0 64 nsresult rv = aURI->GetScheme(scheme);
michael@0 65 NS_ENSURE_SUCCESS(rv, false);
michael@0 66
michael@0 67 return (scheme.Equals("about") || scheme.Equals("moz-safe-about")
michael@0 68 || scheme.Equals("chrome"));
michael@0 69 }
michael@0 70
michael@0 71 // Get the URI associated with a window.
michael@0 72 already_AddRefed<nsIURI>
michael@0 73 ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin)
michael@0 74 {
michael@0 75 nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aWin);
michael@0 76 NS_ENSURE_TRUE(scriptObjPrin, nullptr);
michael@0 77
michael@0 78 nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
michael@0 79 NS_ENSURE_TRUE(prin, nullptr);
michael@0 80
michael@0 81 nsCOMPtr<nsIURI> result;
michael@0 82 prin->GetURI(getter_AddRefs(result));
michael@0 83 return result.forget();
michael@0 84 }
michael@0 85
michael@0 86
michael@0 87 nsresult
michael@0 88 ThirdPartyUtil::GetOriginatingURI(nsIChannel *aChannel, nsIURI **aURI)
michael@0 89 {
michael@0 90 /* to find the originating URI, we use the loadgroup of the channel to obtain
michael@0 91 * the window owning the load, and from there, we find the top same-type
michael@0 92 * window and its URI. there are several possible cases:
michael@0 93 *
michael@0 94 * 1) no channel.
michael@0 95 *
michael@0 96 * 2) a channel with the "force allow third party cookies" option set.
michael@0 97 * since we may not have a window, we return the channel URI in this case.
michael@0 98 *
michael@0 99 * 3) a channel, but no window. this can occur when the consumer kicking
michael@0 100 * off the load doesn't provide one to the channel, and should be limited
michael@0 101 * to loads of certain types of resources.
michael@0 102 *
michael@0 103 * 4) a window equal to the top window of same type, with the channel its
michael@0 104 * document channel. this covers the case of a freshly kicked-off load
michael@0 105 * (e.g. the user typing something in the location bar, or clicking on a
michael@0 106 * bookmark), where the window's URI hasn't yet been set, and will be
michael@0 107 * bogus. we return the channel URI in this case.
michael@0 108 *
michael@0 109 * 5) Anything else. this covers most cases for an ordinary page load from
michael@0 110 * the location bar, and will catch nested frames within a page, image
michael@0 111 * loads, etc. we return the URI of the root window's document's principal
michael@0 112 * in this case.
michael@0 113 */
michael@0 114
michael@0 115 *aURI = nullptr;
michael@0 116
michael@0 117 // case 1)
michael@0 118 if (!aChannel)
michael@0 119 return NS_ERROR_NULL_POINTER;
michael@0 120
michael@0 121 // case 2)
michael@0 122 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(aChannel);
michael@0 123 if (httpChannelInternal)
michael@0 124 {
michael@0 125 bool doForce = false;
michael@0 126 if (NS_SUCCEEDED(httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce)) && doForce)
michael@0 127 {
michael@0 128 // return the channel's URI (we may not have a window)
michael@0 129 aChannel->GetURI(aURI);
michael@0 130 if (!*aURI)
michael@0 131 return NS_ERROR_NULL_POINTER;
michael@0 132
michael@0 133 return NS_OK;
michael@0 134 }
michael@0 135
michael@0 136 // TODO: Why don't we just use this here:
michael@0 137 // httpChannelInternal->GetDocumentURI(aURI);
michael@0 138 }
michael@0 139
michael@0 140 // find the associated window and its top window
michael@0 141 nsCOMPtr<nsILoadContext> ctx;
michael@0 142 NS_QueryNotificationCallbacks(aChannel, ctx);
michael@0 143 nsCOMPtr<nsIDOMWindow> topWin, ourWin;
michael@0 144 if (ctx) {
michael@0 145 ctx->GetTopWindow(getter_AddRefs(topWin));
michael@0 146 ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
michael@0 147 }
michael@0 148
michael@0 149 // case 3)
michael@0 150 if (!topWin)
michael@0 151 return NS_ERROR_INVALID_ARG;
michael@0 152
michael@0 153 // case 4)
michael@0 154 if (ourWin == topWin) {
michael@0 155 // Check whether this is the document channel for this window (representing
michael@0 156 // a load of a new page). This is a bit of a nasty hack, but we will
michael@0 157 // hopefully flag these channels better later.
michael@0 158 nsLoadFlags flags;
michael@0 159 aChannel->GetLoadFlags(&flags);
michael@0 160
michael@0 161 if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
michael@0 162 // get the channel URI - the window's will be bogus
michael@0 163 aChannel->GetURI(aURI);
michael@0 164 if (!*aURI)
michael@0 165 return NS_ERROR_NULL_POINTER;
michael@0 166
michael@0 167 return NS_OK;
michael@0 168 }
michael@0 169 }
michael@0 170
michael@0 171 // case 5) - get the originating URI from the top window's principal
michael@0 172 nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(topWin);
michael@0 173 NS_ENSURE_TRUE(scriptObjPrin, NS_ERROR_UNEXPECTED);
michael@0 174
michael@0 175 nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
michael@0 176 NS_ENSURE_TRUE(prin, NS_ERROR_UNEXPECTED);
michael@0 177
michael@0 178 prin->GetURI(aURI);
michael@0 179
michael@0 180 if (!*aURI)
michael@0 181 return NS_ERROR_NULL_POINTER;
michael@0 182
michael@0 183 // all done!
michael@0 184 return NS_OK;
michael@0 185 }
michael@0 186
michael@0 187
michael@0 188 // Determine if aFirstURI is third party with respect to aSecondURI. See docs
michael@0 189 // for mozIThirdPartyUtil.
michael@0 190 NS_IMETHODIMP
michael@0 191 ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI,
michael@0 192 nsIURI* aSecondURI,
michael@0 193 bool* aResult)
michael@0 194 {
michael@0 195 NS_ENSURE_ARG(aFirstURI);
michael@0 196 NS_ENSURE_ARG(aSecondURI);
michael@0 197 NS_ASSERTION(aResult, "null outparam pointer");
michael@0 198
michael@0 199 nsCString firstHost;
michael@0 200 nsresult rv = GetBaseDomain(aFirstURI, firstHost);
michael@0 201 if (NS_FAILED(rv))
michael@0 202 return rv;
michael@0 203
michael@0 204 return IsThirdPartyInternal(firstHost, aSecondURI, aResult);
michael@0 205 }
michael@0 206
michael@0 207 // Determine if any URI of the window hierarchy of aWindow is foreign with
michael@0 208 // respect to aSecondURI. See docs for mozIThirdPartyUtil.
michael@0 209 NS_IMETHODIMP
michael@0 210 ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow* aWindow,
michael@0 211 nsIURI* aURI,
michael@0 212 bool* aResult)
michael@0 213 {
michael@0 214 NS_ENSURE_ARG(aWindow);
michael@0 215 NS_ASSERTION(aResult, "null outparam pointer");
michael@0 216
michael@0 217 bool result;
michael@0 218
michael@0 219 // Get the URI of the window, and its base domain.
michael@0 220 nsCOMPtr<nsIURI> currentURI = GetURIFromWindow(aWindow);
michael@0 221 NS_ENSURE_TRUE(currentURI, NS_ERROR_INVALID_ARG);
michael@0 222
michael@0 223 nsCString bottomDomain;
michael@0 224 nsresult rv = GetBaseDomain(currentURI, bottomDomain);
michael@0 225 if (NS_FAILED(rv))
michael@0 226 return rv;
michael@0 227
michael@0 228 if (aURI) {
michael@0 229 // Determine whether aURI is foreign with respect to currentURI.
michael@0 230 rv = IsThirdPartyInternal(bottomDomain, aURI, &result);
michael@0 231 if (NS_FAILED(rv))
michael@0 232 return rv;
michael@0 233
michael@0 234 if (result) {
michael@0 235 *aResult = true;
michael@0 236 return NS_OK;
michael@0 237 }
michael@0 238 }
michael@0 239
michael@0 240 nsCOMPtr<nsIDOMWindow> current = aWindow, parent;
michael@0 241 nsCOMPtr<nsIURI> parentURI;
michael@0 242 do {
michael@0 243 // We use GetScriptableParent rather than GetParent because we consider
michael@0 244 // <iframe mozbrowser/mozapp> to be a top-level frame.
michael@0 245 rv = current->GetScriptableParent(getter_AddRefs(parent));
michael@0 246 NS_ENSURE_SUCCESS(rv, rv);
michael@0 247
michael@0 248 if (SameCOMIdentity(parent, current)) {
michael@0 249 // We're at the topmost content window. We already know the answer.
michael@0 250 *aResult = false;
michael@0 251 return NS_OK;
michael@0 252 }
michael@0 253
michael@0 254 parentURI = GetURIFromWindow(parent);
michael@0 255 NS_ENSURE_TRUE(parentURI, NS_ERROR_INVALID_ARG);
michael@0 256
michael@0 257 rv = IsThirdPartyInternal(bottomDomain, parentURI, &result);
michael@0 258 if (NS_FAILED(rv))
michael@0 259 return rv;
michael@0 260
michael@0 261 if (result) {
michael@0 262 *aResult = true;
michael@0 263 return NS_OK;
michael@0 264 }
michael@0 265
michael@0 266 current = parent;
michael@0 267 currentURI = parentURI;
michael@0 268 } while (1);
michael@0 269
michael@0 270 NS_NOTREACHED("should've returned");
michael@0 271 return NS_ERROR_UNEXPECTED;
michael@0 272 }
michael@0 273
michael@0 274 // Determine if the URI associated with aChannel or any URI of the window
michael@0 275 // hierarchy associated with the channel is foreign with respect to aSecondURI.
michael@0 276 // See docs for mozIThirdPartyUtil.
michael@0 277 NS_IMETHODIMP
michael@0 278 ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
michael@0 279 nsIURI* aURI,
michael@0 280 bool* aResult)
michael@0 281 {
michael@0 282 NS_ENSURE_ARG(aChannel);
michael@0 283 NS_ASSERTION(aResult, "null outparam pointer");
michael@0 284
michael@0 285 nsresult rv;
michael@0 286 bool doForce = false;
michael@0 287 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
michael@0 288 do_QueryInterface(aChannel);
michael@0 289 if (httpChannelInternal) {
michael@0 290 rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
michael@0 291 NS_ENSURE_SUCCESS(rv, rv);
michael@0 292
michael@0 293 // If aURI was not supplied, and we're forcing, then we're by definition
michael@0 294 // not foreign. If aURI was supplied, we still want to check whether it's
michael@0 295 // foreign with respect to the channel URI. (The forcing only applies to
michael@0 296 // whatever window hierarchy exists above the channel.)
michael@0 297 if (doForce && !aURI) {
michael@0 298 *aResult = false;
michael@0 299 return NS_OK;
michael@0 300 }
michael@0 301 }
michael@0 302
michael@0 303 // Obtain the URI from the channel, and its base domain.
michael@0 304 nsCOMPtr<nsIURI> channelURI;
michael@0 305 aChannel->GetURI(getter_AddRefs(channelURI));
michael@0 306 NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
michael@0 307
michael@0 308 nsCString channelDomain;
michael@0 309 rv = GetBaseDomain(channelURI, channelDomain);
michael@0 310 if (NS_FAILED(rv))
michael@0 311 return rv;
michael@0 312
michael@0 313 if (aURI) {
michael@0 314 // Determine whether aURI is foreign with respect to channelURI.
michael@0 315 bool result;
michael@0 316 rv = IsThirdPartyInternal(channelDomain, aURI, &result);
michael@0 317 if (NS_FAILED(rv))
michael@0 318 return rv;
michael@0 319
michael@0 320 // If it's foreign, or we're forcing, we're done.
michael@0 321 if (result || doForce) {
michael@0 322 *aResult = result;
michael@0 323 return NS_OK;
michael@0 324 }
michael@0 325 }
michael@0 326
michael@0 327 // Find the associated window and its parent window.
michael@0 328 nsCOMPtr<nsILoadContext> ctx;
michael@0 329 NS_QueryNotificationCallbacks(aChannel, ctx);
michael@0 330 if (!ctx) return NS_ERROR_INVALID_ARG;
michael@0 331
michael@0 332 // If there is no window, the consumer kicking off the load didn't provide one
michael@0 333 // to the channel. This is limited to loads of certain types of resources. If
michael@0 334 // those loads require cookies, the forceAllowThirdPartyCookie property should
michael@0 335 // be set on the channel.
michael@0 336 nsCOMPtr<nsIDOMWindow> ourWin, parentWin;
michael@0 337 ctx->GetAssociatedWindow(getter_AddRefs(ourWin));
michael@0 338 if (!ourWin) return NS_ERROR_INVALID_ARG;
michael@0 339
michael@0 340 // We use GetScriptableParent rather than GetParent because we consider
michael@0 341 // <iframe mozbrowser/mozapp> to be a top-level frame.
michael@0 342 ourWin->GetScriptableParent(getter_AddRefs(parentWin));
michael@0 343 NS_ENSURE_TRUE(parentWin, NS_ERROR_INVALID_ARG);
michael@0 344
michael@0 345 // Check whether this is the document channel for this window (representing a
michael@0 346 // load of a new page). In that situation we want to avoid comparing
michael@0 347 // channelURI to ourWin, since what's in ourWin right now will be replaced as
michael@0 348 // the channel loads. This covers the case of a freshly kicked-off load
michael@0 349 // (e.g. the user typing something in the location bar, or clicking on a
michael@0 350 // bookmark), where the window's URI hasn't yet been set, and will be bogus.
michael@0 351 // It also covers situations where a subframe is navigated to someting that
michael@0 352 // is same-origin with all its ancestors. This is a bit of a nasty hack, but
michael@0 353 // we will hopefully flag these channels better later.
michael@0 354 nsLoadFlags flags;
michael@0 355 rv = aChannel->GetLoadFlags(&flags);
michael@0 356 NS_ENSURE_SUCCESS(rv, rv);
michael@0 357
michael@0 358 if (flags & nsIChannel::LOAD_DOCUMENT_URI) {
michael@0 359 if (SameCOMIdentity(ourWin, parentWin)) {
michael@0 360 // We only need to compare aURI to the channel URI -- the window's will be
michael@0 361 // bogus. We already know the answer.
michael@0 362 *aResult = false;
michael@0 363 return NS_OK;
michael@0 364 }
michael@0 365
michael@0 366 // Make sure to still compare to ourWin's ancestors
michael@0 367 ourWin = parentWin;
michael@0 368 }
michael@0 369
michael@0 370 // Check the window hierarchy. This covers most cases for an ordinary page
michael@0 371 // load from the location bar.
michael@0 372 return IsThirdPartyWindow(ourWin, channelURI, aResult);
michael@0 373 }
michael@0 374
michael@0 375 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
michael@0 376 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
michael@0 377 // dot may be present. If aHostURI is an IP address, an alias such as
michael@0 378 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
michael@0 379 // be the exact host. The result of this function should only be used in exact
michael@0 380 // string comparisons, since substring comparisons will not be valid for the
michael@0 381 // special cases elided above.
michael@0 382 NS_IMETHODIMP
michael@0 383 ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI,
michael@0 384 nsACString& aBaseDomain)
michael@0 385 {
michael@0 386 // Get the base domain. this will fail if the host contains a leading dot,
michael@0 387 // more than one trailing dot, or is otherwise malformed.
michael@0 388 nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
michael@0 389 if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
michael@0 390 rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
michael@0 391 // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
michael@0 392 // such as 'co.uk', or the empty string. Uses the normalized host in such
michael@0 393 // cases.
michael@0 394 rv = aHostURI->GetAsciiHost(aBaseDomain);
michael@0 395 }
michael@0 396 NS_ENSURE_SUCCESS(rv, rv);
michael@0 397
michael@0 398 // aHostURI (and thus aBaseDomain) may be the string '.'. If so, fail.
michael@0 399 if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
michael@0 400 return NS_ERROR_INVALID_ARG;
michael@0 401
michael@0 402 // Reject any URIs without a host that aren't file:// URIs. This makes it the
michael@0 403 // only way we can get a base domain consisting of the empty string, which
michael@0 404 // means we can safely perform foreign tests on such URIs where "not foreign"
michael@0 405 // means "the involved URIs are all file://".
michael@0 406 if (aBaseDomain.IsEmpty()) {
michael@0 407 bool isFileURI = false;
michael@0 408 aHostURI->SchemeIs("file", &isFileURI);
michael@0 409 NS_ENSURE_TRUE(isFileURI, NS_ERROR_INVALID_ARG);
michael@0 410 }
michael@0 411
michael@0 412 return NS_OK;
michael@0 413 }
michael@0 414
michael@0 415 // Returns true if First Party Isolation is currently active for the given nsIChannel.
michael@0 416 // Depends on Preference setting and possibly the state of Private Browsing mode.
michael@0 417 bool ThirdPartyUtil::IsFirstPartyIsolationActive(nsIChannel *aChannel, nsIDocument *aDoc)
michael@0 418 {
michael@0 419 int32_t isolationState = mozilla::Preferences::GetInt("privacy.thirdparty.isolate");
michael@0 420 if (isolationState == 1) {
michael@0 421 if (!aChannel && aDoc) {
michael@0 422 // No channel passed directly. Can we get a channel from aDoc?
michael@0 423 aChannel = aDoc->GetChannel();
michael@0 424 }
michael@0 425 return aChannel && NS_UsePrivateBrowsing(aChannel);
michael@0 426 } else { // (isolationState == 0) || (isolationState == 2)
michael@0 427 return (isolationState == 2);
michael@0 428 }
michael@0 429 }
michael@0 430
michael@0 431 // Produces a URI that uniquely identifies the first party to which
michael@0 432 // image cache and dom storage objects should be isolated. If isolation
michael@0 433 // is deactivated, then aOutput will return null.
michael@0 434 // Not scriptable due to the use of an nsIDocument parameter.
michael@0 435 NS_IMETHODIMP
michael@0 436 ThirdPartyUtil::GetFirstPartyIsolationURI(nsIChannel *aChannel, nsIDocument *aDoc, nsIURI **aOutput)
michael@0 437 {
michael@0 438 bool isolationActive = IsFirstPartyIsolationActive(aChannel, aDoc);
michael@0 439 if (isolationActive) {
michael@0 440 return GetFirstPartyURI(aChannel, aDoc, aOutput);
michael@0 441 } else {
michael@0 442 // We return a null pointer when isolation is off.
michael@0 443 *aOutput = nullptr;
michael@0 444 return NS_OK;
michael@0 445 }
michael@0 446 }
michael@0 447
michael@0 448 // Not scriptable due to the use of an nsIDocument parameter.
michael@0 449 NS_IMETHODIMP
michael@0 450 ThirdPartyUtil::GetFirstPartyURI(nsIChannel *aChannel,
michael@0 451 nsIDocument *aDoc,
michael@0 452 nsIURI **aOutput)
michael@0 453 {
michael@0 454 return GetFirstPartyURIInternal(aChannel, aDoc, true, aOutput);
michael@0 455 }
michael@0 456
michael@0 457 nsresult
michael@0 458 ThirdPartyUtil::GetFirstPartyURIInternal(nsIChannel *aChannel,
michael@0 459 nsIDocument *aDoc,
michael@0 460 bool aLogErrors,
michael@0 461 nsIURI **aOutput)
michael@0 462 {
michael@0 463 nsresult rv = NS_ERROR_NULL_POINTER;
michael@0 464 nsCOMPtr<nsIURI> srcURI;
michael@0 465
michael@0 466 if (!aOutput)
michael@0 467 return rv;
michael@0 468
michael@0 469 *aOutput = nullptr;
michael@0 470
michael@0 471 if (!aChannel && aDoc) {
michael@0 472 aChannel = aDoc->GetChannel();
michael@0 473 }
michael@0 474
michael@0 475 // If aChannel is specified or available, use the official route
michael@0 476 // for sure
michael@0 477 if (aChannel) {
michael@0 478 rv = GetOriginatingURI(aChannel, aOutput);
michael@0 479 aChannel->GetURI(getter_AddRefs(srcURI));
michael@0 480 if (NS_SUCCEEDED(rv) && *aOutput) {
michael@0 481 // At this point, about: and chrome: URLs have been mapped to file: or
michael@0 482 // jar: URLs. Try to recover the original URL.
michael@0 483 nsAutoCString scheme;
michael@0 484 nsresult rv2 = (*aOutput)->GetScheme(scheme);
michael@0 485 NS_ENSURE_SUCCESS(rv2, rv2);
michael@0 486 if (scheme.Equals("file") || scheme.Equals("jar")) {
michael@0 487 nsCOMPtr<nsIURI> originalURI;
michael@0 488 rv2 = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
michael@0 489 if (NS_SUCCEEDED(rv2) && originalURI) {
michael@0 490 NS_RELEASE(*aOutput);
michael@0 491 NS_ADDREF(*aOutput = originalURI);
michael@0 492 }
michael@0 493 }
michael@0 494 }
michael@0 495 }
michael@0 496
michael@0 497 // If the channel was missing, closed or broken, try the
michael@0 498 // window hierarchy directly.
michael@0 499 //
michael@0 500 // This might fail to work for first-party loads themselves, but
michael@0 501 // we don't need this codepath for that case.
michael@0 502 if (NS_FAILED(rv) && aDoc) {
michael@0 503 nsCOMPtr<nsIDOMWindow> top;
michael@0 504 nsCOMPtr<nsIDOMDocument> topDDoc;
michael@0 505 nsIURI *docURI = nullptr;
michael@0 506 srcURI = aDoc->GetDocumentURI();
michael@0 507
michael@0 508 if (aDoc->GetWindow()) {
michael@0 509 aDoc->GetWindow()->GetTop(getter_AddRefs(top));
michael@0 510 top->GetDocument(getter_AddRefs(topDDoc));
michael@0 511
michael@0 512 nsCOMPtr<nsIDocument> topDoc(do_QueryInterface(topDDoc));
michael@0 513 docURI = topDoc->GetOriginalURI();
michael@0 514 if (docURI) {
michael@0 515 // Give us a mutable URI and also addref
michael@0 516 rv = NS_EnsureSafeToReturn(docURI, aOutput);
michael@0 517 }
michael@0 518 } else {
michael@0 519 // XXX: Chrome callers (such as NoScript) can end up here
michael@0 520 // through getImageData/canvas usage with no document state
michael@0 521 // (no Window and a document URI of about:blank). Propogate
michael@0 522 // rv fail (by doing nothing), and hope caller recovers.
michael@0 523 }
michael@0 524
michael@0 525 if (*aOutput)
michael@0 526 rv = NS_OK;
michael@0 527 }
michael@0 528
michael@0 529 if (*aOutput && !SchemeIsWhiteListed(*aOutput)) {
michael@0 530 // If URI scheme is not whitelisted and the URI lacks a hostname, force a
michael@0 531 // failure.
michael@0 532 nsAutoCString host;
michael@0 533 rv = (*aOutput)->GetHost(host);
michael@0 534 if (NS_SUCCEEDED(rv) && (host.Length() == 0)) {
michael@0 535 rv = NS_ERROR_FAILURE;
michael@0 536 }
michael@0 537 }
michael@0 538
michael@0 539 // Log failure to error console.
michael@0 540 if (aLogErrors && NS_FAILED(rv)) {
michael@0 541 nsCOMPtr<nsIConsoleService> console
michael@0 542 (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
michael@0 543 if (console) {
michael@0 544 nsCString spec;
michael@0 545 nsCString srcSpec("unknown");
michael@0 546
michael@0 547 if (srcURI)
michael@0 548 srcURI->GetSpec(srcSpec);
michael@0 549
michael@0 550 if (*aOutput)
michael@0 551 (*aOutput)->GetSpec(spec);
michael@0 552 if (spec.Length() > 0) {
michael@0 553 nsPrintfCString msg("getFirstPartyURI failed for %s: no host in first party URI %s",
michael@0 554 srcSpec.get(), spec.get()); // TODO: L10N
michael@0 555 console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get());
michael@0 556 } else {
michael@0 557 nsPrintfCString msg("getFirstPartyURI failed for %s: 0x%x", srcSpec.get(), rv);
michael@0 558 console->LogStringMessage(NS_ConvertUTF8toUTF16(msg).get());
michael@0 559 }
michael@0 560 }
michael@0 561
michael@0 562 if (*aOutput) {
michael@0 563 // discard return object.
michael@0 564 (*aOutput)->Release();
michael@0 565 *aOutput = nullptr;
michael@0 566 }
michael@0 567 }
michael@0 568
michael@0 569 // TODO: We could provide a route through the loadgroup + notification
michael@0 570 // callbacks too, but either channel or document was always available
michael@0 571 // in the cases where this function was originally needed (the image cache).
michael@0 572 // The notification callbacks also appear to suffers from the same limitation
michael@0 573 // as the document path. See nsICookiePermissions.GetOriginatingURI() for
michael@0 574 // details.
michael@0 575
michael@0 576 return rv;
michael@0 577 }
michael@0 578
michael@0 579 NS_IMETHODIMP
michael@0 580 ThirdPartyUtil::GetFirstPartyURIFromChannel(nsIChannel *aChannel,
michael@0 581 bool aLogErrors,
michael@0 582 nsIURI **aOutput)
michael@0 583 {
michael@0 584 return GetFirstPartyURIInternal(aChannel, nullptr, aLogErrors, aOutput);
michael@0 585 }
michael@0 586
michael@0 587 NS_IMETHODIMP
michael@0 588 ThirdPartyUtil::GetFirstPartyHostForIsolation(nsIURI *aFirstPartyURI,
michael@0 589 nsACString& aHost)
michael@0 590 {
michael@0 591 if (!aFirstPartyURI)
michael@0 592 return NS_ERROR_INVALID_ARG;
michael@0 593
michael@0 594 if (!SchemeIsWhiteListed(aFirstPartyURI)) {
michael@0 595 nsresult rv = aFirstPartyURI->GetHost(aHost);
michael@0 596 return (aHost.Length() > 0) ? NS_OK : rv;
michael@0 597 }
michael@0 598
michael@0 599 // This URI lacks a host, so construct and return a pseudo-host.
michael@0 600 aHost = "--NoFirstPartyHost-";
michael@0 601
michael@0 602 // Append the scheme. To ensure that the pseudo-hosts are consistent
michael@0 603 // when the hacky "moz-safe-about" scheme is used, map it back to "about".
michael@0 604 nsAutoCString scheme;
michael@0 605 nsresult rv = aFirstPartyURI->GetScheme(scheme);
michael@0 606 NS_ENSURE_SUCCESS(rv, rv);
michael@0 607 if (scheme.Equals("moz-safe-about"))
michael@0 608 aHost.Append("about");
michael@0 609 else
michael@0 610 aHost.Append(scheme);
michael@0 611
michael@0 612 // Append the URL's file name (e.g., -browser.xul) or its path (e.g.,
michael@0 613 // -home for about:home)
michael@0 614 nsAutoCString s;
michael@0 615 nsCOMPtr<nsIURL> url = do_QueryInterface(aFirstPartyURI);
michael@0 616 if (url)
michael@0 617 url->GetFileName(s);
michael@0 618 else
michael@0 619 aFirstPartyURI->GetPath(s);
michael@0 620
michael@0 621 if (s.Length() > 0) {
michael@0 622 aHost.Append("-");
michael@0 623 aHost.Append(s);
michael@0 624 }
michael@0 625
michael@0 626 aHost.Append("--");
michael@0 627 return NS_OK;
michael@0 628 }

mercurial