1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef mozilla_dom_ToJSValue_h
8 #define mozilla_dom_ToJSValue_h
9
10 #include "mozilla/TypeTraits.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/dom/BindingUtils.h"
13 #include "mozilla/dom/TypedArray.h"
14 #include "jsapi.h"
15 #include "nsISupports.h"
16 #include "nsTArray.h"
17 #include "nsWrapperCache.h"
18
19 namespace mozilla {
20 namespace dom {
21
22 class Promise;
23
24 // If ToJSValue returns false, it must set an exception on the
25 // JSContext.
26
27 // Accept strings.
28 MOZ_MUST_USE bool
29 ToJSValue(JSContext* aCx,
30 const nsAString& aArgument,
31 JS::MutableHandle<JS::Value> aValue);
32
33 // Accept booleans. But be careful here: if we just have a function that takes
34 // a boolean argument, then any pointer that doesn't match one of our other
35 // signatures/templates will get treated as a boolean, which is clearly not
36 // desirable. So make this a template that only gets used if the argument type
37 // is actually boolean
38 template<typename T>
39 MOZ_MUST_USE
40 typename EnableIf<IsSame<T, bool>::value, bool>::Type
ToJSValue(JSContext * aCx,T aArgument,JS::MutableHandle<JS::Value> aValue)41 ToJSValue(JSContext* aCx,
42 T aArgument,
43 JS::MutableHandle<JS::Value> aValue)
44 {
45 // Make sure we're called in a compartment
46 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
47
48 aValue.setBoolean(aArgument);
49 return true;
50 }
51
52 // Accept integer types
53 inline bool
ToJSValue(JSContext * aCx,int32_t aArgument,JS::MutableHandle<JS::Value> aValue)54 ToJSValue(JSContext* aCx,
55 int32_t aArgument,
56 JS::MutableHandle<JS::Value> aValue)
57 {
58 // Make sure we're called in a compartment
59 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
60
61 aValue.setInt32(aArgument);
62 return true;
63 }
64
65 inline bool
ToJSValue(JSContext * aCx,uint32_t aArgument,JS::MutableHandle<JS::Value> aValue)66 ToJSValue(JSContext* aCx,
67 uint32_t aArgument,
68 JS::MutableHandle<JS::Value> aValue)
69 {
70 // Make sure we're called in a compartment
71 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
72
73 aValue.setNumber(aArgument);
74 return true;
75 }
76
77 inline bool
ToJSValue(JSContext * aCx,int64_t aArgument,JS::MutableHandle<JS::Value> aValue)78 ToJSValue(JSContext* aCx,
79 int64_t aArgument,
80 JS::MutableHandle<JS::Value> aValue)
81 {
82 // Make sure we're called in a compartment
83 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
84
85 aValue.setNumber(double(aArgument));
86 return true;
87 }
88
89 inline bool
ToJSValue(JSContext * aCx,uint64_t aArgument,JS::MutableHandle<JS::Value> aValue)90 ToJSValue(JSContext* aCx,
91 uint64_t aArgument,
92 JS::MutableHandle<JS::Value> aValue)
93 {
94 // Make sure we're called in a compartment
95 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
96
97 aValue.setNumber(double(aArgument));
98 return true;
99 }
100
101 // accept floating point types
102 inline bool
ToJSValue(JSContext * aCx,float aArgument,JS::MutableHandle<JS::Value> aValue)103 ToJSValue(JSContext* aCx,
104 float aArgument,
105 JS::MutableHandle<JS::Value> aValue)
106 {
107 // Make sure we're called in a compartment
108 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
109
110 aValue.setNumber(aArgument);
111 return true;
112 }
113
114 inline bool
ToJSValue(JSContext * aCx,double aArgument,JS::MutableHandle<JS::Value> aValue)115 ToJSValue(JSContext* aCx,
116 double aArgument,
117 JS::MutableHandle<JS::Value> aValue)
118 {
119 // Make sure we're called in a compartment
120 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
121
122 aValue.setNumber(aArgument);
123 return true;
124 }
125
126 // Accept CallbackObjects
127 MOZ_MUST_USE inline bool
ToJSValue(JSContext * aCx,CallbackObject & aArgument,JS::MutableHandle<JS::Value> aValue)128 ToJSValue(JSContext* aCx,
129 CallbackObject& aArgument,
130 JS::MutableHandle<JS::Value> aValue)
131 {
132 // Make sure we're called in a compartment
133 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
134
135 aValue.setObject(*aArgument.Callback());
136
137 return MaybeWrapValue(aCx, aValue);
138 }
139
140 // Accept objects that inherit from nsWrapperCache (e.g. most
141 // DOM objects).
142 template <class T>
143 MOZ_MUST_USE
144 typename EnableIf<IsBaseOf<nsWrapperCache, T>::value, bool>::Type
ToJSValue(JSContext * aCx,T & aArgument,JS::MutableHandle<JS::Value> aValue)145 ToJSValue(JSContext* aCx,
146 T& aArgument,
147 JS::MutableHandle<JS::Value> aValue)
148 {
149 // Make sure we're called in a compartment
150 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
151 // Make sure non-webidl objects don't sneak in here
152 MOZ_ASSERT(aArgument.IsDOMBinding());
153
154 return GetOrCreateDOMReflector(aCx, aArgument, aValue);
155 }
156
157 // Accept typed arrays built from appropriate nsTArray values
158 template<typename T>
159 MOZ_MUST_USE
160 typename EnableIf<IsBaseOf<AllTypedArraysBase, T>::value, bool>::Type
ToJSValue(JSContext * aCx,const TypedArrayCreator<T> & aArgument,JS::MutableHandle<JS::Value> aValue)161 ToJSValue(JSContext* aCx,
162 const TypedArrayCreator<T>& aArgument,
163 JS::MutableHandle<JS::Value> aValue)
164 {
165 // Make sure we're called in a compartment
166 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
167
168 JSObject* obj = aArgument.Create(aCx);
169 if (!obj) {
170 return false;
171 }
172 aValue.setObject(*obj);
173 return true;
174 }
175
176 // Accept objects that inherit from nsISupports but not nsWrapperCache (e.g.
177 // DOM File).
178 template <class T>
179 MOZ_MUST_USE
180 typename EnableIf<!IsBaseOf<nsWrapperCache, T>::value &&
181 !IsBaseOf<CallbackObject, T>::value &&
182 IsBaseOf<nsISupports, T>::value, bool>::Type
ToJSValue(JSContext * aCx,T & aArgument,JS::MutableHandle<JS::Value> aValue)183 ToJSValue(JSContext* aCx,
184 T& aArgument,
185 JS::MutableHandle<JS::Value> aValue)
186 {
187 // Make sure we're called in a compartment
188 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
189
190 qsObjectHelper helper(ToSupports(&aArgument), nullptr);
191 JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
192 return XPCOMObjectToJsval(aCx, scope, helper, nullptr, true, aValue);
193 }
194
195 // Accept nsRefPtr/nsCOMPtr
196 template <typename T>
197 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,const nsCOMPtr<T> & aArgument,JS::MutableHandle<JS::Value> aValue)198 ToJSValue(JSContext* aCx,
199 const nsCOMPtr<T>& aArgument,
200 JS::MutableHandle<JS::Value> aValue)
201 {
202 return ToJSValue(aCx, *aArgument.get(), aValue);
203 }
204
205 template <typename T>
206 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,const RefPtr<T> & aArgument,JS::MutableHandle<JS::Value> aValue)207 ToJSValue(JSContext* aCx,
208 const RefPtr<T>& aArgument,
209 JS::MutableHandle<JS::Value> aValue)
210 {
211 return ToJSValue(aCx, *aArgument.get(), aValue);
212 }
213
214 template <typename T>
215 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,const NonNull<T> & aArgument,JS::MutableHandle<JS::Value> aValue)216 ToJSValue(JSContext* aCx,
217 const NonNull<T>& aArgument,
218 JS::MutableHandle<JS::Value> aValue)
219 {
220 return ToJSValue(aCx, *aArgument.get(), aValue);
221 }
222
223 // Accept WebIDL dictionaries
224 template <class T>
225 MOZ_MUST_USE
226 typename EnableIf<IsBaseOf<DictionaryBase, T>::value, bool>::Type
ToJSValue(JSContext * aCx,const T & aArgument,JS::MutableHandle<JS::Value> aValue)227 ToJSValue(JSContext* aCx,
228 const T& aArgument,
229 JS::MutableHandle<JS::Value> aValue)
230 {
231 return aArgument.ToObjectInternal(aCx, aValue);
232 }
233
234 // Accept existing JS values (which may not be same-compartment with us
235 MOZ_MUST_USE inline bool
ToJSValue(JSContext * aCx,JS::Handle<JS::Value> aArgument,JS::MutableHandle<JS::Value> aValue)236 ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
237 JS::MutableHandle<JS::Value> aValue)
238 {
239 aValue.set(aArgument);
240 return MaybeWrapValue(aCx, aValue);
241 }
242
243 // Accept existing JS values on the Heap (which may not be same-compartment with us
244 MOZ_MUST_USE inline bool
ToJSValue(JSContext * aCx,const JS::Heap<JS::Value> & aArgument,JS::MutableHandle<JS::Value> aValue)245 ToJSValue(JSContext* aCx, const JS::Heap<JS::Value>& aArgument,
246 JS::MutableHandle<JS::Value> aValue)
247 {
248 aValue.set(aArgument);
249 return MaybeWrapValue(aCx, aValue);
250 }
251
252 // Accept existing rooted JS values (which may not be same-compartment with us
253 MOZ_MUST_USE inline bool
ToJSValue(JSContext * aCx,const JS::Rooted<JS::Value> & aArgument,JS::MutableHandle<JS::Value> aValue)254 ToJSValue(JSContext* aCx, const JS::Rooted<JS::Value>& aArgument,
255 JS::MutableHandle<JS::Value> aValue)
256 {
257 aValue.set(aArgument);
258 return MaybeWrapValue(aCx, aValue);
259 }
260
261 // Accept existing rooted JS objects (which may not be same-compartment with
262 // us).
263 MOZ_MUST_USE inline bool
ToJSValue(JSContext * aCx,const JS::Rooted<JSObject * > & aArgument,JS::MutableHandle<JS::Value> aValue)264 ToJSValue(JSContext* aCx, const JS::Rooted<JSObject*>& aArgument,
265 JS::MutableHandle<JS::Value> aValue)
266 {
267 aValue.setObjectOrNull(aArgument);
268 return MaybeWrapObjectOrNullValue(aCx, aValue);
269 }
270
271 // Accept nsresult, for use in rejections, and create an XPCOM
272 // exception object representing that nsresult.
273 MOZ_MUST_USE bool
274 ToJSValue(JSContext* aCx,
275 nsresult aArgument,
276 JS::MutableHandle<JS::Value> aValue);
277
278 // Accept ErrorResult, for use in rejections, and create an exception
279 // representing the failure. Note, the ErrorResult must indicate a failure
280 // with aArgument.Failure() returning true.
281 MOZ_MUST_USE bool
282 ToJSValue(JSContext* aCx,
283 ErrorResult& aArgument,
284 JS::MutableHandle<JS::Value> aValue);
285
286 // Accept owning WebIDL unions.
287 template <typename T>
288 MOZ_MUST_USE
289 typename EnableIf<IsBaseOf<AllOwningUnionBase, T>::value, bool>::Type
ToJSValue(JSContext * aCx,const T & aArgument,JS::MutableHandle<JS::Value> aValue)290 ToJSValue(JSContext* aCx,
291 const T& aArgument,
292 JS::MutableHandle<JS::Value> aValue)
293 {
294 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
295 return aArgument.ToJSVal(aCx, global, aValue);
296 }
297
298 // Accept pointers to other things we accept
299 template <typename T>
300 MOZ_MUST_USE
301 typename EnableIf<IsPointer<T>::value, bool>::Type
ToJSValue(JSContext * aCx,T aArgument,JS::MutableHandle<JS::Value> aValue)302 ToJSValue(JSContext* aCx,
303 T aArgument,
304 JS::MutableHandle<JS::Value> aValue)
305 {
306 return ToJSValue(aCx, *aArgument, aValue);
307 }
308
309 #ifdef SPIDERMONKEY_PROMISE
310 // Accept Promise objects, which need special handling.
311 MOZ_MUST_USE bool
312 ToJSValue(JSContext* aCx,
313 Promise& aArgument,
314 JS::MutableHandle<JS::Value> aValue);
315 #endif // SPIDERMONKEY_PROMISE
316
317 // Accept arrays of other things we accept
318 template <typename T>
319 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,T * aArguments,size_t aLength,JS::MutableHandle<JS::Value> aValue)320 ToJSValue(JSContext* aCx,
321 T* aArguments,
322 size_t aLength,
323 JS::MutableHandle<JS::Value> aValue)
324 {
325 // Make sure we're called in a compartment
326 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
327
328 JS::AutoValueVector v(aCx);
329 if (!v.resize(aLength)) {
330 return false;
331 }
332 for (size_t i = 0; i < aLength; ++i) {
333 if (!ToJSValue(aCx, aArguments[i], v[i])) {
334 return false;
335 }
336 }
337 JSObject* arrayObj = JS_NewArrayObject(aCx, v);
338 if (!arrayObj) {
339 return false;
340 }
341 aValue.setObject(*arrayObj);
342 return true;
343 }
344
345 template <typename T>
346 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,const nsTArray<T> & aArgument,JS::MutableHandle<JS::Value> aValue)347 ToJSValue(JSContext* aCx,
348 const nsTArray<T>& aArgument,
349 JS::MutableHandle<JS::Value> aValue)
350 {
351 return ToJSValue(aCx, aArgument.Elements(),
352 aArgument.Length(), aValue);
353 }
354
355 template <typename T>
356 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,const FallibleTArray<T> & aArgument,JS::MutableHandle<JS::Value> aValue)357 ToJSValue(JSContext* aCx,
358 const FallibleTArray<T>& aArgument,
359 JS::MutableHandle<JS::Value> aValue)
360 {
361 return ToJSValue(aCx, aArgument.Elements(),
362 aArgument.Length(), aValue);
363 }
364
365 template <typename T, int N>
366 MOZ_MUST_USE bool
ToJSValue(JSContext * aCx,const T (& aArgument)[N],JS::MutableHandle<JS::Value> aValue)367 ToJSValue(JSContext* aCx,
368 const T(&aArgument)[N],
369 JS::MutableHandle<JS::Value> aValue)
370 {
371 return ToJSValue(aCx, aArgument, N, aValue);
372 }
373
374 } // namespace dom
375 } // namespace mozilla
376
377 #endif /* mozilla_dom_ToJSValue_h */
378