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