1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 /*
7 * Utility routines for checking content load/process policy settings,
8 * and routines helpful for content policy implementors.
9 *
10 * XXXbz it would be nice if some of this stuff could be out-of-lined in
11 * nsContentUtils. That would work for almost all the callers...
12 */
13
14 #ifndef __nsContentPolicyUtils_h__
15 #define __nsContentPolicyUtils_h__
16
17 #include "nsContentUtils.h"
18 #include "nsIContentPolicy.h"
19 #include "nsIContent.h"
20 #include "nsIScriptSecurityManager.h"
21 #include "nsIURI.h"
22 #include "nsServiceManagerUtils.h"
23
24 //XXXtw sadly, this makes consumers of nsContentPolicyUtils depend on widget
25 #include "nsIDocument.h"
26 #include "nsPIDOMWindow.h"
27
28 class nsACString;
29 class nsIPrincipal;
30
31 #define NS_CONTENTPOLICY_CONTRACTID "@mozilla.org/layout/content-policy;1"
32 #define NS_CONTENTPOLICY_CATEGORY "content-policy"
33 #define NS_SIMPLECONTENTPOLICY_CATEGORY "simple-content-policy"
34 #define NS_CONTENTPOLICY_CID \
35 {0x0e3afd3d, 0xeb60, 0x4c2b, \
36 { 0x96, 0x3b, 0x56, 0xd7, 0xc4, 0x39, 0xf1, 0x24 }}
37
38 /**
39 * Evaluates to true if val is ACCEPT.
40 *
41 * @param val the status returned from shouldProcess/shouldLoad
42 */
43 #define NS_CP_ACCEPTED(val) ((val) == nsIContentPolicy::ACCEPT)
44
45 /**
46 * Evaluates to true if val is a REJECT_* status
47 *
48 * @param val the status returned from shouldProcess/shouldLoad
49 */
50 #define NS_CP_REJECTED(val) ((val) != nsIContentPolicy::ACCEPT)
51
52 // Offer convenient translations of constants -> const char*
53
54 // convenience macro to reduce some repetative typing...
55 // name is the name of a constant from this interface
56 #define CASE_RETURN(name) \
57 case nsIContentPolicy:: name : \
58 return #name
59
60 /**
61 * Returns a string corresponding to the name of the response constant, or
62 * "<Unknown Response>" if an unknown response value is given.
63 *
64 * The return value is static and must not be freed.
65 *
66 * @param response the response code
67 * @return the name of the given response code
68 */
69 inline const char *
NS_CP_ResponseName(int16_t response)70 NS_CP_ResponseName(int16_t response)
71 {
72 switch (response) {
73 CASE_RETURN( REJECT_REQUEST );
74 CASE_RETURN( REJECT_TYPE );
75 CASE_RETURN( REJECT_SERVER );
76 CASE_RETURN( REJECT_OTHER );
77 CASE_RETURN( ACCEPT );
78 default:
79 return "<Unknown Response>";
80 }
81 }
82
83 /**
84 * Returns a string corresponding to the name of the content type constant, or
85 * "<Unknown Type>" if an unknown content type value is given.
86 *
87 * The return value is static and must not be freed.
88 *
89 * @param contentType the content type code
90 * @return the name of the given content type code
91 */
92 inline const char *
NS_CP_ContentTypeName(uint32_t contentType)93 NS_CP_ContentTypeName(uint32_t contentType)
94 {
95 switch (contentType) {
96 CASE_RETURN( TYPE_OTHER );
97 CASE_RETURN( TYPE_SCRIPT );
98 CASE_RETURN( TYPE_IMAGE );
99 CASE_RETURN( TYPE_STYLESHEET );
100 CASE_RETURN( TYPE_OBJECT );
101 CASE_RETURN( TYPE_DOCUMENT );
102 CASE_RETURN( TYPE_SUBDOCUMENT );
103 CASE_RETURN( TYPE_REFRESH );
104 CASE_RETURN( TYPE_XBL );
105 CASE_RETURN( TYPE_PING );
106 CASE_RETURN( TYPE_XMLHTTPREQUEST );
107 CASE_RETURN( TYPE_OBJECT_SUBREQUEST );
108 CASE_RETURN( TYPE_DTD );
109 CASE_RETURN( TYPE_FONT );
110 CASE_RETURN( TYPE_MEDIA );
111 CASE_RETURN( TYPE_WEBSOCKET );
112 CASE_RETURN( TYPE_CSP_REPORT );
113 CASE_RETURN( TYPE_XSLT );
114 CASE_RETURN( TYPE_BEACON );
115 CASE_RETURN( TYPE_FETCH );
116 CASE_RETURN( TYPE_IMAGESET );
117 CASE_RETURN( TYPE_WEB_MANIFEST );
118 CASE_RETURN( TYPE_INTERNAL_SCRIPT );
119 CASE_RETURN( TYPE_INTERNAL_WORKER );
120 CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER );
121 CASE_RETURN( TYPE_INTERNAL_EMBED );
122 CASE_RETURN( TYPE_INTERNAL_OBJECT );
123 CASE_RETURN( TYPE_INTERNAL_FRAME );
124 CASE_RETURN( TYPE_INTERNAL_IFRAME );
125 CASE_RETURN( TYPE_INTERNAL_AUDIO );
126 CASE_RETURN( TYPE_INTERNAL_VIDEO );
127 CASE_RETURN( TYPE_INTERNAL_TRACK );
128 CASE_RETURN( TYPE_INTERNAL_XMLHTTPREQUEST );
129 CASE_RETURN( TYPE_INTERNAL_EVENTSOURCE );
130 CASE_RETURN( TYPE_INTERNAL_SERVICE_WORKER );
131 CASE_RETURN( TYPE_INTERNAL_SCRIPT_PRELOAD );
132 CASE_RETURN( TYPE_INTERNAL_IMAGE );
133 CASE_RETURN( TYPE_INTERNAL_IMAGE_PRELOAD );
134 CASE_RETURN( TYPE_INTERNAL_IMAGE_FAVICON );
135 CASE_RETURN( TYPE_INTERNAL_STYLESHEET );
136 CASE_RETURN( TYPE_INTERNAL_STYLESHEET_PRELOAD );
137 default:
138 return "<Unknown Type>";
139 }
140 }
141
142 #undef CASE_RETURN
143
144 /* Passes on parameters from its "caller"'s context. */
145 #define CHECK_CONTENT_POLICY(action) \
146 PR_BEGIN_MACRO \
147 nsCOMPtr<nsIContentPolicy> policy = \
148 do_GetService(NS_CONTENTPOLICY_CONTRACTID); \
149 if (!policy) \
150 return NS_ERROR_FAILURE; \
151 \
152 return policy-> action (contentType, contentLocation, requestOrigin, \
153 context, mimeType, extra, originPrincipal, \
154 decision); \
155 PR_END_MACRO
156
157 /* Passes on parameters from its "caller"'s context. */
158 #define CHECK_CONTENT_POLICY_WITH_SERVICE(action, _policy) \
159 PR_BEGIN_MACRO \
160 return _policy-> action (contentType, contentLocation, requestOrigin, \
161 context, mimeType, extra, originPrincipal, \
162 decision); \
163 PR_END_MACRO
164
165 /**
166 * Check whether we can short-circuit this check and bail out. If not, get the
167 * origin URI to use.
168 *
169 * Note: requestOrigin is scoped outside the PR_BEGIN_MACRO/PR_END_MACRO on
170 * purpose */
171 #define CHECK_PRINCIPAL_AND_DATA(action) \
172 nsCOMPtr<nsIURI> requestOrigin; \
173 PR_BEGIN_MACRO \
174 if (originPrincipal) { \
175 nsCOMPtr<nsIScriptSecurityManager> secMan = aSecMan; \
176 if (!secMan) { \
177 secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); \
178 } \
179 if (secMan) { \
180 bool isSystem; \
181 nsresult rv = secMan->IsSystemPrincipal(originPrincipal, \
182 &isSystem); \
183 NS_ENSURE_SUCCESS(rv, rv); \
184 if (isSystem && contentType != nsIContentPolicy::TYPE_DOCUMENT) { \
185 *decision = nsIContentPolicy::ACCEPT; \
186 nsCOMPtr<nsINode> n = do_QueryInterface(context); \
187 if (!n) { \
188 nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(context);\
189 n = win ? win->GetExtantDoc() : nullptr; \
190 } \
191 if (n) { \
192 nsIDocument* d = n->OwnerDoc(); \
193 if (d->IsLoadedAsData() || d->IsBeingUsedAsImage() || \
194 d->IsResourceDoc()) { \
195 nsCOMPtr<nsIContentPolicy> dataPolicy = \
196 do_GetService( \
197 "@mozilla.org/data-document-content-policy;1"); \
198 if (dataPolicy) { \
199 nsContentPolicyType externalType = \
200 nsContentUtils::InternalContentPolicyTypeToExternal(contentType);\
201 dataPolicy-> action (externalType, contentLocation, \
202 requestOrigin, context, \
203 mimeType, extra, \
204 originPrincipal, decision); \
205 } \
206 } \
207 } \
208 return NS_OK; \
209 } \
210 } \
211 nsresult rv = originPrincipal->GetURI(getter_AddRefs(requestOrigin)); \
212 NS_ENSURE_SUCCESS(rv, rv); \
213 } \
214 PR_END_MACRO
215
216 /**
217 * Alias for calling ShouldLoad on the content policy service. Parameters are
218 * the same as nsIContentPolicy::shouldLoad, except for the originPrincipal
219 * parameter, which should be non-null if possible, and the last two
220 * parameters, which can be used to pass in pointer to some useful services if
221 * the caller already has them. The origin URI to pass to shouldLoad will be
222 * the URI of originPrincipal, unless originPrincipal is null (in which case a
223 * null origin URI will be passed).
224 */
225 inline nsresult
226 NS_CheckContentLoadPolicy(uint32_t contentType,
227 nsIURI *contentLocation,
228 nsIPrincipal *originPrincipal,
229 nsISupports *context,
230 const nsACString &mimeType,
231 nsISupports *extra,
232 int16_t *decision,
233 nsIContentPolicy *policyService = nullptr,
234 nsIScriptSecurityManager* aSecMan = nullptr)
235 {
236 CHECK_PRINCIPAL_AND_DATA(ShouldLoad);
237 if (policyService) {
238 CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldLoad, policyService);
239 }
240 CHECK_CONTENT_POLICY(ShouldLoad);
241 }
242
243 /**
244 * Alias for calling ShouldProcess on the content policy service. Parameters
245 * are the same as nsIContentPolicy::shouldLoad, except for the originPrincipal
246 * parameter, which should be non-null if possible, and the last two
247 * parameters, which can be used to pass in pointer to some useful services if
248 * the caller already has them. The origin URI to pass to shouldLoad will be
249 * the URI of originPrincipal, unless originPrincipal is null (in which case a
250 * null origin URI will be passed).
251 */
252 inline nsresult
253 NS_CheckContentProcessPolicy(uint32_t contentType,
254 nsIURI *contentLocation,
255 nsIPrincipal *originPrincipal,
256 nsISupports *context,
257 const nsACString &mimeType,
258 nsISupports *extra,
259 int16_t *decision,
260 nsIContentPolicy *policyService = nullptr,
261 nsIScriptSecurityManager* aSecMan = nullptr)
262 {
263 CHECK_PRINCIPAL_AND_DATA(ShouldProcess);
264 if (policyService) {
265 CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldProcess, policyService);
266 }
267 CHECK_CONTENT_POLICY(ShouldProcess);
268 }
269
270 #undef CHECK_CONTENT_POLICY
271 #undef CHECK_CONTENT_POLICY_WITH_SERVICE
272
273 /**
274 * Helper function to get an nsIDocShell given a context.
275 * If the context is a document or window, the corresponding docshell will be
276 * returned.
277 * If the context is a non-document DOM node, the docshell of its ownerDocument
278 * will be returned.
279 *
280 * @param aContext the context to find a docshell for (can be null)
281 *
282 * @return a WEAK pointer to the docshell, or nullptr if it could
283 * not be obtained
284 *
285 * @note As of this writing, calls to nsIContentPolicy::Should{Load,Process}
286 * for TYPE_DOCUMENT and TYPE_SUBDOCUMENT pass in an aContext that either
287 * points to the frameElement of the window the load is happening in
288 * (in which case NS_CP_GetDocShellFromContext will return the parent of the
289 * docshell the load is happening in), or points to the window the load is
290 * happening in (in which case NS_CP_GetDocShellFromContext will return
291 * the docshell the load is happening in). It's up to callers to QI aContext
292 * and handle things accordingly if they want the docshell the load is
293 * happening in. These are somewhat odd semantics, and bug 466687 has been
294 * filed to consider improving them.
295 */
296 inline nsIDocShell*
NS_CP_GetDocShellFromContext(nsISupports * aContext)297 NS_CP_GetDocShellFromContext(nsISupports *aContext)
298 {
299 if (!aContext) {
300 return nullptr;
301 }
302
303 nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aContext);
304
305 if (!window) {
306 // our context might be a document (which also QIs to nsIDOMNode), so
307 // try that first
308 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aContext);
309 if (!doc) {
310 // we were not a document after all, get our ownerDocument,
311 // hopefully
312 nsCOMPtr<nsIContent> content = do_QueryInterface(aContext);
313 if (content) {
314 doc = content->OwnerDoc();
315 }
316 }
317
318 if (doc) {
319 if (doc->GetDisplayDocument()) {
320 doc = doc->GetDisplayDocument();
321 }
322
323 window = doc->GetWindow();
324 }
325 }
326
327 if (!window) {
328 return nullptr;
329 }
330
331 return window->GetDocShell();
332 }
333
334 #endif /* __nsContentPolicyUtils_h__ */
335