1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsMixedContentBlocker.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,622 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsMixedContentBlocker.h" 1.10 + 1.11 +#include "nsContentPolicyUtils.h" 1.12 +#include "nsThreadUtils.h" 1.13 +#include "nsINode.h" 1.14 +#include "nsCOMPtr.h" 1.15 +#include "nsIDocShell.h" 1.16 +#include "nsISecurityEventSink.h" 1.17 +#include "nsIWebProgressListener.h" 1.18 +#include "nsContentUtils.h" 1.19 +#include "nsNetUtil.h" 1.20 +#include "nsIRequest.h" 1.21 +#include "nsIDocument.h" 1.22 +#include "nsIContentViewer.h" 1.23 +#include "nsIChannel.h" 1.24 +#include "nsIHttpChannel.h" 1.25 +#include "mozilla/Preferences.h" 1.26 +#include "nsIScriptObjectPrincipal.h" 1.27 +#include "nsISecureBrowserUI.h" 1.28 +#include "nsIDocumentLoader.h" 1.29 +#include "nsIWebNavigation.h" 1.30 +#include "nsLoadGroup.h" 1.31 +#include "nsIScriptError.h" 1.32 + 1.33 +#include "prlog.h" 1.34 + 1.35 +using namespace mozilla; 1.36 + 1.37 +enum nsMixedContentBlockerMessageType { 1.38 + eBlocked = 0x00, 1.39 + eUserOverride = 0x01 1.40 +}; 1.41 + 1.42 +// Is mixed script blocking (fonts, plugin content, scripts, stylesheets, 1.43 +// iframes, websockets, XHR) enabled? 1.44 +bool nsMixedContentBlocker::sBlockMixedScript = false; 1.45 + 1.46 +// Is mixed display content blocking (images, audio, video, <a ping>) enabled? 1.47 +bool nsMixedContentBlocker::sBlockMixedDisplay = false; 1.48 + 1.49 +// Fired at the document that attempted to load mixed content. The UI could 1.50 +// handle this event, for example, by displaying an info bar that offers the 1.51 +// choice to reload the page with mixed content permitted. 1.52 +class nsMixedContentEvent : public nsRunnable 1.53 +{ 1.54 +public: 1.55 + nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType) 1.56 + : mContext(aContext), mType(aType) 1.57 + {} 1.58 + 1.59 + NS_IMETHOD Run() 1.60 + { 1.61 + NS_ASSERTION(mContext, 1.62 + "You can't call this runnable without a requesting context"); 1.63 + 1.64 + // To update the security UI in the tab with the blocked mixed content, call 1.65 + // nsISecurityEventSink::OnSecurityChange. You can get to the event sink by 1.66 + // calling NS_CP_GetDocShellFromContext on the context, and QI'ing to 1.67 + // nsISecurityEventSink. 1.68 + 1.69 + 1.70 + // Mixed content was allowed and is about to load; get the document and 1.71 + // set the approriate flag to true if we are about to load Mixed Active 1.72 + // Content. 1.73 + nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(mContext); 1.74 + if (!docShell) { 1.75 + return NS_OK; 1.76 + } 1.77 + nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; 1.78 + docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); 1.79 + NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); 1.80 + 1.81 + // now get the document from sameTypeRoot 1.82 + nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot); 1.83 + NS_ASSERTION(rootDoc, "No root document from document shell root tree item."); 1.84 + 1.85 + 1.86 + if (mType == eMixedScript) { 1.87 + // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. 1.88 + if (rootDoc->GetHasMixedActiveContentLoaded()) { 1.89 + return NS_OK; 1.90 + } 1.91 + rootDoc->SetHasMixedActiveContentLoaded(true); 1.92 + 1.93 + // Update the security UI in the tab with the allowed mixed active content 1.94 + nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell); 1.95 + if (eventSink) { 1.96 + // If mixed display content is loaded, make sure to include that in the state. 1.97 + if (rootDoc->GetHasMixedDisplayContentLoaded()) { 1.98 + eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | 1.99 + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | 1.100 + nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); 1.101 + } else { 1.102 + eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | 1.103 + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); 1.104 + } 1.105 + } 1.106 + 1.107 + } else if (mType == eMixedDisplay) { 1.108 + // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. 1.109 + if (rootDoc->GetHasMixedDisplayContentLoaded()) { 1.110 + return NS_OK; 1.111 + } 1.112 + rootDoc->SetHasMixedDisplayContentLoaded(true); 1.113 + 1.114 + // Update the security UI in the tab with the allowed mixed display content. 1.115 + nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell); 1.116 + if (eventSink) { 1.117 + // If mixed active content is loaded, make sure to include that in the state. 1.118 + if (rootDoc->GetHasMixedActiveContentLoaded()) { 1.119 + eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | 1.120 + nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | 1.121 + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); 1.122 + } else { 1.123 + eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | 1.124 + nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); 1.125 + } 1.126 + } 1.127 + } 1.128 + 1.129 + return NS_OK; 1.130 + } 1.131 +private: 1.132 + // The requesting context for the content load. Generally, a DOM node from 1.133 + // the document that caused the load. 1.134 + nsCOMPtr<nsISupports> mContext; 1.135 + 1.136 + // The type of mixed content detected, e.g. active or display 1.137 + const MixedContentTypes mType; 1.138 +}; 1.139 + 1.140 + 1.141 +nsMixedContentBlocker::nsMixedContentBlocker() 1.142 +{ 1.143 + // Cache the pref for mixed script blocking 1.144 + Preferences::AddBoolVarCache(&sBlockMixedScript, 1.145 + "security.mixed_content.block_active_content"); 1.146 + 1.147 + // Cache the pref for mixed display blocking 1.148 + Preferences::AddBoolVarCache(&sBlockMixedDisplay, 1.149 + "security.mixed_content.block_display_content"); 1.150 +} 1.151 + 1.152 +nsMixedContentBlocker::~nsMixedContentBlocker() 1.153 +{ 1.154 +} 1.155 + 1.156 +NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy) 1.157 + 1.158 +static void 1.159 +LogMixedContentMessage(MixedContentTypes aClassification, 1.160 + nsIURI* aContentLocation, 1.161 + nsIDocument* aRootDoc, 1.162 + nsMixedContentBlockerMessageType aMessageType) 1.163 +{ 1.164 + nsAutoCString messageCategory; 1.165 + uint32_t severityFlag; 1.166 + nsAutoCString messageLookupKey; 1.167 + 1.168 + if (aMessageType == eBlocked) { 1.169 + severityFlag = nsIScriptError::errorFlag; 1.170 + messageCategory.AssignLiteral("Mixed Content Blocker"); 1.171 + if (aClassification == eMixedDisplay) { 1.172 + messageLookupKey.AssignLiteral("BlockMixedDisplayContent"); 1.173 + } else { 1.174 + messageLookupKey.AssignLiteral("BlockMixedActiveContent"); 1.175 + } 1.176 + } else { 1.177 + severityFlag = nsIScriptError::warningFlag; 1.178 + messageCategory.AssignLiteral("Mixed Content Message"); 1.179 + if (aClassification == eMixedDisplay) { 1.180 + messageLookupKey.AssignLiteral("LoadingMixedDisplayContent"); 1.181 + } else { 1.182 + messageLookupKey.AssignLiteral("LoadingMixedActiveContent"); 1.183 + } 1.184 + } 1.185 + 1.186 + nsAutoCString locationSpec; 1.187 + aContentLocation->GetSpec(locationSpec); 1.188 + NS_ConvertUTF8toUTF16 locationSpecUTF16(locationSpec); 1.189 + 1.190 + const char16_t* strings[] = { locationSpecUTF16.get() }; 1.191 + nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc, 1.192 + nsContentUtils::eSECURITY_PROPERTIES, 1.193 + messageLookupKey.get(), strings, ArrayLength(strings)); 1.194 +} 1.195 + 1.196 +NS_IMETHODIMP 1.197 +nsMixedContentBlocker::ShouldLoad(uint32_t aContentType, 1.198 + nsIURI* aContentLocation, 1.199 + nsIURI* aRequestingLocation, 1.200 + nsISupports* aRequestingContext, 1.201 + const nsACString& aMimeGuess, 1.202 + nsISupports* aExtra, 1.203 + nsIPrincipal* aRequestPrincipal, 1.204 + int16_t* aDecision) 1.205 +{ 1.206 + // Asserting that we are on the main thread here and hence do not have to lock 1.207 + // and unlock sBlockMixedScript and sBlockMixedDisplay before reading/writing 1.208 + // to them. 1.209 + MOZ_ASSERT(NS_IsMainThread()); 1.210 + 1.211 + // Assume active (high risk) content and blocked by default 1.212 + MixedContentTypes classification = eMixedScript; 1.213 + 1.214 + 1.215 + // Notes on non-obvious decisions: 1.216 + // 1.217 + // TYPE_DTD: A DTD can contain entity definitions that expand to scripts. 1.218 + // 1.219 + // TYPE_FONT: The TrueType hinting mechanism is basically a scripting 1.220 + // language that gets interpreted by the operating system's font rasterizer. 1.221 + // Mixed content web fonts are relatively uncommon, and we can can fall back 1.222 + // to built-in fonts with minimal disruption in almost all cases. 1.223 + // 1.224 + // TYPE_OBJECT_SUBREQUEST could actually be either active content (e.g. a 1.225 + // script that a plugin will execute) or display content (e.g. Flash video 1.226 + // content). Until we have a way to determine active vs passive content 1.227 + // from plugin requests (bug 836352), we will treat this as passive content. 1.228 + // This is to prevent false positives from causing users to become 1.229 + // desensitized to the mixed content blocker. 1.230 + // 1.231 + // TYPE_CSP_REPORT: High-risk because they directly leak information about 1.232 + // the content of the page, and because blocking them does not have any 1.233 + // negative effect on the page loading. 1.234 + // 1.235 + // TYPE_PING: Ping requests are POSTS, not GETs like images and media. 1.236 + // Also, PING requests have no bearing on the rendering or operation of 1.237 + // the page when used as designed, so even though they are lower risk than 1.238 + // scripts, blocking them is basically risk-free as far as compatibility is 1.239 + // concerned. Ping is turned off by default in Firefox, so unless a user 1.240 + // opts into ping, no request will be made. Categorizing this as Mixed 1.241 + // Display Content for now, but this is subject to change. 1.242 + // 1.243 + // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning 1.244 + // and other advanced CSS features can possibly be exploited to cause 1.245 + // spoofing attacks (e.g. make a "grant permission" button look like a 1.246 + // "refuse permission" button). 1.247 + // 1.248 + // TYPE_BEACON: Beacon requests are similar to TYPE_PING, but are default on. 1.249 + // 1.250 + // TYPE_WEBSOCKET: The Websockets API requires browsers to 1.251 + // reject mixed-content websockets: "If secure is false but the origin of 1.252 + // the entry script has a scheme component that is itself a secure protocol, 1.253 + // e.g. HTTPS, then throw a SecurityError exception." We already block mixed 1.254 + // content websockets within the websockets implementation, so we don't need 1.255 + // to do any blocking here, nor do we need to provide a way to undo or 1.256 + // override the blocking. Websockets without TLS are very flaky anyway in the 1.257 + // face of many HTTP-aware proxies. Compared to psasive content, there is 1.258 + // additional risk that the script using WebSockets will disclose sensitive 1.259 + // information from the HTTPS page and/or eval (directly or indirectly) 1.260 + // received data. 1.261 + // 1.262 + // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most 1.263 + // mixed-content XHR will already be blocked by that check. This will also 1.264 + // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned 1.265 + // above for WebSockets apply to XHR, and XHR should have the same security 1.266 + // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects 1.267 + // amplifies these concerns. 1.268 + 1.269 + 1.270 + static_assert(TYPE_DATAREQUEST == TYPE_XMLHTTPREQUEST, 1.271 + "TYPE_DATAREQUEST is not a synonym for " 1.272 + "TYPE_XMLHTTPREQUEST"); 1.273 + 1.274 + switch (aContentType) { 1.275 + // The top-level document cannot be mixed content by definition 1.276 + case TYPE_DOCUMENT: 1.277 + *aDecision = ACCEPT; 1.278 + return NS_OK; 1.279 + // Creating insecure websocket connections in a secure page is blocked already 1.280 + // in the websocket constructor. We don't need to check the blocking here 1.281 + // and we don't want to un-block 1.282 + case TYPE_WEBSOCKET: 1.283 + *aDecision = ACCEPT; 1.284 + return NS_OK; 1.285 + 1.286 + 1.287 + // Static display content is considered moderate risk for mixed content so 1.288 + // these will be blocked according to the mixed display preference 1.289 + case TYPE_IMAGE: 1.290 + case TYPE_MEDIA: 1.291 + case TYPE_OBJECT_SUBREQUEST: 1.292 + case TYPE_PING: 1.293 + case TYPE_BEACON: 1.294 + classification = eMixedDisplay; 1.295 + break; 1.296 + 1.297 + // Active content (or content with a low value/risk-of-blocking ratio) 1.298 + // that has been explicitly evaluated; listed here for documentation 1.299 + // purposes and to avoid the assertion and warning for the default case. 1.300 + case TYPE_CSP_REPORT: 1.301 + case TYPE_DTD: 1.302 + case TYPE_FONT: 1.303 + case TYPE_OBJECT: 1.304 + case TYPE_SCRIPT: 1.305 + case TYPE_STYLESHEET: 1.306 + case TYPE_SUBDOCUMENT: 1.307 + case TYPE_XBL: 1.308 + case TYPE_XMLHTTPREQUEST: 1.309 + case TYPE_XSLT: 1.310 + case TYPE_OTHER: 1.311 + break; 1.312 + 1.313 + 1.314 + // This content policy works as a whitelist. 1.315 + default: 1.316 + MOZ_ASSERT(false, "Mixed content of unknown type"); 1.317 + break; 1.318 + } 1.319 + 1.320 + /* Get the scheme of the sub-document resource to be requested. If it is 1.321 + * a safe to load in an https context then mixed content doesn't apply. 1.322 + * 1.323 + * Check Protocol Flags to determine if scheme is safe to load: 1.324 + * URI_DOES_NOT_RETURN_DATA - e.g. 1.325 + * "mailto" 1.326 + * URI_IS_LOCAL_RESOURCE - e.g. 1.327 + * "data", 1.328 + * "resource", 1.329 + * "moz-icon" 1.330 + * URI_INHERITS_SECURITY_CONTEXT - e.g. 1.331 + * "javascript" 1.332 + * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g. 1.333 + * "https", 1.334 + * "moz-safe-about" 1.335 + * 1.336 + */ 1.337 + bool schemeLocal = false; 1.338 + bool schemeNoReturnData = false; 1.339 + bool schemeInherits = false; 1.340 + bool schemeSecure = false; 1.341 + if (NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) || 1.342 + NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) || 1.343 + NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) || 1.344 + NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) { 1.345 + return NS_ERROR_FAILURE; 1.346 + } 1.347 + 1.348 + if (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure) { 1.349 + return NS_OK; 1.350 + } 1.351 + 1.352 + // Since there are cases where aRequestingLocation and aRequestPrincipal are 1.353 + // definitely not the owning document, we try to ignore them by extracting the 1.354 + // requestingLocation in the following order: 1.355 + // 1) from the aRequestingContext, either extracting 1.356 + // a) the node's principal, or the 1.357 + // b) script object's principal. 1.358 + // 2) if aRequestingContext yields a principal but no location, we check 1.359 + // if its the system principal. If it is, allow the load. 1.360 + // 3) Special case handling for: 1.361 + // a) speculative loads, where shouldLoad is called twice (bug 839235) 1.362 + // and the first speculative load does not include a context. 1.363 + // In this case we use aRequestingLocation to set requestingLocation. 1.364 + // b) TYPE_CSP_REPORT which does not provide a context. In this case we 1.365 + // use aRequestingLocation to set requestingLocation. 1.366 + // c) content scripts from addon code that do not provide aRequestingContext 1.367 + // or aRequestingLocation, but do provide aRequestPrincipal. 1.368 + // If aRequestPrincipal is an expanded principal, we allow the load. 1.369 + // 4) If we still end up not having a requestingLocation, we reject the load. 1.370 + 1.371 + nsCOMPtr<nsIPrincipal> principal; 1.372 + // 1a) Try to get the principal if aRequestingContext is a node. 1.373 + nsCOMPtr<nsINode> node = do_QueryInterface(aRequestingContext); 1.374 + if (node) { 1.375 + principal = node->NodePrincipal(); 1.376 + } 1.377 + 1.378 + // 1b) Try using the window's script object principal if it's not a node. 1.379 + if (!principal) { 1.380 + nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aRequestingContext); 1.381 + if (scriptObjPrin) { 1.382 + principal = scriptObjPrin->GetPrincipal(); 1.383 + } 1.384 + } 1.385 + 1.386 + nsCOMPtr<nsIURI> requestingLocation; 1.387 + if (principal) { 1.388 + principal->GetURI(getter_AddRefs(requestingLocation)); 1.389 + } 1.390 + 1.391 + // 2) if aRequestingContext yields a principal but no location, we check if its a system principal. 1.392 + if (principal && !requestingLocation) { 1.393 + if (nsContentUtils::IsSystemPrincipal(principal)) { 1.394 + *aDecision = ACCEPT; 1.395 + return NS_OK; 1.396 + } 1.397 + } 1.398 + 1.399 + // 3a,b) Special case handling for speculative loads and TYPE_CSP_REPORT. In 1.400 + // such cases, aRequestingContext doesn't exist, so we use aRequestingLocation. 1.401 + // Unfortunately we can not distinguish between speculative and normal loads here, 1.402 + // otherwise we could special case this assignment. 1.403 + if (!requestingLocation) { 1.404 + requestingLocation = aRequestingLocation; 1.405 + } 1.406 + 1.407 + // 3c) Special case handling for content scripts from addons code, which only 1.408 + // provide a aRequestPrincipal; aRequestingContext and aRequestingLocation are 1.409 + // both null; if the aRequestPrincipal is an expandedPrincipal, we allow the load. 1.410 + if (!principal && !requestingLocation && aRequestPrincipal) { 1.411 + nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aRequestPrincipal); 1.412 + if (expanded) { 1.413 + *aDecision = ACCEPT; 1.414 + return NS_OK; 1.415 + } 1.416 + } 1.417 + 1.418 + // 4) Giving up. We still don't have a requesting location, therefore we can't tell 1.419 + // if this is a mixed content load. Deny to be safe. 1.420 + if (!requestingLocation) { 1.421 + *aDecision = REJECT_REQUEST; 1.422 + return NS_OK; 1.423 + } 1.424 + 1.425 + // Check the parent scheme. If it is not an HTTPS page then mixed content 1.426 + // restrictions do not apply. 1.427 + bool parentIsHttps; 1.428 + nsresult rv = requestingLocation->SchemeIs("https", &parentIsHttps); 1.429 + if (NS_FAILED(rv)) { 1.430 + NS_ERROR("requestingLocation->SchemeIs failed"); 1.431 + *aDecision = REJECT_REQUEST; 1.432 + return NS_OK; 1.433 + } 1.434 + if (!parentIsHttps) { 1.435 + *aDecision = ACCEPT; 1.436 + return NS_OK; 1.437 + } 1.438 + 1.439 + // Determine if the rootDoc is https and if the user decided to allow Mixed Content 1.440 + nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext); 1.441 + NS_ENSURE_TRUE(docShell, NS_OK); 1.442 + bool rootHasSecureConnection = false; 1.443 + bool allowMixedContent = false; 1.444 + bool isRootDocShell = false; 1.445 + rv = docShell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isRootDocShell); 1.446 + if (NS_FAILED(rv)) { 1.447 + return rv; 1.448 + } 1.449 + 1.450 + 1.451 + // Get the sameTypeRoot tree item from the docshell 1.452 + nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot; 1.453 + docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); 1.454 + NS_ASSERTION(sameTypeRoot, "No root tree item from docshell!"); 1.455 + 1.456 + // When navigating an iframe, the iframe may be https 1.457 + // but its parents may not be. Check the parents to see if any of them are https. 1.458 + // If none of the parents are https, allow the load. 1.459 + if (aContentType == TYPE_SUBDOCUMENT && !rootHasSecureConnection) { 1.460 + 1.461 + bool httpsParentExists = false; 1.462 + 1.463 + nsCOMPtr<nsIDocShellTreeItem> parentTreeItem; 1.464 + parentTreeItem = docShell; 1.465 + 1.466 + while(!httpsParentExists && parentTreeItem) { 1.467 + nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentTreeItem)); 1.468 + NS_ASSERTION(parentAsNav, "No web navigation object from parent's docshell tree item"); 1.469 + nsCOMPtr<nsIURI> parentURI; 1.470 + 1.471 + parentAsNav->GetCurrentURI(getter_AddRefs(parentURI)); 1.472 + if (!parentURI || NS_FAILED(parentURI->SchemeIs("https", &httpsParentExists))) { 1.473 + // if getting the URI or the scheme fails, assume there is a https parent and break. 1.474 + httpsParentExists = true; 1.475 + break; 1.476 + } 1.477 + 1.478 + // When the parent and the root are the same, we have traversed all the way up 1.479 + // the same type docshell tree. Break out of the while loop. 1.480 + if(sameTypeRoot == parentTreeItem) { 1.481 + break; 1.482 + } 1.483 + 1.484 + // update the parent to the grandparent. 1.485 + nsCOMPtr<nsIDocShellTreeItem> newParentTreeItem; 1.486 + parentTreeItem->GetSameTypeParent(getter_AddRefs(newParentTreeItem)); 1.487 + parentTreeItem = newParentTreeItem; 1.488 + } // end while loop. 1.489 + 1.490 + if (!httpsParentExists) { 1.491 + *aDecision = nsIContentPolicy::ACCEPT; 1.492 + return NS_OK; 1.493 + } 1.494 + } 1.495 + 1.496 + // Get the root document from the sameTypeRoot 1.497 + nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot); 1.498 + NS_ASSERTION(rootDoc, "No root document from document shell root tree item."); 1.499 + 1.500 + // Get eventSink and the current security state from the docShell 1.501 + nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell); 1.502 + NS_ASSERTION(eventSink, "No eventSink from docShell."); 1.503 + nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot); 1.504 + NS_ASSERTION(rootShell, "No root docshell from document shell root tree item."); 1.505 + uint32_t State = nsIWebProgressListener::STATE_IS_BROKEN; 1.506 + nsCOMPtr<nsISecureBrowserUI> securityUI; 1.507 + rootShell->GetSecurityUI(getter_AddRefs(securityUI)); 1.508 + // If there is no securityUI, document doesn't have a security state. 1.509 + // Allow load and return early. 1.510 + if (!securityUI) { 1.511 + *aDecision = nsIContentPolicy::ACCEPT; 1.512 + return NS_OK; 1.513 + } 1.514 + nsresult stateRV = securityUI->GetState(&State); 1.515 + 1.516 + // If the content is display content, and the pref says display content should be blocked, block it. 1.517 + if (sBlockMixedDisplay && classification == eMixedDisplay) { 1.518 + if (allowMixedContent) { 1.519 + LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); 1.520 + *aDecision = nsIContentPolicy::ACCEPT; 1.521 + rootDoc->SetHasMixedActiveContentLoaded(true); 1.522 + if (!rootDoc->GetHasMixedDisplayContentLoaded() && NS_SUCCEEDED(stateRV)) { 1.523 + rootDoc->SetHasMixedDisplayContentLoaded(true); 1.524 + eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); 1.525 + } 1.526 + } else { 1.527 + *aDecision = nsIContentPolicy::REJECT_REQUEST; 1.528 + LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); 1.529 + if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) { 1.530 + rootDoc->SetHasMixedDisplayContentBlocked(true); 1.531 + eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT)); 1.532 + } 1.533 + } 1.534 + return NS_OK; 1.535 + 1.536 + } else if (sBlockMixedScript && classification == eMixedScript) { 1.537 + // If the content is active content, and the pref says active content should be blocked, block it 1.538 + // unless the user has choosen to override the pref 1.539 + if (allowMixedContent) { 1.540 + LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); 1.541 + *aDecision = nsIContentPolicy::ACCEPT; 1.542 + // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. 1.543 + if (rootDoc->GetHasMixedActiveContentLoaded()) { 1.544 + return NS_OK; 1.545 + } 1.546 + rootDoc->SetHasMixedActiveContentLoaded(true); 1.547 + 1.548 + if (rootHasSecureConnection) { 1.549 + // User has decided to override the pref and the root is https, so change the Security State. 1.550 + if (rootDoc->GetHasMixedDisplayContentLoaded()) { 1.551 + // If mixed display content is loaded, make sure to include that in the state. 1.552 + eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN | 1.553 + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | 1.554 + nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); 1.555 + } else { 1.556 + eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN | 1.557 + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); 1.558 + } 1.559 + return NS_OK; 1.560 + } else { 1.561 + // User has already overriden the pref and the root is not https; 1.562 + // mixed content was allowed on an https subframe. 1.563 + if (NS_SUCCEEDED(stateRV)) { 1.564 + eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); 1.565 + } 1.566 + return NS_OK; 1.567 + } 1.568 + } else { 1.569 + //User has not overriden the pref by Disabling protection. Reject the request and update the security state. 1.570 + *aDecision = nsIContentPolicy::REJECT_REQUEST; 1.571 + LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); 1.572 + // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. 1.573 + if (rootDoc->GetHasMixedActiveContentBlocked()) { 1.574 + return NS_OK; 1.575 + } 1.576 + rootDoc->SetHasMixedActiveContentBlocked(true); 1.577 + 1.578 + // The user has not overriden the pref, so make sure they still have an option by calling eventSink 1.579 + // which will invoke the doorhanger 1.580 + if (NS_SUCCEEDED(stateRV)) { 1.581 + eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT)); 1.582 + } 1.583 + return NS_OK; 1.584 + } 1.585 + 1.586 + } else { 1.587 + // The content is not blocked by the mixed content prefs. 1.588 + 1.589 + // Log a message that we are loading mixed content. 1.590 + LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); 1.591 + 1.592 + // Fire the event from a script runner as it is unsafe to run script 1.593 + // from within ShouldLoad 1.594 + nsContentUtils::AddScriptRunner( 1.595 + new nsMixedContentEvent(aRequestingContext, classification)); 1.596 + return NS_OK; 1.597 + } 1.598 + 1.599 + *aDecision = REJECT_REQUEST; 1.600 + return NS_OK; 1.601 +} 1.602 + 1.603 +NS_IMETHODIMP 1.604 +nsMixedContentBlocker::ShouldProcess(uint32_t aContentType, 1.605 + nsIURI* aContentLocation, 1.606 + nsIURI* aRequestingLocation, 1.607 + nsISupports* aRequestingContext, 1.608 + const nsACString& aMimeGuess, 1.609 + nsISupports* aExtra, 1.610 + nsIPrincipal* aRequestPrincipal, 1.611 + int16_t* aDecision) 1.612 +{ 1.613 + if (!aContentLocation) { 1.614 + // aContentLocation may be null when a plugin is loading without an associated URI resource 1.615 + if (aContentType == TYPE_OBJECT) { 1.616 + return NS_OK; 1.617 + } else { 1.618 + return NS_ERROR_FAILURE; 1.619 + } 1.620 + } 1.621 + 1.622 + return ShouldLoad(aContentType, aContentLocation, aRequestingLocation, 1.623 + aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal, 1.624 + aDecision); 1.625 +}