Line data Source code
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/UniquePtr.h"
13 : #include "mozilla/dom/BindingUtils.h"
14 : #include "mozilla/dom/NonRefcountedDOMObject.h"
15 : #include "mozilla/dom/TypedArray.h"
16 : #include "jsapi.h"
17 : #include "nsISupports.h"
18 : #include "nsTArray.h"
19 : #include "nsWrapperCache.h"
20 : #include "nsAutoPtr.h"
21 :
22 : namespace mozilla {
23 : namespace dom {
24 :
25 : class Promise;
26 :
27 : // If ToJSValue returns false, it must set an exception on the
28 : // JSContext.
29 :
30 : // Accept strings.
31 : MOZ_MUST_USE bool
32 : ToJSValue(JSContext* aCx,
33 : const nsAString& aArgument,
34 : JS::MutableHandle<JS::Value> aValue);
35 :
36 : // Accept booleans. But be careful here: if we just have a function that takes
37 : // a boolean argument, then any pointer that doesn't match one of our other
38 : // signatures/templates will get treated as a boolean, which is clearly not
39 : // desirable. So make this a template that only gets used if the argument type
40 : // is actually boolean
41 : template<typename T>
42 : MOZ_MUST_USE
43 : typename EnableIf<IsSame<T, bool>::value, bool>::Type
44 0 : ToJSValue(JSContext* aCx,
45 : T aArgument,
46 : JS::MutableHandle<JS::Value> aValue)
47 : {
48 : // Make sure we're called in a compartment
49 0 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
50 :
51 0 : aValue.setBoolean(aArgument);
52 0 : return true;
53 : }
54 :
55 : // Accept integer types
56 : inline bool
57 0 : ToJSValue(JSContext* aCx,
58 : int32_t aArgument,
59 : JS::MutableHandle<JS::Value> aValue)
60 : {
61 : // Make sure we're called in a compartment
62 0 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
63 :
64 0 : aValue.setInt32(aArgument);
65 0 : return true;
66 : }
67 :
68 : inline bool
69 0 : ToJSValue(JSContext* aCx,
70 : uint32_t aArgument,
71 : JS::MutableHandle<JS::Value> aValue)
72 : {
73 : // Make sure we're called in a compartment
74 0 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
75 :
76 0 : aValue.setNumber(aArgument);
77 0 : return true;
78 : }
79 :
80 : inline bool
81 : ToJSValue(JSContext* aCx,
82 : int64_t aArgument,
83 : JS::MutableHandle<JS::Value> aValue)
84 : {
85 : // Make sure we're called in a compartment
86 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
87 :
88 : aValue.setNumber(double(aArgument));
89 : return true;
90 : }
91 :
92 : inline bool
93 : ToJSValue(JSContext* aCx,
94 : uint64_t aArgument,
95 : JS::MutableHandle<JS::Value> aValue)
96 : {
97 : // Make sure we're called in a compartment
98 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
99 :
100 : aValue.setNumber(double(aArgument));
101 : return true;
102 : }
103 :
104 : // accept floating point types
105 : inline bool
106 : ToJSValue(JSContext* aCx,
107 : float aArgument,
108 : JS::MutableHandle<JS::Value> aValue)
109 : {
110 : // Make sure we're called in a compartment
111 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
112 :
113 : aValue.setNumber(aArgument);
114 : return true;
115 : }
116 :
117 : inline bool
118 0 : ToJSValue(JSContext* aCx,
119 : double aArgument,
120 : JS::MutableHandle<JS::Value> aValue)
121 : {
122 : // Make sure we're called in a compartment
123 0 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
124 :
125 0 : aValue.setNumber(aArgument);
126 0 : return true;
127 : }
128 :
129 : // Accept CallbackObjects
130 : MOZ_MUST_USE inline bool
131 : ToJSValue(JSContext* aCx,
132 : CallbackObject& aArgument,
133 : JS::MutableHandle<JS::Value> aValue)
134 : {
135 : // Make sure we're called in a compartment
136 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
137 :
138 : aValue.setObjectOrNull(aArgument.Callback(aCx));
139 :
140 : return MaybeWrapValue(aCx, aValue);
141 : }
142 :
143 : // Accept objects that inherit from nsWrapperCache (e.g. most
144 : // DOM objects).
145 : template <class T>
146 : MOZ_MUST_USE
147 : typename EnableIf<IsBaseOf<nsWrapperCache, T>::value, bool>::Type
148 210 : ToJSValue(JSContext* aCx,
149 : T& aArgument,
150 : JS::MutableHandle<JS::Value> aValue)
151 : {
152 : // Make sure we're called in a compartment
153 210 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
154 :
155 420 : return GetOrCreateDOMReflector(aCx, aArgument, aValue);
156 : }
157 :
158 : // Accept non-refcounted DOM objects that do not inherit from
159 : // nsWrapperCache. Refcounted ones would be too much of a footgun:
160 : // you could convert them to JS twice and get two different objects.
161 : namespace binding_detail {
162 : template<class T>
163 : MOZ_MUST_USE
164 : typename EnableIf<IsBaseOf<NonRefcountedDOMObject, T>::value, bool>::Type
165 : ToJSValueFromPointerHelper(JSContext* aCx,
166 : T* aArgument,
167 : JS::MutableHandle<JS::Value> aValue)
168 : {
169 : // Make sure we're called in a compartment
170 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
171 :
172 : // This is a cut-down version of
173 : // WrapNewBindingNonWrapperCachedObject that doesn't need to deal
174 : // with nearly as many cases.
175 : if (!aArgument) {
176 : aValue.setNull();
177 : return true;
178 : }
179 :
180 : JS::Rooted<JSObject*> obj(aCx);
181 : if (!aArgument->WrapObject(aCx, nullptr, &obj)) {
182 : return false;
183 : }
184 :
185 : aValue.setObject(*obj);
186 : return true;
187 : }
188 : } // namespace binding_detail
189 :
190 : // We can take a non-refcounted non-wrapper-cached DOM object that lives in an
191 : // nsAutoPtr.
192 : template<class T>
193 : MOZ_MUST_USE
194 : typename EnableIf<IsBaseOf<NonRefcountedDOMObject, T>::value, bool>::Type
195 : ToJSValue(JSContext* aCx,
196 : nsAutoPtr<T>&& aArgument,
197 : JS::MutableHandle<JS::Value> aValue)
198 : {
199 : if (!binding_detail::ToJSValueFromPointerHelper(aCx, aArgument.get(), aValue)) {
200 : return false;
201 : }
202 :
203 : // JS object took ownership
204 : aArgument.forget();
205 : return true;
206 : }
207 :
208 : // We can take a non-refcounted non-wrapper-cached DOM object that lives in a
209 : // UniquePtr.
210 : template<class T>
211 : MOZ_MUST_USE
212 : typename EnableIf<IsBaseOf<NonRefcountedDOMObject, T>::value, bool>::Type
213 : ToJSValue(JSContext* aCx,
214 : UniquePtr<T>&& aArgument,
215 : JS::MutableHandle<JS::Value> aValue)
216 : {
217 : if (!binding_detail::ToJSValueFromPointerHelper(aCx, aArgument.get(), aValue)) {
218 : return false;
219 : }
220 :
221 : // JS object took ownership
222 : Unused << aArgument.release();
223 : return true;
224 : }
225 :
226 : // Accept typed arrays built from appropriate nsTArray values
227 : template<typename T>
228 : MOZ_MUST_USE
229 : typename EnableIf<IsBaseOf<AllTypedArraysBase, T>::value, bool>::Type
230 0 : ToJSValue(JSContext* aCx,
231 : const TypedArrayCreator<T>& aArgument,
232 : JS::MutableHandle<JS::Value> aValue)
233 : {
234 : // Make sure we're called in a compartment
235 0 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
236 :
237 0 : JSObject* obj = aArgument.Create(aCx);
238 0 : if (!obj) {
239 : return false;
240 : }
241 0 : aValue.setObject(*obj);
242 0 : return true;
243 : }
244 :
245 : // Accept objects that inherit from nsISupports but not nsWrapperCache (e.g.
246 : // DOM File).
247 : template <class T>
248 : MOZ_MUST_USE
249 : typename EnableIf<!IsBaseOf<nsWrapperCache, T>::value &&
250 : !IsBaseOf<CallbackObject, T>::value &&
251 : IsBaseOf<nsISupports, T>::value, bool>::Type
252 61 : ToJSValue(JSContext* aCx,
253 : T& aArgument,
254 : JS::MutableHandle<JS::Value> aValue)
255 : {
256 : // Make sure we're called in a compartment
257 61 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
258 :
259 0 : xpcObjectHelper helper(ToSupports(&aArgument));
260 122 : JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
261 122 : return XPCOMObjectToJsval(aCx, scope, helper, nullptr, true, aValue);
262 : }
263 :
264 : // Accept nsRefPtr/nsCOMPtr
265 : template <typename T>
266 : MOZ_MUST_USE bool
267 0 : ToJSValue(JSContext* aCx,
268 : const nsCOMPtr<T>& aArgument,
269 : JS::MutableHandle<JS::Value> aValue)
270 : {
271 0 : return ToJSValue(aCx, *aArgument.get(), aValue);
272 : }
273 :
274 : template <typename T>
275 : MOZ_MUST_USE bool
276 79 : ToJSValue(JSContext* aCx,
277 : const RefPtr<T>& aArgument,
278 : JS::MutableHandle<JS::Value> aValue)
279 : {
280 79 : return ToJSValue(aCx, *aArgument.get(), aValue);
281 : }
282 :
283 : template <typename T>
284 : MOZ_MUST_USE bool
285 0 : ToJSValue(JSContext* aCx,
286 : const NonNull<T>& aArgument,
287 : JS::MutableHandle<JS::Value> aValue)
288 : {
289 0 : return ToJSValue(aCx, *aArgument.get(), aValue);
290 : }
291 :
292 : // Accept WebIDL dictionaries
293 : template <class T>
294 : MOZ_MUST_USE
295 : typename EnableIf<IsBaseOf<DictionaryBase, T>::value, bool>::Type
296 : ToJSValue(JSContext* aCx,
297 : const T& aArgument,
298 : JS::MutableHandle<JS::Value> aValue)
299 : {
300 0 : return aArgument.ToObjectInternal(aCx, aValue);
301 : }
302 :
303 : // Accept existing JS values (which may not be same-compartment with us
304 : MOZ_MUST_USE inline bool
305 : ToJSValue(JSContext* aCx, const JS::Value& aArgument,
306 : JS::MutableHandle<JS::Value> aValue)
307 : {
308 : aValue.set(aArgument);
309 : return MaybeWrapValue(aCx, aValue);
310 : }
311 : MOZ_MUST_USE inline bool
312 0 : ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
313 : JS::MutableHandle<JS::Value> aValue)
314 : {
315 0 : aValue.set(aArgument);
316 0 : return MaybeWrapValue(aCx, aValue);
317 : }
318 :
319 : // Accept existing JS values on the Heap (which may not be same-compartment with us
320 : MOZ_MUST_USE inline bool
321 0 : ToJSValue(JSContext* aCx, const JS::Heap<JS::Value>& aArgument,
322 : JS::MutableHandle<JS::Value> aValue)
323 : {
324 0 : aValue.set(aArgument);
325 0 : return MaybeWrapValue(aCx, aValue);
326 : }
327 :
328 : // Accept existing rooted JS values (which may not be same-compartment with us
329 : MOZ_MUST_USE inline bool
330 0 : ToJSValue(JSContext* aCx, const JS::Rooted<JS::Value>& aArgument,
331 : JS::MutableHandle<JS::Value> aValue)
332 : {
333 50 : aValue.set(aArgument);
334 0 : return MaybeWrapValue(aCx, aValue);
335 : }
336 :
337 : // Accept existing rooted JS objects (which may not be same-compartment with
338 : // us).
339 : MOZ_MUST_USE inline bool
340 0 : ToJSValue(JSContext* aCx, const JS::Rooted<JSObject*>& aArgument,
341 : JS::MutableHandle<JS::Value> aValue)
342 : {
343 0 : aValue.setObjectOrNull(aArgument);
344 0 : return MaybeWrapObjectOrNullValue(aCx, aValue);
345 : }
346 :
347 : // Accept nsresult, for use in rejections, and create an XPCOM
348 : // exception object representing that nsresult.
349 : MOZ_MUST_USE bool
350 : ToJSValue(JSContext* aCx,
351 : nsresult aArgument,
352 : JS::MutableHandle<JS::Value> aValue);
353 :
354 : // Accept ErrorResult, for use in rejections, and create an exception
355 : // representing the failure. Note, the ErrorResult must indicate a failure
356 : // with aArgument.Failure() returning true.
357 : MOZ_MUST_USE bool
358 : ToJSValue(JSContext* aCx,
359 : ErrorResult& aArgument,
360 : JS::MutableHandle<JS::Value> aValue);
361 :
362 : // Accept owning WebIDL unions.
363 : template <typename T>
364 : MOZ_MUST_USE
365 : typename EnableIf<IsBaseOf<AllOwningUnionBase, T>::value, bool>::Type
366 0 : ToJSValue(JSContext* aCx,
367 : const T& aArgument,
368 : JS::MutableHandle<JS::Value> aValue)
369 : {
370 0 : JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
371 0 : return aArgument.ToJSVal(aCx, global, aValue);
372 : }
373 :
374 : // Accept pointers to other things we accept
375 : template <typename T>
376 : MOZ_MUST_USE
377 : typename EnableIf<IsPointer<T>::value, bool>::Type
378 : ToJSValue(JSContext* aCx,
379 : T aArgument,
380 : JS::MutableHandle<JS::Value> aValue)
381 : {
382 0 : return ToJSValue(aCx, *aArgument, aValue);
383 : }
384 :
385 : // Accept Promise objects, which need special handling.
386 : MOZ_MUST_USE bool
387 : ToJSValue(JSContext* aCx,
388 : Promise& aArgument,
389 : JS::MutableHandle<JS::Value> aValue);
390 :
391 : // Accept arrays (and nested arrays) of other things we accept
392 : template <typename T>
393 : MOZ_MUST_USE bool
394 : ToJSValue(JSContext* aCx,
395 : T* aArguments,
396 : size_t aLength,
397 : JS::MutableHandle<JS::Value> aValue);
398 :
399 : template <typename T>
400 : MOZ_MUST_USE bool
401 : ToJSValue(JSContext* aCx,
402 : const nsTArray<T>& aArgument,
403 : JS::MutableHandle<JS::Value> aValue)
404 : {
405 : return ToJSValue(aCx, aArgument.Elements(),
406 : aArgument.Length(), aValue);
407 : }
408 :
409 : template <typename T>
410 : MOZ_MUST_USE bool
411 : ToJSValue(JSContext* aCx,
412 : const FallibleTArray<T>& aArgument,
413 : JS::MutableHandle<JS::Value> aValue)
414 : {
415 : return ToJSValue(aCx, aArgument.Elements(),
416 : aArgument.Length(), aValue);
417 : }
418 :
419 : template <typename T, int N>
420 : MOZ_MUST_USE bool
421 : ToJSValue(JSContext* aCx,
422 : const T(&aArgument)[N],
423 : JS::MutableHandle<JS::Value> aValue)
424 : {
425 : return ToJSValue(aCx, aArgument, N, aValue);
426 : }
427 :
428 : // Accept arrays of other things we accept
429 : template <typename T>
430 : MOZ_MUST_USE bool
431 : ToJSValue(JSContext* aCx,
432 : T* aArguments,
433 : size_t aLength,
434 : JS::MutableHandle<JS::Value> aValue)
435 : {
436 : // Make sure we're called in a compartment
437 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
438 :
439 : JS::AutoValueVector v(aCx);
440 : if (!v.resize(aLength)) {
441 : return false;
442 : }
443 : for (size_t i = 0; i < aLength; ++i) {
444 : if (!ToJSValue(aCx, aArguments[i], v[i])) {
445 : return false;
446 : }
447 : }
448 : JSObject* arrayObj = JS_NewArrayObject(aCx, v);
449 : if (!arrayObj) {
450 : return false;
451 : }
452 : aValue.setObject(*arrayObj);
453 : return true;
454 : }
455 :
456 : } // namespace dom
457 : } // namespace mozilla
458 :
459 : #endif /* mozilla_dom_ToJSValue_h */
|