Thu, 15 Jan 2015 21:03:48 +0100
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 | } |