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/. */
5 #include "ThirdPartyUtil.h"
7 #include "nsIServiceManager.h"
8 #include "nsIHttpChannelInternal.h"
9 #include "nsIDOMWindow.h"
10 #include "nsILoadContext.h"
11 #include "nsIPrincipal.h"
12 #include "nsIScriptObjectPrincipal.h"
13 #include "nsThreadUtils.h"
15 NS_IMPL_ISUPPORTS(ThirdPartyUtil
, mozIThirdPartyUtil
)
18 ThirdPartyUtil::Init()
20 NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE
);
23 mTLDService
= do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
, &rv
);
27 // Determine if aFirstDomain is a different base domain to aSecondURI; or, if
28 // the concept of base domain does not apply, determine if the two hosts are not
31 ThirdPartyUtil::IsThirdPartyInternal(const nsCString
& aFirstDomain
,
35 NS_ENSURE_ARG(aSecondURI
);
37 // Get the base domain for aSecondURI.
38 nsCString secondDomain
;
39 nsresult rv
= GetBaseDomain(aSecondURI
, secondDomain
);
43 // Check strict equality.
44 *aResult
= aFirstDomain
!= secondDomain
;
48 // Get the URI associated with a window.
50 ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow
* aWin
, nsIURI
** result
)
53 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptObjPrin
= do_QueryInterface(aWin
);
55 return NS_ERROR_INVALID_ARG
;
58 nsIPrincipal
* prin
= scriptObjPrin
->GetPrincipal();
60 return NS_ERROR_INVALID_ARG
;
64 rv
= prin
->GetURI(result
);
68 // Determine if aFirstURI is third party with respect to aSecondURI. See docs
69 // for mozIThirdPartyUtil.
71 ThirdPartyUtil::IsThirdPartyURI(nsIURI
* aFirstURI
,
75 NS_ENSURE_ARG(aFirstURI
);
76 NS_ENSURE_ARG(aSecondURI
);
77 NS_ASSERTION(aResult
, "null outparam pointer");
80 nsresult rv
= GetBaseDomain(aFirstURI
, firstHost
);
84 return IsThirdPartyInternal(firstHost
, aSecondURI
, aResult
);
87 // Determine if any URI of the window hierarchy of aWindow is foreign with
88 // respect to aSecondURI. See docs for mozIThirdPartyUtil.
90 ThirdPartyUtil::IsThirdPartyWindow(nsIDOMWindow
* aWindow
,
94 NS_ENSURE_ARG(aWindow
);
95 NS_ASSERTION(aResult
, "null outparam pointer");
99 // Get the URI of the window, and its base domain.
101 nsCOMPtr
<nsIURI
> currentURI
;
102 rv
= GetURIFromWindow(aWindow
, getter_AddRefs(currentURI
));
103 NS_ENSURE_SUCCESS(rv
, rv
);
105 nsCString bottomDomain
;
106 rv
= GetBaseDomain(currentURI
, bottomDomain
);
111 // Determine whether aURI is foreign with respect to currentURI.
112 rv
= IsThirdPartyInternal(bottomDomain
, aURI
, &result
);
122 nsCOMPtr
<nsIDOMWindow
> current
= aWindow
, parent
;
123 nsCOMPtr
<nsIURI
> parentURI
;
125 // We use GetScriptableParent rather than GetParent because we consider
126 // <iframe mozbrowser/mozapp> to be a top-level frame.
127 rv
= current
->GetScriptableParent(getter_AddRefs(parent
));
128 NS_ENSURE_SUCCESS(rv
, rv
);
130 if (SameCOMIdentity(parent
, current
)) {
131 // We're at the topmost content window. We already know the answer.
136 rv
= GetURIFromWindow(parent
, getter_AddRefs(parentURI
));
137 NS_ENSURE_SUCCESS(rv
, rv
);
139 rv
= IsThirdPartyInternal(bottomDomain
, parentURI
, &result
);
149 currentURI
= parentURI
;
152 NS_NOTREACHED("should've returned");
153 return NS_ERROR_UNEXPECTED
;
156 // Determine if the URI associated with aChannel or any URI of the window
157 // hierarchy associated with the channel is foreign with respect to aSecondURI.
158 // See docs for mozIThirdPartyUtil.
160 ThirdPartyUtil::IsThirdPartyChannel(nsIChannel
* aChannel
,
164 NS_ENSURE_ARG(aChannel
);
165 NS_ASSERTION(aResult
, "null outparam pointer");
168 bool doForce
= false;
169 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInternal
=
170 do_QueryInterface(aChannel
);
171 if (httpChannelInternal
) {
172 rv
= httpChannelInternal
->GetForceAllowThirdPartyCookie(&doForce
);
173 NS_ENSURE_SUCCESS(rv
, rv
);
175 // If aURI was not supplied, and we're forcing, then we're by definition
176 // not foreign. If aURI was supplied, we still want to check whether it's
177 // foreign with respect to the channel URI. (The forcing only applies to
178 // whatever window hierarchy exists above the channel.)
179 if (doForce
&& !aURI
) {
185 // Obtain the URI from the channel, and its base domain.
186 nsCOMPtr
<nsIURI
> channelURI
;
187 aChannel
->GetURI(getter_AddRefs(channelURI
));
188 NS_ENSURE_TRUE(channelURI
, NS_ERROR_INVALID_ARG
);
190 nsCString channelDomain
;
191 rv
= GetBaseDomain(channelURI
, channelDomain
);
196 // Determine whether aURI is foreign with respect to channelURI.
198 rv
= IsThirdPartyInternal(channelDomain
, aURI
, &result
);
202 // If it's foreign, or we're forcing, we're done.
203 if (result
|| doForce
) {
209 // Find the associated window and its parent window.
210 nsCOMPtr
<nsILoadContext
> ctx
;
211 NS_QueryNotificationCallbacks(aChannel
, ctx
);
212 if (!ctx
) return NS_ERROR_INVALID_ARG
;
214 // If there is no window, the consumer kicking off the load didn't provide one
215 // to the channel. This is limited to loads of certain types of resources. If
216 // those loads require cookies, the forceAllowThirdPartyCookie property should
217 // be set on the channel.
218 nsCOMPtr
<nsIDOMWindow
> ourWin
, parentWin
;
219 ctx
->GetAssociatedWindow(getter_AddRefs(ourWin
));
220 if (!ourWin
) return NS_ERROR_INVALID_ARG
;
222 // We use GetScriptableParent rather than GetParent because we consider
223 // <iframe mozbrowser/mozapp> to be a top-level frame.
224 ourWin
->GetScriptableParent(getter_AddRefs(parentWin
));
225 NS_ENSURE_TRUE(parentWin
, NS_ERROR_INVALID_ARG
);
227 // Check whether this is the document channel for this window (representing a
228 // load of a new page). In that situation we want to avoid comparing
229 // channelURI to ourWin, since what's in ourWin right now will be replaced as
230 // the channel loads. This covers the case of a freshly kicked-off load
231 // (e.g. the user typing something in the location bar, or clicking on a
232 // bookmark), where the window's URI hasn't yet been set, and will be bogus.
233 // It also covers situations where a subframe is navigated to someting that
234 // is same-origin with all its ancestors. This is a bit of a nasty hack, but
235 // we will hopefully flag these channels better later.
237 rv
= aChannel
->GetLoadFlags(&flags
);
238 NS_ENSURE_SUCCESS(rv
, rv
);
240 if (flags
& nsIChannel::LOAD_DOCUMENT_URI
) {
241 if (SameCOMIdentity(ourWin
, parentWin
)) {
242 // We only need to compare aURI to the channel URI -- the window's will be
243 // bogus. We already know the answer.
248 // Make sure to still compare to ourWin's ancestors
252 // Check the window hierarchy. This covers most cases for an ordinary page
253 // load from the location bar.
254 return IsThirdPartyWindow(ourWin
, channelURI
, aResult
);
258 ThirdPartyUtil::GetTopWindowForChannel(nsIChannel
* aChannel
, nsIDOMWindow
** aWin
)
263 // Find the associated window and its parent window.
264 nsCOMPtr
<nsILoadContext
> ctx
;
265 NS_QueryNotificationCallbacks(aChannel
, ctx
);
267 return NS_ERROR_INVALID_ARG
;
270 nsCOMPtr
<nsIDOMWindow
> window
;
271 rv
= ctx
->GetAssociatedWindow(getter_AddRefs(window
));
273 return NS_ERROR_INVALID_ARG
;
276 rv
= window
->GetTop(aWin
);
280 // Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
281 // "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
282 // dot may be present. If aHostURI is an IP address, an alias such as
283 // 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
284 // be the exact host. The result of this function should only be used in exact
285 // string comparisons, since substring comparisons will not be valid for the
286 // special cases elided above.
288 ThirdPartyUtil::GetBaseDomain(nsIURI
* aHostURI
,
289 nsACString
& aBaseDomain
)
291 // Get the base domain. this will fail if the host contains a leading dot,
292 // more than one trailing dot, or is otherwise malformed.
293 nsresult rv
= mTLDService
->GetBaseDomain(aHostURI
, 0, aBaseDomain
);
294 if (rv
== NS_ERROR_HOST_IS_IP_ADDRESS
||
295 rv
== NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
) {
296 // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
297 // such as 'co.uk', or the empty string. Uses the normalized host in such
299 rv
= aHostURI
->GetAsciiHost(aBaseDomain
);
301 NS_ENSURE_SUCCESS(rv
, rv
);
303 // aHostURI (and thus aBaseDomain) may be the string '.'. If so, fail.
304 if (aBaseDomain
.Length() == 1 && aBaseDomain
.Last() == '.')
305 return NS_ERROR_INVALID_ARG
;
307 // Reject any URIs without a host that aren't file:// URIs. This makes it the
308 // only way we can get a base domain consisting of the empty string, which
309 // means we can safely perform foreign tests on such URIs where "not foreign"
310 // means "the involved URIs are all file://".
311 if (aBaseDomain
.IsEmpty()) {
312 bool isFileURI
= false;
313 aHostURI
->SchemeIs("file", &isFileURI
);
314 NS_ENSURE_TRUE(isFileURI
, NS_ERROR_INVALID_ARG
);