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