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