|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsMixedContentBlocker.h" |
|
7 |
|
8 #include "nsContentPolicyUtils.h" |
|
9 #include "nsThreadUtils.h" |
|
10 #include "nsINode.h" |
|
11 #include "nsCOMPtr.h" |
|
12 #include "nsIDocShell.h" |
|
13 #include "nsISecurityEventSink.h" |
|
14 #include "nsIWebProgressListener.h" |
|
15 #include "nsContentUtils.h" |
|
16 #include "nsNetUtil.h" |
|
17 #include "nsIRequest.h" |
|
18 #include "nsIDocument.h" |
|
19 #include "nsIContentViewer.h" |
|
20 #include "nsIChannel.h" |
|
21 #include "nsIHttpChannel.h" |
|
22 #include "mozilla/Preferences.h" |
|
23 #include "nsIScriptObjectPrincipal.h" |
|
24 #include "nsISecureBrowserUI.h" |
|
25 #include "nsIDocumentLoader.h" |
|
26 #include "nsIWebNavigation.h" |
|
27 #include "nsLoadGroup.h" |
|
28 #include "nsIScriptError.h" |
|
29 |
|
30 #include "prlog.h" |
|
31 |
|
32 using namespace mozilla; |
|
33 |
|
34 enum nsMixedContentBlockerMessageType { |
|
35 eBlocked = 0x00, |
|
36 eUserOverride = 0x01 |
|
37 }; |
|
38 |
|
39 // Is mixed script blocking (fonts, plugin content, scripts, stylesheets, |
|
40 // iframes, websockets, XHR) enabled? |
|
41 bool nsMixedContentBlocker::sBlockMixedScript = false; |
|
42 |
|
43 // Is mixed display content blocking (images, audio, video, <a ping>) enabled? |
|
44 bool nsMixedContentBlocker::sBlockMixedDisplay = false; |
|
45 |
|
46 // Fired at the document that attempted to load mixed content. The UI could |
|
47 // handle this event, for example, by displaying an info bar that offers the |
|
48 // choice to reload the page with mixed content permitted. |
|
49 class nsMixedContentEvent : public nsRunnable |
|
50 { |
|
51 public: |
|
52 nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType) |
|
53 : mContext(aContext), mType(aType) |
|
54 {} |
|
55 |
|
56 NS_IMETHOD Run() |
|
57 { |
|
58 NS_ASSERTION(mContext, |
|
59 "You can't call this runnable without a requesting context"); |
|
60 |
|
61 // To update the security UI in the tab with the blocked mixed content, call |
|
62 // nsISecurityEventSink::OnSecurityChange. You can get to the event sink by |
|
63 // calling NS_CP_GetDocShellFromContext on the context, and QI'ing to |
|
64 // nsISecurityEventSink. |
|
65 |
|
66 |
|
67 // Mixed content was allowed and is about to load; get the document and |
|
68 // set the approriate flag to true if we are about to load Mixed Active |
|
69 // Content. |
|
70 nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(mContext); |
|
71 if (!docShell) { |
|
72 return NS_OK; |
|
73 } |
|
74 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; |
|
75 docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); |
|
76 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); |
|
77 |
|
78 // now get the document from sameTypeRoot |
|
79 nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot); |
|
80 NS_ASSERTION(rootDoc, "No root document from document shell root tree item."); |
|
81 |
|
82 |
|
83 if (mType == eMixedScript) { |
|
84 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. |
|
85 if (rootDoc->GetHasMixedActiveContentLoaded()) { |
|
86 return NS_OK; |
|
87 } |
|
88 rootDoc->SetHasMixedActiveContentLoaded(true); |
|
89 |
|
90 // Update the security UI in the tab with the allowed mixed active content |
|
91 nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell); |
|
92 if (eventSink) { |
|
93 // If mixed display content is loaded, make sure to include that in the state. |
|
94 if (rootDoc->GetHasMixedDisplayContentLoaded()) { |
|
95 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | |
|
96 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | |
|
97 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); |
|
98 } else { |
|
99 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | |
|
100 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); |
|
101 } |
|
102 } |
|
103 |
|
104 } else if (mType == eMixedDisplay) { |
|
105 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. |
|
106 if (rootDoc->GetHasMixedDisplayContentLoaded()) { |
|
107 return NS_OK; |
|
108 } |
|
109 rootDoc->SetHasMixedDisplayContentLoaded(true); |
|
110 |
|
111 // Update the security UI in the tab with the allowed mixed display content. |
|
112 nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell); |
|
113 if (eventSink) { |
|
114 // If mixed active content is loaded, make sure to include that in the state. |
|
115 if (rootDoc->GetHasMixedActiveContentLoaded()) { |
|
116 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | |
|
117 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | |
|
118 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); |
|
119 } else { |
|
120 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | |
|
121 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); |
|
122 } |
|
123 } |
|
124 } |
|
125 |
|
126 return NS_OK; |
|
127 } |
|
128 private: |
|
129 // The requesting context for the content load. Generally, a DOM node from |
|
130 // the document that caused the load. |
|
131 nsCOMPtr<nsISupports> mContext; |
|
132 |
|
133 // The type of mixed content detected, e.g. active or display |
|
134 const MixedContentTypes mType; |
|
135 }; |
|
136 |
|
137 |
|
138 nsMixedContentBlocker::nsMixedContentBlocker() |
|
139 { |
|
140 // Cache the pref for mixed script blocking |
|
141 Preferences::AddBoolVarCache(&sBlockMixedScript, |
|
142 "security.mixed_content.block_active_content"); |
|
143 |
|
144 // Cache the pref for mixed display blocking |
|
145 Preferences::AddBoolVarCache(&sBlockMixedDisplay, |
|
146 "security.mixed_content.block_display_content"); |
|
147 } |
|
148 |
|
149 nsMixedContentBlocker::~nsMixedContentBlocker() |
|
150 { |
|
151 } |
|
152 |
|
153 NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy) |
|
154 |
|
155 static void |
|
156 LogMixedContentMessage(MixedContentTypes aClassification, |
|
157 nsIURI* aContentLocation, |
|
158 nsIDocument* aRootDoc, |
|
159 nsMixedContentBlockerMessageType aMessageType) |
|
160 { |
|
161 nsAutoCString messageCategory; |
|
162 uint32_t severityFlag; |
|
163 nsAutoCString messageLookupKey; |
|
164 |
|
165 if (aMessageType == eBlocked) { |
|
166 severityFlag = nsIScriptError::errorFlag; |
|
167 messageCategory.AssignLiteral("Mixed Content Blocker"); |
|
168 if (aClassification == eMixedDisplay) { |
|
169 messageLookupKey.AssignLiteral("BlockMixedDisplayContent"); |
|
170 } else { |
|
171 messageLookupKey.AssignLiteral("BlockMixedActiveContent"); |
|
172 } |
|
173 } else { |
|
174 severityFlag = nsIScriptError::warningFlag; |
|
175 messageCategory.AssignLiteral("Mixed Content Message"); |
|
176 if (aClassification == eMixedDisplay) { |
|
177 messageLookupKey.AssignLiteral("LoadingMixedDisplayContent"); |
|
178 } else { |
|
179 messageLookupKey.AssignLiteral("LoadingMixedActiveContent"); |
|
180 } |
|
181 } |
|
182 |
|
183 nsAutoCString locationSpec; |
|
184 aContentLocation->GetSpec(locationSpec); |
|
185 NS_ConvertUTF8toUTF16 locationSpecUTF16(locationSpec); |
|
186 |
|
187 const char16_t* strings[] = { locationSpecUTF16.get() }; |
|
188 nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc, |
|
189 nsContentUtils::eSECURITY_PROPERTIES, |
|
190 messageLookupKey.get(), strings, ArrayLength(strings)); |
|
191 } |
|
192 |
|
193 NS_IMETHODIMP |
|
194 nsMixedContentBlocker::ShouldLoad(uint32_t aContentType, |
|
195 nsIURI* aContentLocation, |
|
196 nsIURI* aRequestingLocation, |
|
197 nsISupports* aRequestingContext, |
|
198 const nsACString& aMimeGuess, |
|
199 nsISupports* aExtra, |
|
200 nsIPrincipal* aRequestPrincipal, |
|
201 int16_t* aDecision) |
|
202 { |
|
203 // Asserting that we are on the main thread here and hence do not have to lock |
|
204 // and unlock sBlockMixedScript and sBlockMixedDisplay before reading/writing |
|
205 // to them. |
|
206 MOZ_ASSERT(NS_IsMainThread()); |
|
207 |
|
208 // Assume active (high risk) content and blocked by default |
|
209 MixedContentTypes classification = eMixedScript; |
|
210 |
|
211 |
|
212 // Notes on non-obvious decisions: |
|
213 // |
|
214 // TYPE_DTD: A DTD can contain entity definitions that expand to scripts. |
|
215 // |
|
216 // TYPE_FONT: The TrueType hinting mechanism is basically a scripting |
|
217 // language that gets interpreted by the operating system's font rasterizer. |
|
218 // Mixed content web fonts are relatively uncommon, and we can can fall back |
|
219 // to built-in fonts with minimal disruption in almost all cases. |
|
220 // |
|
221 // TYPE_OBJECT_SUBREQUEST could actually be either active content (e.g. a |
|
222 // script that a plugin will execute) or display content (e.g. Flash video |
|
223 // content). Until we have a way to determine active vs passive content |
|
224 // from plugin requests (bug 836352), we will treat this as passive content. |
|
225 // This is to prevent false positives from causing users to become |
|
226 // desensitized to the mixed content blocker. |
|
227 // |
|
228 // TYPE_CSP_REPORT: High-risk because they directly leak information about |
|
229 // the content of the page, and because blocking them does not have any |
|
230 // negative effect on the page loading. |
|
231 // |
|
232 // TYPE_PING: Ping requests are POSTS, not GETs like images and media. |
|
233 // Also, PING requests have no bearing on the rendering or operation of |
|
234 // the page when used as designed, so even though they are lower risk than |
|
235 // scripts, blocking them is basically risk-free as far as compatibility is |
|
236 // concerned. Ping is turned off by default in Firefox, so unless a user |
|
237 // opts into ping, no request will be made. Categorizing this as Mixed |
|
238 // Display Content for now, but this is subject to change. |
|
239 // |
|
240 // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning |
|
241 // and other advanced CSS features can possibly be exploited to cause |
|
242 // spoofing attacks (e.g. make a "grant permission" button look like a |
|
243 // "refuse permission" button). |
|
244 // |
|
245 // TYPE_BEACON: Beacon requests are similar to TYPE_PING, but are default on. |
|
246 // |
|
247 // TYPE_WEBSOCKET: The Websockets API requires browsers to |
|
248 // reject mixed-content websockets: "If secure is false but the origin of |
|
249 // the entry script has a scheme component that is itself a secure protocol, |
|
250 // e.g. HTTPS, then throw a SecurityError exception." We already block mixed |
|
251 // content websockets within the websockets implementation, so we don't need |
|
252 // to do any blocking here, nor do we need to provide a way to undo or |
|
253 // override the blocking. Websockets without TLS are very flaky anyway in the |
|
254 // face of many HTTP-aware proxies. Compared to psasive content, there is |
|
255 // additional risk that the script using WebSockets will disclose sensitive |
|
256 // information from the HTTPS page and/or eval (directly or indirectly) |
|
257 // received data. |
|
258 // |
|
259 // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most |
|
260 // mixed-content XHR will already be blocked by that check. This will also |
|
261 // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned |
|
262 // above for WebSockets apply to XHR, and XHR should have the same security |
|
263 // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects |
|
264 // amplifies these concerns. |
|
265 |
|
266 |
|
267 static_assert(TYPE_DATAREQUEST == TYPE_XMLHTTPREQUEST, |
|
268 "TYPE_DATAREQUEST is not a synonym for " |
|
269 "TYPE_XMLHTTPREQUEST"); |
|
270 |
|
271 switch (aContentType) { |
|
272 // The top-level document cannot be mixed content by definition |
|
273 case TYPE_DOCUMENT: |
|
274 *aDecision = ACCEPT; |
|
275 return NS_OK; |
|
276 // Creating insecure websocket connections in a secure page is blocked already |
|
277 // in the websocket constructor. We don't need to check the blocking here |
|
278 // and we don't want to un-block |
|
279 case TYPE_WEBSOCKET: |
|
280 *aDecision = ACCEPT; |
|
281 return NS_OK; |
|
282 |
|
283 |
|
284 // Static display content is considered moderate risk for mixed content so |
|
285 // these will be blocked according to the mixed display preference |
|
286 case TYPE_IMAGE: |
|
287 case TYPE_MEDIA: |
|
288 case TYPE_OBJECT_SUBREQUEST: |
|
289 case TYPE_PING: |
|
290 case TYPE_BEACON: |
|
291 classification = eMixedDisplay; |
|
292 break; |
|
293 |
|
294 // Active content (or content with a low value/risk-of-blocking ratio) |
|
295 // that has been explicitly evaluated; listed here for documentation |
|
296 // purposes and to avoid the assertion and warning for the default case. |
|
297 case TYPE_CSP_REPORT: |
|
298 case TYPE_DTD: |
|
299 case TYPE_FONT: |
|
300 case TYPE_OBJECT: |
|
301 case TYPE_SCRIPT: |
|
302 case TYPE_STYLESHEET: |
|
303 case TYPE_SUBDOCUMENT: |
|
304 case TYPE_XBL: |
|
305 case TYPE_XMLHTTPREQUEST: |
|
306 case TYPE_XSLT: |
|
307 case TYPE_OTHER: |
|
308 break; |
|
309 |
|
310 |
|
311 // This content policy works as a whitelist. |
|
312 default: |
|
313 MOZ_ASSERT(false, "Mixed content of unknown type"); |
|
314 break; |
|
315 } |
|
316 |
|
317 /* Get the scheme of the sub-document resource to be requested. If it is |
|
318 * a safe to load in an https context then mixed content doesn't apply. |
|
319 * |
|
320 * Check Protocol Flags to determine if scheme is safe to load: |
|
321 * URI_DOES_NOT_RETURN_DATA - e.g. |
|
322 * "mailto" |
|
323 * URI_IS_LOCAL_RESOURCE - e.g. |
|
324 * "data", |
|
325 * "resource", |
|
326 * "moz-icon" |
|
327 * URI_INHERITS_SECURITY_CONTEXT - e.g. |
|
328 * "javascript" |
|
329 * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g. |
|
330 * "https", |
|
331 * "moz-safe-about" |
|
332 * |
|
333 */ |
|
334 bool schemeLocal = false; |
|
335 bool schemeNoReturnData = false; |
|
336 bool schemeInherits = false; |
|
337 bool schemeSecure = false; |
|
338 if (NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) || |
|
339 NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) || |
|
340 NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) || |
|
341 NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) { |
|
342 return NS_ERROR_FAILURE; |
|
343 } |
|
344 |
|
345 if (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure) { |
|
346 return NS_OK; |
|
347 } |
|
348 |
|
349 // Since there are cases where aRequestingLocation and aRequestPrincipal are |
|
350 // definitely not the owning document, we try to ignore them by extracting the |
|
351 // requestingLocation in the following order: |
|
352 // 1) from the aRequestingContext, either extracting |
|
353 // a) the node's principal, or the |
|
354 // b) script object's principal. |
|
355 // 2) if aRequestingContext yields a principal but no location, we check |
|
356 // if its the system principal. If it is, allow the load. |
|
357 // 3) Special case handling for: |
|
358 // a) speculative loads, where shouldLoad is called twice (bug 839235) |
|
359 // and the first speculative load does not include a context. |
|
360 // In this case we use aRequestingLocation to set requestingLocation. |
|
361 // b) TYPE_CSP_REPORT which does not provide a context. In this case we |
|
362 // use aRequestingLocation to set requestingLocation. |
|
363 // c) content scripts from addon code that do not provide aRequestingContext |
|
364 // or aRequestingLocation, but do provide aRequestPrincipal. |
|
365 // If aRequestPrincipal is an expanded principal, we allow the load. |
|
366 // 4) If we still end up not having a requestingLocation, we reject the load. |
|
367 |
|
368 nsCOMPtr<nsIPrincipal> principal; |
|
369 // 1a) Try to get the principal if aRequestingContext is a node. |
|
370 nsCOMPtr<nsINode> node = do_QueryInterface(aRequestingContext); |
|
371 if (node) { |
|
372 principal = node->NodePrincipal(); |
|
373 } |
|
374 |
|
375 // 1b) Try using the window's script object principal if it's not a node. |
|
376 if (!principal) { |
|
377 nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aRequestingContext); |
|
378 if (scriptObjPrin) { |
|
379 principal = scriptObjPrin->GetPrincipal(); |
|
380 } |
|
381 } |
|
382 |
|
383 nsCOMPtr<nsIURI> requestingLocation; |
|
384 if (principal) { |
|
385 principal->GetURI(getter_AddRefs(requestingLocation)); |
|
386 } |
|
387 |
|
388 // 2) if aRequestingContext yields a principal but no location, we check if its a system principal. |
|
389 if (principal && !requestingLocation) { |
|
390 if (nsContentUtils::IsSystemPrincipal(principal)) { |
|
391 *aDecision = ACCEPT; |
|
392 return NS_OK; |
|
393 } |
|
394 } |
|
395 |
|
396 // 3a,b) Special case handling for speculative loads and TYPE_CSP_REPORT. In |
|
397 // such cases, aRequestingContext doesn't exist, so we use aRequestingLocation. |
|
398 // Unfortunately we can not distinguish between speculative and normal loads here, |
|
399 // otherwise we could special case this assignment. |
|
400 if (!requestingLocation) { |
|
401 requestingLocation = aRequestingLocation; |
|
402 } |
|
403 |
|
404 // 3c) Special case handling for content scripts from addons code, which only |
|
405 // provide a aRequestPrincipal; aRequestingContext and aRequestingLocation are |
|
406 // both null; if the aRequestPrincipal is an expandedPrincipal, we allow the load. |
|
407 if (!principal && !requestingLocation && aRequestPrincipal) { |
|
408 nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aRequestPrincipal); |
|
409 if (expanded) { |
|
410 *aDecision = ACCEPT; |
|
411 return NS_OK; |
|
412 } |
|
413 } |
|
414 |
|
415 // 4) Giving up. We still don't have a requesting location, therefore we can't tell |
|
416 // if this is a mixed content load. Deny to be safe. |
|
417 if (!requestingLocation) { |
|
418 *aDecision = REJECT_REQUEST; |
|
419 return NS_OK; |
|
420 } |
|
421 |
|
422 // Check the parent scheme. If it is not an HTTPS page then mixed content |
|
423 // restrictions do not apply. |
|
424 bool parentIsHttps; |
|
425 nsresult rv = requestingLocation->SchemeIs("https", &parentIsHttps); |
|
426 if (NS_FAILED(rv)) { |
|
427 NS_ERROR("requestingLocation->SchemeIs failed"); |
|
428 *aDecision = REJECT_REQUEST; |
|
429 return NS_OK; |
|
430 } |
|
431 if (!parentIsHttps) { |
|
432 *aDecision = ACCEPT; |
|
433 return NS_OK; |
|
434 } |
|
435 |
|
436 // Determine if the rootDoc is https and if the user decided to allow Mixed Content |
|
437 nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext); |
|
438 NS_ENSURE_TRUE(docShell, NS_OK); |
|
439 bool rootHasSecureConnection = false; |
|
440 bool allowMixedContent = false; |
|
441 bool isRootDocShell = false; |
|
442 rv = docShell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isRootDocShell); |
|
443 if (NS_FAILED(rv)) { |
|
444 return rv; |
|
445 } |
|
446 |
|
447 |
|
448 // Get the sameTypeRoot tree item from the docshell |
|
449 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; |
|
450 docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); |
|
451 NS_ASSERTION(sameTypeRoot, "No root tree item from docshell!"); |
|
452 |
|
453 // When navigating an iframe, the iframe may be https |
|
454 // but its parents may not be. Check the parents to see if any of them are https. |
|
455 // If none of the parents are https, allow the load. |
|
456 if (aContentType == TYPE_SUBDOCUMENT && !rootHasSecureConnection) { |
|
457 |
|
458 bool httpsParentExists = false; |
|
459 |
|
460 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem; |
|
461 parentTreeItem = docShell; |
|
462 |
|
463 while(!httpsParentExists && parentTreeItem) { |
|
464 nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentTreeItem)); |
|
465 NS_ASSERTION(parentAsNav, "No web navigation object from parent's docshell tree item"); |
|
466 nsCOMPtr<nsIURI> parentURI; |
|
467 |
|
468 parentAsNav->GetCurrentURI(getter_AddRefs(parentURI)); |
|
469 if (!parentURI || NS_FAILED(parentURI->SchemeIs("https", &httpsParentExists))) { |
|
470 // if getting the URI or the scheme fails, assume there is a https parent and break. |
|
471 httpsParentExists = true; |
|
472 break; |
|
473 } |
|
474 |
|
475 // When the parent and the root are the same, we have traversed all the way up |
|
476 // the same type docshell tree. Break out of the while loop. |
|
477 if(sameTypeRoot == parentTreeItem) { |
|
478 break; |
|
479 } |
|
480 |
|
481 // update the parent to the grandparent. |
|
482 nsCOMPtr<nsIDocShellTreeItem> newParentTreeItem; |
|
483 parentTreeItem->GetSameTypeParent(getter_AddRefs(newParentTreeItem)); |
|
484 parentTreeItem = newParentTreeItem; |
|
485 } // end while loop. |
|
486 |
|
487 if (!httpsParentExists) { |
|
488 *aDecision = nsIContentPolicy::ACCEPT; |
|
489 return NS_OK; |
|
490 } |
|
491 } |
|
492 |
|
493 // Get the root document from the sameTypeRoot |
|
494 nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot); |
|
495 NS_ASSERTION(rootDoc, "No root document from document shell root tree item."); |
|
496 |
|
497 // Get eventSink and the current security state from the docShell |
|
498 nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell); |
|
499 NS_ASSERTION(eventSink, "No eventSink from docShell."); |
|
500 nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot); |
|
501 NS_ASSERTION(rootShell, "No root docshell from document shell root tree item."); |
|
502 uint32_t State = nsIWebProgressListener::STATE_IS_BROKEN; |
|
503 nsCOMPtr<nsISecureBrowserUI> securityUI; |
|
504 rootShell->GetSecurityUI(getter_AddRefs(securityUI)); |
|
505 // If there is no securityUI, document doesn't have a security state. |
|
506 // Allow load and return early. |
|
507 if (!securityUI) { |
|
508 *aDecision = nsIContentPolicy::ACCEPT; |
|
509 return NS_OK; |
|
510 } |
|
511 nsresult stateRV = securityUI->GetState(&State); |
|
512 |
|
513 // If the content is display content, and the pref says display content should be blocked, block it. |
|
514 if (sBlockMixedDisplay && classification == eMixedDisplay) { |
|
515 if (allowMixedContent) { |
|
516 LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); |
|
517 *aDecision = nsIContentPolicy::ACCEPT; |
|
518 rootDoc->SetHasMixedActiveContentLoaded(true); |
|
519 if (!rootDoc->GetHasMixedDisplayContentLoaded() && NS_SUCCEEDED(stateRV)) { |
|
520 rootDoc->SetHasMixedDisplayContentLoaded(true); |
|
521 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); |
|
522 } |
|
523 } else { |
|
524 *aDecision = nsIContentPolicy::REJECT_REQUEST; |
|
525 LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); |
|
526 if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) { |
|
527 rootDoc->SetHasMixedDisplayContentBlocked(true); |
|
528 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT)); |
|
529 } |
|
530 } |
|
531 return NS_OK; |
|
532 |
|
533 } else if (sBlockMixedScript && classification == eMixedScript) { |
|
534 // If the content is active content, and the pref says active content should be blocked, block it |
|
535 // unless the user has choosen to override the pref |
|
536 if (allowMixedContent) { |
|
537 LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); |
|
538 *aDecision = nsIContentPolicy::ACCEPT; |
|
539 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. |
|
540 if (rootDoc->GetHasMixedActiveContentLoaded()) { |
|
541 return NS_OK; |
|
542 } |
|
543 rootDoc->SetHasMixedActiveContentLoaded(true); |
|
544 |
|
545 if (rootHasSecureConnection) { |
|
546 // User has decided to override the pref and the root is https, so change the Security State. |
|
547 if (rootDoc->GetHasMixedDisplayContentLoaded()) { |
|
548 // If mixed display content is loaded, make sure to include that in the state. |
|
549 eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN | |
|
550 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | |
|
551 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); |
|
552 } else { |
|
553 eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN | |
|
554 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); |
|
555 } |
|
556 return NS_OK; |
|
557 } else { |
|
558 // User has already overriden the pref and the root is not https; |
|
559 // mixed content was allowed on an https subframe. |
|
560 if (NS_SUCCEEDED(stateRV)) { |
|
561 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); |
|
562 } |
|
563 return NS_OK; |
|
564 } |
|
565 } else { |
|
566 //User has not overriden the pref by Disabling protection. Reject the request and update the security state. |
|
567 *aDecision = nsIContentPolicy::REJECT_REQUEST; |
|
568 LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); |
|
569 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. |
|
570 if (rootDoc->GetHasMixedActiveContentBlocked()) { |
|
571 return NS_OK; |
|
572 } |
|
573 rootDoc->SetHasMixedActiveContentBlocked(true); |
|
574 |
|
575 // The user has not overriden the pref, so make sure they still have an option by calling eventSink |
|
576 // which will invoke the doorhanger |
|
577 if (NS_SUCCEEDED(stateRV)) { |
|
578 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT)); |
|
579 } |
|
580 return NS_OK; |
|
581 } |
|
582 |
|
583 } else { |
|
584 // The content is not blocked by the mixed content prefs. |
|
585 |
|
586 // Log a message that we are loading mixed content. |
|
587 LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); |
|
588 |
|
589 // Fire the event from a script runner as it is unsafe to run script |
|
590 // from within ShouldLoad |
|
591 nsContentUtils::AddScriptRunner( |
|
592 new nsMixedContentEvent(aRequestingContext, classification)); |
|
593 return NS_OK; |
|
594 } |
|
595 |
|
596 *aDecision = REJECT_REQUEST; |
|
597 return NS_OK; |
|
598 } |
|
599 |
|
600 NS_IMETHODIMP |
|
601 nsMixedContentBlocker::ShouldProcess(uint32_t aContentType, |
|
602 nsIURI* aContentLocation, |
|
603 nsIURI* aRequestingLocation, |
|
604 nsISupports* aRequestingContext, |
|
605 const nsACString& aMimeGuess, |
|
606 nsISupports* aExtra, |
|
607 nsIPrincipal* aRequestPrincipal, |
|
608 int16_t* aDecision) |
|
609 { |
|
610 if (!aContentLocation) { |
|
611 // aContentLocation may be null when a plugin is loading without an associated URI resource |
|
612 if (aContentType == TYPE_OBJECT) { |
|
613 return NS_OK; |
|
614 } else { |
|
615 return NS_ERROR_FAILURE; |
|
616 } |
|
617 } |
|
618 |
|
619 return ShouldLoad(aContentType, aContentLocation, aRequestingLocation, |
|
620 aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal, |
|
621 aDecision); |
|
622 } |