michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMixedContentBlocker.h" michael@0: michael@0: #include "nsContentPolicyUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsINode.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsISecurityEventSink.h" michael@0: #include "nsIWebProgressListener.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIRequest.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsISecureBrowserUI.h" michael@0: #include "nsIDocumentLoader.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsLoadGroup.h" michael@0: #include "nsIScriptError.h" michael@0: michael@0: #include "prlog.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: enum nsMixedContentBlockerMessageType { michael@0: eBlocked = 0x00, michael@0: eUserOverride = 0x01 michael@0: }; michael@0: michael@0: // Is mixed script blocking (fonts, plugin content, scripts, stylesheets, michael@0: // iframes, websockets, XHR) enabled? michael@0: bool nsMixedContentBlocker::sBlockMixedScript = false; michael@0: michael@0: // Is mixed display content blocking (images, audio, video, ) enabled? michael@0: bool nsMixedContentBlocker::sBlockMixedDisplay = false; michael@0: michael@0: // Fired at the document that attempted to load mixed content. The UI could michael@0: // handle this event, for example, by displaying an info bar that offers the michael@0: // choice to reload the page with mixed content permitted. michael@0: class nsMixedContentEvent : public nsRunnable michael@0: { michael@0: public: michael@0: nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType) michael@0: : mContext(aContext), mType(aType) michael@0: {} michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: NS_ASSERTION(mContext, michael@0: "You can't call this runnable without a requesting context"); michael@0: michael@0: // To update the security UI in the tab with the blocked mixed content, call michael@0: // nsISecurityEventSink::OnSecurityChange. You can get to the event sink by michael@0: // calling NS_CP_GetDocShellFromContext on the context, and QI'ing to michael@0: // nsISecurityEventSink. michael@0: michael@0: michael@0: // Mixed content was allowed and is about to load; get the document and michael@0: // set the approriate flag to true if we are about to load Mixed Active michael@0: // Content. michael@0: nsCOMPtr docShell = NS_CP_GetDocShellFromContext(mContext); michael@0: if (!docShell) { michael@0: return NS_OK; michael@0: } michael@0: nsCOMPtr sameTypeRoot; michael@0: docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); michael@0: NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); michael@0: michael@0: // now get the document from sameTypeRoot michael@0: nsCOMPtr rootDoc = do_GetInterface(sameTypeRoot); michael@0: NS_ASSERTION(rootDoc, "No root document from document shell root tree item."); michael@0: michael@0: michael@0: if (mType == eMixedScript) { michael@0: // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. michael@0: if (rootDoc->GetHasMixedActiveContentLoaded()) { michael@0: return NS_OK; michael@0: } michael@0: rootDoc->SetHasMixedActiveContentLoaded(true); michael@0: michael@0: // Update the security UI in the tab with the allowed mixed active content michael@0: nsCOMPtr eventSink = do_QueryInterface(docShell); michael@0: if (eventSink) { michael@0: // If mixed display content is loaded, make sure to include that in the state. michael@0: if (rootDoc->GetHasMixedDisplayContentLoaded()) { michael@0: eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); michael@0: } else { michael@0: eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); michael@0: } michael@0: } michael@0: michael@0: } else if (mType == eMixedDisplay) { michael@0: // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. michael@0: if (rootDoc->GetHasMixedDisplayContentLoaded()) { michael@0: return NS_OK; michael@0: } michael@0: rootDoc->SetHasMixedDisplayContentLoaded(true); michael@0: michael@0: // Update the security UI in the tab with the allowed mixed display content. michael@0: nsCOMPtr eventSink = do_QueryInterface(docShell); michael@0: if (eventSink) { michael@0: // If mixed active content is loaded, make sure to include that in the state. michael@0: if (rootDoc->GetHasMixedActiveContentLoaded()) { michael@0: eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); michael@0: } else { michael@0: eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: // The requesting context for the content load. Generally, a DOM node from michael@0: // the document that caused the load. michael@0: nsCOMPtr mContext; michael@0: michael@0: // The type of mixed content detected, e.g. active or display michael@0: const MixedContentTypes mType; michael@0: }; michael@0: michael@0: michael@0: nsMixedContentBlocker::nsMixedContentBlocker() michael@0: { michael@0: // Cache the pref for mixed script blocking michael@0: Preferences::AddBoolVarCache(&sBlockMixedScript, michael@0: "security.mixed_content.block_active_content"); michael@0: michael@0: // Cache the pref for mixed display blocking michael@0: Preferences::AddBoolVarCache(&sBlockMixedDisplay, michael@0: "security.mixed_content.block_display_content"); michael@0: } michael@0: michael@0: nsMixedContentBlocker::~nsMixedContentBlocker() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy) michael@0: michael@0: static void michael@0: LogMixedContentMessage(MixedContentTypes aClassification, michael@0: nsIURI* aContentLocation, michael@0: nsIDocument* aRootDoc, michael@0: nsMixedContentBlockerMessageType aMessageType) michael@0: { michael@0: nsAutoCString messageCategory; michael@0: uint32_t severityFlag; michael@0: nsAutoCString messageLookupKey; michael@0: michael@0: if (aMessageType == eBlocked) { michael@0: severityFlag = nsIScriptError::errorFlag; michael@0: messageCategory.AssignLiteral("Mixed Content Blocker"); michael@0: if (aClassification == eMixedDisplay) { michael@0: messageLookupKey.AssignLiteral("BlockMixedDisplayContent"); michael@0: } else { michael@0: messageLookupKey.AssignLiteral("BlockMixedActiveContent"); michael@0: } michael@0: } else { michael@0: severityFlag = nsIScriptError::warningFlag; michael@0: messageCategory.AssignLiteral("Mixed Content Message"); michael@0: if (aClassification == eMixedDisplay) { michael@0: messageLookupKey.AssignLiteral("LoadingMixedDisplayContent"); michael@0: } else { michael@0: messageLookupKey.AssignLiteral("LoadingMixedActiveContent"); michael@0: } michael@0: } michael@0: michael@0: nsAutoCString locationSpec; michael@0: aContentLocation->GetSpec(locationSpec); michael@0: NS_ConvertUTF8toUTF16 locationSpecUTF16(locationSpec); michael@0: michael@0: const char16_t* strings[] = { locationSpecUTF16.get() }; michael@0: nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc, michael@0: nsContentUtils::eSECURITY_PROPERTIES, michael@0: messageLookupKey.get(), strings, ArrayLength(strings)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMixedContentBlocker::ShouldLoad(uint32_t aContentType, michael@0: nsIURI* aContentLocation, michael@0: nsIURI* aRequestingLocation, michael@0: nsISupports* aRequestingContext, michael@0: const nsACString& aMimeGuess, michael@0: nsISupports* aExtra, michael@0: nsIPrincipal* aRequestPrincipal, michael@0: int16_t* aDecision) michael@0: { michael@0: // Asserting that we are on the main thread here and hence do not have to lock michael@0: // and unlock sBlockMixedScript and sBlockMixedDisplay before reading/writing michael@0: // to them. michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Assume active (high risk) content and blocked by default michael@0: MixedContentTypes classification = eMixedScript; michael@0: michael@0: michael@0: // Notes on non-obvious decisions: michael@0: // michael@0: // TYPE_DTD: A DTD can contain entity definitions that expand to scripts. michael@0: // michael@0: // TYPE_FONT: The TrueType hinting mechanism is basically a scripting michael@0: // language that gets interpreted by the operating system's font rasterizer. michael@0: // Mixed content web fonts are relatively uncommon, and we can can fall back michael@0: // to built-in fonts with minimal disruption in almost all cases. michael@0: // michael@0: // TYPE_OBJECT_SUBREQUEST could actually be either active content (e.g. a michael@0: // script that a plugin will execute) or display content (e.g. Flash video michael@0: // content). Until we have a way to determine active vs passive content michael@0: // from plugin requests (bug 836352), we will treat this as passive content. michael@0: // This is to prevent false positives from causing users to become michael@0: // desensitized to the mixed content blocker. michael@0: // michael@0: // TYPE_CSP_REPORT: High-risk because they directly leak information about michael@0: // the content of the page, and because blocking them does not have any michael@0: // negative effect on the page loading. michael@0: // michael@0: // TYPE_PING: Ping requests are POSTS, not GETs like images and media. michael@0: // Also, PING requests have no bearing on the rendering or operation of michael@0: // the page when used as designed, so even though they are lower risk than michael@0: // scripts, blocking them is basically risk-free as far as compatibility is michael@0: // concerned. Ping is turned off by default in Firefox, so unless a user michael@0: // opts into ping, no request will be made. Categorizing this as Mixed michael@0: // Display Content for now, but this is subject to change. michael@0: // michael@0: // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning michael@0: // and other advanced CSS features can possibly be exploited to cause michael@0: // spoofing attacks (e.g. make a "grant permission" button look like a michael@0: // "refuse permission" button). michael@0: // michael@0: // TYPE_BEACON: Beacon requests are similar to TYPE_PING, but are default on. michael@0: // michael@0: // TYPE_WEBSOCKET: The Websockets API requires browsers to michael@0: // reject mixed-content websockets: "If secure is false but the origin of michael@0: // the entry script has a scheme component that is itself a secure protocol, michael@0: // e.g. HTTPS, then throw a SecurityError exception." We already block mixed michael@0: // content websockets within the websockets implementation, so we don't need michael@0: // to do any blocking here, nor do we need to provide a way to undo or michael@0: // override the blocking. Websockets without TLS are very flaky anyway in the michael@0: // face of many HTTP-aware proxies. Compared to psasive content, there is michael@0: // additional risk that the script using WebSockets will disclose sensitive michael@0: // information from the HTTPS page and/or eval (directly or indirectly) michael@0: // received data. michael@0: // michael@0: // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most michael@0: // mixed-content XHR will already be blocked by that check. This will also michael@0: // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned michael@0: // above for WebSockets apply to XHR, and XHR should have the same security michael@0: // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects michael@0: // amplifies these concerns. michael@0: michael@0: michael@0: static_assert(TYPE_DATAREQUEST == TYPE_XMLHTTPREQUEST, michael@0: "TYPE_DATAREQUEST is not a synonym for " michael@0: "TYPE_XMLHTTPREQUEST"); michael@0: michael@0: switch (aContentType) { michael@0: // The top-level document cannot be mixed content by definition michael@0: case TYPE_DOCUMENT: michael@0: *aDecision = ACCEPT; michael@0: return NS_OK; michael@0: // Creating insecure websocket connections in a secure page is blocked already michael@0: // in the websocket constructor. We don't need to check the blocking here michael@0: // and we don't want to un-block michael@0: case TYPE_WEBSOCKET: michael@0: *aDecision = ACCEPT; michael@0: return NS_OK; michael@0: michael@0: michael@0: // Static display content is considered moderate risk for mixed content so michael@0: // these will be blocked according to the mixed display preference michael@0: case TYPE_IMAGE: michael@0: case TYPE_MEDIA: michael@0: case TYPE_OBJECT_SUBREQUEST: michael@0: case TYPE_PING: michael@0: case TYPE_BEACON: michael@0: classification = eMixedDisplay; michael@0: break; michael@0: michael@0: // Active content (or content with a low value/risk-of-blocking ratio) michael@0: // that has been explicitly evaluated; listed here for documentation michael@0: // purposes and to avoid the assertion and warning for the default case. michael@0: case TYPE_CSP_REPORT: michael@0: case TYPE_DTD: michael@0: case TYPE_FONT: michael@0: case TYPE_OBJECT: michael@0: case TYPE_SCRIPT: michael@0: case TYPE_STYLESHEET: michael@0: case TYPE_SUBDOCUMENT: michael@0: case TYPE_XBL: michael@0: case TYPE_XMLHTTPREQUEST: michael@0: case TYPE_XSLT: michael@0: case TYPE_OTHER: michael@0: break; michael@0: michael@0: michael@0: // This content policy works as a whitelist. michael@0: default: michael@0: MOZ_ASSERT(false, "Mixed content of unknown type"); michael@0: break; michael@0: } michael@0: michael@0: /* Get the scheme of the sub-document resource to be requested. If it is michael@0: * a safe to load in an https context then mixed content doesn't apply. michael@0: * michael@0: * Check Protocol Flags to determine if scheme is safe to load: michael@0: * URI_DOES_NOT_RETURN_DATA - e.g. michael@0: * "mailto" michael@0: * URI_IS_LOCAL_RESOURCE - e.g. michael@0: * "data", michael@0: * "resource", michael@0: * "moz-icon" michael@0: * URI_INHERITS_SECURITY_CONTEXT - e.g. michael@0: * "javascript" michael@0: * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g. michael@0: * "https", michael@0: * "moz-safe-about" michael@0: * michael@0: */ michael@0: bool schemeLocal = false; michael@0: bool schemeNoReturnData = false; michael@0: bool schemeInherits = false; michael@0: bool schemeSecure = false; michael@0: if (NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) || michael@0: NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) || michael@0: NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) || michael@0: NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Since there are cases where aRequestingLocation and aRequestPrincipal are michael@0: // definitely not the owning document, we try to ignore them by extracting the michael@0: // requestingLocation in the following order: michael@0: // 1) from the aRequestingContext, either extracting michael@0: // a) the node's principal, or the michael@0: // b) script object's principal. michael@0: // 2) if aRequestingContext yields a principal but no location, we check michael@0: // if its the system principal. If it is, allow the load. michael@0: // 3) Special case handling for: michael@0: // a) speculative loads, where shouldLoad is called twice (bug 839235) michael@0: // and the first speculative load does not include a context. michael@0: // In this case we use aRequestingLocation to set requestingLocation. michael@0: // b) TYPE_CSP_REPORT which does not provide a context. In this case we michael@0: // use aRequestingLocation to set requestingLocation. michael@0: // c) content scripts from addon code that do not provide aRequestingContext michael@0: // or aRequestingLocation, but do provide aRequestPrincipal. michael@0: // If aRequestPrincipal is an expanded principal, we allow the load. michael@0: // 4) If we still end up not having a requestingLocation, we reject the load. michael@0: michael@0: nsCOMPtr principal; michael@0: // 1a) Try to get the principal if aRequestingContext is a node. michael@0: nsCOMPtr node = do_QueryInterface(aRequestingContext); michael@0: if (node) { michael@0: principal = node->NodePrincipal(); michael@0: } michael@0: michael@0: // 1b) Try using the window's script object principal if it's not a node. michael@0: if (!principal) { michael@0: nsCOMPtr scriptObjPrin = do_QueryInterface(aRequestingContext); michael@0: if (scriptObjPrin) { michael@0: principal = scriptObjPrin->GetPrincipal(); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr requestingLocation; michael@0: if (principal) { michael@0: principal->GetURI(getter_AddRefs(requestingLocation)); michael@0: } michael@0: michael@0: // 2) if aRequestingContext yields a principal but no location, we check if its a system principal. michael@0: if (principal && !requestingLocation) { michael@0: if (nsContentUtils::IsSystemPrincipal(principal)) { michael@0: *aDecision = ACCEPT; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // 3a,b) Special case handling for speculative loads and TYPE_CSP_REPORT. In michael@0: // such cases, aRequestingContext doesn't exist, so we use aRequestingLocation. michael@0: // Unfortunately we can not distinguish between speculative and normal loads here, michael@0: // otherwise we could special case this assignment. michael@0: if (!requestingLocation) { michael@0: requestingLocation = aRequestingLocation; michael@0: } michael@0: michael@0: // 3c) Special case handling for content scripts from addons code, which only michael@0: // provide a aRequestPrincipal; aRequestingContext and aRequestingLocation are michael@0: // both null; if the aRequestPrincipal is an expandedPrincipal, we allow the load. michael@0: if (!principal && !requestingLocation && aRequestPrincipal) { michael@0: nsCOMPtr expanded = do_QueryInterface(aRequestPrincipal); michael@0: if (expanded) { michael@0: *aDecision = ACCEPT; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // 4) Giving up. We still don't have a requesting location, therefore we can't tell michael@0: // if this is a mixed content load. Deny to be safe. michael@0: if (!requestingLocation) { michael@0: *aDecision = REJECT_REQUEST; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Check the parent scheme. If it is not an HTTPS page then mixed content michael@0: // restrictions do not apply. michael@0: bool parentIsHttps; michael@0: nsresult rv = requestingLocation->SchemeIs("https", &parentIsHttps); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("requestingLocation->SchemeIs failed"); michael@0: *aDecision = REJECT_REQUEST; michael@0: return NS_OK; michael@0: } michael@0: if (!parentIsHttps) { michael@0: *aDecision = ACCEPT; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Determine if the rootDoc is https and if the user decided to allow Mixed Content michael@0: nsCOMPtr docShell = NS_CP_GetDocShellFromContext(aRequestingContext); michael@0: NS_ENSURE_TRUE(docShell, NS_OK); michael@0: bool rootHasSecureConnection = false; michael@0: bool allowMixedContent = false; michael@0: bool isRootDocShell = false; michael@0: rv = docShell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isRootDocShell); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: // Get the sameTypeRoot tree item from the docshell michael@0: nsCOMPtr sameTypeRoot; michael@0: docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); michael@0: NS_ASSERTION(sameTypeRoot, "No root tree item from docshell!"); michael@0: michael@0: // When navigating an iframe, the iframe may be https michael@0: // but its parents may not be. Check the parents to see if any of them are https. michael@0: // If none of the parents are https, allow the load. michael@0: if (aContentType == TYPE_SUBDOCUMENT && !rootHasSecureConnection) { michael@0: michael@0: bool httpsParentExists = false; michael@0: michael@0: nsCOMPtr parentTreeItem; michael@0: parentTreeItem = docShell; michael@0: michael@0: while(!httpsParentExists && parentTreeItem) { michael@0: nsCOMPtr parentAsNav(do_QueryInterface(parentTreeItem)); michael@0: NS_ASSERTION(parentAsNav, "No web navigation object from parent's docshell tree item"); michael@0: nsCOMPtr parentURI; michael@0: michael@0: parentAsNav->GetCurrentURI(getter_AddRefs(parentURI)); michael@0: if (!parentURI || NS_FAILED(parentURI->SchemeIs("https", &httpsParentExists))) { michael@0: // if getting the URI or the scheme fails, assume there is a https parent and break. michael@0: httpsParentExists = true; michael@0: break; michael@0: } michael@0: michael@0: // When the parent and the root are the same, we have traversed all the way up michael@0: // the same type docshell tree. Break out of the while loop. michael@0: if(sameTypeRoot == parentTreeItem) { michael@0: break; michael@0: } michael@0: michael@0: // update the parent to the grandparent. michael@0: nsCOMPtr newParentTreeItem; michael@0: parentTreeItem->GetSameTypeParent(getter_AddRefs(newParentTreeItem)); michael@0: parentTreeItem = newParentTreeItem; michael@0: } // end while loop. michael@0: michael@0: if (!httpsParentExists) { michael@0: *aDecision = nsIContentPolicy::ACCEPT; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Get the root document from the sameTypeRoot michael@0: nsCOMPtr rootDoc = do_GetInterface(sameTypeRoot); michael@0: NS_ASSERTION(rootDoc, "No root document from document shell root tree item."); michael@0: michael@0: // Get eventSink and the current security state from the docShell michael@0: nsCOMPtr eventSink = do_QueryInterface(docShell); michael@0: NS_ASSERTION(eventSink, "No eventSink from docShell."); michael@0: nsCOMPtr rootShell = do_GetInterface(sameTypeRoot); michael@0: NS_ASSERTION(rootShell, "No root docshell from document shell root tree item."); michael@0: uint32_t State = nsIWebProgressListener::STATE_IS_BROKEN; michael@0: nsCOMPtr securityUI; michael@0: rootShell->GetSecurityUI(getter_AddRefs(securityUI)); michael@0: // If there is no securityUI, document doesn't have a security state. michael@0: // Allow load and return early. michael@0: if (!securityUI) { michael@0: *aDecision = nsIContentPolicy::ACCEPT; michael@0: return NS_OK; michael@0: } michael@0: nsresult stateRV = securityUI->GetState(&State); michael@0: michael@0: // If the content is display content, and the pref says display content should be blocked, block it. michael@0: if (sBlockMixedDisplay && classification == eMixedDisplay) { michael@0: if (allowMixedContent) { michael@0: LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); michael@0: *aDecision = nsIContentPolicy::ACCEPT; michael@0: rootDoc->SetHasMixedActiveContentLoaded(true); michael@0: if (!rootDoc->GetHasMixedDisplayContentLoaded() && NS_SUCCEEDED(stateRV)) { michael@0: rootDoc->SetHasMixedDisplayContentLoaded(true); michael@0: eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); michael@0: } michael@0: } else { michael@0: *aDecision = nsIContentPolicy::REJECT_REQUEST; michael@0: LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); michael@0: if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) { michael@0: rootDoc->SetHasMixedDisplayContentBlocked(true); michael@0: eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT)); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: michael@0: } else if (sBlockMixedScript && classification == eMixedScript) { michael@0: // If the content is active content, and the pref says active content should be blocked, block it michael@0: // unless the user has choosen to override the pref michael@0: if (allowMixedContent) { michael@0: LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); michael@0: *aDecision = nsIContentPolicy::ACCEPT; michael@0: // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. michael@0: if (rootDoc->GetHasMixedActiveContentLoaded()) { michael@0: return NS_OK; michael@0: } michael@0: rootDoc->SetHasMixedActiveContentLoaded(true); michael@0: michael@0: if (rootHasSecureConnection) { michael@0: // User has decided to override the pref and the root is https, so change the Security State. michael@0: if (rootDoc->GetHasMixedDisplayContentLoaded()) { michael@0: // If mixed display content is loaded, make sure to include that in the state. michael@0: eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); michael@0: } else { michael@0: eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN | michael@0: nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); michael@0: } michael@0: return NS_OK; michael@0: } else { michael@0: // User has already overriden the pref and the root is not https; michael@0: // mixed content was allowed on an https subframe. michael@0: if (NS_SUCCEEDED(stateRV)) { michael@0: eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: } else { michael@0: //User has not overriden the pref by Disabling protection. Reject the request and update the security state. michael@0: *aDecision = nsIContentPolicy::REJECT_REQUEST; michael@0: LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked); michael@0: // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI. michael@0: if (rootDoc->GetHasMixedActiveContentBlocked()) { michael@0: return NS_OK; michael@0: } michael@0: rootDoc->SetHasMixedActiveContentBlocked(true); michael@0: michael@0: // The user has not overriden the pref, so make sure they still have an option by calling eventSink michael@0: // which will invoke the doorhanger michael@0: if (NS_SUCCEEDED(stateRV)) { michael@0: eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT)); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } else { michael@0: // The content is not blocked by the mixed content prefs. michael@0: michael@0: // Log a message that we are loading mixed content. michael@0: LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride); michael@0: michael@0: // Fire the event from a script runner as it is unsafe to run script michael@0: // from within ShouldLoad michael@0: nsContentUtils::AddScriptRunner( michael@0: new nsMixedContentEvent(aRequestingContext, classification)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aDecision = REJECT_REQUEST; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMixedContentBlocker::ShouldProcess(uint32_t aContentType, michael@0: nsIURI* aContentLocation, michael@0: nsIURI* aRequestingLocation, michael@0: nsISupports* aRequestingContext, michael@0: const nsACString& aMimeGuess, michael@0: nsISupports* aExtra, michael@0: nsIPrincipal* aRequestPrincipal, michael@0: int16_t* aDecision) michael@0: { michael@0: if (!aContentLocation) { michael@0: // aContentLocation may be null when a plugin is loading without an associated URI resource michael@0: if (aContentType == TYPE_OBJECT) { michael@0: return NS_OK; michael@0: } else { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: return ShouldLoad(aContentType, aContentLocation, aRequestingLocation, michael@0: aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal, michael@0: aDecision); michael@0: }