1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is content blocker code.
16 * The Initial Developer of the Original Code is
17 * Michiel van Leeuwen <mvl@exedo.nl>.
18 * Portions created by the Initial Developer are Copyright (C) 2004
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
36 #include "nsContentBlocker.h"
37 #include "nsIDocument.h"
38 #include "nsIContent.h"
40 #include "nsIServiceManager.h"
41 #include "nsIDocShellTreeItem.h"
42 #include "nsIPrefService.h"
43 #include "nsIPrefBranch.h"
44 #include "nsIDocShell.h"
46 #include "nsContentPolicyUtils.h"
47 #include "nsIObjectLoadingContent.h"
49 // Possible behavior pref values
50 // Those map to the nsIPermissionManager values where possible
51 #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
52 #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
53 #define BEHAVIOR_NOFOREIGN 3
55 // From nsIContentPolicy
56 static const char *kTypeString
[NUMBER_OF_TYPES
] = {"other",
71 NS_IMPL_ISUPPORTS3(nsContentBlocker
,
74 nsSupportsWeakReference
)
76 nsContentBlocker::nsContentBlocker()
78 memset(mBehaviorPref
, BEHAVIOR_ACCEPT
, NUMBER_OF_TYPES
);
82 nsContentBlocker::Init()
85 mPermissionManager
= do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
, &rv
);
86 NS_ENSURE_SUCCESS(rv
, rv
);
88 nsCOMPtr
<nsIPrefService
> prefService
= do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
89 NS_ENSURE_SUCCESS(rv
, rv
);
91 nsCOMPtr
<nsIPrefBranch
> prefBranch
;
92 rv
= prefService
->GetBranch("permissions.default.", getter_AddRefs(prefBranch
));
93 NS_ENSURE_SUCCESS(rv
, rv
);
95 // Migrate old image blocker pref
96 nsCOMPtr
<nsIPrefBranch
> oldPrefBranch
;
97 oldPrefBranch
= do_QueryInterface(prefService
);
99 rv
= oldPrefBranch
->GetIntPref("network.image.imageBehavior", &oldPref
);
100 if (NS_SUCCEEDED(rv
) && oldPref
) {
104 newPref
= BEHAVIOR_ACCEPT
;
107 newPref
= BEHAVIOR_NOFOREIGN
;
110 newPref
= BEHAVIOR_REJECT
;
113 prefBranch
->SetIntPref("image", newPref
);
114 oldPrefBranch
->ClearUserPref("network.image.imageBehavior");
118 // The branch is not a copy of the prefservice, but a new object, because
119 // it is a non-default branch. Adding obeservers to it will only work if
120 // we make sure that the object doesn't die. So, keep a reference to it.
121 mPrefBranchInternal
= do_QueryInterface(prefBranch
, &rv
);
122 NS_ENSURE_SUCCESS(rv
, rv
);
124 rv
= mPrefBranchInternal
->AddObserver("", this, PR_TRUE
);
125 PrefChanged(prefBranch
, nsnull
);
131 #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
134 nsContentBlocker::PrefChanged(nsIPrefBranch
*aPrefBranch
,
139 #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
141 for(PRUint32 i
= 0; i
< NUMBER_OF_TYPES
; ++i
) {
142 if (PREF_CHANGED(kTypeString
[i
]) &&
143 NS_SUCCEEDED(aPrefBranch
->GetIntPref(kTypeString
[i
], &val
)))
144 mBehaviorPref
[i
] = LIMIT(val
, 1, 3, 1);
149 // nsIContentPolicy Implementation
151 nsContentBlocker::ShouldLoad(PRUint32 aContentType
,
152 nsIURI
*aContentLocation
,
153 nsIURI
*aRequestingLocation
,
154 nsISupports
*aRequestingContext
,
155 const nsACString
&aMimeGuess
,
159 *aDecision
= nsIContentPolicy::ACCEPT
;
162 // Ony support NUMBER_OF_TYPES content types. that all there is at the
163 // moment, but you never know...
164 if (aContentType
> NUMBER_OF_TYPES
)
167 // we can't do anything without this
168 if (!aContentLocation
)
171 // we only want to check http, https, ftp
172 // for chrome:// and resources and others, no need to check.
173 nsCAutoString scheme
;
174 aContentLocation
->GetScheme(scheme
);
175 if (!scheme
.LowerCaseEqualsLiteral("ftp") &&
176 !scheme
.LowerCaseEqualsLiteral("http") &&
177 !scheme
.LowerCaseEqualsLiteral("https"))
180 PRBool shouldLoad
, fromPrefs
;
181 rv
= TestPermission(aContentLocation
, aRequestingLocation
, aContentType
,
182 &shouldLoad
, &fromPrefs
);
183 NS_ENSURE_SUCCESS(rv
, rv
);
186 *aDecision
= nsIContentPolicy::REJECT_TYPE
;
188 *aDecision
= nsIContentPolicy::REJECT_SERVER
;
190 if (aContentType
!= nsIContentPolicy::TYPE_OBJECT
|| aMimeGuess
.IsEmpty())
193 // For TYPE_OBJECT we should check what aMimeGuess might tell us
194 // about what sort of object it is.
195 nsCOMPtr
<nsIObjectLoadingContent
> objectLoader
=
196 do_QueryInterface(aRequestingContext
);
200 PRUint32 contentType
;
201 rv
= objectLoader
->GetContentTypeForMIMEType(aMimeGuess
, &contentType
);
205 switch (contentType
) {
206 case nsIObjectLoadingContent::TYPE_IMAGE
:
207 aContentType
= nsIContentPolicy::TYPE_IMAGE
;
209 case nsIObjectLoadingContent::TYPE_DOCUMENT
:
210 aContentType
= nsIContentPolicy::TYPE_SUBDOCUMENT
;
216 NS_ASSERTION(aContentType
!= nsIContentPolicy::TYPE_OBJECT
,
217 "Shouldn't happen. Infinite loops are bad!");
219 // Found a type that tells us more about what we're loading. Try
220 // the permissions check again!
221 return ShouldLoad(aContentType
, aContentLocation
, aRequestingLocation
,
222 aRequestingContext
, aMimeGuess
, aExtra
, aDecision
);
226 nsContentBlocker::ShouldProcess(PRUint32 aContentType
,
227 nsIURI
*aContentLocation
,
228 nsIURI
*aRequestingLocation
,
229 nsISupports
*aRequestingContext
,
230 const nsACString
&aMimeGuess
,
234 // For loads where aRequestingContext is chrome, we should just
235 // accept. Those are most likely toplevel loads in windows, and
236 // chrome generally knows what it's doing anyway.
237 nsCOMPtr
<nsIDocShellTreeItem
> item
=
238 do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext
));
242 item
->GetItemType(&type
);
243 if (type
== nsIDocShellTreeItem::typeChrome
) {
244 *aDecision
= nsIContentPolicy::ACCEPT
;
249 // This isn't a load from chrome. Just do a ShouldLoad() check --
250 // we want the same answer here
251 return ShouldLoad(aContentType
, aContentLocation
, aRequestingLocation
,
252 aRequestingContext
, aMimeGuess
, aExtra
, aDecision
);
256 nsContentBlocker::TestPermission(nsIURI
*aCurrentURI
,
258 PRInt32 aContentType
,
262 *aFromPrefs
= PR_FALSE
;
263 // This default will also get used if there is an unknown value in the
264 // permission list, or if the permission manager returns unknown values.
265 *aPermission
= PR_TRUE
;
267 // check the permission list first; if we find an entry, it overrides
269 // Don't forget the aContentType ranges from 1..8, while the
270 // array is indexed 0..7
272 nsresult rv
= mPermissionManager
->TestPermission(aCurrentURI
,
273 kTypeString
[aContentType
- 1],
275 NS_ENSURE_SUCCESS(rv
, rv
);
277 // If there is nothing on the list, use the default.
279 permission
= mBehaviorPref
[aContentType
- 1];
280 *aFromPrefs
= PR_TRUE
;
283 // Use the fact that the nsIPermissionManager values map to
284 // the BEHAVIOR_* values above.
285 switch (permission
) {
286 case BEHAVIOR_ACCEPT
:
287 *aPermission
= PR_TRUE
;
289 case BEHAVIOR_REJECT
:
290 *aPermission
= PR_FALSE
;
293 case BEHAVIOR_NOFOREIGN
:
294 // Third party checking
296 // Need a requesting uri for third party checks to work.
300 PRBool trustedSource
= PR_FALSE
;
301 rv
= aFirstURI
->SchemeIs("chrome", &trustedSource
);
302 NS_ENSURE_SUCCESS(rv
,rv
);
303 if (!trustedSource
) {
304 rv
= aFirstURI
->SchemeIs("resource", &trustedSource
);
305 NS_ENSURE_SUCCESS(rv
,rv
);
310 // compare tails of names checking to see if they have a common domain
311 // we do this by comparing the tails of both names where each tail
312 // includes at least one dot
314 // A more generic method somewhere would be nice
316 nsCAutoString currentHost
;
317 rv
= aCurrentURI
->GetAsciiHost(currentHost
);
318 NS_ENSURE_SUCCESS(rv
, rv
);
320 // Search for two dots, starting at the end.
321 // If there are no two dots found, ++dot will turn to zero,
322 // that will return the entire string.
323 PRInt32 dot
= currentHost
.RFindChar('.');
324 dot
= currentHost
.RFindChar('.', dot
-1);
327 // Get the domain, ie the last part of the host (www.domain.com -> domain.com)
328 // This will break on co.uk
329 const nsCSubstring
&tail
=
330 Substring(currentHost
, dot
, currentHost
.Length() - dot
);
332 nsCAutoString firstHost
;
333 rv
= aFirstURI
->GetAsciiHost(firstHost
);
334 NS_ENSURE_SUCCESS(rv
, rv
);
336 // If the tail is longer then the whole firstHost, it will never match
337 if (firstHost
.Length() < tail
.Length()) {
338 *aPermission
= PR_FALSE
;
342 // Get the last part of the firstUri with the same length as |tail|
343 const nsCSubstring
&firstTail
=
344 Substring(firstHost
, firstHost
.Length() - tail
.Length(), tail
.Length());
346 // Check that both tails are the same, and that just before the tail in
347 // |firstUri| there is a dot. That means both url are in the same domain
348 if ((firstHost
.Length() > tail
.Length() &&
349 firstHost
.CharAt(firstHost
.Length() - tail
.Length() - 1) != '.') ||
350 !tail
.Equals(firstTail
)) {
351 *aPermission
= PR_FALSE
;
360 nsContentBlocker::Observe(nsISupports
*aSubject
,
362 const PRUnichar
*aData
)
364 NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
, aTopic
),
365 "unexpected topic - we only deal with pref changes!");
367 if (mPrefBranchInternal
)
368 PrefChanged(mPrefBranchInternal
, NS_LossyConvertUTF16toASCII(aData
).get());