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 #include "nsStringFwd.h"
24
25 // XXXtw sadly, this makes consumers of nsContentPolicyUtils depend on widget
26 #include "nsIDocument.h"
27 #include "nsPIDOMWindow.h"
28
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_CONTENTPOLICY_CID \
34 { \
35 0x0e3afd3d, 0xeb60, 0x4c2b, { \
36 0x96, 0x3b, 0x56, 0xd7, 0xc4, 0x39, 0xf1, 0x24 \
37 } \
38 }
39
40 /**
41 * Evaluates to true if val is ACCEPT.
42 *
43 * @param val the status returned from shouldProcess/shouldLoad
44 */
45 #define NS_CP_ACCEPTED(val) ((val) == nsIContentPolicy::ACCEPT)
46
47 /**
48 * Evaluates to true if val is a REJECT_* status
49 *
50 * @param val the status returned from shouldProcess/shouldLoad
51 */
52 #define NS_CP_REJECTED(val) ((val) != nsIContentPolicy::ACCEPT)
53
54 // Offer convenient translations of constants -> const char*
55
56 // convenience macro to reduce some repetative typing...
57 // name is the name of a constant from this interface
58 #define CASE_RETURN(name) \
59 case nsIContentPolicy::name: \
60 return #name
61
62 /**
63 * Returns a string corresponding to the name of the response constant, or
64 * "<Unknown Response>" if an unknown response value is given.
65 *
66 * The return value is static and must not be freed.
67 *
68 * @param response the response code
69 * @return the name of the given response code
70 */
NS_CP_ResponseName(int16_t response)71 inline const char *NS_CP_ResponseName(int16_t response) {
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 */
NS_CP_ContentTypeName(uint32_t contentType)92 inline const char *NS_CP_ContentTypeName(uint32_t contentType) {
93 switch (contentType) {
94 CASE_RETURN(TYPE_OTHER);
95 CASE_RETURN(TYPE_SCRIPT);
96 CASE_RETURN(TYPE_IMAGE);
97 CASE_RETURN(TYPE_STYLESHEET);
98 CASE_RETURN(TYPE_OBJECT);
99 CASE_RETURN(TYPE_DOCUMENT);
100 CASE_RETURN(TYPE_SUBDOCUMENT);
101 CASE_RETURN(TYPE_REFRESH);
102 CASE_RETURN(TYPE_XBL);
103 CASE_RETURN(TYPE_PING);
104 CASE_RETURN(TYPE_XMLHTTPREQUEST);
105 CASE_RETURN(TYPE_OBJECT_SUBREQUEST);
106 CASE_RETURN(TYPE_DTD);
107 CASE_RETURN(TYPE_FONT);
108 CASE_RETURN(TYPE_MEDIA);
109 CASE_RETURN(TYPE_WEBSOCKET);
110 CASE_RETURN(TYPE_CSP_REPORT);
111 CASE_RETURN(TYPE_XSLT);
112 CASE_RETURN(TYPE_BEACON);
113 CASE_RETURN(TYPE_FETCH);
114 CASE_RETURN(TYPE_IMAGESET);
115 CASE_RETURN(TYPE_WEB_MANIFEST);
116 CASE_RETURN(TYPE_SAVEAS_DOWNLOAD);
117 CASE_RETURN(TYPE_INTERNAL_SCRIPT);
118 CASE_RETURN(TYPE_INTERNAL_WORKER);
119 CASE_RETURN(TYPE_INTERNAL_SHARED_WORKER);
120 CASE_RETURN(TYPE_INTERNAL_EMBED);
121 CASE_RETURN(TYPE_INTERNAL_OBJECT);
122 CASE_RETURN(TYPE_INTERNAL_FRAME);
123 CASE_RETURN(TYPE_INTERNAL_IFRAME);
124 CASE_RETURN(TYPE_INTERNAL_AUDIO);
125 CASE_RETURN(TYPE_INTERNAL_VIDEO);
126 CASE_RETURN(TYPE_INTERNAL_TRACK);
127 CASE_RETURN(TYPE_INTERNAL_XMLHTTPREQUEST);
128 CASE_RETURN(TYPE_INTERNAL_EVENTSOURCE);
129 CASE_RETURN(TYPE_INTERNAL_SERVICE_WORKER);
130 CASE_RETURN(TYPE_INTERNAL_SCRIPT_PRELOAD);
131 CASE_RETURN(TYPE_INTERNAL_IMAGE);
132 CASE_RETURN(TYPE_INTERNAL_IMAGE_PRELOAD);
133 CASE_RETURN(TYPE_INTERNAL_IMAGE_FAVICON);
134 CASE_RETURN(TYPE_INTERNAL_STYLESHEET);
135 CASE_RETURN(TYPE_INTERNAL_STYLESHEET_PRELOAD);
136 CASE_RETURN(TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS);
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) return NS_ERROR_FAILURE; \
150 \
151 return policy->action(contentType, contentLocation, requestOrigin, context, \
152 mimeType, extra, triggeringPrincipal, decision); \
153 PR_END_MACRO
154
155 /* Passes on parameters from its "caller"'s context. */
156 #define CHECK_CONTENT_POLICY_WITH_SERVICE(action, _policy) \
157 PR_BEGIN_MACRO \
158 return _policy->action(contentType, contentLocation, requestOrigin, context, \
159 mimeType, extra, triggeringPrincipal, decision); \
160 PR_END_MACRO
161
162 /**
163 * Check whether we can short-circuit this check and bail out. If not, get the
164 * origin URI to use.
165 *
166 * Note: requestOrigin is scoped outside the PR_BEGIN_MACRO/PR_END_MACRO on
167 * purpose */
168 #define CHECK_PRINCIPAL_AND_DATA(action) \
169 nsCOMPtr<nsIURI> requestOrigin; \
170 PR_BEGIN_MACRO \
171 if (loadingPrincipal) { \
172 /* We exempt most loads into any document with the system principal \
173 * from content policy checks, mostly as an optimization. Which means \
174 * that we need to apply this check to the loading principal, not the \
175 * principal that triggered the load. */ \
176 bool isSystem = loadingPrincipal->GetIsSystemPrincipal(); \
177 if (isSystem && contentType != nsIContentPolicy::TYPE_DOCUMENT) { \
178 *decision = nsIContentPolicy::ACCEPT; \
179 nsCOMPtr<nsINode> n = do_QueryInterface(context); \
180 if (!n) { \
181 nsCOMPtr<nsPIDOMWindowOuter> win = do_QueryInterface(context); \
182 n = win ? win->GetExtantDoc() : nullptr; \
183 } \
184 if (n) { \
185 nsIDocument *d = n->OwnerDoc(); \
186 if (d->IsLoadedAsData() || d->IsBeingUsedAsImage() || \
187 d->IsResourceDoc()) { \
188 nsCOMPtr<nsIContentPolicy> dataPolicy = \
189 do_GetService("@mozilla.org/data-document-content-policy;1"); \
190 if (dataPolicy) { \
191 nsContentPolicyType externalType = \
192 nsContentUtils::InternalContentPolicyTypeToExternal( \
193 contentType); \
194 dataPolicy->action(externalType, contentLocation, requestOrigin, \
195 context, mimeType, extra, triggeringPrincipal, \
196 decision); \
197 } \
198 } \
199 } \
200 return NS_OK; \
201 } \
202 nsresult rv = loadingPrincipal->GetURI(getter_AddRefs(requestOrigin)); \
203 NS_ENSURE_SUCCESS(rv, rv); \
204 } \
205 PR_END_MACRO
206
207 /**
208 * Alias for calling ShouldLoad on the content policy service. Parameters are
209 * the same as nsIContentPolicy::shouldLoad, except for the loadingPrincipal
210 * and triggeringPrincipal parameters (which should be non-null if possible,
211 * and have the same semantics as in nsLoadInfo), and the last parameter,
212 * which can be used to pass in a pointer to a useful service if the caller
213 * already has it. The origin URI to pass to shouldLoad will be the URI of
214 * loadingPrincipal, unless loadingPrincipal is null (in which case a null
215 * origin URI will be passed).
216 */
217 inline nsresult NS_CheckContentLoadPolicy(
218 uint32_t contentType, nsIURI *contentLocation,
219 nsIPrincipal *loadingPrincipal, nsIPrincipal *triggeringPrincipal,
220 nsISupports *context, const nsACString &mimeType, nsISupports *extra,
221 int16_t *decision, nsIContentPolicy *policyService = nullptr) {
222 CHECK_PRINCIPAL_AND_DATA(ShouldLoad);
223 if (policyService) {
224 CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldLoad, policyService);
225 }
226 CHECK_CONTENT_POLICY(ShouldLoad);
227 }
228
229 /**
230 * Alias for calling ShouldProcess on the content policy service. Parameters
231 * are the same as nsIContentPolicy::shouldLoad, except for the and
232 * triggeringPrincipal parameters (which should be non-null if possible, and
233 * have the same semantics as in nsLoadInfo), and the last parameter, which
234 * can be used to pass in a pointer to a useful service if the caller already
235 * has it. The origin URI to pass to shouldLoad will be the URI of
236 * loadingPrincipal, unless loadingPrincipal is null (in which case a null
237 * origin URI will be passed).
238 */
239 inline nsresult NS_CheckContentProcessPolicy(
240 uint32_t contentType, nsIURI *contentLocation,
241 nsIPrincipal *loadingPrincipal, nsIPrincipal *triggeringPrincipal,
242 nsISupports *context, const nsACString &mimeType, nsISupports *extra,
243 int16_t *decision, nsIContentPolicy *policyService = nullptr) {
244 CHECK_PRINCIPAL_AND_DATA(ShouldProcess);
245 if (policyService) {
246 CHECK_CONTENT_POLICY_WITH_SERVICE(ShouldProcess, policyService);
247 }
248 CHECK_CONTENT_POLICY(ShouldProcess);
249 }
250
251 #undef CHECK_CONTENT_POLICY
252 #undef CHECK_CONTENT_POLICY_WITH_SERVICE
253
254 /**
255 * Helper function to get an nsIDocShell given a context.
256 * If the context is a document or window, the corresponding docshell will be
257 * returned.
258 * If the context is a non-document DOM node, the docshell of its ownerDocument
259 * will be returned.
260 *
261 * @param aContext the context to find a docshell for (can be null)
262 *
263 * @return a WEAK pointer to the docshell, or nullptr if it could
264 * not be obtained
265 *
266 * @note As of this writing, calls to nsIContentPolicy::Should{Load,Process}
267 * for TYPE_DOCUMENT and TYPE_SUBDOCUMENT pass in an aContext that either
268 * points to the frameElement of the window the load is happening in
269 * (in which case NS_CP_GetDocShellFromContext will return the parent of the
270 * docshell the load is happening in), or points to the window the load is
271 * happening in (in which case NS_CP_GetDocShellFromContext will return
272 * the docshell the load is happening in). It's up to callers to QI aContext
273 * and handle things accordingly if they want the docshell the load is
274 * happening in. These are somewhat odd semantics, and bug 466687 has been
275 * filed to consider improving them.
276 */
NS_CP_GetDocShellFromContext(nsISupports * aContext)277 inline nsIDocShell *NS_CP_GetDocShellFromContext(nsISupports *aContext) {
278 if (!aContext) {
279 return nullptr;
280 }
281
282 nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aContext);
283
284 if (!window) {
285 // our context might be a document (which also QIs to nsIDOMNode), so
286 // try that first
287 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aContext);
288 if (!doc) {
289 // we were not a document after all, get our ownerDocument,
290 // hopefully
291 nsCOMPtr<nsIContent> content = do_QueryInterface(aContext);
292 if (content) {
293 doc = content->OwnerDoc();
294 }
295 }
296
297 if (doc) {
298 if (doc->GetDisplayDocument()) {
299 doc = doc->GetDisplayDocument();
300 }
301
302 window = doc->GetWindow();
303 }
304 }
305
306 if (!window) {
307 return nullptr;
308 }
309
310 return window->GetDocShell();
311 }
312
313 #endif /* __nsContentPolicyUtils_h__ */
314