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 "nsContentBlocker.h"
6 #include "nsIContent.h"
8 #include "nsIServiceManager.h"
9 #include "nsIDocShellTreeItem.h"
10 #include "nsIPrefService.h"
11 #include "nsIPrefBranch.h"
12 #include "nsIDocShell.h"
14 #include "nsContentPolicyUtils.h"
15 #include "nsIObjectLoadingContent.h"
16 #include "mozilla/ArrayUtils.h"
18 // Possible behavior pref values
19 // Those map to the nsIPermissionManager values where possible
20 #define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
21 #define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
22 #define BEHAVIOR_NOFOREIGN 3
24 // From nsIContentPolicy
25 static const char *kTypeString
[] = {"other",
44 #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
45 uint8_t nsContentBlocker::mBehaviorPref
[NUMBER_OF_TYPES
];
47 NS_IMPL_ISUPPORTS(nsContentBlocker
,
50 nsISupportsWeakReference
)
52 nsContentBlocker::nsContentBlocker()
54 memset(mBehaviorPref
, BEHAVIOR_ACCEPT
, NUMBER_OF_TYPES
);
58 nsContentBlocker::Init()
61 mPermissionManager
= do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
, &rv
);
62 NS_ENSURE_SUCCESS(rv
, rv
);
64 nsCOMPtr
<nsIPrefService
> prefService
= do_GetService(NS_PREFSERVICE_CONTRACTID
, &rv
);
65 NS_ENSURE_SUCCESS(rv
, rv
);
67 nsCOMPtr
<nsIPrefBranch
> prefBranch
;
68 rv
= prefService
->GetBranch("permissions.default.", getter_AddRefs(prefBranch
));
69 NS_ENSURE_SUCCESS(rv
, rv
);
71 // Migrate old image blocker pref
72 nsCOMPtr
<nsIPrefBranch
> oldPrefBranch
;
73 oldPrefBranch
= do_QueryInterface(prefService
);
75 rv
= oldPrefBranch
->GetIntPref("network.image.imageBehavior", &oldPref
);
76 if (NS_SUCCEEDED(rv
) && oldPref
) {
80 newPref
= BEHAVIOR_ACCEPT
;
83 newPref
= BEHAVIOR_NOFOREIGN
;
86 newPref
= BEHAVIOR_REJECT
;
89 prefBranch
->SetIntPref("image", newPref
);
90 oldPrefBranch
->ClearUserPref("network.image.imageBehavior");
94 // The branch is not a copy of the prefservice, but a new object, because
95 // it is a non-default branch. Adding obeservers to it will only work if
96 // we make sure that the object doesn't die. So, keep a reference to it.
97 mPrefBranchInternal
= do_QueryInterface(prefBranch
, &rv
);
98 NS_ENSURE_SUCCESS(rv
, rv
);
100 rv
= mPrefBranchInternal
->AddObserver("", this, true);
101 PrefChanged(prefBranch
, nullptr);
107 #define LIMIT(x, low, high, default) ((x) >= (low) && (x) <= (high) ? (x) : (default))
110 nsContentBlocker::PrefChanged(nsIPrefBranch
*aPrefBranch
,
115 #define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
117 for(uint32_t i
= 0; i
< NUMBER_OF_TYPES
; ++i
) {
118 if (PREF_CHANGED(kTypeString
[i
]) &&
119 NS_SUCCEEDED(aPrefBranch
->GetIntPref(kTypeString
[i
], &val
)))
120 mBehaviorPref
[i
] = LIMIT(val
, 1, 3, 1);
125 // nsIContentPolicy Implementation
127 nsContentBlocker::ShouldLoad(uint32_t aContentType
,
128 nsIURI
*aContentLocation
,
129 nsIURI
*aRequestingLocation
,
130 nsISupports
*aRequestingContext
,
131 const nsACString
&aMimeGuess
,
133 nsIPrincipal
*aRequestPrincipal
,
136 *aDecision
= nsIContentPolicy::ACCEPT
;
139 // Ony support NUMBER_OF_TYPES content types. that all there is at the
140 // moment, but you never know...
141 if (aContentType
> NUMBER_OF_TYPES
)
144 // we can't do anything without this
145 if (!aContentLocation
)
148 // The final type of an object tag may mutate before it reaches
149 // shouldProcess, so we cannot make any sane blocking decisions here
150 if (aContentType
== nsIContentPolicy::TYPE_OBJECT
)
153 // we only want to check http, https, ftp
154 // for chrome:// and resources and others, no need to check.
155 nsAutoCString scheme
;
156 aContentLocation
->GetScheme(scheme
);
157 if (!scheme
.LowerCaseEqualsLiteral("ftp") &&
158 !scheme
.LowerCaseEqualsLiteral("http") &&
159 !scheme
.LowerCaseEqualsLiteral("https"))
162 bool shouldLoad
, fromPrefs
;
163 rv
= TestPermission(aContentLocation
, aRequestingLocation
, aContentType
,
164 &shouldLoad
, &fromPrefs
);
165 NS_ENSURE_SUCCESS(rv
, rv
);
168 *aDecision
= nsIContentPolicy::REJECT_TYPE
;
170 *aDecision
= nsIContentPolicy::REJECT_SERVER
;
178 nsContentBlocker::ShouldProcess(uint32_t aContentType
,
179 nsIURI
*aContentLocation
,
180 nsIURI
*aRequestingLocation
,
181 nsISupports
*aRequestingContext
,
182 const nsACString
&aMimeGuess
,
184 nsIPrincipal
*aRequestPrincipal
,
187 // For loads where aRequestingContext is chrome, we should just
188 // accept. Those are most likely toplevel loads in windows, and
189 // chrome generally knows what it's doing anyway.
190 nsCOMPtr
<nsIDocShellTreeItem
> item
=
191 do_QueryInterface(NS_CP_GetDocShellFromContext(aRequestingContext
));
193 if (item
&& item
->ItemType() == nsIDocShellTreeItem::typeChrome
) {
194 *aDecision
= nsIContentPolicy::ACCEPT
;
198 // For objects, we only check policy in shouldProcess, as the final type isn't
199 // determined until the channel is open -- We don't want to block images in
200 // object tags because plugins are disallowed.
201 // NOTE that this bypasses the aContentLocation checks in ShouldLoad - this is
202 // intentional, as aContentLocation may be null for plugins that load by type
204 if (aContentType
== nsIContentPolicy::TYPE_OBJECT
) {
205 *aDecision
= nsIContentPolicy::ACCEPT
;
207 bool shouldLoad
, fromPrefs
;
208 nsresult rv
= TestPermission(aContentLocation
, aRequestingLocation
,
209 aContentType
, &shouldLoad
, &fromPrefs
);
210 NS_ENSURE_SUCCESS(rv
, rv
);
213 *aDecision
= nsIContentPolicy::REJECT_TYPE
;
215 *aDecision
= nsIContentPolicy::REJECT_SERVER
;
221 // This isn't a load from chrome or an object tag - Just do a ShouldLoad()
222 // check -- we want the same answer here
223 return ShouldLoad(aContentType
, aContentLocation
, aRequestingLocation
,
224 aRequestingContext
, aMimeGuess
, aExtra
, aRequestPrincipal
,
229 nsContentBlocker::TestPermission(nsIURI
*aCurrentURI
,
231 int32_t aContentType
,
236 // This default will also get used if there is an unknown value in the
237 // permission list, or if the permission manager returns unknown values.
240 // check the permission list first; if we find an entry, it overrides
242 // Don't forget the aContentType ranges from 1..8, while the
243 // array is indexed 0..7
245 nsresult rv
= mPermissionManager
->TestPermission(aCurrentURI
,
246 kTypeString
[aContentType
- 1],
248 NS_ENSURE_SUCCESS(rv
, rv
);
250 // If there is nothing on the list, use the default.
252 permission
= mBehaviorPref
[aContentType
- 1];
256 // Use the fact that the nsIPermissionManager values map to
257 // the BEHAVIOR_* values above.
258 switch (permission
) {
259 case BEHAVIOR_ACCEPT
:
262 case BEHAVIOR_REJECT
:
263 *aPermission
= false;
266 case BEHAVIOR_NOFOREIGN
:
267 // Third party checking
269 // Need a requesting uri for third party checks to work.
273 bool trustedSource
= false;
274 rv
= aFirstURI
->SchemeIs("chrome", &trustedSource
);
275 NS_ENSURE_SUCCESS(rv
,rv
);
276 if (!trustedSource
) {
277 rv
= aFirstURI
->SchemeIs("resource", &trustedSource
);
278 NS_ENSURE_SUCCESS(rv
,rv
);
283 // compare tails of names checking to see if they have a common domain
284 // we do this by comparing the tails of both names where each tail
285 // includes at least one dot
287 // A more generic method somewhere would be nice
289 nsAutoCString currentHost
;
290 rv
= aCurrentURI
->GetAsciiHost(currentHost
);
291 NS_ENSURE_SUCCESS(rv
, rv
);
293 // Search for two dots, starting at the end.
294 // If there are no two dots found, ++dot will turn to zero,
295 // that will return the entire string.
296 int32_t dot
= currentHost
.RFindChar('.');
297 dot
= currentHost
.RFindChar('.', dot
-1);
300 // Get the domain, ie the last part of the host (www.domain.com -> domain.com)
301 // This will break on co.uk
302 const nsCSubstring
&tail
=
303 Substring(currentHost
, dot
, currentHost
.Length() - dot
);
305 nsAutoCString firstHost
;
306 rv
= aFirstURI
->GetAsciiHost(firstHost
);
307 NS_ENSURE_SUCCESS(rv
, rv
);
309 // If the tail is longer then the whole firstHost, it will never match
310 if (firstHost
.Length() < tail
.Length()) {
311 *aPermission
= false;
315 // Get the last part of the firstUri with the same length as |tail|
316 const nsCSubstring
&firstTail
=
317 Substring(firstHost
, firstHost
.Length() - tail
.Length(), tail
.Length());
319 // Check that both tails are the same, and that just before the tail in
320 // |firstUri| there is a dot. That means both url are in the same domain
321 if ((firstHost
.Length() > tail
.Length() &&
322 firstHost
.CharAt(firstHost
.Length() - tail
.Length() - 1) != '.') ||
323 !tail
.Equals(firstTail
)) {
324 *aPermission
= false;
333 nsContentBlocker::Observe(nsISupports
*aSubject
,
335 const char16_t
*aData
)
337 NS_ASSERTION(!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
, aTopic
),
338 "unexpected topic - we only deal with pref changes!");
340 if (mPrefBranchInternal
)
341 PrefChanged(mPrefBranchInternal
, NS_LossyConvertUTF16toASCII(aData
).get());