|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
|
2 /* vim: set ts=2 sw=2 et tw=79: */ |
|
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_BindingUtils_h__ |
|
8 #define mozilla_dom_BindingUtils_h__ |
|
9 |
|
10 #include "jsfriendapi.h" |
|
11 #include "jswrapper.h" |
|
12 #include "mozilla/ArrayUtils.h" |
|
13 #include "mozilla/Alignment.h" |
|
14 #include "mozilla/Array.h" |
|
15 #include "mozilla/dom/BindingDeclarations.h" |
|
16 #include "mozilla/dom/CallbackObject.h" |
|
17 #include "mozilla/dom/DOMJSClass.h" |
|
18 #include "mozilla/dom/DOMJSProxyHandler.h" |
|
19 #include "mozilla/dom/Exceptions.h" |
|
20 #include "mozilla/dom/NonRefcountedDOMObject.h" |
|
21 #include "mozilla/dom/Nullable.h" |
|
22 #include "mozilla/dom/RootedDictionary.h" |
|
23 #include "mozilla/dom/workers/Workers.h" |
|
24 #include "mozilla/ErrorResult.h" |
|
25 #include "mozilla/Likely.h" |
|
26 #include "mozilla/MemoryReporting.h" |
|
27 #include "nsCycleCollector.h" |
|
28 #include "nsIXPConnect.h" |
|
29 #include "MainThreadUtils.h" |
|
30 #include "nsISupportsImpl.h" |
|
31 #include "qsObjectHelper.h" |
|
32 #include "xpcpublic.h" |
|
33 #include "nsIVariant.h" |
|
34 #include "pldhash.h" // For PLDHashOperator |
|
35 |
|
36 #include "nsWrapperCacheInlines.h" |
|
37 |
|
38 class nsIJSID; |
|
39 class nsPIDOMWindow; |
|
40 |
|
41 extern nsresult |
|
42 xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg, |
|
43 nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp); |
|
44 |
|
45 namespace mozilla { |
|
46 namespace dom { |
|
47 template<typename DataType> class MozMap; |
|
48 |
|
49 struct SelfRef |
|
50 { |
|
51 SelfRef() : ptr(nullptr) {} |
|
52 explicit SelfRef(nsISupports *p) : ptr(p) {} |
|
53 ~SelfRef() { NS_IF_RELEASE(ptr); } |
|
54 |
|
55 nsISupports* ptr; |
|
56 }; |
|
57 |
|
58 /** Convert a jsval to an XPCOM pointer. */ |
|
59 template <class Interface, class StrongRefType> |
|
60 inline nsresult |
|
61 UnwrapArg(JSContext* cx, JS::Handle<JS::Value> v, Interface** ppArg, |
|
62 StrongRefType** ppArgRef, JS::MutableHandle<JS::Value> vp) |
|
63 { |
|
64 nsISupports* argRef = *ppArgRef; |
|
65 nsresult rv = xpc_qsUnwrapArgImpl(cx, v, NS_GET_TEMPLATE_IID(Interface), |
|
66 reinterpret_cast<void**>(ppArg), &argRef, |
|
67 vp); |
|
68 *ppArgRef = static_cast<StrongRefType*>(argRef); |
|
69 return rv; |
|
70 } |
|
71 |
|
72 inline const ErrNum |
|
73 GetInvalidThisErrorForMethod(bool aSecurityError) |
|
74 { |
|
75 return aSecurityError ? MSG_METHOD_THIS_UNWRAPPING_DENIED : |
|
76 MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE; |
|
77 } |
|
78 |
|
79 inline const ErrNum |
|
80 GetInvalidThisErrorForGetter(bool aSecurityError) |
|
81 { |
|
82 return aSecurityError ? MSG_GETTER_THIS_UNWRAPPING_DENIED : |
|
83 MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE; |
|
84 } |
|
85 |
|
86 inline const ErrNum |
|
87 GetInvalidThisErrorForSetter(bool aSecurityError) |
|
88 { |
|
89 return aSecurityError ? MSG_SETTER_THIS_UNWRAPPING_DENIED : |
|
90 MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE; |
|
91 } |
|
92 |
|
93 bool |
|
94 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
|
95 const ErrNum aErrorNumber, |
|
96 const char* aInterfaceName); |
|
97 |
|
98 bool |
|
99 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, |
|
100 const ErrNum aErrorNumber, |
|
101 prototypes::ID aProtoId); |
|
102 |
|
103 inline bool |
|
104 ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv, |
|
105 const char* ifaceName, |
|
106 const char* memberName, |
|
107 bool reportJSContentExceptions = false) |
|
108 { |
|
109 if (rv.IsTypeError()) { |
|
110 rv.ReportTypeError(cx); |
|
111 return false; |
|
112 } |
|
113 if (rv.IsJSException()) { |
|
114 if (reportJSContentExceptions) { |
|
115 rv.ReportJSExceptionFromJSImplementation(cx); |
|
116 } else { |
|
117 rv.ReportJSException(cx); |
|
118 } |
|
119 return false; |
|
120 } |
|
121 if (rv.IsNotEnoughArgsError()) { |
|
122 rv.ReportNotEnoughArgsError(cx, ifaceName, memberName); |
|
123 return false; |
|
124 } |
|
125 return Throw(cx, rv.ErrorCode()); |
|
126 } |
|
127 |
|
128 // Returns true if the JSClass is used for DOM objects. |
|
129 inline bool |
|
130 IsDOMClass(const JSClass* clasp) |
|
131 { |
|
132 return clasp->flags & JSCLASS_IS_DOMJSCLASS; |
|
133 } |
|
134 |
|
135 inline bool |
|
136 IsDOMClass(const js::Class* clasp) |
|
137 { |
|
138 return IsDOMClass(Jsvalify(clasp)); |
|
139 } |
|
140 |
|
141 // Return true if the JSClass is used for non-proxy DOM objects. |
|
142 inline bool |
|
143 IsNonProxyDOMClass(const js::Class* clasp) |
|
144 { |
|
145 return IsDOMClass(clasp) && !clasp->isProxy(); |
|
146 } |
|
147 |
|
148 inline bool |
|
149 IsNonProxyDOMClass(const JSClass* clasp) |
|
150 { |
|
151 return IsNonProxyDOMClass(js::Valueify(clasp)); |
|
152 } |
|
153 |
|
154 // Returns true if the JSClass is used for DOM interface and interface |
|
155 // prototype objects. |
|
156 inline bool |
|
157 IsDOMIfaceAndProtoClass(const JSClass* clasp) |
|
158 { |
|
159 return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS; |
|
160 } |
|
161 |
|
162 inline bool |
|
163 IsDOMIfaceAndProtoClass(const js::Class* clasp) |
|
164 { |
|
165 return IsDOMIfaceAndProtoClass(Jsvalify(clasp)); |
|
166 } |
|
167 |
|
168 static_assert(DOM_OBJECT_SLOT == js::PROXY_PRIVATE_SLOT, |
|
169 "js::PROXY_PRIVATE_SLOT doesn't match DOM_OBJECT_SLOT. " |
|
170 "Expect bad things"); |
|
171 template <class T> |
|
172 inline T* |
|
173 UnwrapDOMObject(JSObject* obj) |
|
174 { |
|
175 MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)), |
|
176 "Don't pass non-DOM objects to this function"); |
|
177 |
|
178 JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT); |
|
179 return static_cast<T*>(val.toPrivate()); |
|
180 } |
|
181 |
|
182 inline const DOMClass* |
|
183 GetDOMClass(JSObject* obj) |
|
184 { |
|
185 const js::Class* clasp = js::GetObjectClass(obj); |
|
186 if (IsDOMClass(clasp)) { |
|
187 return &DOMJSClass::FromJSClass(clasp)->mClass; |
|
188 } |
|
189 return nullptr; |
|
190 } |
|
191 |
|
192 inline nsISupports* |
|
193 UnwrapDOMObjectToISupports(JSObject* aObject) |
|
194 { |
|
195 const DOMClass* clasp = GetDOMClass(aObject); |
|
196 if (!clasp || !clasp->mDOMObjectIsISupports) { |
|
197 return nullptr; |
|
198 } |
|
199 |
|
200 return UnwrapDOMObject<nsISupports>(aObject); |
|
201 } |
|
202 |
|
203 inline bool |
|
204 IsDOMObject(JSObject* obj) |
|
205 { |
|
206 return IsDOMClass(js::GetObjectClass(obj)); |
|
207 } |
|
208 |
|
209 #define UNWRAP_OBJECT(Interface, obj, value) \ |
|
210 mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface, \ |
|
211 mozilla::dom::Interface##Binding::NativeType>(obj, value) |
|
212 |
|
213 // Some callers don't want to set an exception when unwrapping fails |
|
214 // (for example, overload resolution uses unwrapping to tell what sort |
|
215 // of thing it's looking at). |
|
216 // U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>). |
|
217 template <class T, typename U> |
|
218 MOZ_ALWAYS_INLINE nsresult |
|
219 UnwrapObject(JSObject* obj, U& value, prototypes::ID protoID, |
|
220 uint32_t protoDepth) |
|
221 { |
|
222 /* First check to see whether we have a DOM object */ |
|
223 const DOMClass* domClass = GetDOMClass(obj); |
|
224 if (!domClass) { |
|
225 /* Maybe we have a security wrapper or outer window? */ |
|
226 if (!js::IsWrapper(obj)) { |
|
227 /* Not a DOM object, not a wrapper, just bail */ |
|
228 return NS_ERROR_XPC_BAD_CONVERT_JS; |
|
229 } |
|
230 |
|
231 obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); |
|
232 if (!obj) { |
|
233 return NS_ERROR_XPC_SECURITY_MANAGER_VETO; |
|
234 } |
|
235 MOZ_ASSERT(!js::IsWrapper(obj)); |
|
236 domClass = GetDOMClass(obj); |
|
237 if (!domClass) { |
|
238 /* We don't have a DOM object */ |
|
239 return NS_ERROR_XPC_BAD_CONVERT_JS; |
|
240 } |
|
241 } |
|
242 |
|
243 /* This object is a DOM object. Double-check that it is safely |
|
244 castable to T by checking whether it claims to inherit from the |
|
245 class identified by protoID. */ |
|
246 if (domClass->mInterfaceChain[protoDepth] == protoID) { |
|
247 value = UnwrapDOMObject<T>(obj); |
|
248 return NS_OK; |
|
249 } |
|
250 |
|
251 /* It's the wrong sort of DOM object */ |
|
252 return NS_ERROR_XPC_BAD_CONVERT_JS; |
|
253 } |
|
254 |
|
255 template <prototypes::ID PrototypeID, class T, typename U> |
|
256 MOZ_ALWAYS_INLINE nsresult |
|
257 UnwrapObject(JSObject* obj, U& value) |
|
258 { |
|
259 return UnwrapObject<T>(obj, value, PrototypeID, |
|
260 PrototypeTraits<PrototypeID>::Depth); |
|
261 } |
|
262 |
|
263 inline bool |
|
264 IsNotDateOrRegExp(JSContext* cx, JS::Handle<JSObject*> obj) |
|
265 { |
|
266 MOZ_ASSERT(obj); |
|
267 return !JS_ObjectIsDate(cx, obj) && !JS_ObjectIsRegExp(cx, obj); |
|
268 } |
|
269 |
|
270 MOZ_ALWAYS_INLINE bool |
|
271 IsObjectValueConvertibleToDictionary(JSContext* cx, |
|
272 JS::Handle<JS::Value> objVal) |
|
273 { |
|
274 JS::Rooted<JSObject*> obj(cx, &objVal.toObject()); |
|
275 return IsNotDateOrRegExp(cx, obj); |
|
276 } |
|
277 |
|
278 MOZ_ALWAYS_INLINE bool |
|
279 IsConvertibleToDictionary(JSContext* cx, JS::Handle<JS::Value> val) |
|
280 { |
|
281 return val.isNullOrUndefined() || |
|
282 (val.isObject() && IsObjectValueConvertibleToDictionary(cx, val)); |
|
283 } |
|
284 |
|
285 MOZ_ALWAYS_INLINE bool |
|
286 IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj) |
|
287 { |
|
288 return IsNotDateOrRegExp(cx, obj); |
|
289 } |
|
290 |
|
291 // The items in the protoAndIfaceCache are indexed by the prototypes::id::ID and |
|
292 // constructors::id::ID enums, in that order. The end of the prototype objects |
|
293 // should be the start of the interface objects. |
|
294 static_assert((size_t)constructors::id::_ID_Start == |
|
295 (size_t)prototypes::id::_ID_Count, |
|
296 "Overlapping or discontiguous indexes."); |
|
297 const size_t kProtoAndIfaceCacheCount = constructors::id::_ID_Count; |
|
298 |
|
299 class ProtoAndIfaceCache |
|
300 { |
|
301 // The caching strategy we use depends on what sort of global we're dealing |
|
302 // with. For a window-like global, we want everything to be as fast as |
|
303 // possible, so we use a flat array, indexed by prototype/constructor ID. |
|
304 // For everything else (e.g. globals for JSMs), space is more important than |
|
305 // speed, so we use a two-level lookup table. |
|
306 |
|
307 class ArrayCache : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount> |
|
308 { |
|
309 public: |
|
310 JSObject* EntrySlotIfExists(size_t i) { |
|
311 return (*this)[i]; |
|
312 } |
|
313 |
|
314 JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { |
|
315 return (*this)[i]; |
|
316 } |
|
317 |
|
318 JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { |
|
319 MOZ_ASSERT((*this)[i]); |
|
320 return (*this)[i]; |
|
321 } |
|
322 |
|
323 void Trace(JSTracer* aTracer) { |
|
324 for (size_t i = 0; i < ArrayLength(*this); ++i) { |
|
325 if ((*this)[i]) { |
|
326 JS_CallHeapObjectTracer(aTracer, &(*this)[i], "protoAndIfaceCache[i]"); |
|
327 } |
|
328 } |
|
329 } |
|
330 |
|
331 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { |
|
332 return aMallocSizeOf(this); |
|
333 } |
|
334 }; |
|
335 |
|
336 class PageTableCache |
|
337 { |
|
338 public: |
|
339 PageTableCache() { |
|
340 memset(&mPages, 0, sizeof(mPages)); |
|
341 } |
|
342 |
|
343 ~PageTableCache() { |
|
344 for (size_t i = 0; i < ArrayLength(mPages); ++i) { |
|
345 delete mPages[i]; |
|
346 } |
|
347 } |
|
348 |
|
349 JSObject* EntrySlotIfExists(size_t i) { |
|
350 MOZ_ASSERT(i < kProtoAndIfaceCacheCount); |
|
351 size_t pageIndex = i / kPageSize; |
|
352 size_t leafIndex = i % kPageSize; |
|
353 Page* p = mPages[pageIndex]; |
|
354 if (!p) { |
|
355 return nullptr; |
|
356 } |
|
357 return (*p)[leafIndex]; |
|
358 } |
|
359 |
|
360 JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { |
|
361 MOZ_ASSERT(i < kProtoAndIfaceCacheCount); |
|
362 size_t pageIndex = i / kPageSize; |
|
363 size_t leafIndex = i % kPageSize; |
|
364 Page* p = mPages[pageIndex]; |
|
365 if (!p) { |
|
366 p = new Page; |
|
367 mPages[pageIndex] = p; |
|
368 } |
|
369 return (*p)[leafIndex]; |
|
370 } |
|
371 |
|
372 JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { |
|
373 MOZ_ASSERT(i < kProtoAndIfaceCacheCount); |
|
374 size_t pageIndex = i / kPageSize; |
|
375 size_t leafIndex = i % kPageSize; |
|
376 Page* p = mPages[pageIndex]; |
|
377 MOZ_ASSERT(p); |
|
378 return (*p)[leafIndex]; |
|
379 } |
|
380 |
|
381 void Trace(JSTracer* trc) { |
|
382 for (size_t i = 0; i < ArrayLength(mPages); ++i) { |
|
383 Page* p = mPages[i]; |
|
384 if (p) { |
|
385 for (size_t j = 0; j < ArrayLength(*p); ++j) { |
|
386 if ((*p)[j]) { |
|
387 JS_CallHeapObjectTracer(trc, &(*p)[j], "protoAndIfaceCache[i]"); |
|
388 } |
|
389 } |
|
390 } |
|
391 } |
|
392 } |
|
393 |
|
394 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { |
|
395 size_t n = aMallocSizeOf(this); |
|
396 for (size_t i = 0; i < ArrayLength(mPages); ++i) { |
|
397 n += aMallocSizeOf(mPages[i]); |
|
398 } |
|
399 return n; |
|
400 } |
|
401 |
|
402 private: |
|
403 static const size_t kPageSize = 16; |
|
404 typedef Array<JS::Heap<JSObject*>, kPageSize> Page; |
|
405 static const size_t kNPages = kProtoAndIfaceCacheCount / kPageSize + |
|
406 size_t(bool(kProtoAndIfaceCacheCount % kPageSize)); |
|
407 Array<Page*, kNPages> mPages; |
|
408 }; |
|
409 |
|
410 public: |
|
411 enum Kind { |
|
412 WindowLike, |
|
413 NonWindowLike |
|
414 }; |
|
415 |
|
416 ProtoAndIfaceCache(Kind aKind) : mKind(aKind) { |
|
417 MOZ_COUNT_CTOR(ProtoAndIfaceCache); |
|
418 if (aKind == WindowLike) { |
|
419 mArrayCache = new ArrayCache(); |
|
420 } else { |
|
421 mPageTableCache = new PageTableCache(); |
|
422 } |
|
423 } |
|
424 |
|
425 ~ProtoAndIfaceCache() { |
|
426 if (mKind == WindowLike) { |
|
427 delete mArrayCache; |
|
428 } else { |
|
429 delete mPageTableCache; |
|
430 } |
|
431 MOZ_COUNT_DTOR(ProtoAndIfaceCache); |
|
432 } |
|
433 |
|
434 #define FORWARD_OPERATION(opName, args) \ |
|
435 do { \ |
|
436 if (mKind == WindowLike) { \ |
|
437 return mArrayCache->opName args; \ |
|
438 } else { \ |
|
439 return mPageTableCache->opName args; \ |
|
440 } \ |
|
441 } while(0) |
|
442 |
|
443 JSObject* EntrySlotIfExists(size_t i) { |
|
444 FORWARD_OPERATION(EntrySlotIfExists, (i)); |
|
445 } |
|
446 |
|
447 JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { |
|
448 FORWARD_OPERATION(EntrySlotOrCreate, (i)); |
|
449 } |
|
450 |
|
451 JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { |
|
452 FORWARD_OPERATION(EntrySlotMustExist, (i)); |
|
453 } |
|
454 |
|
455 void Trace(JSTracer *aTracer) { |
|
456 FORWARD_OPERATION(Trace, (aTracer)); |
|
457 } |
|
458 |
|
459 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { |
|
460 size_t n = aMallocSizeOf(this); |
|
461 n += (mKind == WindowLike |
|
462 ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf) |
|
463 : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf)); |
|
464 return n; |
|
465 } |
|
466 #undef FORWARD_OPERATION |
|
467 |
|
468 private: |
|
469 union { |
|
470 ArrayCache *mArrayCache; |
|
471 PageTableCache *mPageTableCache; |
|
472 }; |
|
473 Kind mKind; |
|
474 }; |
|
475 |
|
476 inline void |
|
477 AllocateProtoAndIfaceCache(JSObject* obj, ProtoAndIfaceCache::Kind aKind) |
|
478 { |
|
479 MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL); |
|
480 MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined()); |
|
481 |
|
482 ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind); |
|
483 |
|
484 js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT, |
|
485 JS::PrivateValue(protoAndIfaceCache)); |
|
486 } |
|
487 |
|
488 inline void |
|
489 TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj) |
|
490 { |
|
491 MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL); |
|
492 |
|
493 if (!HasProtoAndIfaceCache(obj)) |
|
494 return; |
|
495 ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj); |
|
496 protoAndIfaceCache->Trace(trc); |
|
497 } |
|
498 |
|
499 inline void |
|
500 DestroyProtoAndIfaceCache(JSObject* obj) |
|
501 { |
|
502 MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL); |
|
503 |
|
504 ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj); |
|
505 |
|
506 delete protoAndIfaceCache; |
|
507 } |
|
508 |
|
509 /** |
|
510 * Add constants to an object. |
|
511 */ |
|
512 bool |
|
513 DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj, |
|
514 const ConstantSpec* cs); |
|
515 |
|
516 struct JSNativeHolder |
|
517 { |
|
518 JSNative mNative; |
|
519 const NativePropertyHooks* mPropertyHooks; |
|
520 }; |
|
521 |
|
522 struct NamedConstructor |
|
523 { |
|
524 const char* mName; |
|
525 const JSNativeHolder mHolder; |
|
526 unsigned mNargs; |
|
527 }; |
|
528 |
|
529 /* |
|
530 * Create a DOM interface object (if constructorClass is non-null) and/or a |
|
531 * DOM interface prototype object (if protoClass is non-null). |
|
532 * |
|
533 * global is used as the parent of the interface object and the interface |
|
534 * prototype object |
|
535 * protoProto is the prototype to use for the interface prototype object. |
|
536 * interfaceProto is the prototype to use for the interface object. |
|
537 * protoClass is the JSClass to use for the interface prototype object. |
|
538 * This is null if we should not create an interface prototype |
|
539 * object. |
|
540 * protoCache a pointer to a JSObject pointer where we should cache the |
|
541 * interface prototype object. This must be null if protoClass is and |
|
542 * vice versa. |
|
543 * constructorClass is the JSClass to use for the interface object. |
|
544 * This is null if we should not create an interface object or |
|
545 * if it should be a function object. |
|
546 * constructor holds the JSNative to back the interface object which should be a |
|
547 * Function, unless constructorClass is non-null in which case it is |
|
548 * ignored. If this is null and constructorClass is also null then |
|
549 * we should not create an interface object at all. |
|
550 * ctorNargs is the length of the constructor function; 0 if no constructor |
|
551 * constructorCache a pointer to a JSObject pointer where we should cache the |
|
552 * interface object. This must be null if both constructorClass |
|
553 * and constructor are null, and non-null otherwise. |
|
554 * domClass is the DOMClass of instance objects for this class. This can be |
|
555 * null if this is not a concrete proto. |
|
556 * properties contains the methods, attributes and constants to be defined on |
|
557 * objects in any compartment. |
|
558 * chromeProperties contains the methods, attributes and constants to be defined |
|
559 * on objects in chrome compartments. This must be null if the |
|
560 * interface doesn't have any ChromeOnly properties or if the |
|
561 * object is being created in non-chrome compartment. |
|
562 * defineOnGlobal controls whether properties should be defined on the given |
|
563 * global for the interface object (if any) and named |
|
564 * constructors (if any) for this interface. This can be |
|
565 * false in situations where we want the properties to only |
|
566 * appear on privileged Xrays but not on the unprivileged |
|
567 * underlying global. |
|
568 * |
|
569 * At least one of protoClass, constructorClass or constructor should be |
|
570 * non-null. If constructorClass or constructor are non-null, the resulting |
|
571 * interface object will be defined on the given global with property name |
|
572 * |name|, which must also be non-null. |
|
573 */ |
|
574 void |
|
575 CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global, |
|
576 JS::Handle<JSObject*> protoProto, |
|
577 const JSClass* protoClass, JS::Heap<JSObject*>* protoCache, |
|
578 JS::Handle<JSObject*> interfaceProto, |
|
579 const JSClass* constructorClass, const JSNativeHolder* constructor, |
|
580 unsigned ctorNargs, const NamedConstructor* namedConstructors, |
|
581 JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass, |
|
582 const NativeProperties* regularProperties, |
|
583 const NativeProperties* chromeOnlyProperties, |
|
584 const char* name, bool defineOnGlobal); |
|
585 |
|
586 /* |
|
587 * Define the unforgeable attributes on an object. |
|
588 */ |
|
589 bool |
|
590 DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj, |
|
591 const Prefable<const JSPropertySpec>* props); |
|
592 |
|
593 bool |
|
594 DefineWebIDLBindingPropertiesOnXPCObject(JSContext* cx, |
|
595 JS::Handle<JSObject*> obj, |
|
596 const NativeProperties* properties, |
|
597 bool defineUnforgeableAttributes); |
|
598 |
|
599 #ifdef _MSC_VER |
|
600 #define HAS_MEMBER_CHECK(_name) \ |
|
601 template<typename V> static yes& Check(char (*)[(&V::_name == 0) + 1]) |
|
602 #else |
|
603 #define HAS_MEMBER_CHECK(_name) \ |
|
604 template<typename V> static yes& Check(char (*)[sizeof(&V::_name) + 1]) |
|
605 #endif |
|
606 |
|
607 #define HAS_MEMBER(_name) \ |
|
608 template<typename T> \ |
|
609 class Has##_name##Member { \ |
|
610 typedef char yes[1]; \ |
|
611 typedef char no[2]; \ |
|
612 HAS_MEMBER_CHECK(_name); \ |
|
613 template<typename V> static no& Check(...); \ |
|
614 \ |
|
615 public: \ |
|
616 static bool const Value = sizeof(Check<T>(nullptr)) == sizeof(yes); \ |
|
617 }; |
|
618 |
|
619 HAS_MEMBER(WrapObject) |
|
620 |
|
621 // HasWrapObject<T>::Value will be true if T has a WrapObject member but it's |
|
622 // not nsWrapperCache::WrapObject. |
|
623 template<typename T> |
|
624 struct HasWrapObject |
|
625 { |
|
626 private: |
|
627 typedef char yes[1]; |
|
628 typedef char no[2]; |
|
629 typedef JSObject* (nsWrapperCache::*WrapObject)(JSContext*, |
|
630 JS::Handle<JSObject*>); |
|
631 template<typename U, U> struct SFINAE; |
|
632 template <typename V> static no& Check(SFINAE<WrapObject, &V::WrapObject>*); |
|
633 template <typename V> static yes& Check(...); |
|
634 |
|
635 public: |
|
636 static bool const Value = HasWrapObjectMember<T>::Value && |
|
637 sizeof(Check<T>(nullptr)) == sizeof(yes); |
|
638 }; |
|
639 |
|
640 #ifdef DEBUG |
|
641 template <class T, bool isISupports=IsBaseOf<nsISupports, T>::value> |
|
642 struct |
|
643 CheckWrapperCacheCast |
|
644 { |
|
645 static bool Check() |
|
646 { |
|
647 return reinterpret_cast<uintptr_t>( |
|
648 static_cast<nsWrapperCache*>( |
|
649 reinterpret_cast<T*>(1))) == 1; |
|
650 } |
|
651 }; |
|
652 template <class T> |
|
653 struct |
|
654 CheckWrapperCacheCast<T, true> |
|
655 { |
|
656 static bool Check() |
|
657 { |
|
658 return true; |
|
659 } |
|
660 }; |
|
661 #endif |
|
662 |
|
663 MOZ_ALWAYS_INLINE bool |
|
664 CouldBeDOMBinding(void*) |
|
665 { |
|
666 return true; |
|
667 } |
|
668 |
|
669 MOZ_ALWAYS_INLINE bool |
|
670 CouldBeDOMBinding(nsWrapperCache* aCache) |
|
671 { |
|
672 return aCache->IsDOMBinding(); |
|
673 } |
|
674 |
|
675 inline bool |
|
676 TryToOuterize(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
677 { |
|
678 if (js::IsInnerObject(&rval.toObject())) { |
|
679 JS::Rooted<JSObject*> obj(cx, &rval.toObject()); |
|
680 obj = JS_ObjectToOuterObject(cx, obj); |
|
681 if (!obj) { |
|
682 return false; |
|
683 } |
|
684 |
|
685 rval.set(JS::ObjectValue(*obj)); |
|
686 } |
|
687 |
|
688 return true; |
|
689 } |
|
690 |
|
691 // Make sure to wrap the given string value into the right compartment, as |
|
692 // needed. |
|
693 MOZ_ALWAYS_INLINE |
|
694 bool |
|
695 MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
696 { |
|
697 MOZ_ASSERT(rval.isString()); |
|
698 JSString* str = rval.toString(); |
|
699 if (JS::GetGCThingZone(str) != js::GetContextZone(cx)) { |
|
700 return JS_WrapValue(cx, rval); |
|
701 } |
|
702 return true; |
|
703 } |
|
704 |
|
705 // Make sure to wrap the given object value into the right compartment as |
|
706 // needed. This will work correctly, but possibly slowly, on all objects. |
|
707 MOZ_ALWAYS_INLINE |
|
708 bool |
|
709 MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
710 { |
|
711 MOZ_ASSERT(rval.isObject()); |
|
712 |
|
713 // Cross-compartment always requires wrapping. |
|
714 JSObject* obj = &rval.toObject(); |
|
715 if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) { |
|
716 return JS_WrapValue(cx, rval); |
|
717 } |
|
718 |
|
719 // We're same-compartment, but even then we might need to wrap |
|
720 // objects specially. Check for that. |
|
721 if (IsDOMObject(obj)) { |
|
722 return TryToOuterize(cx, rval); |
|
723 } |
|
724 |
|
725 // It's not a WebIDL object. But it might be an XPConnect one, in which case |
|
726 // we may need to outerize here, so make sure to call JS_WrapValue. |
|
727 return JS_WrapValue(cx, rval); |
|
728 } |
|
729 |
|
730 // Like MaybeWrapObjectValue, but also allows null |
|
731 MOZ_ALWAYS_INLINE |
|
732 bool |
|
733 MaybeWrapObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
734 { |
|
735 MOZ_ASSERT(rval.isObjectOrNull()); |
|
736 if (rval.isNull()) { |
|
737 return true; |
|
738 } |
|
739 return MaybeWrapObjectValue(cx, rval); |
|
740 } |
|
741 |
|
742 // Wrapping for objects that are known to not be DOM or XPConnect objects |
|
743 MOZ_ALWAYS_INLINE |
|
744 bool |
|
745 MaybeWrapNonDOMObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
746 { |
|
747 MOZ_ASSERT(rval.isObject()); |
|
748 MOZ_ASSERT(!GetDOMClass(&rval.toObject())); |
|
749 MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags & |
|
750 JSCLASS_PRIVATE_IS_NSISUPPORTS)); |
|
751 |
|
752 JSObject* obj = &rval.toObject(); |
|
753 if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) { |
|
754 return true; |
|
755 } |
|
756 return JS_WrapValue(cx, rval); |
|
757 } |
|
758 |
|
759 // Like MaybeWrapNonDOMObjectValue but allows null |
|
760 MOZ_ALWAYS_INLINE |
|
761 bool |
|
762 MaybeWrapNonDOMObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
763 { |
|
764 MOZ_ASSERT(rval.isObjectOrNull()); |
|
765 if (rval.isNull()) { |
|
766 return true; |
|
767 } |
|
768 return MaybeWrapNonDOMObjectValue(cx, rval); |
|
769 } |
|
770 |
|
771 // If rval is a gcthing and is not in the compartment of cx, wrap rval |
|
772 // into the compartment of cx (typically by replacing it with an Xray or |
|
773 // cross-compartment wrapper around the original object). |
|
774 MOZ_ALWAYS_INLINE bool |
|
775 MaybeWrapValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) |
|
776 { |
|
777 if (rval.isString()) { |
|
778 return MaybeWrapStringValue(cx, rval); |
|
779 } |
|
780 |
|
781 if (!rval.isObject()) { |
|
782 return true; |
|
783 } |
|
784 |
|
785 return MaybeWrapObjectValue(cx, rval); |
|
786 } |
|
787 |
|
788 // Create a JSObject wrapping "value", if there isn't one already, and store it |
|
789 // in rval. "value" must be a concrete class that implements a |
|
790 // GetWrapperPreserveColor() which can return its existing wrapper, if any, and |
|
791 // a WrapObject() which will try to create a wrapper. Typically, this is done by |
|
792 // having "value" inherit from nsWrapperCache. |
|
793 template <class T> |
|
794 MOZ_ALWAYS_INLINE bool |
|
795 WrapNewBindingObject(JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval) |
|
796 { |
|
797 MOZ_ASSERT(value); |
|
798 JSObject* obj = value->GetWrapperPreserveColor(); |
|
799 // We can get rid of this when we remove support for hasXPConnectImpls. |
|
800 bool couldBeDOMBinding = CouldBeDOMBinding(value); |
|
801 if (obj) { |
|
802 JS::ExposeObjectToActiveJS(obj); |
|
803 } else { |
|
804 // Inline this here while we have non-dom objects in wrapper caches. |
|
805 if (!couldBeDOMBinding) { |
|
806 return false; |
|
807 } |
|
808 |
|
809 obj = value->WrapObject(cx); |
|
810 if (!obj) { |
|
811 // At this point, obj is null, so just return false. |
|
812 // Callers seem to be testing JS_IsExceptionPending(cx) to |
|
813 // figure out whether WrapObject() threw. |
|
814 return false; |
|
815 } |
|
816 } |
|
817 |
|
818 #ifdef DEBUG |
|
819 const DOMClass* clasp = GetDOMClass(obj); |
|
820 // clasp can be null if the cache contained a non-DOM object. |
|
821 if (clasp) { |
|
822 // Some sanity asserts about our object. Specifically: |
|
823 // 1) If our class claims we're nsISupports, we better be nsISupports |
|
824 // XXXbz ideally, we could assert that reinterpret_cast to nsISupports |
|
825 // does the right thing, but I don't see a way to do it. :( |
|
826 // 2) If our class doesn't claim we're nsISupports we better be |
|
827 // reinterpret_castable to nsWrapperCache. |
|
828 MOZ_ASSERT(clasp, "What happened here?"); |
|
829 MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports, (IsBaseOf<nsISupports, T>::value)); |
|
830 MOZ_ASSERT(CheckWrapperCacheCast<T>::Check()); |
|
831 } |
|
832 #endif |
|
833 |
|
834 rval.set(JS::ObjectValue(*obj)); |
|
835 |
|
836 bool sameCompartment = |
|
837 js::GetObjectCompartment(obj) == js::GetContextCompartment(cx); |
|
838 if (sameCompartment && couldBeDOMBinding) { |
|
839 // We only need to outerize Window objects, so anything inheriting from |
|
840 // nsGlobalWindow (which inherits from EventTarget itself). |
|
841 return IsBaseOf<nsGlobalWindow, T>::value || IsSame<EventTarget, T>::value ? |
|
842 TryToOuterize(cx, rval) : true; |
|
843 } |
|
844 |
|
845 return JS_WrapValue(cx, rval); |
|
846 } |
|
847 |
|
848 // Create a JSObject wrapping "value", for cases when "value" is a |
|
849 // non-wrapper-cached object using WebIDL bindings. "value" must implement a |
|
850 // WrapObject() method taking a JSContext and a scope. |
|
851 template <class T> |
|
852 inline bool |
|
853 WrapNewBindingNonWrapperCachedObject(JSContext* cx, |
|
854 JS::Handle<JSObject*> scopeArg, |
|
855 T* value, |
|
856 JS::MutableHandle<JS::Value> rval) |
|
857 { |
|
858 MOZ_ASSERT(value); |
|
859 // We try to wrap in the compartment of the underlying object of "scope" |
|
860 JS::Rooted<JSObject*> obj(cx); |
|
861 { |
|
862 // scope for the JSAutoCompartment so that we restore the compartment |
|
863 // before we call JS_WrapValue. |
|
864 Maybe<JSAutoCompartment> ac; |
|
865 // Maybe<Handle> doesn't so much work, and in any case, adding |
|
866 // more Maybe (one for a Rooted and one for a Handle) adds more |
|
867 // code (and branches!) than just adding a single rooted. |
|
868 JS::Rooted<JSObject*> scope(cx, scopeArg); |
|
869 if (js::IsWrapper(scope)) { |
|
870 scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false); |
|
871 if (!scope) |
|
872 return false; |
|
873 ac.construct(cx, scope); |
|
874 } |
|
875 |
|
876 MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx)); |
|
877 obj = value->WrapObject(cx); |
|
878 } |
|
879 |
|
880 if (!obj) { |
|
881 return false; |
|
882 } |
|
883 |
|
884 // We can end up here in all sorts of compartments, per above. Make |
|
885 // sure to JS_WrapValue! |
|
886 rval.set(JS::ObjectValue(*obj)); |
|
887 return JS_WrapValue(cx, rval); |
|
888 } |
|
889 |
|
890 // Create a JSObject wrapping "value", for cases when "value" is a |
|
891 // non-wrapper-cached owned object using WebIDL bindings. "value" must implement a |
|
892 // WrapObject() method taking a JSContext, a scope, and a boolean outparam that |
|
893 // is true if the JSObject took ownership |
|
894 template <class T> |
|
895 inline bool |
|
896 WrapNewBindingNonWrapperCachedOwnedObject(JSContext* cx, |
|
897 JS::Handle<JSObject*> scopeArg, |
|
898 nsAutoPtr<T>& value, |
|
899 JS::MutableHandle<JS::Value> rval) |
|
900 { |
|
901 // We do a runtime check on value, because otherwise we might in |
|
902 // fact end up wrapping a null and invoking methods on it later. |
|
903 if (!value) { |
|
904 NS_RUNTIMEABORT("Don't try to wrap null objects"); |
|
905 } |
|
906 // We try to wrap in the compartment of the underlying object of "scope" |
|
907 JS::Rooted<JSObject*> obj(cx); |
|
908 { |
|
909 // scope for the JSAutoCompartment so that we restore the compartment |
|
910 // before we call JS_WrapValue. |
|
911 Maybe<JSAutoCompartment> ac; |
|
912 // Maybe<Handle> doesn't so much work, and in any case, adding |
|
913 // more Maybe (one for a Rooted and one for a Handle) adds more |
|
914 // code (and branches!) than just adding a single rooted. |
|
915 JS::Rooted<JSObject*> scope(cx, scopeArg); |
|
916 if (js::IsWrapper(scope)) { |
|
917 scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false); |
|
918 if (!scope) |
|
919 return false; |
|
920 ac.construct(cx, scope); |
|
921 } |
|
922 |
|
923 bool tookOwnership = false; |
|
924 MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx)); |
|
925 obj = value->WrapObject(cx, &tookOwnership); |
|
926 MOZ_ASSERT_IF(obj, tookOwnership); |
|
927 if (tookOwnership) { |
|
928 value.forget(); |
|
929 } |
|
930 } |
|
931 |
|
932 if (!obj) { |
|
933 return false; |
|
934 } |
|
935 |
|
936 // We can end up here in all sorts of compartments, per above. Make |
|
937 // sure to JS_WrapValue! |
|
938 rval.set(JS::ObjectValue(*obj)); |
|
939 return JS_WrapValue(cx, rval); |
|
940 } |
|
941 |
|
942 // Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr). |
|
943 template <template <typename> class SmartPtr, typename T> |
|
944 inline bool |
|
945 WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope, |
|
946 const SmartPtr<T>& value, |
|
947 JS::MutableHandle<JS::Value> rval) |
|
948 { |
|
949 return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval); |
|
950 } |
|
951 |
|
952 // Only set allowNativeWrapper to false if you really know you need it, if in |
|
953 // doubt use true. Setting it to false disables security wrappers. |
|
954 bool |
|
955 NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx, |
|
956 JS::Handle<JSObject*> aScope, |
|
957 JS::MutableHandle<JS::Value> aRetval, |
|
958 xpcObjectHelper& aHelper, |
|
959 const nsIID* aIID, |
|
960 bool aAllowNativeWrapper); |
|
961 |
|
962 /** |
|
963 * A method to handle new-binding wrap failure, by possibly falling back to |
|
964 * wrapping as a non-new-binding object. |
|
965 */ |
|
966 template <class T> |
|
967 MOZ_ALWAYS_INLINE bool |
|
968 HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope, |
|
969 T* value, JS::MutableHandle<JS::Value> rval) |
|
970 { |
|
971 if (JS_IsExceptionPending(cx)) { |
|
972 return false; |
|
973 } |
|
974 |
|
975 qsObjectHelper helper(value, GetWrapperCache(value)); |
|
976 return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, |
|
977 helper, nullptr, true); |
|
978 } |
|
979 |
|
980 // Helper for calling HandleNewBindingWrappingFailure with smart pointers |
|
981 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references. |
|
982 HAS_MEMBER(get) |
|
983 |
|
984 template <class T, bool isSmartPtr=HasgetMember<T>::Value> |
|
985 struct HandleNewBindingWrappingFailureHelper |
|
986 { |
|
987 static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, |
|
988 const T& value, JS::MutableHandle<JS::Value> rval) |
|
989 { |
|
990 return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval); |
|
991 } |
|
992 }; |
|
993 |
|
994 template <class T> |
|
995 struct HandleNewBindingWrappingFailureHelper<T, false> |
|
996 { |
|
997 static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, T& value, |
|
998 JS::MutableHandle<JS::Value> rval) |
|
999 { |
|
1000 return HandleNewBindingWrappingFailure(cx, scope, &value, rval); |
|
1001 } |
|
1002 }; |
|
1003 |
|
1004 template<class T> |
|
1005 inline bool |
|
1006 HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope, |
|
1007 T& value, JS::MutableHandle<JS::Value> rval) |
|
1008 { |
|
1009 return HandleNewBindingWrappingFailureHelper<T>::Wrap(cx, scope, value, rval); |
|
1010 } |
|
1011 |
|
1012 template<bool Fatal> |
|
1013 inline bool |
|
1014 EnumValueNotFound(JSContext* cx, const jschar* chars, size_t length, |
|
1015 const char* type, const char* sourceDescription) |
|
1016 { |
|
1017 return false; |
|
1018 } |
|
1019 |
|
1020 template<> |
|
1021 inline bool |
|
1022 EnumValueNotFound<false>(JSContext* cx, const jschar* chars, size_t length, |
|
1023 const char* type, const char* sourceDescription) |
|
1024 { |
|
1025 // TODO: Log a warning to the console. |
|
1026 return true; |
|
1027 } |
|
1028 |
|
1029 template<> |
|
1030 inline bool |
|
1031 EnumValueNotFound<true>(JSContext* cx, const jschar* chars, size_t length, |
|
1032 const char* type, const char* sourceDescription) |
|
1033 { |
|
1034 NS_LossyConvertUTF16toASCII deflated(static_cast<const char16_t*>(chars), |
|
1035 length); |
|
1036 return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription, |
|
1037 deflated.get(), type); |
|
1038 } |
|
1039 |
|
1040 |
|
1041 template<bool InvalidValueFatal> |
|
1042 inline int |
|
1043 FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* values, |
|
1044 const char* type, const char* sourceDescription, bool* ok) |
|
1045 { |
|
1046 // JS_StringEqualsAscii is slow as molasses, so don't use it here. |
|
1047 JSString* str = JS::ToString(cx, v); |
|
1048 if (!str) { |
|
1049 *ok = false; |
|
1050 return 0; |
|
1051 } |
|
1052 JS::Anchor<JSString*> anchor(str); |
|
1053 size_t length; |
|
1054 const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); |
|
1055 if (!chars) { |
|
1056 *ok = false; |
|
1057 return 0; |
|
1058 } |
|
1059 int i = 0; |
|
1060 for (const EnumEntry* value = values; value->value; ++value, ++i) { |
|
1061 if (length != value->length) { |
|
1062 continue; |
|
1063 } |
|
1064 |
|
1065 bool equal = true; |
|
1066 const char* val = value->value; |
|
1067 for (size_t j = 0; j != length; ++j) { |
|
1068 if (unsigned(val[j]) != unsigned(chars[j])) { |
|
1069 equal = false; |
|
1070 break; |
|
1071 } |
|
1072 } |
|
1073 |
|
1074 if (equal) { |
|
1075 *ok = true; |
|
1076 return i; |
|
1077 } |
|
1078 } |
|
1079 |
|
1080 *ok = EnumValueNotFound<InvalidValueFatal>(cx, chars, length, type, |
|
1081 sourceDescription); |
|
1082 return -1; |
|
1083 } |
|
1084 |
|
1085 inline nsWrapperCache* |
|
1086 GetWrapperCache(const ParentObject& aParentObject) |
|
1087 { |
|
1088 return aParentObject.mWrapperCache; |
|
1089 } |
|
1090 |
|
1091 template<class T> |
|
1092 inline T* |
|
1093 GetParentPointer(T* aObject) |
|
1094 { |
|
1095 return aObject; |
|
1096 } |
|
1097 |
|
1098 inline nsISupports* |
|
1099 GetParentPointer(const ParentObject& aObject) |
|
1100 { |
|
1101 return aObject.mObject; |
|
1102 } |
|
1103 |
|
1104 template <typename T> |
|
1105 inline bool |
|
1106 GetUseXBLScope(T* aParentObject) |
|
1107 { |
|
1108 return false; |
|
1109 } |
|
1110 |
|
1111 inline bool |
|
1112 GetUseXBLScope(const ParentObject& aParentObject) |
|
1113 { |
|
1114 return aParentObject.mUseXBLScope; |
|
1115 } |
|
1116 |
|
1117 template<class T> |
|
1118 inline void |
|
1119 ClearWrapper(T* p, nsWrapperCache* cache) |
|
1120 { |
|
1121 cache->ClearWrapper(); |
|
1122 } |
|
1123 |
|
1124 template<class T> |
|
1125 inline void |
|
1126 ClearWrapper(T* p, void*) |
|
1127 { |
|
1128 nsWrapperCache* cache; |
|
1129 CallQueryInterface(p, &cache); |
|
1130 ClearWrapper(p, cache); |
|
1131 } |
|
1132 |
|
1133 // Attempt to preserve the wrapper, if any, for a Paris DOM bindings object. |
|
1134 // Return true if we successfully preserved the wrapper, or there is no wrapper |
|
1135 // to preserve. In the latter case we don't need to preserve the wrapper, because |
|
1136 // the object can only be obtained by JS once, or they cannot be meaningfully |
|
1137 // owned from the native side. |
|
1138 // |
|
1139 // This operation will return false only for non-nsISupports cycle-collected |
|
1140 // objects, because we cannot determine if they are wrappercached or not. |
|
1141 bool |
|
1142 TryPreserveWrapper(JSObject* obj); |
|
1143 |
|
1144 // Can only be called with the immediate prototype of the instance object. Can |
|
1145 // only be called on the prototype of an object known to be a DOM instance. |
|
1146 bool |
|
1147 InstanceClassHasProtoAtDepth(JSObject* protoObject, uint32_t protoID, |
|
1148 uint32_t depth); |
|
1149 |
|
1150 // Only set allowNativeWrapper to false if you really know you need it, if in |
|
1151 // doubt use true. Setting it to false disables security wrappers. |
|
1152 bool |
|
1153 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope, |
|
1154 xpcObjectHelper& helper, const nsIID* iid, |
|
1155 bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval); |
|
1156 |
|
1157 // Special-cased wrapping for variants |
|
1158 bool |
|
1159 VariantToJsval(JSContext* aCx, nsIVariant* aVariant, |
|
1160 JS::MutableHandle<JS::Value> aRetval); |
|
1161 |
|
1162 // Wrap an object "p" which is not using WebIDL bindings yet. This _will_ |
|
1163 // actually work on WebIDL binding objects that are wrappercached, but will be |
|
1164 // much slower than WrapNewBindingObject. "cache" must either be null or be the |
|
1165 // nsWrapperCache for "p". |
|
1166 template<class T> |
|
1167 inline bool |
|
1168 WrapObject(JSContext* cx, T* p, nsWrapperCache* cache, const nsIID* iid, |
|
1169 JS::MutableHandle<JS::Value> rval) |
|
1170 { |
|
1171 if (xpc_FastGetCachedWrapper(cx, cache, rval)) |
|
1172 return true; |
|
1173 qsObjectHelper helper(p, cache); |
|
1174 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx)); |
|
1175 return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval); |
|
1176 } |
|
1177 |
|
1178 // A specialization of the above for nsIVariant, because that needs to |
|
1179 // do something different. |
|
1180 template<> |
|
1181 inline bool |
|
1182 WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p, |
|
1183 nsWrapperCache* cache, const nsIID* iid, |
|
1184 JS::MutableHandle<JS::Value> rval) |
|
1185 { |
|
1186 MOZ_ASSERT(iid); |
|
1187 MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant))); |
|
1188 return VariantToJsval(cx, p, rval); |
|
1189 } |
|
1190 |
|
1191 // Wrap an object "p" which is not using WebIDL bindings yet. Just like the |
|
1192 // variant that takes an nsWrapperCache above, but will try to auto-derive the |
|
1193 // nsWrapperCache* from "p". |
|
1194 template<class T> |
|
1195 inline bool |
|
1196 WrapObject(JSContext* cx, T* p, const nsIID* iid, |
|
1197 JS::MutableHandle<JS::Value> rval) |
|
1198 { |
|
1199 return WrapObject(cx, p, GetWrapperCache(p), iid, rval); |
|
1200 } |
|
1201 |
|
1202 // Just like the WrapObject above, but without requiring you to pick which |
|
1203 // interface you're wrapping as. This should only be used for objects that have |
|
1204 // classinfo, for which it doesn't matter what IID is used to wrap. |
|
1205 template<class T> |
|
1206 inline bool |
|
1207 WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval) |
|
1208 { |
|
1209 return WrapObject(cx, p, nullptr, rval); |
|
1210 } |
|
1211 |
|
1212 // Helper to make it possible to wrap directly out of an nsCOMPtr |
|
1213 template<class T> |
|
1214 inline bool |
|
1215 WrapObject(JSContext* cx, const nsCOMPtr<T>& p, |
|
1216 const nsIID* iid, JS::MutableHandle<JS::Value> rval) |
|
1217 { |
|
1218 return WrapObject(cx, p.get(), iid, rval); |
|
1219 } |
|
1220 |
|
1221 // Helper to make it possible to wrap directly out of an nsCOMPtr |
|
1222 template<class T> |
|
1223 inline bool |
|
1224 WrapObject(JSContext* cx, const nsCOMPtr<T>& p, |
|
1225 JS::MutableHandle<JS::Value> rval) |
|
1226 { |
|
1227 return WrapObject(cx, p, nullptr, rval); |
|
1228 } |
|
1229 |
|
1230 // Helper to make it possible to wrap directly out of an nsRefPtr |
|
1231 template<class T> |
|
1232 inline bool |
|
1233 WrapObject(JSContext* cx, const nsRefPtr<T>& p, |
|
1234 const nsIID* iid, JS::MutableHandle<JS::Value> rval) |
|
1235 { |
|
1236 return WrapObject(cx, p.get(), iid, rval); |
|
1237 } |
|
1238 |
|
1239 // Helper to make it possible to wrap directly out of an nsRefPtr |
|
1240 template<class T> |
|
1241 inline bool |
|
1242 WrapObject(JSContext* cx, const nsRefPtr<T>& p, |
|
1243 JS::MutableHandle<JS::Value> rval) |
|
1244 { |
|
1245 return WrapObject(cx, p, nullptr, rval); |
|
1246 } |
|
1247 |
|
1248 // Specialization to make it easy to use WrapObject in codegen. |
|
1249 template<> |
|
1250 inline bool |
|
1251 WrapObject<JSObject>(JSContext* cx, JSObject* p, |
|
1252 JS::MutableHandle<JS::Value> rval) |
|
1253 { |
|
1254 rval.set(JS::ObjectOrNullValue(p)); |
|
1255 return true; |
|
1256 } |
|
1257 |
|
1258 inline bool |
|
1259 WrapObject(JSContext* cx, JSObject& p, JS::MutableHandle<JS::Value> rval) |
|
1260 { |
|
1261 rval.set(JS::ObjectValue(p)); |
|
1262 return true; |
|
1263 } |
|
1264 |
|
1265 // Given an object "p" that inherits from nsISupports, wrap it and return the |
|
1266 // result. Null is returned on wrapping failure. This is somewhat similar to |
|
1267 // WrapObject() above, but does NOT allow Xrays around the result, since we |
|
1268 // don't want those for our parent object. |
|
1269 template<typename T> |
|
1270 static inline JSObject* |
|
1271 WrapNativeISupportsParent(JSContext* cx, T* p, nsWrapperCache* cache) |
|
1272 { |
|
1273 qsObjectHelper helper(ToSupports(p), cache); |
|
1274 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx)); |
|
1275 JS::Rooted<JS::Value> v(cx); |
|
1276 return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ? |
|
1277 v.toObjectOrNull() : |
|
1278 nullptr; |
|
1279 } |
|
1280 |
|
1281 |
|
1282 // Fallback for when our parent is not a WebIDL binding object. |
|
1283 template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value> |
|
1284 struct WrapNativeParentFallback |
|
1285 { |
|
1286 static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache) |
|
1287 { |
|
1288 return nullptr; |
|
1289 } |
|
1290 }; |
|
1291 |
|
1292 // Fallback for when our parent is not a WebIDL binding object but _is_ an |
|
1293 // nsISupports object. |
|
1294 template<typename T > |
|
1295 struct WrapNativeParentFallback<T, true > |
|
1296 { |
|
1297 static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache) |
|
1298 { |
|
1299 return WrapNativeISupportsParent(cx, parent, cache); |
|
1300 } |
|
1301 }; |
|
1302 |
|
1303 // Wrapping of our native parent, for cases when it's a WebIDL object (though |
|
1304 // possibly preffed off). |
|
1305 template<typename T, bool hasWrapObject=HasWrapObject<T>::Value > |
|
1306 struct WrapNativeParentHelper |
|
1307 { |
|
1308 static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache) |
|
1309 { |
|
1310 MOZ_ASSERT(cache); |
|
1311 |
|
1312 JSObject* obj; |
|
1313 if ((obj = cache->GetWrapper())) { |
|
1314 return obj; |
|
1315 } |
|
1316 |
|
1317 // Inline this here while we have non-dom objects in wrapper caches. |
|
1318 if (!CouldBeDOMBinding(parent)) { |
|
1319 obj = WrapNativeParentFallback<T>::Wrap(cx, parent, cache); |
|
1320 } else { |
|
1321 obj = parent->WrapObject(cx); |
|
1322 } |
|
1323 |
|
1324 return obj; |
|
1325 } |
|
1326 }; |
|
1327 |
|
1328 // Wrapping of our native parent, for cases when it's not a WebIDL object. In |
|
1329 // this case it must be nsISupports. |
|
1330 template<typename T> |
|
1331 struct WrapNativeParentHelper<T, false > |
|
1332 { |
|
1333 static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache) |
|
1334 { |
|
1335 JSObject* obj; |
|
1336 if (cache && (obj = cache->GetWrapper())) { |
|
1337 #ifdef DEBUG |
|
1338 NS_ASSERTION(WrapNativeISupportsParent(cx, parent, cache) == obj, |
|
1339 "Unexpected object in nsWrapperCache"); |
|
1340 #endif |
|
1341 return obj; |
|
1342 } |
|
1343 |
|
1344 return WrapNativeISupportsParent(cx, parent, cache); |
|
1345 } |
|
1346 }; |
|
1347 |
|
1348 // Wrapping of our native parent. |
|
1349 template<typename T> |
|
1350 static inline JSObject* |
|
1351 WrapNativeParent(JSContext* cx, T* p, nsWrapperCache* cache, |
|
1352 bool useXBLScope = false) |
|
1353 { |
|
1354 if (!p) { |
|
1355 return JS::CurrentGlobalOrNull(cx); |
|
1356 } |
|
1357 |
|
1358 JSObject* parent = WrapNativeParentHelper<T>::Wrap(cx, p, cache); |
|
1359 if (!useXBLScope) { |
|
1360 return parent; |
|
1361 } |
|
1362 |
|
1363 // If useXBLScope is true, it means that the canonical reflector for this |
|
1364 // native object should live in the XBL scope. |
|
1365 if (xpc::IsInXBLScope(parent)) { |
|
1366 return parent; |
|
1367 } |
|
1368 JS::Rooted<JSObject*> rootedParent(cx, parent); |
|
1369 JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScope(cx, rootedParent)); |
|
1370 NS_ENSURE_TRUE(xblScope, nullptr); |
|
1371 JSAutoCompartment ac(cx, xblScope); |
|
1372 if (NS_WARN_IF(!JS_WrapObject(cx, &rootedParent))) { |
|
1373 return nullptr; |
|
1374 } |
|
1375 |
|
1376 return rootedParent; |
|
1377 } |
|
1378 |
|
1379 // Wrapping of our native parent, when we don't want to explicitly pass in |
|
1380 // things like the nsWrapperCache for it. |
|
1381 template<typename T> |
|
1382 static inline JSObject* |
|
1383 WrapNativeParent(JSContext* cx, const T& p) |
|
1384 { |
|
1385 return WrapNativeParent(cx, GetParentPointer(p), GetWrapperCache(p), GetUseXBLScope(p)); |
|
1386 } |
|
1387 |
|
1388 // A way to differentiate between nodes, which use the parent object |
|
1389 // returned by native->GetParentObject(), and all other objects, which |
|
1390 // just use the parent's global. |
|
1391 static inline JSObject* |
|
1392 GetRealParentObject(void* aParent, JSObject* aParentObject) |
|
1393 { |
|
1394 return aParentObject ? |
|
1395 js::GetGlobalForObjectCrossCompartment(aParentObject) : nullptr; |
|
1396 } |
|
1397 |
|
1398 static inline JSObject* |
|
1399 GetRealParentObject(Element* aParent, JSObject* aParentObject) |
|
1400 { |
|
1401 return aParentObject; |
|
1402 } |
|
1403 |
|
1404 HAS_MEMBER(GetParentObject) |
|
1405 |
|
1406 template<typename T, bool WrapperCached=HasGetParentObjectMember<T>::Value> |
|
1407 struct GetParentObject |
|
1408 { |
|
1409 static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) |
|
1410 { |
|
1411 MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx)); |
|
1412 T* native = UnwrapDOMObject<T>(obj); |
|
1413 return |
|
1414 GetRealParentObject(native, |
|
1415 WrapNativeParent(cx, native->GetParentObject())); |
|
1416 } |
|
1417 }; |
|
1418 |
|
1419 template<typename T> |
|
1420 struct GetParentObject<T, false> |
|
1421 { |
|
1422 static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) |
|
1423 { |
|
1424 MOZ_CRASH(); |
|
1425 return nullptr; |
|
1426 } |
|
1427 }; |
|
1428 |
|
1429 MOZ_ALWAYS_INLINE |
|
1430 JSObject* GetJSObjectFromCallback(CallbackObject* callback) |
|
1431 { |
|
1432 return callback->Callback(); |
|
1433 } |
|
1434 |
|
1435 MOZ_ALWAYS_INLINE |
|
1436 JSObject* GetJSObjectFromCallback(void* noncallback) |
|
1437 { |
|
1438 return nullptr; |
|
1439 } |
|
1440 |
|
1441 template<typename T> |
|
1442 static inline JSObject* |
|
1443 WrapCallThisObject(JSContext* cx, const T& p) |
|
1444 { |
|
1445 // Callbacks are nsISupports, so WrapNativeParent will just happily wrap them |
|
1446 // up as an nsISupports XPCWrappedNative... which is not at all what we want. |
|
1447 // So we need to special-case them. |
|
1448 JS::Rooted<JSObject*> obj(cx, GetJSObjectFromCallback(p)); |
|
1449 if (!obj) { |
|
1450 // WrapNativeParent is a bit of a Swiss army knife that will |
|
1451 // wrap anything for us. |
|
1452 obj = WrapNativeParent(cx, p); |
|
1453 if (!obj) { |
|
1454 return nullptr; |
|
1455 } |
|
1456 } |
|
1457 |
|
1458 // But all that won't necessarily put things in the compartment of cx. |
|
1459 if (!JS_WrapObject(cx, &obj)) { |
|
1460 return nullptr; |
|
1461 } |
|
1462 |
|
1463 return obj; |
|
1464 } |
|
1465 |
|
1466 /* |
|
1467 * This specialized function simply wraps a JS::Rooted<> since |
|
1468 * WrapNativeParent() is not applicable for JS objects. |
|
1469 */ |
|
1470 template<> |
|
1471 inline JSObject* |
|
1472 WrapCallThisObject<JS::Rooted<JSObject*>>(JSContext* cx, |
|
1473 const JS::Rooted<JSObject*>& p) |
|
1474 { |
|
1475 JS::Rooted<JSObject*> obj(cx, p); |
|
1476 |
|
1477 if (!JS_WrapObject(cx, &obj)) { |
|
1478 return nullptr; |
|
1479 } |
|
1480 |
|
1481 return obj; |
|
1482 } |
|
1483 |
|
1484 // Helper for calling WrapNewBindingObject with smart pointers |
|
1485 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references. |
|
1486 template <class T, bool isSmartPtr=HasgetMember<T>::Value> |
|
1487 struct WrapNewBindingObjectHelper |
|
1488 { |
|
1489 static inline bool Wrap(JSContext* cx, const T& value, |
|
1490 JS::MutableHandle<JS::Value> rval) |
|
1491 { |
|
1492 return WrapNewBindingObject(cx, value.get(), rval); |
|
1493 } |
|
1494 }; |
|
1495 |
|
1496 template <class T> |
|
1497 struct WrapNewBindingObjectHelper<T, false> |
|
1498 { |
|
1499 static inline bool Wrap(JSContext* cx, T& value, |
|
1500 JS::MutableHandle<JS::Value> rval) |
|
1501 { |
|
1502 return WrapNewBindingObject(cx, &value, rval); |
|
1503 } |
|
1504 }; |
|
1505 |
|
1506 template<class T> |
|
1507 inline bool |
|
1508 WrapNewBindingObject(JSContext* cx, T& value, JS::MutableHandle<JS::Value> rval) |
|
1509 { |
|
1510 return WrapNewBindingObjectHelper<T>::Wrap(cx, value, rval); |
|
1511 } |
|
1512 |
|
1513 // We need this version of WrapNewBindingObject for codegen, so it'll have the |
|
1514 // same signature as WrapNewBindingNonWrapperCachedObject and |
|
1515 // WrapNewBindingNonWrapperCachedOwnedObject, which still need the scope. |
|
1516 template<class T> |
|
1517 inline bool |
|
1518 WrapNewBindingObject(JSContext* cx, JS::Handle<JSObject*> scope, T& value, |
|
1519 JS::MutableHandle<JS::Value> rval) |
|
1520 { |
|
1521 return WrapNewBindingObject(cx, value, rval); |
|
1522 } |
|
1523 |
|
1524 template <class T> |
|
1525 inline JSObject* |
|
1526 GetCallbackFromCallbackObject(T* aObj) |
|
1527 { |
|
1528 return aObj->Callback(); |
|
1529 } |
|
1530 |
|
1531 // Helper for getting the callback JSObject* of a smart ptr around a |
|
1532 // CallbackObject or a reference to a CallbackObject or something like |
|
1533 // that. |
|
1534 template <class T, bool isSmartPtr=HasgetMember<T>::Value> |
|
1535 struct GetCallbackFromCallbackObjectHelper |
|
1536 { |
|
1537 static inline JSObject* Get(const T& aObj) |
|
1538 { |
|
1539 return GetCallbackFromCallbackObject(aObj.get()); |
|
1540 } |
|
1541 }; |
|
1542 |
|
1543 template <class T> |
|
1544 struct GetCallbackFromCallbackObjectHelper<T, false> |
|
1545 { |
|
1546 static inline JSObject* Get(T& aObj) |
|
1547 { |
|
1548 return GetCallbackFromCallbackObject(&aObj); |
|
1549 } |
|
1550 }; |
|
1551 |
|
1552 template<class T> |
|
1553 inline JSObject* |
|
1554 GetCallbackFromCallbackObject(T& aObj) |
|
1555 { |
|
1556 return GetCallbackFromCallbackObjectHelper<T>::Get(aObj); |
|
1557 } |
|
1558 |
|
1559 static inline bool |
|
1560 InternJSString(JSContext* cx, jsid& id, const char* chars) |
|
1561 { |
|
1562 if (JSString *str = ::JS_InternString(cx, chars)) { |
|
1563 id = INTERNED_STRING_TO_JSID(cx, str); |
|
1564 return true; |
|
1565 } |
|
1566 return false; |
|
1567 } |
|
1568 |
|
1569 // Spec needs a name property |
|
1570 template <typename Spec> |
|
1571 static bool |
|
1572 InitIds(JSContext* cx, const Prefable<Spec>* prefableSpecs, jsid* ids) |
|
1573 { |
|
1574 MOZ_ASSERT(prefableSpecs); |
|
1575 MOZ_ASSERT(prefableSpecs->specs); |
|
1576 do { |
|
1577 // We ignore whether the set of ids is enabled and just intern all the IDs, |
|
1578 // because this is only done once per application runtime. |
|
1579 Spec* spec = prefableSpecs->specs; |
|
1580 do { |
|
1581 if (!InternJSString(cx, *ids, spec->name)) { |
|
1582 return false; |
|
1583 } |
|
1584 } while (++ids, (++spec)->name); |
|
1585 |
|
1586 // We ran out of ids for that pref. Put a JSID_VOID in on the id |
|
1587 // corresponding to the list terminator for the pref. |
|
1588 *ids = JSID_VOID; |
|
1589 ++ids; |
|
1590 } while ((++prefableSpecs)->specs); |
|
1591 |
|
1592 return true; |
|
1593 } |
|
1594 |
|
1595 bool |
|
1596 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp); |
|
1597 |
|
1598 template <class T> |
|
1599 struct |
|
1600 WantsQueryInterface |
|
1601 { |
|
1602 static_assert(IsBaseOf<nsISupports, T>::value, |
|
1603 "QueryInterface can't work without an nsISupports."); |
|
1604 static bool Enabled(JSContext* aCx, JSObject* aGlobal) |
|
1605 { |
|
1606 return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal); |
|
1607 } |
|
1608 }; |
|
1609 |
|
1610 void |
|
1611 GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, |
|
1612 nsWrapperCache* aCache, nsIJSID* aIID, |
|
1613 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError); |
|
1614 |
|
1615 template<class T> |
|
1616 void |
|
1617 GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, |
|
1618 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) |
|
1619 { |
|
1620 GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError); |
|
1621 } |
|
1622 |
|
1623 bool |
|
1624 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp); |
|
1625 |
|
1626 bool |
|
1627 ThrowConstructorWithoutNew(JSContext* cx, const char* name); |
|
1628 |
|
1629 // vp is allowed to be null; in that case no get will be attempted, |
|
1630 // and *found will simply indicate whether the property exists. |
|
1631 bool |
|
1632 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, |
|
1633 JS::Handle<jsid> id, bool* found, |
|
1634 JS::Value* vp); |
|
1635 |
|
1636 bool |
|
1637 HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, |
|
1638 JS::Handle<jsid> id); |
|
1639 |
|
1640 |
|
1641 // Append the property names in "names" to "props". If |
|
1642 // shadowPrototypeProperties is false then skip properties that are also |
|
1643 // present on the proto chain of proxy. If shadowPrototypeProperties is true, |
|
1644 // then the "proxy" argument is ignored. |
|
1645 bool |
|
1646 AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy, |
|
1647 nsTArray<nsString>& names, |
|
1648 bool shadowPrototypeProperties, JS::AutoIdVector& props); |
|
1649 |
|
1650 namespace binding_detail { |
|
1651 |
|
1652 // A struct that has the same layout as an nsDependentString but much |
|
1653 // faster constructor and destructor behavior |
|
1654 struct FakeDependentString { |
|
1655 FakeDependentString() : |
|
1656 mFlags(nsDependentString::F_TERMINATED) |
|
1657 { |
|
1658 } |
|
1659 |
|
1660 void SetData(const nsDependentString::char_type* aData, |
|
1661 nsDependentString::size_type aLength) { |
|
1662 MOZ_ASSERT(mFlags == nsDependentString::F_TERMINATED); |
|
1663 mData = aData; |
|
1664 mLength = aLength; |
|
1665 } |
|
1666 |
|
1667 void Truncate() { |
|
1668 mData = nsDependentString::char_traits::sEmptyBuffer; |
|
1669 mLength = 0; |
|
1670 } |
|
1671 |
|
1672 void SetNull() { |
|
1673 Truncate(); |
|
1674 mFlags |= nsDependentString::F_VOIDED; |
|
1675 } |
|
1676 |
|
1677 const nsDependentString::char_type* Data() const |
|
1678 { |
|
1679 return mData; |
|
1680 } |
|
1681 |
|
1682 nsDependentString::size_type Length() const |
|
1683 { |
|
1684 return mLength; |
|
1685 } |
|
1686 |
|
1687 // If this ever changes, change the corresponding code in the |
|
1688 // Optional<nsAString> specialization as well. |
|
1689 const nsAString* ToAStringPtr() const { |
|
1690 return reinterpret_cast<const nsDependentString*>(this); |
|
1691 } |
|
1692 |
|
1693 nsAString* ToAStringPtr() { |
|
1694 return reinterpret_cast<nsDependentString*>(this); |
|
1695 } |
|
1696 |
|
1697 operator const nsAString& () const { |
|
1698 return *reinterpret_cast<const nsDependentString*>(this); |
|
1699 } |
|
1700 |
|
1701 private: |
|
1702 const nsDependentString::char_type* mData; |
|
1703 nsDependentString::size_type mLength; |
|
1704 uint32_t mFlags; |
|
1705 |
|
1706 // A class to use for our static asserts to ensure our object layout |
|
1707 // matches that of nsDependentString. |
|
1708 class DependentStringAsserter; |
|
1709 friend class DependentStringAsserter; |
|
1710 |
|
1711 class DepedentStringAsserter : public nsDependentString { |
|
1712 public: |
|
1713 static void StaticAsserts() { |
|
1714 static_assert(sizeof(FakeDependentString) == sizeof(nsDependentString), |
|
1715 "Must have right object size"); |
|
1716 static_assert(offsetof(FakeDependentString, mData) == |
|
1717 offsetof(DepedentStringAsserter, mData), |
|
1718 "Offset of mData should match"); |
|
1719 static_assert(offsetof(FakeDependentString, mLength) == |
|
1720 offsetof(DepedentStringAsserter, mLength), |
|
1721 "Offset of mLength should match"); |
|
1722 static_assert(offsetof(FakeDependentString, mFlags) == |
|
1723 offsetof(DepedentStringAsserter, mFlags), |
|
1724 "Offset of mFlags should match"); |
|
1725 } |
|
1726 }; |
|
1727 }; |
|
1728 |
|
1729 } // namespace binding_detail |
|
1730 |
|
1731 enum StringificationBehavior { |
|
1732 eStringify, |
|
1733 eEmpty, |
|
1734 eNull |
|
1735 }; |
|
1736 |
|
1737 // pval must not be null and must point to a rooted JS::Value |
|
1738 static inline bool |
|
1739 ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v, |
|
1740 JS::MutableHandle<JS::Value> pval, |
|
1741 StringificationBehavior nullBehavior, |
|
1742 StringificationBehavior undefinedBehavior, |
|
1743 binding_detail::FakeDependentString& result) |
|
1744 { |
|
1745 JSString *s; |
|
1746 if (v.isString()) { |
|
1747 s = v.toString(); |
|
1748 } else { |
|
1749 StringificationBehavior behavior; |
|
1750 if (v.isNull()) { |
|
1751 behavior = nullBehavior; |
|
1752 } else if (v.isUndefined()) { |
|
1753 behavior = undefinedBehavior; |
|
1754 } else { |
|
1755 behavior = eStringify; |
|
1756 } |
|
1757 |
|
1758 if (behavior != eStringify) { |
|
1759 if (behavior == eEmpty) { |
|
1760 result.Truncate(); |
|
1761 } else { |
|
1762 result.SetNull(); |
|
1763 } |
|
1764 return true; |
|
1765 } |
|
1766 |
|
1767 s = JS::ToString(cx, v); |
|
1768 if (!s) { |
|
1769 return false; |
|
1770 } |
|
1771 pval.set(JS::StringValue(s)); // Root the new string. |
|
1772 } |
|
1773 |
|
1774 size_t len; |
|
1775 const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len); |
|
1776 if (!chars) { |
|
1777 return false; |
|
1778 } |
|
1779 |
|
1780 result.SetData(chars, len); |
|
1781 return true; |
|
1782 } |
|
1783 |
|
1784 bool |
|
1785 ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v, |
|
1786 JS::MutableHandle<JS::Value> pval, bool nullable, |
|
1787 nsACString& result); |
|
1788 |
|
1789 template<typename T> |
|
1790 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq); |
|
1791 template<typename T> |
|
1792 void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq); |
|
1793 |
|
1794 // Class for simple sequence arguments, only used internally by codegen. |
|
1795 namespace binding_detail { |
|
1796 |
|
1797 template<typename T> |
|
1798 class AutoSequence : public AutoFallibleTArray<T, 16> |
|
1799 { |
|
1800 public: |
|
1801 AutoSequence() : AutoFallibleTArray<T, 16>() |
|
1802 {} |
|
1803 |
|
1804 // Allow converting to const sequences as needed |
|
1805 operator const Sequence<T>&() const { |
|
1806 return *reinterpret_cast<const Sequence<T>*>(this); |
|
1807 } |
|
1808 }; |
|
1809 |
|
1810 } // namespace binding_detail |
|
1811 |
|
1812 // Class used to trace sequences, with specializations for various |
|
1813 // sequence types. |
|
1814 template<typename T, |
|
1815 bool isDictionary=IsBaseOf<DictionaryBase, T>::value, |
|
1816 bool isTypedArray=IsBaseOf<AllTypedArraysBase, T>::value, |
|
1817 bool isOwningUnion=IsBaseOf<AllOwningUnionBase, T>::value> |
|
1818 class SequenceTracer |
|
1819 { |
|
1820 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1821 }; |
|
1822 |
|
1823 // sequence<object> or sequence<object?> |
|
1824 template<> |
|
1825 class SequenceTracer<JSObject*, false, false, false> |
|
1826 { |
|
1827 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1828 |
|
1829 public: |
|
1830 static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) { |
|
1831 for (; objp != end; ++objp) { |
|
1832 JS_CallObjectTracer(trc, objp, "sequence<object>"); |
|
1833 } |
|
1834 } |
|
1835 }; |
|
1836 |
|
1837 // sequence<any> |
|
1838 template<> |
|
1839 class SequenceTracer<JS::Value, false, false, false> |
|
1840 { |
|
1841 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1842 |
|
1843 public: |
|
1844 static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) { |
|
1845 for (; valp != end; ++valp) { |
|
1846 JS_CallValueTracer(trc, valp, "sequence<any>"); |
|
1847 } |
|
1848 } |
|
1849 }; |
|
1850 |
|
1851 // sequence<sequence<T>> |
|
1852 template<typename T> |
|
1853 class SequenceTracer<Sequence<T>, false, false, false> |
|
1854 { |
|
1855 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1856 |
|
1857 public: |
|
1858 static void TraceSequence(JSTracer* trc, Sequence<T>* seqp, Sequence<T>* end) { |
|
1859 for (; seqp != end; ++seqp) { |
|
1860 DoTraceSequence(trc, *seqp); |
|
1861 } |
|
1862 } |
|
1863 }; |
|
1864 |
|
1865 // sequence<sequence<T>> as return value |
|
1866 template<typename T> |
|
1867 class SequenceTracer<nsTArray<T>, false, false, false> |
|
1868 { |
|
1869 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1870 |
|
1871 public: |
|
1872 static void TraceSequence(JSTracer* trc, nsTArray<T>* seqp, nsTArray<T>* end) { |
|
1873 for (; seqp != end; ++seqp) { |
|
1874 DoTraceSequence(trc, *seqp); |
|
1875 } |
|
1876 } |
|
1877 }; |
|
1878 |
|
1879 // sequence<someDictionary> |
|
1880 template<typename T> |
|
1881 class SequenceTracer<T, true, false, false> |
|
1882 { |
|
1883 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1884 |
|
1885 public: |
|
1886 static void TraceSequence(JSTracer* trc, T* dictp, T* end) { |
|
1887 for (; dictp != end; ++dictp) { |
|
1888 dictp->TraceDictionary(trc); |
|
1889 } |
|
1890 } |
|
1891 }; |
|
1892 |
|
1893 // sequence<SomeTypedArray> |
|
1894 template<typename T> |
|
1895 class SequenceTracer<T, false, true, false> |
|
1896 { |
|
1897 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1898 |
|
1899 public: |
|
1900 static void TraceSequence(JSTracer* trc, T* arrayp, T* end) { |
|
1901 for (; arrayp != end; ++arrayp) { |
|
1902 arrayp->TraceSelf(trc); |
|
1903 } |
|
1904 } |
|
1905 }; |
|
1906 |
|
1907 // sequence<SomeOwningUnion> |
|
1908 template<typename T> |
|
1909 class SequenceTracer<T, false, false, true> |
|
1910 { |
|
1911 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1912 |
|
1913 public: |
|
1914 static void TraceSequence(JSTracer* trc, T* arrayp, T* end) { |
|
1915 for (; arrayp != end; ++arrayp) { |
|
1916 arrayp->TraceUnion(trc); |
|
1917 } |
|
1918 } |
|
1919 }; |
|
1920 |
|
1921 // sequence<T?> with T? being a Nullable<T> |
|
1922 template<typename T> |
|
1923 class SequenceTracer<Nullable<T>, false, false, false> |
|
1924 { |
|
1925 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1926 |
|
1927 public: |
|
1928 static void TraceSequence(JSTracer* trc, Nullable<T>* seqp, |
|
1929 Nullable<T>* end) { |
|
1930 for (; seqp != end; ++seqp) { |
|
1931 if (!seqp->IsNull()) { |
|
1932 // Pretend like we actually have a length-one sequence here so |
|
1933 // we can do template instantiation correctly for T. |
|
1934 T& val = seqp->Value(); |
|
1935 T* ptr = &val; |
|
1936 SequenceTracer<T>::TraceSequence(trc, ptr, ptr+1); |
|
1937 } |
|
1938 } |
|
1939 } |
|
1940 }; |
|
1941 |
|
1942 // XXXbz It's not clear whether it's better to add a pldhash dependency here |
|
1943 // (for PLDHashOperator) or add a BindingUtils.h dependency (for |
|
1944 // SequenceTracer) to MozMap.h... |
|
1945 template<typename T> |
|
1946 static PLDHashOperator |
|
1947 TraceMozMapValue(T* aValue, void* aClosure) |
|
1948 { |
|
1949 JSTracer* trc = static_cast<JSTracer*>(aClosure); |
|
1950 // Act like it's a one-element sequence to leverage all that infrastructure. |
|
1951 SequenceTracer<T>::TraceSequence(trc, aValue, aValue + 1); |
|
1952 return PL_DHASH_NEXT; |
|
1953 } |
|
1954 |
|
1955 // sequence<MozMap> |
|
1956 template<typename T> |
|
1957 class SequenceTracer<MozMap<T>, false, false, false> |
|
1958 { |
|
1959 explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated |
|
1960 |
|
1961 public: |
|
1962 static void TraceSequence(JSTracer* trc, MozMap<T>* seqp, MozMap<T>* end) { |
|
1963 for (; seqp != end; ++seqp) { |
|
1964 seqp->EnumerateValues(TraceMozMapValue<T>, trc); |
|
1965 } |
|
1966 } |
|
1967 }; |
|
1968 |
|
1969 template<typename T> |
|
1970 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq) |
|
1971 { |
|
1972 SequenceTracer<T>::TraceSequence(trc, seq.Elements(), |
|
1973 seq.Elements() + seq.Length()); |
|
1974 } |
|
1975 |
|
1976 template<typename T> |
|
1977 void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq) |
|
1978 { |
|
1979 SequenceTracer<T>::TraceSequence(trc, seq.Elements(), |
|
1980 seq.Elements() + seq.Length()); |
|
1981 } |
|
1982 |
|
1983 // Rooter class for sequences; this is what we mostly use in the codegen |
|
1984 template<typename T> |
|
1985 class MOZ_STACK_CLASS SequenceRooter : private JS::CustomAutoRooter |
|
1986 { |
|
1987 public: |
|
1988 SequenceRooter(JSContext *aCx, FallibleTArray<T>* aSequence |
|
1989 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
1990 : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
1991 mFallibleArray(aSequence), |
|
1992 mSequenceType(eFallibleArray) |
|
1993 { |
|
1994 } |
|
1995 |
|
1996 SequenceRooter(JSContext *aCx, InfallibleTArray<T>* aSequence |
|
1997 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
1998 : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
1999 mInfallibleArray(aSequence), |
|
2000 mSequenceType(eInfallibleArray) |
|
2001 { |
|
2002 } |
|
2003 |
|
2004 SequenceRooter(JSContext *aCx, Nullable<nsTArray<T> >* aSequence |
|
2005 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
2006 : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
2007 mNullableArray(aSequence), |
|
2008 mSequenceType(eNullableArray) |
|
2009 { |
|
2010 } |
|
2011 |
|
2012 private: |
|
2013 enum SequenceType { |
|
2014 eInfallibleArray, |
|
2015 eFallibleArray, |
|
2016 eNullableArray |
|
2017 }; |
|
2018 |
|
2019 virtual void trace(JSTracer *trc) MOZ_OVERRIDE |
|
2020 { |
|
2021 if (mSequenceType == eFallibleArray) { |
|
2022 DoTraceSequence(trc, *mFallibleArray); |
|
2023 } else if (mSequenceType == eInfallibleArray) { |
|
2024 DoTraceSequence(trc, *mInfallibleArray); |
|
2025 } else { |
|
2026 MOZ_ASSERT(mSequenceType == eNullableArray); |
|
2027 if (!mNullableArray->IsNull()) { |
|
2028 DoTraceSequence(trc, mNullableArray->Value()); |
|
2029 } |
|
2030 } |
|
2031 } |
|
2032 |
|
2033 union { |
|
2034 InfallibleTArray<T>* mInfallibleArray; |
|
2035 FallibleTArray<T>* mFallibleArray; |
|
2036 Nullable<nsTArray<T> >* mNullableArray; |
|
2037 }; |
|
2038 |
|
2039 SequenceType mSequenceType; |
|
2040 }; |
|
2041 |
|
2042 // Rooter class for MozMap; this is what we mostly use in the codegen. |
|
2043 template<typename T> |
|
2044 class MOZ_STACK_CLASS MozMapRooter : private JS::CustomAutoRooter |
|
2045 { |
|
2046 public: |
|
2047 MozMapRooter(JSContext *aCx, MozMap<T>* aMozMap |
|
2048 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
2049 : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
2050 mMozMap(aMozMap), |
|
2051 mMozMapType(eMozMap) |
|
2052 { |
|
2053 } |
|
2054 |
|
2055 MozMapRooter(JSContext *aCx, Nullable<MozMap<T>>* aMozMap |
|
2056 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
2057 : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT), |
|
2058 mNullableMozMap(aMozMap), |
|
2059 mMozMapType(eNullableMozMap) |
|
2060 { |
|
2061 } |
|
2062 |
|
2063 private: |
|
2064 enum MozMapType { |
|
2065 eMozMap, |
|
2066 eNullableMozMap |
|
2067 }; |
|
2068 |
|
2069 virtual void trace(JSTracer *trc) MOZ_OVERRIDE |
|
2070 { |
|
2071 MozMap<T>* mozMap; |
|
2072 if (mMozMapType == eMozMap) { |
|
2073 mozMap = mMozMap; |
|
2074 } else { |
|
2075 MOZ_ASSERT(mMozMapType == eNullableMozMap); |
|
2076 if (mNullableMozMap->IsNull()) { |
|
2077 // Nothing to do |
|
2078 return; |
|
2079 } |
|
2080 mozMap = &mNullableMozMap->Value(); |
|
2081 } |
|
2082 |
|
2083 mozMap->EnumerateValues(TraceMozMapValue<T>, trc); |
|
2084 } |
|
2085 |
|
2086 union { |
|
2087 MozMap<T>* mMozMap; |
|
2088 Nullable<MozMap<T>>* mNullableMozMap; |
|
2089 }; |
|
2090 |
|
2091 MozMapType mMozMapType; |
|
2092 }; |
|
2093 |
|
2094 template<typename T> |
|
2095 class MOZ_STACK_CLASS RootedUnion : public T, |
|
2096 private JS::CustomAutoRooter |
|
2097 { |
|
2098 public: |
|
2099 RootedUnion(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : |
|
2100 T(), |
|
2101 JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) |
|
2102 { |
|
2103 } |
|
2104 |
|
2105 virtual void trace(JSTracer *trc) MOZ_OVERRIDE |
|
2106 { |
|
2107 this->TraceUnion(trc); |
|
2108 } |
|
2109 }; |
|
2110 |
|
2111 template<typename T> |
|
2112 class MOZ_STACK_CLASS NullableRootedUnion : public Nullable<T>, |
|
2113 private JS::CustomAutoRooter |
|
2114 { |
|
2115 public: |
|
2116 NullableRootedUnion(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : |
|
2117 Nullable<T>(), |
|
2118 JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) |
|
2119 { |
|
2120 } |
|
2121 |
|
2122 virtual void trace(JSTracer *trc) MOZ_OVERRIDE |
|
2123 { |
|
2124 if (!this->IsNull()) { |
|
2125 this->Value().TraceUnion(trc); |
|
2126 } |
|
2127 } |
|
2128 }; |
|
2129 |
|
2130 inline bool |
|
2131 IdEquals(jsid id, const char* string) |
|
2132 { |
|
2133 return JSID_IS_STRING(id) && |
|
2134 JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), string); |
|
2135 } |
|
2136 |
|
2137 inline bool |
|
2138 AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name) |
|
2139 { |
|
2140 return vector.growBy(1) && |
|
2141 InternJSString(cx, vector[vector.length() - 1], name); |
|
2142 } |
|
2143 |
|
2144 // Implementation of the bits that XrayWrapper needs |
|
2145 |
|
2146 /** |
|
2147 * This resolves indexed or named properties of obj. |
|
2148 * |
|
2149 * wrapper is the Xray JS object. |
|
2150 * obj is the target object of the Xray, a binding's instance object or a |
|
2151 * interface or interface prototype object. |
|
2152 */ |
|
2153 bool |
|
2154 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
|
2155 JS::Handle<JSObject*> obj, |
|
2156 JS::Handle<jsid> id, |
|
2157 JS::MutableHandle<JSPropertyDescriptor> desc); |
|
2158 |
|
2159 /** |
|
2160 * This resolves operations, attributes and constants of the interfaces for obj. |
|
2161 * |
|
2162 * wrapper is the Xray JS object. |
|
2163 * obj is the target object of the Xray, a binding's instance object or a |
|
2164 * interface or interface prototype object. |
|
2165 */ |
|
2166 bool |
|
2167 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
|
2168 JS::Handle<JSObject*> obj, |
|
2169 JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc); |
|
2170 |
|
2171 /** |
|
2172 * Define a property on obj through an Xray wrapper. |
|
2173 * |
|
2174 * wrapper is the Xray JS object. |
|
2175 * obj is the target object of the Xray, a binding's instance object or a |
|
2176 * interface or interface prototype object. |
|
2177 * defined will be set to true if a property was set as a result of this call. |
|
2178 */ |
|
2179 bool |
|
2180 XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, |
|
2181 JS::Handle<JSObject*> obj, JS::Handle<jsid> id, |
|
2182 JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined); |
|
2183 |
|
2184 /** |
|
2185 * This enumerates indexed or named properties of obj and operations, attributes |
|
2186 * and constants of the interfaces for obj. |
|
2187 * |
|
2188 * wrapper is the Xray JS object. |
|
2189 * obj is the target object of the Xray, a binding's instance object or a |
|
2190 * interface or interface prototype object. |
|
2191 * flags are JSITER_* flags. |
|
2192 */ |
|
2193 bool |
|
2194 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper, |
|
2195 JS::Handle<JSObject*> obj, |
|
2196 unsigned flags, JS::AutoIdVector& props); |
|
2197 |
|
2198 extern NativePropertyHooks sWorkerNativePropertyHooks; |
|
2199 |
|
2200 // We use one constructor JSNative to represent all DOM interface objects (so |
|
2201 // we can easily detect when we need to wrap them in an Xray wrapper). We store |
|
2202 // the real JSNative in the mNative member of a JSNativeHolder in the |
|
2203 // CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a |
|
2204 // specific interface object. We also store the NativeProperties in the |
|
2205 // JSNativeHolder. |
|
2206 // Note that some interface objects are not yet a JSFunction but a normal |
|
2207 // JSObject with a DOMJSClass, those do not use these slots. |
|
2208 |
|
2209 enum { |
|
2210 CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0 |
|
2211 }; |
|
2212 |
|
2213 bool |
|
2214 Constructor(JSContext* cx, unsigned argc, JS::Value* vp); |
|
2215 |
|
2216 inline bool |
|
2217 UseDOMXray(JSObject* obj) |
|
2218 { |
|
2219 const js::Class* clasp = js::GetObjectClass(obj); |
|
2220 return IsDOMClass(clasp) || |
|
2221 JS_IsNativeFunction(obj, Constructor) || |
|
2222 IsDOMIfaceAndProtoClass(clasp); |
|
2223 } |
|
2224 |
|
2225 #ifdef DEBUG |
|
2226 inline bool |
|
2227 HasConstructor(JSObject* obj) |
|
2228 { |
|
2229 return JS_IsNativeFunction(obj, Constructor) || |
|
2230 js::GetObjectClass(obj)->construct; |
|
2231 } |
|
2232 #endif |
|
2233 |
|
2234 // Transfer reference in ptr to smartPtr. |
|
2235 template<class T> |
|
2236 inline void |
|
2237 Take(nsRefPtr<T>& smartPtr, T* ptr) |
|
2238 { |
|
2239 smartPtr = dont_AddRef(ptr); |
|
2240 } |
|
2241 |
|
2242 // Transfer ownership of ptr to smartPtr. |
|
2243 template<class T> |
|
2244 inline void |
|
2245 Take(nsAutoPtr<T>& smartPtr, T* ptr) |
|
2246 { |
|
2247 smartPtr = ptr; |
|
2248 } |
|
2249 |
|
2250 inline void |
|
2251 MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*) |
|
2252 { |
|
2253 } |
|
2254 |
|
2255 /** |
|
2256 * This creates a JSString containing the value that the toString function for |
|
2257 * obj should create according to the WebIDL specification, ignoring any |
|
2258 * modifications by script. The value is prefixed with pre and postfixed with |
|
2259 * post, unless this is called for an object that has a stringifier. It is |
|
2260 * specifically for use by Xray code. |
|
2261 * |
|
2262 * wrapper is the Xray JS object. |
|
2263 * obj is the target object of the Xray, a binding's instance object or a |
|
2264 * interface or interface prototype object. |
|
2265 * pre is a string that should be prefixed to the value. |
|
2266 * post is a string that should be prefixed to the value. |
|
2267 * v contains the JSString for the value if the function returns true. |
|
2268 */ |
|
2269 bool |
|
2270 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper, |
|
2271 JS::Handle<JSObject*> obj, const char* pre, |
|
2272 const char* post, |
|
2273 JS::MutableHandle<JS::Value> v); |
|
2274 |
|
2275 HAS_MEMBER(JSBindingFinalized) |
|
2276 |
|
2277 template<class T, bool hasCallback=HasJSBindingFinalizedMember<T>::Value> |
|
2278 struct JSBindingFinalized |
|
2279 { |
|
2280 static void Finalized(T* self) |
|
2281 { |
|
2282 } |
|
2283 }; |
|
2284 |
|
2285 template<class T> |
|
2286 struct JSBindingFinalized<T, true> |
|
2287 { |
|
2288 static void Finalized(T* self) |
|
2289 { |
|
2290 self->JSBindingFinalized(); |
|
2291 } |
|
2292 }; |
|
2293 |
|
2294 // Helpers for creating a const version of a type. |
|
2295 template<typename T> |
|
2296 const T& Constify(T& arg) |
|
2297 { |
|
2298 return arg; |
|
2299 } |
|
2300 |
|
2301 // Helper for turning (Owning)NonNull<T> into T& |
|
2302 template<typename T> |
|
2303 T& NonNullHelper(T& aArg) |
|
2304 { |
|
2305 return aArg; |
|
2306 } |
|
2307 |
|
2308 template<typename T> |
|
2309 T& NonNullHelper(NonNull<T>& aArg) |
|
2310 { |
|
2311 return aArg; |
|
2312 } |
|
2313 |
|
2314 template<typename T> |
|
2315 const T& NonNullHelper(const NonNull<T>& aArg) |
|
2316 { |
|
2317 return aArg; |
|
2318 } |
|
2319 |
|
2320 template<typename T> |
|
2321 T& NonNullHelper(OwningNonNull<T>& aArg) |
|
2322 { |
|
2323 return aArg; |
|
2324 } |
|
2325 |
|
2326 template<typename T> |
|
2327 const T& NonNullHelper(const OwningNonNull<T>& aArg) |
|
2328 { |
|
2329 return aArg; |
|
2330 } |
|
2331 |
|
2332 inline |
|
2333 void NonNullHelper(NonNull<binding_detail::FakeDependentString>& aArg) |
|
2334 { |
|
2335 // This overload is here to make sure that we never end up applying |
|
2336 // NonNullHelper to a NonNull<binding_detail::FakeDependentString>. If we |
|
2337 // try to, it should fail to compile, since presumably the caller will try to |
|
2338 // use our nonexistent return value. |
|
2339 } |
|
2340 |
|
2341 inline |
|
2342 void NonNullHelper(const NonNull<binding_detail::FakeDependentString>& aArg) |
|
2343 { |
|
2344 // This overload is here to make sure that we never end up applying |
|
2345 // NonNullHelper to a NonNull<binding_detail::FakeDependentString>. If we |
|
2346 // try to, it should fail to compile, since presumably the caller will try to |
|
2347 // use our nonexistent return value. |
|
2348 } |
|
2349 |
|
2350 inline |
|
2351 void NonNullHelper(binding_detail::FakeDependentString& aArg) |
|
2352 { |
|
2353 // This overload is here to make sure that we never end up applying |
|
2354 // NonNullHelper to a FakeDependentString before we've constified it. If we |
|
2355 // try to, it should fail to compile, since presumably the caller will try to |
|
2356 // use our nonexistent return value. |
|
2357 } |
|
2358 |
|
2359 MOZ_ALWAYS_INLINE |
|
2360 const nsAString& NonNullHelper(const binding_detail::FakeDependentString& aArg) |
|
2361 { |
|
2362 return aArg; |
|
2363 } |
|
2364 |
|
2365 // Reparent the wrapper of aObj to whatever its native now thinks its |
|
2366 // parent should be. |
|
2367 nsresult |
|
2368 ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObj); |
|
2369 |
|
2370 /** |
|
2371 * Used to implement the hasInstance hook of an interface object. |
|
2372 * |
|
2373 * instance should not be a security wrapper. |
|
2374 */ |
|
2375 bool |
|
2376 InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, |
|
2377 JS::Handle<JSObject*> instance, |
|
2378 bool* bp); |
|
2379 bool |
|
2380 InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp, |
|
2381 bool* bp); |
|
2382 bool |
|
2383 InterfaceHasInstance(JSContext* cx, int prototypeID, int depth, |
|
2384 JS::Handle<JSObject*> instance, |
|
2385 bool* bp); |
|
2386 |
|
2387 // Helper for lenient getters/setters to report to console. If this |
|
2388 // returns false, we couldn't even get a global. |
|
2389 bool |
|
2390 ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj); |
|
2391 |
|
2392 inline JSObject* |
|
2393 GetUnforgeableHolder(JSObject* aGlobal, prototypes::ID aId) |
|
2394 { |
|
2395 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal); |
|
2396 JSObject* interfaceProto = protoAndIfaceCache.EntrySlotMustExist(aId); |
|
2397 return &js::GetReservedSlot(interfaceProto, |
|
2398 DOM_INTERFACE_PROTO_SLOTS_BASE).toObject(); |
|
2399 } |
|
2400 |
|
2401 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL |
|
2402 // interface, get the nsPIDOMWindow corresponding to the content side, if any. |
|
2403 // A false return means an exception was thrown. |
|
2404 bool |
|
2405 GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj, |
|
2406 nsPIDOMWindow** window); |
|
2407 |
|
2408 void |
|
2409 ConstructJSImplementation(JSContext* aCx, const char* aContractId, |
|
2410 nsPIDOMWindow* aWindow, |
|
2411 JS::MutableHandle<JSObject*> aObject, |
|
2412 ErrorResult& aRv); |
|
2413 |
|
2414 already_AddRefed<nsPIDOMWindow> |
|
2415 ConstructJSImplementation(JSContext* aCx, const char* aContractId, |
|
2416 const GlobalObject& aGlobal, |
|
2417 JS::MutableHandle<JSObject*> aObject, |
|
2418 ErrorResult& aRv); |
|
2419 |
|
2420 /** |
|
2421 * Convert an nsCString to jsval, returning true on success. |
|
2422 * These functions are intended for ByteString implementations. |
|
2423 * As such, the string is not UTF-8 encoded. Any UTF8 strings passed to these |
|
2424 * methods will be mangled. |
|
2425 */ |
|
2426 bool NonVoidByteStringToJsval(JSContext *cx, const nsACString &str, |
|
2427 JS::MutableHandle<JS::Value> rval); |
|
2428 inline bool ByteStringToJsval(JSContext *cx, const nsACString &str, |
|
2429 JS::MutableHandle<JS::Value> rval) |
|
2430 { |
|
2431 if (str.IsVoid()) { |
|
2432 rval.setNull(); |
|
2433 return true; |
|
2434 } |
|
2435 return NonVoidByteStringToJsval(cx, str, rval); |
|
2436 } |
|
2437 |
|
2438 template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value> |
|
2439 struct PreserveWrapperHelper |
|
2440 { |
|
2441 static void PreserveWrapper(T* aObject) |
|
2442 { |
|
2443 aObject->PreserveWrapper(aObject, NS_CYCLE_COLLECTION_PARTICIPANT(T)); |
|
2444 } |
|
2445 }; |
|
2446 |
|
2447 template<class T> |
|
2448 struct PreserveWrapperHelper<T, true> |
|
2449 { |
|
2450 static void PreserveWrapper(T* aObject) |
|
2451 { |
|
2452 aObject->PreserveWrapper(reinterpret_cast<nsISupports*>(aObject)); |
|
2453 } |
|
2454 }; |
|
2455 |
|
2456 template<class T> |
|
2457 void PreserveWrapper(T* aObject) |
|
2458 { |
|
2459 PreserveWrapperHelper<T>::PreserveWrapper(aObject); |
|
2460 } |
|
2461 |
|
2462 template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value> |
|
2463 struct CastingAssertions |
|
2464 { |
|
2465 static bool ToSupportsIsCorrect(T*) |
|
2466 { |
|
2467 return true; |
|
2468 } |
|
2469 static bool ToSupportsIsOnPrimaryInheritanceChain(T*, nsWrapperCache*) |
|
2470 { |
|
2471 return true; |
|
2472 } |
|
2473 }; |
|
2474 |
|
2475 template<class T> |
|
2476 struct CastingAssertions<T, true> |
|
2477 { |
|
2478 static bool ToSupportsIsCorrect(T* aObject) |
|
2479 { |
|
2480 return ToSupports(aObject) == reinterpret_cast<nsISupports*>(aObject); |
|
2481 } |
|
2482 static bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject, |
|
2483 nsWrapperCache* aCache) |
|
2484 { |
|
2485 return reinterpret_cast<void*>(aObject) != aCache; |
|
2486 } |
|
2487 }; |
|
2488 |
|
2489 template<class T> |
|
2490 bool |
|
2491 ToSupportsIsCorrect(T* aObject) |
|
2492 { |
|
2493 return CastingAssertions<T>::ToSupportsIsCorrect(aObject); |
|
2494 } |
|
2495 |
|
2496 template<class T> |
|
2497 bool |
|
2498 ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache) |
|
2499 { |
|
2500 return CastingAssertions<T>::ToSupportsIsOnPrimaryInheritanceChain(aObject, |
|
2501 aCache); |
|
2502 } |
|
2503 |
|
2504 template<class T, template <typename> class SmartPtr, |
|
2505 bool isISupports=IsBaseOf<nsISupports, T>::value> |
|
2506 class DeferredFinalizer |
|
2507 { |
|
2508 typedef nsTArray<SmartPtr<T> > SmartPtrArray; |
|
2509 |
|
2510 static void* |
|
2511 AppendDeferredFinalizePointer(void* aData, void* aObject) |
|
2512 { |
|
2513 SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData); |
|
2514 if (!pointers) { |
|
2515 pointers = new SmartPtrArray(); |
|
2516 } |
|
2517 |
|
2518 T* self = static_cast<T*>(aObject); |
|
2519 |
|
2520 SmartPtr<T>* defer = pointers->AppendElement(); |
|
2521 Take(*defer, self); |
|
2522 return pointers; |
|
2523 } |
|
2524 static bool |
|
2525 DeferredFinalize(uint32_t aSlice, void* aData) |
|
2526 { |
|
2527 MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with aSlice == 0"); |
|
2528 SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData); |
|
2529 uint32_t oldLen = pointers->Length(); |
|
2530 if (oldLen < aSlice) { |
|
2531 aSlice = oldLen; |
|
2532 } |
|
2533 uint32_t newLen = oldLen - aSlice; |
|
2534 pointers->RemoveElementsAt(newLen, aSlice); |
|
2535 if (newLen == 0) { |
|
2536 delete pointers; |
|
2537 return true; |
|
2538 } |
|
2539 return false; |
|
2540 } |
|
2541 |
|
2542 public: |
|
2543 static void |
|
2544 AddForDeferredFinalization(T* aObject) |
|
2545 { |
|
2546 cyclecollector::DeferredFinalize(AppendDeferredFinalizePointer, |
|
2547 DeferredFinalize, aObject); |
|
2548 } |
|
2549 }; |
|
2550 |
|
2551 template<class T, template <typename> class SmartPtr> |
|
2552 class DeferredFinalizer<T, SmartPtr, true> |
|
2553 { |
|
2554 public: |
|
2555 static void |
|
2556 AddForDeferredFinalization(T* aObject) |
|
2557 { |
|
2558 cyclecollector::DeferredFinalize(reinterpret_cast<nsISupports*>(aObject)); |
|
2559 } |
|
2560 }; |
|
2561 |
|
2562 template<class T, template <typename> class SmartPtr> |
|
2563 static void |
|
2564 AddForDeferredFinalization(T* aObject) |
|
2565 { |
|
2566 DeferredFinalizer<T, SmartPtr>::AddForDeferredFinalization(aObject); |
|
2567 } |
|
2568 |
|
2569 // This returns T's CC participant if it participates in CC or null if it |
|
2570 // doesn't. This also returns null for classes that don't inherit from |
|
2571 // nsISupports (QI should be used to get the participant for those). |
|
2572 template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value> |
|
2573 class GetCCParticipant |
|
2574 { |
|
2575 // Helper for GetCCParticipant for classes that participate in CC. |
|
2576 template<class U> |
|
2577 static MOZ_CONSTEXPR nsCycleCollectionParticipant* |
|
2578 GetHelper(int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy=nullptr) |
|
2579 { |
|
2580 return T::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant(); |
|
2581 } |
|
2582 // Helper for GetCCParticipant for classes that don't participate in CC. |
|
2583 template<class U> |
|
2584 static MOZ_CONSTEXPR nsCycleCollectionParticipant* |
|
2585 GetHelper(double) |
|
2586 { |
|
2587 return nullptr; |
|
2588 } |
|
2589 |
|
2590 public: |
|
2591 static MOZ_CONSTEXPR nsCycleCollectionParticipant* |
|
2592 Get() |
|
2593 { |
|
2594 // Passing int() here will try to call the GetHelper that takes an int as |
|
2595 // its firt argument. If T doesn't participate in CC then substitution for |
|
2596 // the second argument (with a default value) will fail and because of |
|
2597 // SFINAE the next best match (the variant taking a double) will be called. |
|
2598 return GetHelper<T>(int()); |
|
2599 } |
|
2600 }; |
|
2601 |
|
2602 template<class T> |
|
2603 class GetCCParticipant<T, true> |
|
2604 { |
|
2605 public: |
|
2606 static MOZ_CONSTEXPR nsCycleCollectionParticipant* |
|
2607 Get() |
|
2608 { |
|
2609 return nullptr; |
|
2610 } |
|
2611 }; |
|
2612 |
|
2613 /* |
|
2614 * Helper function for testing whether the given object comes from a |
|
2615 * privileged app. |
|
2616 */ |
|
2617 bool |
|
2618 IsInPrivilegedApp(JSContext* aCx, JSObject* aObj); |
|
2619 |
|
2620 /* |
|
2621 * Helper function for testing whether the given object comes from a |
|
2622 * certified app. |
|
2623 */ |
|
2624 bool |
|
2625 IsInCertifiedApp(JSContext* aCx, JSObject* aObj); |
|
2626 |
|
2627 void |
|
2628 TraceGlobal(JSTracer* aTrc, JSObject* aObj); |
|
2629 |
|
2630 void |
|
2631 FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj); |
|
2632 |
|
2633 bool |
|
2634 ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj, |
|
2635 JS::Handle<jsid> aId, JS::MutableHandle<JSObject*> aObjp); |
|
2636 |
|
2637 bool |
|
2638 EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj); |
|
2639 |
|
2640 template <class T, JS::Handle<JSObject*> (*ProtoGetter)(JSContext*, |
|
2641 JS::Handle<JSObject*>)> |
|
2642 JSObject* |
|
2643 CreateGlobal(JSContext* aCx, T* aObject, nsWrapperCache* aCache, |
|
2644 const JSClass* aClass, JS::CompartmentOptions& aOptions, |
|
2645 JSPrincipals* aPrincipal) |
|
2646 { |
|
2647 MOZ_ASSERT(!NS_IsMainThread()); |
|
2648 |
|
2649 aOptions.setTrace(TraceGlobal); |
|
2650 |
|
2651 JS::Rooted<JSObject*> global(aCx, |
|
2652 JS_NewGlobalObject(aCx, aClass, aPrincipal, JS::DontFireOnNewGlobalHook, |
|
2653 aOptions)); |
|
2654 if (!global) { |
|
2655 NS_WARNING("Failed to create global"); |
|
2656 return nullptr; |
|
2657 } |
|
2658 |
|
2659 JSAutoCompartment ac(aCx, global); |
|
2660 |
|
2661 dom::AllocateProtoAndIfaceCache(global, ProtoAndIfaceCache::WindowLike); |
|
2662 |
|
2663 js::SetReservedSlot(global, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject)); |
|
2664 NS_ADDREF(aObject); |
|
2665 |
|
2666 aCache->SetIsDOMBinding(); |
|
2667 aCache->SetWrapper(global); |
|
2668 |
|
2669 /* Intl API is broken and makes this fail intermittently, see bug 934889. |
|
2670 if (!JS_InitStandardClasses(aCx, global)) { |
|
2671 NS_WARNING("Failed to init standard classes"); |
|
2672 return nullptr; |
|
2673 } |
|
2674 */ |
|
2675 |
|
2676 JS::Handle<JSObject*> proto = ProtoGetter(aCx, global); |
|
2677 NS_ENSURE_TRUE(proto, nullptr); |
|
2678 |
|
2679 if (!JS_SetPrototype(aCx, global, proto)) { |
|
2680 NS_WARNING("Failed to set proto"); |
|
2681 return nullptr; |
|
2682 } |
|
2683 |
|
2684 MOZ_ALWAYS_TRUE(TryPreserveWrapper(global)); |
|
2685 |
|
2686 MOZ_ASSERT(UnwrapDOMObjectToISupports(global)); |
|
2687 |
|
2688 return global; |
|
2689 } |
|
2690 |
|
2691 /* |
|
2692 * Holds a jsid that is initialized to an interned string, with conversion to |
|
2693 * Handle<jsid>. |
|
2694 */ |
|
2695 class InternedStringId |
|
2696 { |
|
2697 jsid id; |
|
2698 |
|
2699 public: |
|
2700 InternedStringId() : id(JSID_VOID) {} |
|
2701 |
|
2702 bool init(JSContext *cx, const char *string) { |
|
2703 JSString* str = JS_InternString(cx, string); |
|
2704 if (!str) |
|
2705 return false; |
|
2706 id = INTERNED_STRING_TO_JSID(cx, str); |
|
2707 return true; |
|
2708 } |
|
2709 |
|
2710 operator const jsid& () { |
|
2711 return id; |
|
2712 } |
|
2713 |
|
2714 operator JS::Handle<jsid> () { |
|
2715 /* This is safe because we have interned the string. */ |
|
2716 return JS::Handle<jsid>::fromMarkedLocation(&id); |
|
2717 } |
|
2718 }; |
|
2719 |
|
2720 bool |
|
2721 GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp); |
|
2722 |
|
2723 bool |
|
2724 GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp); |
|
2725 |
|
2726 bool |
|
2727 GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp); |
|
2728 |
|
2729 bool |
|
2730 GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp); |
|
2731 |
|
2732 bool |
|
2733 StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp); |
|
2734 |
|
2735 // ConvertExceptionToPromise should only be called when we have an error |
|
2736 // condition (e.g. returned false from a JSAPI method). Note that there may be |
|
2737 // no exception on cx, in which case this is an uncatchable failure that will |
|
2738 // simply be propagated. Otherwise this method will attempt to convert the |
|
2739 // exception to a Promise rejected with the exception that it will store in |
|
2740 // rval. |
|
2741 // |
|
2742 // promiseScope should be the scope in which the Promise should be created. |
|
2743 bool |
|
2744 ConvertExceptionToPromise(JSContext* cx, |
|
2745 JSObject* promiseScope, |
|
2746 JS::MutableHandle<JS::Value> rval); |
|
2747 |
|
2748 } // namespace dom |
|
2749 } // namespace mozilla |
|
2750 |
|
2751 #endif /* mozilla_dom_BindingUtils_h__ */ |