michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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: /* michael@0: * Utility routines for checking content load/process policy settings, michael@0: * and routines helpful for content policy implementors. michael@0: * michael@0: * XXXbz it would be nice if some of this stuff could be out-of-lined in michael@0: * nsContentUtils. That would work for almost all the callers... michael@0: */ michael@0: michael@0: #ifndef __nsContentPolicyUtils_h__ michael@0: #define __nsContentPolicyUtils_h__ michael@0: michael@0: #include "nsIContentPolicy.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIURI.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: //XXXtw sadly, this makes consumers of nsContentPolicyUtils depend on widget michael@0: #include "nsIDocument.h" michael@0: #include "nsPIDOMWindow.h" michael@0: michael@0: class nsACString; michael@0: class nsIPrincipal; michael@0: michael@0: #define NS_CONTENTPOLICY_CONTRACTID "@mozilla.org/layout/content-policy;1" michael@0: #define NS_CONTENTPOLICY_CATEGORY "content-policy" michael@0: #define NS_CONTENTPOLICY_CID \ michael@0: {0x0e3afd3d, 0xeb60, 0x4c2b, \ michael@0: { 0x96, 0x3b, 0x56, 0xd7, 0xc4, 0x39, 0xf1, 0x24 }} michael@0: michael@0: /** michael@0: * Evaluates to true if val is ACCEPT. michael@0: * michael@0: * @param val the status returned from shouldProcess/shouldLoad michael@0: */ michael@0: #define NS_CP_ACCEPTED(val) ((val) == nsIContentPolicy::ACCEPT) michael@0: michael@0: /** michael@0: * Evaluates to true if val is a REJECT_* status michael@0: * michael@0: * @param val the status returned from shouldProcess/shouldLoad michael@0: */ michael@0: #define NS_CP_REJECTED(val) ((val) != nsIContentPolicy::ACCEPT) michael@0: michael@0: // Offer convenient translations of constants -> const char* michael@0: michael@0: // convenience macro to reduce some repetative typing... michael@0: // name is the name of a constant from this interface michael@0: #define CASE_RETURN(name) \ michael@0: case nsIContentPolicy:: name : \ michael@0: return #name michael@0: michael@0: #ifdef PR_LOGGING michael@0: /** michael@0: * Returns a string corresponding to the name of the response constant, or michael@0: * "" if an unknown response value is given. michael@0: * michael@0: * The return value is static and must not be freed. michael@0: * michael@0: * @param response the response code michael@0: * @return the name of the given response code michael@0: */ michael@0: inline const char * michael@0: NS_CP_ResponseName(int16_t response) michael@0: { michael@0: switch (response) { michael@0: CASE_RETURN( REJECT_REQUEST ); michael@0: CASE_RETURN( REJECT_TYPE ); michael@0: CASE_RETURN( REJECT_SERVER ); michael@0: CASE_RETURN( REJECT_OTHER ); michael@0: CASE_RETURN( ACCEPT ); michael@0: default: michael@0: return ""; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Returns a string corresponding to the name of the content type constant, or michael@0: * "" if an unknown content type value is given. michael@0: * michael@0: * The return value is static and must not be freed. michael@0: * michael@0: * @param contentType the content type code michael@0: * @return the name of the given content type code michael@0: */ michael@0: inline const char * michael@0: NS_CP_ContentTypeName(uint32_t contentType) michael@0: { michael@0: switch (contentType) { michael@0: CASE_RETURN( TYPE_OTHER ); michael@0: CASE_RETURN( TYPE_SCRIPT ); michael@0: CASE_RETURN( TYPE_IMAGE ); michael@0: CASE_RETURN( TYPE_STYLESHEET ); michael@0: CASE_RETURN( TYPE_OBJECT ); michael@0: CASE_RETURN( TYPE_DOCUMENT ); michael@0: CASE_RETURN( TYPE_SUBDOCUMENT ); michael@0: CASE_RETURN( TYPE_REFRESH ); michael@0: CASE_RETURN( TYPE_XBL ); michael@0: CASE_RETURN( TYPE_PING ); michael@0: CASE_RETURN( TYPE_XMLHTTPREQUEST ); michael@0: CASE_RETURN( TYPE_OBJECT_SUBREQUEST ); michael@0: CASE_RETURN( TYPE_DTD ); michael@0: CASE_RETURN( TYPE_FONT ); michael@0: CASE_RETURN( TYPE_MEDIA ); michael@0: CASE_RETURN( TYPE_WEBSOCKET ); michael@0: CASE_RETURN( TYPE_CSP_REPORT ); michael@0: CASE_RETURN( TYPE_XSLT ); michael@0: CASE_RETURN( TYPE_BEACON ); michael@0: default: michael@0: return ""; michael@0: } michael@0: } michael@0: michael@0: #endif // defined(PR_LOGGING) michael@0: michael@0: #undef CASE_RETURN michael@0: michael@0: /* Passes on parameters from its "caller"'s context. */ michael@0: #define CHECK_CONTENT_POLICY(action) \ michael@0: PR_BEGIN_MACRO \ michael@0: nsCOMPtr policy = \ michael@0: do_GetService(NS_CONTENTPOLICY_CONTRACTID); \ michael@0: if (!policy) \ michael@0: return NS_ERROR_FAILURE; \ michael@0: \ michael@0: return policy-> action (contentType, contentLocation, requestOrigin, \ michael@0: context, mimeType, extra, originPrincipal, \ michael@0: decision); \ michael@0: PR_END_MACRO michael@0: michael@0: /* Passes on parameters from its "caller"'s context. */ michael@0: #define CHECK_CONTENT_POLICY_WITH_SERVICE(action, _policy) \ michael@0: PR_BEGIN_MACRO \ michael@0: return _policy-> action (contentType, contentLocation, requestOrigin, \ michael@0: context, mimeType, extra, originPrincipal, \ michael@0: decision); \ michael@0: PR_END_MACRO michael@0: michael@0: /** michael@0: * Check whether we can short-circuit this check and bail out. If not, get the michael@0: * origin URI to use. michael@0: * michael@0: * Note: requestOrigin is scoped outside the PR_BEGIN_MACRO/PR_END_MACRO on michael@0: * purpose */ michael@0: #define CHECK_PRINCIPAL_AND_DATA(action) \ michael@0: nsCOMPtr requestOrigin; \ michael@0: PR_BEGIN_MACRO \ michael@0: if (originPrincipal) { \ michael@0: nsCOMPtr secMan = aSecMan; \ michael@0: if (!secMan) { \ michael@0: secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); \ michael@0: } \ michael@0: if (secMan) { \ michael@0: bool isSystem; \ michael@0: nsresult rv = secMan->IsSystemPrincipal(originPrincipal, \ michael@0: &isSystem); \ michael@0: NS_ENSURE_SUCCESS(rv, rv); \ michael@0: if (isSystem) { \ michael@0: *decision = nsIContentPolicy::ACCEPT; \ michael@0: nsCOMPtr n = do_QueryInterface(context); \ michael@0: if (!n) { \ michael@0: nsCOMPtr win = do_QueryInterface(context); \ michael@0: n = win ? win->GetExtantDoc() : nullptr; \ michael@0: } \ michael@0: if (n) { \ michael@0: nsIDocument* d = n->OwnerDoc(); \ michael@0: if (d->IsLoadedAsData() || d->IsBeingUsedAsImage() || \ michael@0: d->IsResourceDoc()) { \ michael@0: nsCOMPtr dataPolicy = \ michael@0: do_GetService( \ michael@0: "@mozilla.org/data-document-content-policy;1"); \ michael@0: if (dataPolicy) { \ michael@0: dataPolicy-> action (contentType, contentLocation, \ michael@0: requestOrigin, context, \ michael@0: mimeType, extra, \ michael@0: originPrincipal, decision); \ michael@0: } \ michael@0: } \ michael@0: } \ michael@0: return NS_OK; \ michael@0: } \ michael@0: } \ michael@0: nsresult rv = originPrincipal->GetURI(getter_AddRefs(requestOrigin)); \ michael@0: NS_ENSURE_SUCCESS(rv, rv); \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: /** michael@0: * Alias for calling ShouldLoad on the content policy service. Parameters are michael@0: * the same as nsIContentPolicy::shouldLoad, except for the originPrincipal michael@0: * parameter, which should be non-null if possible, and the last two michael@0: * parameters, which can be used to pass in pointer to some useful services if michael@0: * the caller already has them. The origin URI to pass to shouldLoad will be michael@0: * the URI of originPrincipal, unless originPrincipal is null (in which case a michael@0: * null origin URI will be passed). michael@0: */ michael@0: inline nsresult michael@0: NS_CheckContentLoadPolicy(uint32_t contentType, michael@0: nsIURI *contentLocation, michael@0: nsIPrincipal *originPrincipal, michael@0: nsISupports *context, michael@0: const nsACString &mimeType, michael@0: nsISupports *extra, michael@0: int16_t *decision, michael@0: nsIContentPolicy *policyService = nullptr, michael@0: nsIScriptSecurityManager* aSecMan = nullptr) michael@0: { michael@0: CHECK_PRINCIPAL_AND_DATA(ShouldLoad); michael@0: if (policyService) { michael@0: CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldLoad, policyService); michael@0: } michael@0: CHECK_CONTENT_POLICY(ShouldLoad); michael@0: } michael@0: michael@0: /** michael@0: * Alias for calling ShouldProcess on the content policy service. Parameters michael@0: * are the same as nsIContentPolicy::shouldLoad, except for the originPrincipal michael@0: * parameter, which should be non-null if possible, and the last two michael@0: * parameters, which can be used to pass in pointer to some useful services if michael@0: * the caller already has them. The origin URI to pass to shouldLoad will be michael@0: * the URI of originPrincipal, unless originPrincipal is null (in which case a michael@0: * null origin URI will be passed). michael@0: */ michael@0: inline nsresult michael@0: NS_CheckContentProcessPolicy(uint32_t contentType, michael@0: nsIURI *contentLocation, michael@0: nsIPrincipal *originPrincipal, michael@0: nsISupports *context, michael@0: const nsACString &mimeType, michael@0: nsISupports *extra, michael@0: int16_t *decision, michael@0: nsIContentPolicy *policyService = nullptr, michael@0: nsIScriptSecurityManager* aSecMan = nullptr) michael@0: { michael@0: CHECK_PRINCIPAL_AND_DATA(ShouldProcess); michael@0: if (policyService) { michael@0: CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldProcess, policyService); michael@0: } michael@0: CHECK_CONTENT_POLICY(ShouldProcess); michael@0: } michael@0: michael@0: #undef CHECK_CONTENT_POLICY michael@0: #undef CHECK_CONTENT_POLICY_WITH_SERVICE michael@0: michael@0: /** michael@0: * Helper function to get an nsIDocShell given a context. michael@0: * If the context is a document or window, the corresponding docshell will be michael@0: * returned. michael@0: * If the context is a non-document DOM node, the docshell of its ownerDocument michael@0: * will be returned. michael@0: * michael@0: * @param aContext the context to find a docshell for (can be null) michael@0: * michael@0: * @return a WEAK pointer to the docshell, or nullptr if it could michael@0: * not be obtained michael@0: * michael@0: * @note As of this writing, calls to nsIContentPolicy::Should{Load,Process} michael@0: * for TYPE_DOCUMENT and TYPE_SUBDOCUMENT pass in an aContext that either michael@0: * points to the frameElement of the window the load is happening in michael@0: * (in which case NS_CP_GetDocShellFromContext will return the parent of the michael@0: * docshell the load is happening in), or points to the window the load is michael@0: * happening in (in which case NS_CP_GetDocShellFromContext will return michael@0: * the docshell the load is happening in). It's up to callers to QI aContext michael@0: * and handle things accordingly if they want the docshell the load is michael@0: * happening in. These are somewhat odd semantics, and bug 466687 has been michael@0: * filed to consider improving them. michael@0: */ michael@0: inline nsIDocShell* michael@0: NS_CP_GetDocShellFromContext(nsISupports *aContext) michael@0: { michael@0: if (!aContext) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr window = do_QueryInterface(aContext); michael@0: michael@0: if (!window) { michael@0: // our context might be a document (which also QIs to nsIDOMNode), so michael@0: // try that first michael@0: nsCOMPtr doc = do_QueryInterface(aContext); michael@0: if (!doc) { michael@0: // we were not a document after all, get our ownerDocument, michael@0: // hopefully michael@0: nsCOMPtr content = do_QueryInterface(aContext); michael@0: if (content) { michael@0: doc = content->OwnerDoc(); michael@0: } michael@0: } michael@0: michael@0: if (doc) { michael@0: if (doc->GetDisplayDocument()) { michael@0: doc = doc->GetDisplayDocument(); michael@0: } michael@0: michael@0: window = doc->GetWindow(); michael@0: } michael@0: } michael@0: michael@0: if (!window) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return window->GetDocShell(); michael@0: } michael@0: michael@0: #endif /* __nsContentPolicyUtils_h__ */