1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCWrappedNative.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2700 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Wrapper object for reflecting native xpcom objects into JavaScript. */ 1.11 + 1.12 +#include "xpcprivate.h" 1.13 +#include "nsWrapperCacheInlines.h" 1.14 +#include "XPCLog.h" 1.15 +#include "jsprf.h" 1.16 +#include "AccessCheck.h" 1.17 +#include "WrapperFactory.h" 1.18 +#include "XrayWrapper.h" 1.19 + 1.20 +#include "nsContentUtils.h" 1.21 +#include "nsCxPusher.h" 1.22 + 1.23 +#include <stdint.h> 1.24 +#include "mozilla/Likely.h" 1.25 +#include "mozilla/dom/BindingUtils.h" 1.26 +#include <algorithm> 1.27 + 1.28 +using namespace xpc; 1.29 +using namespace mozilla; 1.30 +using namespace mozilla::dom; 1.31 +using namespace JS; 1.32 + 1.33 +/***************************************************************************/ 1.34 + 1.35 +NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative) 1.36 + 1.37 +// No need to unlink the JS objects: if the XPCWrappedNative is cycle 1.38 +// collected then its mFlatJSObject will be cycle collected too and 1.39 +// finalization of the mFlatJSObject will unlink the JS objects (see 1.40 +// XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized). 1.41 +NS_IMETHODIMP_(void) 1.42 +NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(void *p) 1.43 +{ 1.44 + XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p); 1.45 + tmp->ExpireWrapper(); 1.46 +} 1.47 + 1.48 +NS_IMETHODIMP 1.49 +NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse 1.50 + (void *p, nsCycleCollectionTraversalCallback &cb) 1.51 +{ 1.52 + XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p); 1.53 + if (!tmp->IsValid()) 1.54 + return NS_OK; 1.55 + 1.56 + if (MOZ_UNLIKELY(cb.WantDebugInfo())) { 1.57 + char name[72]; 1.58 + XPCNativeScriptableInfo* si = tmp->GetScriptableInfo(); 1.59 + if (si) 1.60 + JS_snprintf(name, sizeof(name), "XPCWrappedNative (%s)", 1.61 + si->GetJSClass()->name); 1.62 + else 1.63 + JS_snprintf(name, sizeof(name), "XPCWrappedNative"); 1.64 + 1.65 + cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); 1.66 + } else { 1.67 + NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get()) 1.68 + } 1.69 + 1.70 + if (tmp->mRefCnt.get() > 1) { 1.71 + 1.72 + // If our refcount is > 1, our reference to the flat JS object is 1.73 + // considered "strong", and we're going to traverse it. 1.74 + // 1.75 + // If our refcount is <= 1, our reference to the flat JS object is 1.76 + // considered "weak", and we're *not* going to traverse it. 1.77 + // 1.78 + // This reasoning is in line with the slightly confusing lifecycle rules 1.79 + // for XPCWrappedNatives, described in a larger comment below and also 1.80 + // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping 1.81 + 1.82 + JSObject *obj = tmp->GetFlatJSObjectPreserveColor(); 1.83 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject"); 1.84 + cb.NoteJSChild(obj); 1.85 + } 1.86 + 1.87 + // XPCWrappedNative keeps its native object alive. 1.88 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity"); 1.89 + cb.NoteXPCOMChild(tmp->GetIdentityObject()); 1.90 + 1.91 + tmp->NoteTearoffs(cb); 1.92 + 1.93 + return NS_OK; 1.94 +} 1.95 + 1.96 +void 1.97 +XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb) 1.98 +{ 1.99 + // Tearoffs hold their native object alive. If their JS object hasn't been 1.100 + // finalized yet we'll note the edge between the JS object and the native 1.101 + // (see nsXPConnect::Traverse), but if their JS object has been finalized 1.102 + // then the tearoff is only reachable through the XPCWrappedNative, so we 1.103 + // record an edge here. 1.104 + XPCWrappedNativeTearOffChunk* chunk; 1.105 + for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { 1.106 + XPCWrappedNativeTearOff* to = chunk->mTearOffs; 1.107 + for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) { 1.108 + JSObject* jso = to->GetJSObjectPreserveColor(); 1.109 + if (!jso) { 1.110 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative"); 1.111 + cb.NoteXPCOMChild(to->GetNative()); 1.112 + } 1.113 + } 1.114 + } 1.115 +} 1.116 + 1.117 +#ifdef XPC_CHECK_CLASSINFO_CLAIMS 1.118 +static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper); 1.119 +#else 1.120 +#define DEBUG_CheckClassInfoClaims(wrapper) ((void)0) 1.121 +#endif 1.122 + 1.123 +/***************************************************************************/ 1.124 +static nsresult 1.125 +FinishCreate(XPCWrappedNativeScope* Scope, 1.126 + XPCNativeInterface* Interface, 1.127 + nsWrapperCache *cache, 1.128 + XPCWrappedNative* inWrapper, 1.129 + XPCWrappedNative** resultWrapper); 1.130 + 1.131 +// static 1.132 +// 1.133 +// This method handles the special case of wrapping a new global object. 1.134 +// 1.135 +// The normal code path for wrapping natives goes through 1.136 +// XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed, 1.137 +// and finally into XPCWrappedNative::Init. Unfortunately, this path assumes 1.138 +// very early on that we have an XPCWrappedNativeScope and corresponding global 1.139 +// JS object, which are the very things we need to create here. So we special- 1.140 +// case the logic and do some things in a different order. 1.141 +nsresult 1.142 +XPCWrappedNative::WrapNewGlobal(xpcObjectHelper &nativeHelper, 1.143 + nsIPrincipal *principal, 1.144 + bool initStandardClasses, 1.145 + JS::CompartmentOptions& aOptions, 1.146 + XPCWrappedNative **wrappedGlobal) 1.147 +{ 1.148 + AutoJSContext cx; 1.149 + nsISupports *identity = nativeHelper.GetCanonical(); 1.150 + 1.151 + // The object should specify that it's meant to be global. 1.152 + MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT); 1.153 + 1.154 + // We shouldn't be reusing globals. 1.155 + MOZ_ASSERT(!nativeHelper.GetWrapperCache() || 1.156 + !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor()); 1.157 + 1.158 + // Put together the ScriptableCreateInfo... 1.159 + XPCNativeScriptableCreateInfo sciProto; 1.160 + XPCNativeScriptableCreateInfo sciMaybe; 1.161 + const XPCNativeScriptableCreateInfo& sciWrapper = 1.162 + GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(), 1.163 + sciProto, sciMaybe); 1.164 + 1.165 + // ...and then ScriptableInfo. We need all this stuff now because it's going 1.166 + // to tell us the JSClass of the object we're going to create. 1.167 + AutoMarkingNativeScriptableInfoPtr si(cx, XPCNativeScriptableInfo::Construct(&sciWrapper)); 1.168 + MOZ_ASSERT(si.get()); 1.169 + 1.170 + // Finally, we get to the JSClass. 1.171 + const JSClass *clasp = si->GetJSClass(); 1.172 + MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL); 1.173 + 1.174 + // Create the global. 1.175 + aOptions.setTrace(XPCWrappedNative::Trace); 1.176 + RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions)); 1.177 + if (!global) 1.178 + return NS_ERROR_FAILURE; 1.179 + XPCWrappedNativeScope *scope = GetCompartmentPrivate(global)->scope; 1.180 + 1.181 + // Immediately enter the global's compartment, so that everything else we 1.182 + // create ends up there. 1.183 + JSAutoCompartment ac(cx, global); 1.184 + 1.185 + // If requested, initialize the standard classes on the global. 1.186 + if (initStandardClasses && ! JS_InitStandardClasses(cx, global)) 1.187 + return NS_ERROR_FAILURE; 1.188 + 1.189 + // Make a proto. 1.190 + XPCWrappedNativeProto *proto = 1.191 + XPCWrappedNativeProto::GetNewOrUsed(scope, 1.192 + nativeHelper.GetClassInfo(), &sciProto, 1.193 + /* callPostCreatePrototype = */ false); 1.194 + if (!proto) 1.195 + return NS_ERROR_FAILURE; 1.196 + 1.197 + // Set up the prototype on the global. 1.198 + MOZ_ASSERT(proto->GetJSProtoObject()); 1.199 + RootedObject protoObj(cx, proto->GetJSProtoObject()); 1.200 + bool success = JS_SplicePrototype(cx, global, protoObj); 1.201 + if (!success) 1.202 + return NS_ERROR_FAILURE; 1.203 + 1.204 + // Construct the wrapper, which takes over the strong reference to the 1.205 + // native object. 1.206 + nsRefPtr<XPCWrappedNative> wrapper = 1.207 + new XPCWrappedNative(nativeHelper.forgetCanonical(), proto); 1.208 + 1.209 + // 1.210 + // We don't call ::Init() on this wrapper, because our setup requirements 1.211 + // are different for globals. We do our setup inline here, instead. 1.212 + // 1.213 + 1.214 + // Share mScriptableInfo with the proto. 1.215 + // 1.216 + // This is probably more trouble than it's worth, since we've already created 1.217 + // an XPCNativeScriptableInfo for ourselves. Moreover, most of that class is 1.218 + // shared internally via XPCNativeScriptableInfoShared, so the memory 1.219 + // savings are negligible. Nevertheless, this is what ::Init() does, and we 1.220 + // want to be as consistent as possible with that code. 1.221 + XPCNativeScriptableInfo* siProto = proto->GetScriptableInfo(); 1.222 + if (siProto && siProto->GetCallback() == sciWrapper.GetCallback()) { 1.223 + wrapper->mScriptableInfo = siProto; 1.224 + // XPCNativeScriptableShared instances live in a map, and are 1.225 + // GCed, but XPCNativeScriptableInfo is per-instance and must be 1.226 + // manually managed. If we're switching over to that of the proto, we 1.227 + // need to destroy the one we've allocated, and also null out the 1.228 + // AutoMarkingPtr, so that it doesn't try to mark garbage data. 1.229 + delete si; 1.230 + si = nullptr; 1.231 + } else { 1.232 + wrapper->mScriptableInfo = si; 1.233 + } 1.234 + 1.235 + // Set the JS object to the global we already created. 1.236 + wrapper->mFlatJSObject = global; 1.237 + wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); 1.238 + 1.239 + // Set the private to the XPCWrappedNative. 1.240 + JS_SetPrivate(global, wrapper); 1.241 + 1.242 + // There are dire comments elsewhere in the code about how a GC can 1.243 + // happen somewhere after wrapper initialization but before the wrapper is 1.244 + // added to the hashtable in FinishCreate(). It's not clear if that can 1.245 + // happen here, but let's just be safe for now. 1.246 + AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper); 1.247 + 1.248 + // Call the common Init finish routine. This mainly just does an AddRef 1.249 + // on behalf of XPConnect (the corresponding Release is in the finalizer 1.250 + // hook), but it does some other miscellaneous things too, so we don't 1.251 + // inline it. 1.252 + success = wrapper->FinishInit(); 1.253 + MOZ_ASSERT(success); 1.254 + 1.255 + // Go through some extra work to find the tearoff. This is kind of silly 1.256 + // on a conceptual level: the point of tearoffs is to cache the results 1.257 + // of QI-ing mIdentity to different interfaces, and we don't need that 1.258 + // since we're dealing with nsISupports. But lots of code expects tearoffs 1.259 + // to exist for everything, so we just follow along. 1.260 + XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(&NS_GET_IID(nsISupports)); 1.261 + MOZ_ASSERT(iface); 1.262 + nsresult status; 1.263 + success = wrapper->FindTearOff(iface, false, &status); 1.264 + if (!success) 1.265 + return status; 1.266 + 1.267 + // Call the common creation finish routine. This does all of the bookkeeping 1.268 + // like inserting the wrapper into the wrapper map and setting up the wrapper 1.269 + // cache. 1.270 + nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(), 1.271 + wrapper, wrappedGlobal); 1.272 + NS_ENSURE_SUCCESS(rv, rv); 1.273 + 1.274 + return NS_OK; 1.275 +} 1.276 + 1.277 +// static 1.278 +nsresult 1.279 +XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper, 1.280 + XPCWrappedNativeScope* Scope, 1.281 + XPCNativeInterface* Interface, 1.282 + XPCWrappedNative** resultWrapper) 1.283 +{ 1.284 + MOZ_ASSERT(Interface); 1.285 + AutoJSContext cx; 1.286 + nsWrapperCache *cache = helper.GetWrapperCache(); 1.287 + 1.288 + MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(), 1.289 + "We assume the caller already checked if it could get the " 1.290 + "wrapper from the cache."); 1.291 + 1.292 + nsresult rv; 1.293 + 1.294 + MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(), 1.295 + "XPCWrappedNative::GetNewOrUsed called during GC"); 1.296 + 1.297 + nsISupports *identity = helper.GetCanonical(); 1.298 + 1.299 + if (!identity) { 1.300 + NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!"); 1.301 + return NS_ERROR_FAILURE; 1.302 + } 1.303 + 1.304 + nsRefPtr<XPCWrappedNative> wrapper; 1.305 + 1.306 + Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap(); 1.307 + // Some things are nsWrapperCache subclasses but never use the cache, so go 1.308 + // ahead and check our map even if we have a cache and it has no existing 1.309 + // wrapper: we might have an XPCWrappedNative anyway. 1.310 + wrapper = map->Find(identity); 1.311 + 1.312 + if (wrapper) { 1.313 + if (!wrapper->FindTearOff(Interface, false, &rv)) { 1.314 + MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure"); 1.315 + return rv; 1.316 + } 1.317 + wrapper.forget(resultWrapper); 1.318 + return NS_OK; 1.319 + } 1.320 + 1.321 + // There is a chance that the object wants to have the self-same JSObject 1.322 + // reflection regardless of the scope into which we are reflecting it. 1.323 + // Many DOM objects require this. The scriptable helper specifies this 1.324 + // in preCreate by indicating a 'parent' of a particular scope. 1.325 + // 1.326 + // To handle this we need to get the scriptable helper early and ask it. 1.327 + // It is possible that we will then end up forwarding this entire call 1.328 + // to this same function but with a different scope. 1.329 + 1.330 + // If we are making a wrapper for the nsIClassInfo interface then 1.331 + // We *don't* want to have it use the prototype meant for instances 1.332 + // of that class. 1.333 + bool iidIsClassInfo = Interface->GetIID()->Equals(NS_GET_IID(nsIClassInfo)); 1.334 + uint32_t classInfoFlags; 1.335 + bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() && 1.336 + NS_SUCCEEDED(helper.GetClassInfo() 1.337 + ->GetFlags(&classInfoFlags)) && 1.338 + (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO); 1.339 + bool isClassInfo = iidIsClassInfo || isClassInfoSingleton; 1.340 + 1.341 + nsIClassInfo *info = helper.GetClassInfo(); 1.342 + 1.343 + XPCNativeScriptableCreateInfo sciProto; 1.344 + XPCNativeScriptableCreateInfo sci; 1.345 + 1.346 + // Gather scriptable create info if we are wrapping something 1.347 + // other than an nsIClassInfo object. We need to not do this for 1.348 + // nsIClassInfo objects because often nsIClassInfo implementations 1.349 + // are also nsIXPCScriptable helper implementations, but the helper 1.350 + // code is obviously intended for the implementation of the class 1.351 + // described by the nsIClassInfo, not for the class info object 1.352 + // itself. 1.353 + const XPCNativeScriptableCreateInfo& sciWrapper = 1.354 + isClassInfo ? sci : 1.355 + GatherScriptableCreateInfo(identity, info, sciProto, sci); 1.356 + 1.357 + RootedObject parent(cx, Scope->GetGlobalJSObject()); 1.358 + 1.359 + RootedValue newParentVal(cx, NullValue()); 1.360 + 1.361 + mozilla::Maybe<JSAutoCompartment> ac; 1.362 + 1.363 + if (sciWrapper.GetFlags().WantPreCreate()) { 1.364 + // PreCreate may touch dead compartments. 1.365 + js::AutoMaybeTouchDeadZones agc(parent); 1.366 + 1.367 + RootedObject plannedParent(cx, parent); 1.368 + nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx, 1.369 + parent, parent.address()); 1.370 + if (NS_FAILED(rv)) 1.371 + return rv; 1.372 + rv = NS_OK; 1.373 + 1.374 + MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent), 1.375 + "Xray wrapper being used to parent XPCWrappedNative?"); 1.376 + 1.377 + ac.construct(static_cast<JSContext*>(cx), parent); 1.378 + 1.379 + if (parent != plannedParent) { 1.380 + XPCWrappedNativeScope* betterScope = GetObjectScope(parent); 1.381 + if (betterScope != Scope) 1.382 + return GetNewOrUsed(helper, betterScope, Interface, resultWrapper); 1.383 + 1.384 + newParentVal = OBJECT_TO_JSVAL(parent); 1.385 + } 1.386 + 1.387 + // Take the performance hit of checking the hashtable again in case 1.388 + // the preCreate call caused the wrapper to get created through some 1.389 + // interesting path (the DOM code tends to make this happen sometimes). 1.390 + 1.391 + if (cache) { 1.392 + RootedObject cached(cx, cache->GetWrapper()); 1.393 + if (cached) 1.394 + wrapper = XPCWrappedNative::Get(cached); 1.395 + } else { 1.396 + wrapper = map->Find(identity); 1.397 + } 1.398 + 1.399 + if (wrapper) { 1.400 + if (wrapper->FindTearOff(Interface, false, &rv)) { 1.401 + MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure"); 1.402 + return rv; 1.403 + } 1.404 + wrapper.forget(resultWrapper); 1.405 + return NS_OK; 1.406 + } 1.407 + } else { 1.408 + ac.construct(static_cast<JSContext*>(cx), parent); 1.409 + } 1.410 + 1.411 + AutoMarkingWrappedNativeProtoPtr proto(cx); 1.412 + 1.413 + // If there is ClassInfo (and we are not building a wrapper for the 1.414 + // nsIClassInfo interface) then we use a wrapper that needs a prototype. 1.415 + 1.416 + // Note that the security check happens inside FindTearOff - after the 1.417 + // wrapper is actually created, but before JS code can see it. 1.418 + 1.419 + if (info && !isClassInfo) { 1.420 + proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, &sciProto); 1.421 + if (!proto) 1.422 + return NS_ERROR_FAILURE; 1.423 + 1.424 + wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto); 1.425 + } else { 1.426 + AutoMarkingNativeInterfacePtr iface(cx, Interface); 1.427 + if (!iface) 1.428 + iface = XPCNativeInterface::GetISupports(); 1.429 + 1.430 + AutoMarkingNativeSetPtr set(cx); 1.431 + set = XPCNativeSet::GetNewOrUsed(nullptr, iface, 0); 1.432 + 1.433 + if (!set) 1.434 + return NS_ERROR_FAILURE; 1.435 + 1.436 + wrapper = 1.437 + new XPCWrappedNative(helper.forgetCanonical(), Scope, set); 1.438 + } 1.439 + 1.440 + MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent), 1.441 + "Xray wrapper being used to parent XPCWrappedNative?"); 1.442 + 1.443 + // We use an AutoMarkingPtr here because it is possible for JS gc to happen 1.444 + // after we have Init'd the wrapper but *before* we add it to the hashtable. 1.445 + // This would cause the mSet to get collected and we'd later crash. I've 1.446 + // *seen* this happen. 1.447 + AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper); 1.448 + 1.449 + if (!wrapper->Init(parent, &sciWrapper)) 1.450 + return NS_ERROR_FAILURE; 1.451 + 1.452 + if (!wrapper->FindTearOff(Interface, false, &rv)) { 1.453 + MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure"); 1.454 + return rv; 1.455 + } 1.456 + 1.457 + return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper); 1.458 +} 1.459 + 1.460 +static nsresult 1.461 +FinishCreate(XPCWrappedNativeScope* Scope, 1.462 + XPCNativeInterface* Interface, 1.463 + nsWrapperCache *cache, 1.464 + XPCWrappedNative* inWrapper, 1.465 + XPCWrappedNative** resultWrapper) 1.466 +{ 1.467 + AutoJSContext cx; 1.468 + MOZ_ASSERT(inWrapper); 1.469 + 1.470 + Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap(); 1.471 + 1.472 + nsRefPtr<XPCWrappedNative> wrapper; 1.473 + // Deal with the case where the wrapper got created as a side effect 1.474 + // of one of our calls out of this code. Add() returns the (possibly 1.475 + // pre-existing) wrapper that ultimately ends up in the map, which is 1.476 + // what we want. 1.477 + wrapper = map->Add(inWrapper); 1.478 + if (!wrapper) 1.479 + return NS_ERROR_FAILURE; 1.480 + 1.481 + if (wrapper == inWrapper) { 1.482 + JSObject *flat = wrapper->GetFlatJSObject(); 1.483 + MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() || 1.484 + flat == cache->GetWrapperPreserveColor(), 1.485 + "This object has a cached wrapper that's different from " 1.486 + "the JSObject held by its native wrapper?"); 1.487 + 1.488 + if (cache && !cache->GetWrapperPreserveColor()) 1.489 + cache->SetWrapper(flat); 1.490 + 1.491 + // Our newly created wrapper is the one that we just added to the table. 1.492 + // All is well. Call PostCreate as necessary. 1.493 + XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); 1.494 + if (si && si->GetFlags().WantPostCreate()) { 1.495 + nsresult rv = si->GetCallback()->PostCreate(wrapper, cx, flat); 1.496 + if (NS_FAILED(rv)) { 1.497 + // PostCreate failed and that's Very Bad. We'll remove it from 1.498 + // the map and mark it as invalid, but the PostCreate function 1.499 + // may have handed the partially-constructed-and-now-invalid 1.500 + // wrapper to someone before failing. Or, perhaps worse, the 1.501 + // PostCreate call could have triggered code that reentered 1.502 + // XPConnect and tried to wrap the same object. In that case 1.503 + // *we* hand out the invalid wrapper since it is already in our 1.504 + // map :( 1.505 + NS_ERROR("PostCreate failed! This is known to cause " 1.506 + "inconsistent state for some class types and may even " 1.507 + "cause a crash in combination with a JS GC. Fix the " 1.508 + "failing PostCreate ASAP!"); 1.509 + 1.510 + map->Remove(wrapper); 1.511 + 1.512 + // This would be a good place to tell the wrapper not to remove 1.513 + // itself from the map when it dies... See bug 429442. 1.514 + 1.515 + if (cache) 1.516 + cache->ClearWrapper(); 1.517 + wrapper->Release(); 1.518 + return rv; 1.519 + } 1.520 + } 1.521 + } 1.522 + 1.523 + DEBUG_CheckClassInfoClaims(wrapper); 1.524 + wrapper.forget(resultWrapper); 1.525 + return NS_OK; 1.526 +} 1.527 + 1.528 +// static 1.529 +nsresult 1.530 +XPCWrappedNative::GetUsedOnly(nsISupports* Object, 1.531 + XPCWrappedNativeScope* Scope, 1.532 + XPCNativeInterface* Interface, 1.533 + XPCWrappedNative** resultWrapper) 1.534 +{ 1.535 + AutoJSContext cx; 1.536 + MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object"); 1.537 + MOZ_ASSERT(Interface); 1.538 + 1.539 + nsRefPtr<XPCWrappedNative> wrapper; 1.540 + nsWrapperCache* cache = nullptr; 1.541 + CallQueryInterface(Object, &cache); 1.542 + if (cache) { 1.543 + RootedObject flat(cx, cache->GetWrapper()); 1.544 + if (!flat) { 1.545 + *resultWrapper = nullptr; 1.546 + return NS_OK; 1.547 + } 1.548 + wrapper = XPCWrappedNative::Get(flat); 1.549 + } else { 1.550 + nsCOMPtr<nsISupports> identity = do_QueryInterface(Object); 1.551 + 1.552 + if (!identity) { 1.553 + NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!"); 1.554 + return NS_ERROR_FAILURE; 1.555 + } 1.556 + 1.557 + Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap(); 1.558 + 1.559 + wrapper = map->Find(identity); 1.560 + if (!wrapper) { 1.561 + *resultWrapper = nullptr; 1.562 + return NS_OK; 1.563 + } 1.564 + } 1.565 + 1.566 + nsresult rv; 1.567 + if (!wrapper->FindTearOff(Interface, false, &rv)) { 1.568 + MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure"); 1.569 + return rv; 1.570 + } 1.571 + 1.572 + wrapper.forget(resultWrapper); 1.573 + return NS_OK; 1.574 +} 1.575 + 1.576 +// This ctor is used if this object will have a proto. 1.577 +XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity, 1.578 + XPCWrappedNativeProto* aProto) 1.579 + : mMaybeProto(aProto), 1.580 + mSet(aProto->GetSet()), 1.581 + mScriptableInfo(nullptr) 1.582 +{ 1.583 + MOZ_ASSERT(NS_IsMainThread()); 1.584 + 1.585 + mIdentity = aIdentity.take(); 1.586 + mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); 1.587 + 1.588 + MOZ_ASSERT(mMaybeProto, "bad ctor param"); 1.589 + MOZ_ASSERT(mSet, "bad ctor param"); 1.590 +} 1.591 + 1.592 +// This ctor is used if this object will NOT have a proto. 1.593 +XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity, 1.594 + XPCWrappedNativeScope* aScope, 1.595 + XPCNativeSet* aSet) 1.596 + 1.597 + : mMaybeScope(TagScope(aScope)), 1.598 + mSet(aSet), 1.599 + mScriptableInfo(nullptr) 1.600 +{ 1.601 + MOZ_ASSERT(NS_IsMainThread()); 1.602 + 1.603 + mIdentity = aIdentity.take(); 1.604 + mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); 1.605 + 1.606 + MOZ_ASSERT(aScope, "bad ctor param"); 1.607 + MOZ_ASSERT(aSet, "bad ctor param"); 1.608 +} 1.609 + 1.610 +XPCWrappedNative::~XPCWrappedNative() 1.611 +{ 1.612 + Destroy(); 1.613 +} 1.614 + 1.615 +void 1.616 +XPCWrappedNative::Destroy() 1.617 +{ 1.618 + XPCWrappedNativeProto* proto = GetProto(); 1.619 + 1.620 + if (mScriptableInfo && 1.621 + (!HasProto() || 1.622 + (proto && proto->GetScriptableInfo() != mScriptableInfo))) { 1.623 + delete mScriptableInfo; 1.624 + mScriptableInfo = nullptr; 1.625 + } 1.626 + 1.627 + XPCWrappedNativeScope *scope = GetScope(); 1.628 + if (scope) { 1.629 + Native2WrappedNativeMap* map = scope->GetWrappedNativeMap(); 1.630 + 1.631 + // Post-1.9 we should not remove this wrapper from the map if it is 1.632 + // uninitialized. 1.633 + map->Remove(this); 1.634 + } 1.635 + 1.636 + if (mIdentity) { 1.637 + XPCJSRuntime* rt = GetRuntime(); 1.638 + if (rt && rt->GetDoingFinalization()) { 1.639 + nsContentUtils::DeferredFinalize(mIdentity); 1.640 + mIdentity = nullptr; 1.641 + } else { 1.642 + NS_RELEASE(mIdentity); 1.643 + } 1.644 + } 1.645 + 1.646 + mMaybeScope = nullptr; 1.647 +} 1.648 + 1.649 +void 1.650 +XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si) 1.651 +{ 1.652 + MOZ_ASSERT(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info"); 1.653 + 1.654 + // Write barrier for incremental GC. 1.655 + JSRuntime* rt = GetRuntime()->Runtime(); 1.656 + if (IsIncrementalBarrierNeeded(rt)) 1.657 + mScriptableInfo->Mark(); 1.658 + 1.659 + mScriptableInfo = si; 1.660 +} 1.661 + 1.662 +void 1.663 +XPCWrappedNative::SetProto(XPCWrappedNativeProto* p) 1.664 +{ 1.665 + MOZ_ASSERT(!IsWrapperExpired(), "bad ptr!"); 1.666 + 1.667 + MOZ_ASSERT(HasProto()); 1.668 + 1.669 + // Write barrier for incremental GC. 1.670 + JSRuntime* rt = GetRuntime()->Runtime(); 1.671 + GetProto()->WriteBarrierPre(rt); 1.672 + 1.673 + mMaybeProto = p; 1.674 +} 1.675 + 1.676 +// This is factored out so that it can be called publicly 1.677 +// static 1.678 +void 1.679 +XPCWrappedNative::GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo, 1.680 + XPCNativeScriptableCreateInfo& sciProto) 1.681 +{ 1.682 + MOZ_ASSERT(classInfo, "bad param"); 1.683 + MOZ_ASSERT(!sciProto.GetCallback(), "bad param"); 1.684 + 1.685 + nsXPCClassInfo *classInfoHelper = nullptr; 1.686 + CallQueryInterface(classInfo, &classInfoHelper); 1.687 + if (classInfoHelper) { 1.688 + nsCOMPtr<nsIXPCScriptable> helper = 1.689 + dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper)); 1.690 + uint32_t flags = classInfoHelper->GetScriptableFlags(); 1.691 + sciProto.SetCallback(helper.forget()); 1.692 + sciProto.SetFlags(flags); 1.693 + sciProto.SetInterfacesBitmap(classInfoHelper->GetInterfacesBitmap()); 1.694 + 1.695 + return; 1.696 + } 1.697 + 1.698 + nsCOMPtr<nsISupports> possibleHelper; 1.699 + nsresult rv = classInfo->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT, 1.700 + getter_AddRefs(possibleHelper)); 1.701 + if (NS_SUCCEEDED(rv) && possibleHelper) { 1.702 + nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(possibleHelper)); 1.703 + if (helper) { 1.704 + uint32_t flags = helper->GetScriptableFlags(); 1.705 + sciProto.SetCallback(helper.forget()); 1.706 + sciProto.SetFlags(flags); 1.707 + } 1.708 + } 1.709 +} 1.710 + 1.711 +// static 1.712 +const XPCNativeScriptableCreateInfo& 1.713 +XPCWrappedNative::GatherScriptableCreateInfo(nsISupports* obj, 1.714 + nsIClassInfo* classInfo, 1.715 + XPCNativeScriptableCreateInfo& sciProto, 1.716 + XPCNativeScriptableCreateInfo& sciWrapper) 1.717 +{ 1.718 + MOZ_ASSERT(!sciWrapper.GetCallback(), "bad param"); 1.719 + 1.720 + // Get the class scriptable helper (if present) 1.721 + if (classInfo) { 1.722 + GatherProtoScriptableCreateInfo(classInfo, sciProto); 1.723 + 1.724 + if (sciProto.GetFlags().DontAskInstanceForScriptable()) 1.725 + return sciProto; 1.726 + } 1.727 + 1.728 + // Do the same for the wrapper specific scriptable 1.729 + nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(obj)); 1.730 + if (helper) { 1.731 + uint32_t flags = helper->GetScriptableFlags(); 1.732 + sciWrapper.SetCallback(helper.forget()); 1.733 + sciWrapper.SetFlags(flags); 1.734 + 1.735 + // A whole series of assertions to catch bad uses of scriptable flags on 1.736 + // the siWrapper... 1.737 + 1.738 + MOZ_ASSERT(!(sciWrapper.GetFlags().WantPreCreate() && 1.739 + !sciProto.GetFlags().WantPreCreate()), 1.740 + "Can't set WANT_PRECREATE on an instance scriptable " 1.741 + "without also setting it on the class scriptable"); 1.742 + 1.743 + MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumStaticProps() && 1.744 + !sciProto.GetFlags().DontEnumStaticProps() && 1.745 + sciProto.GetCallback()), 1.746 + "Can't set DONT_ENUM_STATIC_PROPS on an instance scriptable " 1.747 + "without also setting it on the class scriptable (if present and shared)"); 1.748 + 1.749 + MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumQueryInterface() && 1.750 + !sciProto.GetFlags().DontEnumQueryInterface() && 1.751 + sciProto.GetCallback()), 1.752 + "Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable " 1.753 + "without also setting it on the class scriptable (if present and shared)"); 1.754 + 1.755 + MOZ_ASSERT(!(sciWrapper.GetFlags().DontAskInstanceForScriptable() && 1.756 + !sciProto.GetFlags().DontAskInstanceForScriptable()), 1.757 + "Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable " 1.758 + "without also setting it on the class scriptable"); 1.759 + 1.760 + MOZ_ASSERT(!(sciWrapper.GetFlags().ClassInfoInterfacesOnly() && 1.761 + !sciProto.GetFlags().ClassInfoInterfacesOnly() && 1.762 + sciProto.GetCallback()), 1.763 + "Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable " 1.764 + "without also setting it on the class scriptable (if present and shared)"); 1.765 + 1.766 + MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsDuringResolve() && 1.767 + !sciProto.GetFlags().AllowPropModsDuringResolve() && 1.768 + sciProto.GetCallback()), 1.769 + "Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable " 1.770 + "without also setting it on the class scriptable (if present and shared)"); 1.771 + 1.772 + MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsToPrototype() && 1.773 + !sciProto.GetFlags().AllowPropModsToPrototype() && 1.774 + sciProto.GetCallback()), 1.775 + "Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable " 1.776 + "without also setting it on the class scriptable (if present and shared)"); 1.777 + 1.778 + return sciWrapper; 1.779 + } 1.780 + 1.781 + return sciProto; 1.782 +} 1.783 + 1.784 +bool 1.785 +XPCWrappedNative::Init(HandleObject parent, 1.786 + const XPCNativeScriptableCreateInfo* sci) 1.787 +{ 1.788 + AutoJSContext cx; 1.789 + // setup our scriptable info... 1.790 + 1.791 + if (sci->GetCallback()) { 1.792 + if (HasProto()) { 1.793 + XPCNativeScriptableInfo* siProto = GetProto()->GetScriptableInfo(); 1.794 + if (siProto && siProto->GetCallback() == sci->GetCallback()) 1.795 + mScriptableInfo = siProto; 1.796 + } 1.797 + if (!mScriptableInfo) { 1.798 + mScriptableInfo = 1.799 + XPCNativeScriptableInfo::Construct(sci); 1.800 + 1.801 + if (!mScriptableInfo) 1.802 + return false; 1.803 + } 1.804 + } 1.805 + XPCNativeScriptableInfo* si = mScriptableInfo; 1.806 + 1.807 + // create our flatJSObject 1.808 + 1.809 + const JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass.base); 1.810 + 1.811 + // We should have the global jsclass flag if and only if we're a global. 1.812 + MOZ_ASSERT_IF(si, !!si->GetFlags().IsGlobalObject() == !!(jsclazz->flags & JSCLASS_IS_GLOBAL)); 1.813 + 1.814 + MOZ_ASSERT(jsclazz && 1.815 + jsclazz->name && 1.816 + jsclazz->flags && 1.817 + jsclazz->addProperty && 1.818 + jsclazz->delProperty && 1.819 + jsclazz->getProperty && 1.820 + jsclazz->setProperty && 1.821 + jsclazz->enumerate && 1.822 + jsclazz->resolve && 1.823 + jsclazz->convert && 1.824 + jsclazz->finalize, "bad class"); 1.825 + 1.826 + RootedObject protoJSObject(cx, HasProto() ? 1.827 + GetProto()->GetJSProtoObject() : 1.828 + JS_GetObjectPrototype(cx, parent)); 1.829 + if (!protoJSObject) { 1.830 + return false; 1.831 + } 1.832 + 1.833 + mFlatJSObject = JS_NewObject(cx, jsclazz, protoJSObject, parent); 1.834 + if (!mFlatJSObject) { 1.835 + mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID); 1.836 + return false; 1.837 + } 1.838 + 1.839 + mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); 1.840 + JS_SetPrivate(mFlatJSObject, this); 1.841 + 1.842 + return FinishInit(); 1.843 +} 1.844 + 1.845 +bool 1.846 +XPCWrappedNative::FinishInit() 1.847 +{ 1.848 + AutoJSContext cx; 1.849 + 1.850 + // This reference will be released when mFlatJSObject is finalized. 1.851 + // Since this reference will push the refcount to 2 it will also root 1.852 + // mFlatJSObject; 1.853 + MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value"); 1.854 + NS_ADDREF(this); 1.855 + 1.856 + if (mScriptableInfo && mScriptableInfo->GetFlags().WantCreate() && 1.857 + NS_FAILED(mScriptableInfo->GetCallback()->Create(this, cx, 1.858 + mFlatJSObject))) { 1.859 + return false; 1.860 + } 1.861 + 1.862 + // A hack for bug 517665, increase the probability for GC. 1.863 + JS_updateMallocCounter(cx, 2 * sizeof(XPCWrappedNative)); 1.864 + 1.865 + return true; 1.866 +} 1.867 + 1.868 + 1.869 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative) 1.870 + NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative) 1.871 + NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder) 1.872 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative) 1.873 +NS_INTERFACE_MAP_END 1.874 + 1.875 +NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCWrappedNative) 1.876 + 1.877 +// Release calls Destroy() immediately when the refcount drops to 0 to 1.878 +// clear the weak references nsXPConnect has to XPCWNs and to ensure there 1.879 +// are no pointers to dying protos. 1.880 +NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(XPCWrappedNative, Destroy()) 1.881 + 1.882 +/* 1.883 + * Wrapped Native lifetime management is messy! 1.884 + * 1.885 + * - At creation we push the refcount to 2 (only one of which is owned by 1.886 + * the native caller that caused the wrapper creation). 1.887 + * - During the JS GC Mark phase we mark any wrapper with a refcount > 1. 1.888 + * - The *only* thing that can make the wrapper get destroyed is the 1.889 + * finalization of mFlatJSObject. And *that* should only happen if the only 1.890 + * reference is the single extra (internal) reference we hold. 1.891 + * 1.892 + * - The wrapper has a pointer to the nsISupports 'view' of the wrapped native 1.893 + * object i.e... mIdentity. This is held until the wrapper's refcount goes 1.894 + * to zero and the wrapper is released, or until an expired wrapper (i.e., 1.895 + * one unlinked by the cycle collector) has had its JS object finalized. 1.896 + * 1.897 + * - The wrapper also has 'tearoffs'. It has one tearoff for each interface 1.898 + * that is actually used on the native object. 'Used' means we have either 1.899 + * needed to QueryInterface to verify the availability of that interface 1.900 + * of that we've had to QueryInterface in order to actually make a call 1.901 + * into the wrapped object via the pointer for the given interface. 1.902 + * 1.903 + * - Each tearoff's 'mNative' member (if non-null) indicates one reference 1.904 + * held by our wrapper on the wrapped native for the given interface 1.905 + * associated with the tearoff. If we release that reference then we set 1.906 + * the tearoff's 'mNative' to null. 1.907 + * 1.908 + * - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END 1.909 + * event to scan the tearoffs of all wrappers for non-null mNative members 1.910 + * that represent unused references. We can tell that a given tearoff's 1.911 + * mNative is unused by noting that no live XPCCallContexts hold a pointer 1.912 + * to the tearoff. 1.913 + * 1.914 + * - As a time/space tradeoff we may decide to not do this scanning on 1.915 + * *every* JavaScript GC. We *do* want to do this *sometimes* because 1.916 + * we want to allow for wrapped native's to do their own tearoff patterns. 1.917 + * So, we want to avoid holding references to interfaces that we don't need. 1.918 + * At the same time, we don't want to be bracketing every call into a 1.919 + * wrapped native object with a QueryInterface/Release pair. And we *never* 1.920 + * make a call into the object except via the correct interface for which 1.921 + * we've QI'd. 1.922 + * 1.923 + * - Each tearoff *can* have a mJSObject whose lazily resolved properties 1.924 + * represent the methods/attributes/constants of that specific interface. 1.925 + * This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo" 1.926 + * is the name of mFlatJSObject and "nsIFoo" is the name of the given 1.927 + * interface associated with the tearoff. When we create the tearoff's 1.928 + * mJSObject we set it's parent to be mFlatJSObject. This way we know that 1.929 + * when mFlatJSObject get's collected there are no outstanding reachable 1.930 + * tearoff mJSObjects. Note that we must clear the private of any lingering 1.931 + * mJSObjects at this point because we have no guarentee of the *order* of 1.932 + * finalization within a given gc cycle. 1.933 + */ 1.934 + 1.935 +void 1.936 +XPCWrappedNative::FlatJSObjectFinalized() 1.937 +{ 1.938 + if (!IsValid()) 1.939 + return; 1.940 + 1.941 + // Iterate the tearoffs and null out each of their JSObject's privates. 1.942 + // This will keep them from trying to access their pointers to the 1.943 + // dying tearoff object. We can safely assume that those remaining 1.944 + // JSObjects are about to be finalized too. 1.945 + 1.946 + XPCWrappedNativeTearOffChunk* chunk; 1.947 + for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { 1.948 + XPCWrappedNativeTearOff* to = chunk->mTearOffs; 1.949 + for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) { 1.950 + JSObject* jso = to->GetJSObjectPreserveColor(); 1.951 + if (jso) { 1.952 + MOZ_ASSERT(JS_IsAboutToBeFinalizedUnbarriered(&jso)); 1.953 + JS_SetPrivate(jso, nullptr); 1.954 + to->JSObjectFinalized(); 1.955 + } 1.956 + 1.957 + // We also need to release any native pointers held... 1.958 + nsISupports* obj = to->GetNative(); 1.959 + if (obj) { 1.960 +#ifdef XP_WIN 1.961 + // Try to detect free'd pointer 1.962 + MOZ_ASSERT(*(int*)obj != 0xdddddddd, "bad pointer!"); 1.963 + MOZ_ASSERT(*(int*)obj != 0, "bad pointer!"); 1.964 +#endif 1.965 + XPCJSRuntime* rt = GetRuntime(); 1.966 + if (rt) { 1.967 + nsContentUtils::DeferredFinalize(obj); 1.968 + } else { 1.969 + obj->Release(); 1.970 + } 1.971 + to->SetNative(nullptr); 1.972 + } 1.973 + 1.974 + to->SetInterface(nullptr); 1.975 + } 1.976 + } 1.977 + 1.978 + nsWrapperCache *cache = nullptr; 1.979 + CallQueryInterface(mIdentity, &cache); 1.980 + if (cache) 1.981 + cache->ClearWrapper(); 1.982 + 1.983 + mFlatJSObject = nullptr; 1.984 + mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID); 1.985 + 1.986 + MOZ_ASSERT(mIdentity, "bad pointer!"); 1.987 +#ifdef XP_WIN 1.988 + // Try to detect free'd pointer 1.989 + MOZ_ASSERT(*(int*)mIdentity != 0xdddddddd, "bad pointer!"); 1.990 + MOZ_ASSERT(*(int*)mIdentity != 0, "bad pointer!"); 1.991 +#endif 1.992 + 1.993 + if (IsWrapperExpired()) { 1.994 + Destroy(); 1.995 + } 1.996 + 1.997 + // Note that it's not safe to touch mNativeWrapper here since it's 1.998 + // likely that it has already been finalized. 1.999 + 1.1000 + Release(); 1.1001 +} 1.1002 + 1.1003 +void 1.1004 +XPCWrappedNative::SystemIsBeingShutDown() 1.1005 +{ 1.1006 + if (!IsValid()) 1.1007 + return; 1.1008 + 1.1009 + // The long standing strategy is to leak some objects still held at shutdown. 1.1010 + // The general problem is that propagating release out of xpconnect at 1.1011 + // shutdown time causes a world of problems. 1.1012 + 1.1013 + // We leak mIdentity (see above). 1.1014 + 1.1015 + // short circuit future finalization 1.1016 + JS_SetPrivate(mFlatJSObject, nullptr); 1.1017 + mFlatJSObject = nullptr; 1.1018 + mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID); 1.1019 + 1.1020 + XPCWrappedNativeProto* proto = GetProto(); 1.1021 + 1.1022 + if (HasProto()) 1.1023 + proto->SystemIsBeingShutDown(); 1.1024 + 1.1025 + if (mScriptableInfo && 1.1026 + (!HasProto() || 1.1027 + (proto && proto->GetScriptableInfo() != mScriptableInfo))) { 1.1028 + delete mScriptableInfo; 1.1029 + } 1.1030 + 1.1031 + // cleanup the tearoffs... 1.1032 + 1.1033 + XPCWrappedNativeTearOffChunk* chunk; 1.1034 + for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) { 1.1035 + XPCWrappedNativeTearOff* to = chunk->mTearOffs; 1.1036 + for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) { 1.1037 + if (JSObject *jso = to->GetJSObjectPreserveColor()) { 1.1038 + JS_SetPrivate(jso, nullptr); 1.1039 + to->SetJSObject(nullptr); 1.1040 + } 1.1041 + // We leak the tearoff mNative 1.1042 + // (for the same reason we leak mIdentity - see above). 1.1043 + to->SetNative(nullptr); 1.1044 + to->SetInterface(nullptr); 1.1045 + } 1.1046 + } 1.1047 + 1.1048 + if (mFirstChunk.mNextChunk) { 1.1049 + delete mFirstChunk.mNextChunk; 1.1050 + mFirstChunk.mNextChunk = nullptr; 1.1051 + } 1.1052 +} 1.1053 + 1.1054 +/***************************************************************************/ 1.1055 + 1.1056 +// Dynamically ensure that two objects don't end up with the same private. 1.1057 +class MOZ_STACK_CLASS AutoClonePrivateGuard { 1.1058 +public: 1.1059 + AutoClonePrivateGuard(JSContext *cx, JSObject *aOld, JSObject *aNew) 1.1060 + : mOldReflector(cx, aOld), mNewReflector(cx, aNew) 1.1061 + { 1.1062 + MOZ_ASSERT(JS_GetPrivate(aOld) == JS_GetPrivate(aNew)); 1.1063 + } 1.1064 + 1.1065 + ~AutoClonePrivateGuard() 1.1066 + { 1.1067 + if (JS_GetPrivate(mOldReflector)) { 1.1068 + JS_SetPrivate(mNewReflector, nullptr); 1.1069 + } 1.1070 + } 1.1071 + 1.1072 +private: 1.1073 + RootedObject mOldReflector; 1.1074 + RootedObject mNewReflector; 1.1075 +}; 1.1076 + 1.1077 +// static 1.1078 +nsresult 1.1079 +XPCWrappedNative::ReparentWrapperIfFound(XPCWrappedNativeScope* aOldScope, 1.1080 + XPCWrappedNativeScope* aNewScope, 1.1081 + HandleObject aNewParent, 1.1082 + nsISupports* aCOMObj) 1.1083 +{ 1.1084 + // Check if we're near the stack limit before we get anywhere near the 1.1085 + // transplanting code. 1.1086 + AutoJSContext cx; 1.1087 + JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE); 1.1088 + 1.1089 + XPCNativeInterface* iface = XPCNativeInterface::GetISupports(); 1.1090 + if (!iface) 1.1091 + return NS_ERROR_FAILURE; 1.1092 + 1.1093 + nsresult rv; 1.1094 + 1.1095 + nsRefPtr<XPCWrappedNative> wrapper; 1.1096 + RootedObject flat(cx); 1.1097 + nsWrapperCache* cache = nullptr; 1.1098 + CallQueryInterface(aCOMObj, &cache); 1.1099 + if (cache) { 1.1100 + flat = cache->GetWrapper(); 1.1101 + if (flat) { 1.1102 + wrapper = XPCWrappedNative::Get(flat); 1.1103 + MOZ_ASSERT(wrapper->GetScope() == aOldScope, 1.1104 + "Incorrect scope passed"); 1.1105 + } 1.1106 + } else { 1.1107 + rv = XPCWrappedNative::GetUsedOnly(aCOMObj, aOldScope, iface, 1.1108 + getter_AddRefs(wrapper)); 1.1109 + if (NS_FAILED(rv)) 1.1110 + return rv; 1.1111 + 1.1112 + if (wrapper) 1.1113 + flat = wrapper->GetFlatJSObject(); 1.1114 + } 1.1115 + 1.1116 + if (!flat) 1.1117 + return NS_OK; 1.1118 + 1.1119 + JSAutoCompartment ac(cx, aNewScope->GetGlobalJSObject()); 1.1120 + 1.1121 + if (aOldScope != aNewScope) { 1.1122 + // Oh, so now we need to move the wrapper to a different scope. 1.1123 + AutoMarkingWrappedNativeProtoPtr oldProto(cx); 1.1124 + AutoMarkingWrappedNativeProtoPtr newProto(cx); 1.1125 + 1.1126 + // Cross-scope means cross-compartment. 1.1127 + MOZ_ASSERT(js::GetObjectCompartment(aOldScope->GetGlobalJSObject()) != 1.1128 + js::GetObjectCompartment(aNewScope->GetGlobalJSObject())); 1.1129 + MOZ_ASSERT(aNewParent, "won't be able to find the new parent"); 1.1130 + 1.1131 + if (wrapper->HasProto()) { 1.1132 + oldProto = wrapper->GetProto(); 1.1133 + XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo(); 1.1134 + XPCNativeScriptableCreateInfo ci(*info); 1.1135 + newProto = 1.1136 + XPCWrappedNativeProto::GetNewOrUsed(aNewScope, 1.1137 + oldProto->GetClassInfo(), 1.1138 + &ci); 1.1139 + if (!newProto) { 1.1140 + return NS_ERROR_FAILURE; 1.1141 + } 1.1142 + } 1.1143 + 1.1144 + // First, the clone of the reflector, get a copy of its 1.1145 + // properties and clone its expando chain. The only part that is 1.1146 + // dangerous here if we have to return early is that we must avoid 1.1147 + // ending up with two reflectors pointing to the same WN. Other than 1.1148 + // that, the objects we create will just go away if we return early. 1.1149 + 1.1150 + RootedObject proto(cx, newProto->GetJSProtoObject()); 1.1151 + RootedObject newobj(cx, JS_CloneObject(cx, flat, proto, aNewParent)); 1.1152 + if (!newobj) 1.1153 + return NS_ERROR_FAILURE; 1.1154 + 1.1155 + // At this point, both |flat| and |newobj| point to the same wrapped 1.1156 + // native, which is bad, because one of them will end up finalizing 1.1157 + // a wrapped native it does not own. |cloneGuard| ensures that if we 1.1158 + // exit before calling clearing |flat|'s private the private of 1.1159 + // |newobj| will be set to nullptr. |flat| will go away soon, because 1.1160 + // we swap it with another object during the transplant and let that 1.1161 + // object die. 1.1162 + RootedObject propertyHolder(cx); 1.1163 + { 1.1164 + AutoClonePrivateGuard cloneGuard(cx, flat, newobj); 1.1165 + 1.1166 + propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), 1.1167 + aNewParent); 1.1168 + if (!propertyHolder) 1.1169 + return NS_ERROR_OUT_OF_MEMORY; 1.1170 + if (!JS_CopyPropertiesFrom(cx, propertyHolder, flat)) 1.1171 + return NS_ERROR_FAILURE; 1.1172 + 1.1173 + // Expandos from other compartments are attached to the target JS object. 1.1174 + // Copy them over, and let the old ones die a natural death. 1.1175 + if (!XrayUtils::CloneExpandoChain(cx, newobj, flat)) 1.1176 + return NS_ERROR_FAILURE; 1.1177 + 1.1178 + // We've set up |newobj|, so we make it own the WN by nulling out 1.1179 + // the private of |flat|. 1.1180 + // 1.1181 + // NB: It's important to do this _after_ copying the properties to 1.1182 + // propertyHolder. Otherwise, an object with |foo.x === foo| will 1.1183 + // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x. 1.1184 + JS_SetPrivate(flat, nullptr); 1.1185 + } 1.1186 + 1.1187 + // Update scope maps. This section modifies global state, so from 1.1188 + // here on out we crash if anything fails. 1.1189 + Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap(); 1.1190 + Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap(); 1.1191 + 1.1192 + oldMap->Remove(wrapper); 1.1193 + 1.1194 + if (wrapper->HasProto()) 1.1195 + wrapper->SetProto(newProto); 1.1196 + 1.1197 + // If the wrapper has no scriptable or it has a non-shared 1.1198 + // scriptable, then we don't need to mess with it. 1.1199 + // Otherwise... 1.1200 + 1.1201 + if (wrapper->mScriptableInfo && 1.1202 + wrapper->mScriptableInfo == oldProto->GetScriptableInfo()) { 1.1203 + // The new proto had better have the same JSClass stuff as 1.1204 + // the old one! We maintain a runtime wide unique map of 1.1205 + // this stuff. So, if these don't match then the caller is 1.1206 + // doing something bad here. 1.1207 + 1.1208 + MOZ_ASSERT(oldProto->GetScriptableInfo()->GetScriptableShared() == 1.1209 + newProto->GetScriptableInfo()->GetScriptableShared(), 1.1210 + "Changing proto is also changing JSObject Classname or " 1.1211 + "helper's nsIXPScriptable flags. This is not allowed!"); 1.1212 + 1.1213 + wrapper->UpdateScriptableInfo(newProto->GetScriptableInfo()); 1.1214 + } 1.1215 + 1.1216 + // Crash if the wrapper is already in the new scope. 1.1217 + if (newMap->Find(wrapper->GetIdentityObject())) 1.1218 + MOZ_CRASH(); 1.1219 + 1.1220 + if (!newMap->Add(wrapper)) 1.1221 + MOZ_CRASH(); 1.1222 + 1.1223 + flat = xpc::TransplantObject(cx, flat, newobj); 1.1224 + if (!flat) 1.1225 + MOZ_CRASH(); 1.1226 + 1.1227 + MOZ_ASSERT(flat); 1.1228 + wrapper->mFlatJSObject = flat; 1.1229 + wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID); 1.1230 + 1.1231 + if (cache) { 1.1232 + bool preserving = cache->PreservingWrapper(); 1.1233 + cache->SetPreservingWrapper(false); 1.1234 + cache->SetWrapper(flat); 1.1235 + cache->SetPreservingWrapper(preserving); 1.1236 + } 1.1237 + if (!JS_CopyPropertiesFrom(cx, flat, propertyHolder)) 1.1238 + MOZ_CRASH(); 1.1239 + 1.1240 + // Call the scriptable hook to indicate that we transplanted. 1.1241 + XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); 1.1242 + if (si->GetFlags().WantPostCreate()) 1.1243 + (void) si->GetCallback()->PostTransplant(wrapper, cx, flat); 1.1244 + } 1.1245 + 1.1246 + // Now we can just fix up the parent and return the wrapper 1.1247 + 1.1248 + if (aNewParent) { 1.1249 + if (!JS_SetParent(cx, flat, aNewParent)) 1.1250 + MOZ_CRASH(); 1.1251 + } 1.1252 + 1.1253 + return NS_OK; 1.1254 +} 1.1255 + 1.1256 +// Orphans are sad little things - If only we could treat them better. :-( 1.1257 +// 1.1258 +// When a wrapper gets reparented to another scope (for example, when calling 1.1259 +// adoptNode), it's entirely possible that it previously served as the parent for 1.1260 +// other wrappers (via PreCreate hooks). When it moves, the old mFlatJSObject is 1.1261 +// replaced by a cross-compartment wrapper. Its descendants really _should_ move 1.1262 +// too, but we have no way of locating them short of a compartment-wide sweep 1.1263 +// (which we believe to be prohibitively expensive). 1.1264 +// 1.1265 +// So we just leave them behind. In practice, the only time this turns out to 1.1266 +// be a problem is during subsequent wrapper reparenting. When this happens, we 1.1267 +// call into the below fixup code at the last minute and straighten things out 1.1268 +// before proceeding. 1.1269 +// 1.1270 +// See bug 751995 for more information. 1.1271 + 1.1272 +static nsresult 1.1273 +RescueOrphans(HandleObject obj) 1.1274 +{ 1.1275 + AutoJSContext cx; 1.1276 + // 1.1277 + // Even if we're not an orphan at the moment, one of our ancestors might 1.1278 + // be. If so, we need to recursively rescue up the parent chain. 1.1279 + // 1.1280 + 1.1281 + // First, get the parent object. If we're currently an orphan, the parent 1.1282 + // object is a cross-compartment wrapper. Follow the parent into its own 1.1283 + // compartment and fix it up there. We'll fix up |this| afterwards. 1.1284 + // 1.1285 + // NB: We pass stopAtOuter=false during the unwrap because Location objects 1.1286 + // are parented to outer window proxies. 1.1287 + nsresult rv; 1.1288 + RootedObject parentObj(cx, js::GetObjectParent(obj)); 1.1289 + if (!parentObj) 1.1290 + return NS_OK; // Global object. We're done. 1.1291 + parentObj = js::UncheckedUnwrap(parentObj, /* stopAtOuter = */ false); 1.1292 + 1.1293 + // PreCreate may touch dead compartments. 1.1294 + js::AutoMaybeTouchDeadZones agc(parentObj); 1.1295 + 1.1296 + // Recursively fix up orphans on the parent chain. 1.1297 + rv = RescueOrphans(parentObj); 1.1298 + NS_ENSURE_SUCCESS(rv, rv); 1.1299 + 1.1300 + // Now that we know our parent is in the right place, determine if we've 1.1301 + // been orphaned. If not, we have nothing to do. 1.1302 + if (!js::IsCrossCompartmentWrapper(parentObj)) 1.1303 + return NS_OK; 1.1304 + 1.1305 + // We've been orphaned. Find where our parent went, and follow it. 1.1306 + if (IS_WN_REFLECTOR(obj)) { 1.1307 + RootedObject realParent(cx, js::UncheckedUnwrap(parentObj)); 1.1308 + XPCWrappedNative *wn = 1.1309 + static_cast<XPCWrappedNative*>(js::GetObjectPrivate(obj)); 1.1310 + return wn->ReparentWrapperIfFound(GetObjectScope(parentObj), 1.1311 + GetObjectScope(realParent), 1.1312 + realParent, wn->GetIdentityObject()); 1.1313 + } 1.1314 + 1.1315 + JSAutoCompartment ac(cx, obj); 1.1316 + return ReparentWrapper(cx, obj); 1.1317 +} 1.1318 + 1.1319 +// Recursively fix up orphans on the parent chain of a wrapper. Note that this 1.1320 +// can cause a wrapper to move even if it is not an orphan, since its parent 1.1321 +// might be an orphan and fixing the parent causes this wrapper to become an 1.1322 +// orphan. 1.1323 +nsresult 1.1324 +XPCWrappedNative::RescueOrphans() 1.1325 +{ 1.1326 + AutoJSContext cx; 1.1327 + RootedObject flatJSObject(cx, mFlatJSObject); 1.1328 + return ::RescueOrphans(flatJSObject); 1.1329 +} 1.1330 + 1.1331 +bool 1.1332 +XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface) 1.1333 +{ 1.1334 + AutoJSContext cx; 1.1335 + 1.1336 + if (!mSet->HasInterface(aInterface)) { 1.1337 + AutoMarkingNativeSetPtr newSet(cx); 1.1338 + newSet = XPCNativeSet::GetNewOrUsed(mSet, aInterface, 1.1339 + mSet->GetInterfaceCount()); 1.1340 + if (!newSet) 1.1341 + return false; 1.1342 + 1.1343 + mSet = newSet; 1.1344 + } 1.1345 + return true; 1.1346 +} 1.1347 + 1.1348 +XPCWrappedNativeTearOff* 1.1349 +XPCWrappedNative::LocateTearOff(XPCNativeInterface* aInterface) 1.1350 +{ 1.1351 + for (XPCWrappedNativeTearOffChunk* chunk = &mFirstChunk; 1.1352 + chunk != nullptr; 1.1353 + chunk = chunk->mNextChunk) { 1.1354 + XPCWrappedNativeTearOff* tearOff = chunk->mTearOffs; 1.1355 + XPCWrappedNativeTearOff* const end = tearOff + 1.1356 + XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK; 1.1357 + for (tearOff = chunk->mTearOffs; 1.1358 + tearOff < end; 1.1359 + tearOff++) { 1.1360 + if (tearOff->GetInterface() == aInterface) { 1.1361 + return tearOff; 1.1362 + } 1.1363 + } 1.1364 + } 1.1365 + return nullptr; 1.1366 +} 1.1367 + 1.1368 +XPCWrappedNativeTearOff* 1.1369 +XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface, 1.1370 + bool needJSObject /* = false */, 1.1371 + nsresult* pError /* = nullptr */) 1.1372 +{ 1.1373 + AutoJSContext cx; 1.1374 + nsresult rv = NS_OK; 1.1375 + XPCWrappedNativeTearOff* to; 1.1376 + XPCWrappedNativeTearOff* firstAvailable = nullptr; 1.1377 + 1.1378 + XPCWrappedNativeTearOffChunk* lastChunk; 1.1379 + XPCWrappedNativeTearOffChunk* chunk; 1.1380 + for (lastChunk = chunk = &mFirstChunk; 1.1381 + chunk; 1.1382 + lastChunk = chunk, chunk = chunk->mNextChunk) { 1.1383 + to = chunk->mTearOffs; 1.1384 + XPCWrappedNativeTearOff* const end = chunk->mTearOffs + 1.1385 + XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK; 1.1386 + for (to = chunk->mTearOffs; 1.1387 + to < end; 1.1388 + to++) { 1.1389 + if (to->GetInterface() == aInterface) { 1.1390 + if (needJSObject && !to->GetJSObjectPreserveColor()) { 1.1391 + AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to); 1.1392 + bool ok = InitTearOffJSObject(to); 1.1393 + // During shutdown, we don't sweep tearoffs. So make sure 1.1394 + // to unmark manually in case the auto-marker marked us. 1.1395 + // We shouldn't ever be getting here _during_ our 1.1396 + // Mark/Sweep cycle, so this should be safe. 1.1397 + to->Unmark(); 1.1398 + if (!ok) { 1.1399 + to = nullptr; 1.1400 + rv = NS_ERROR_OUT_OF_MEMORY; 1.1401 + } 1.1402 + } 1.1403 + if (pError) 1.1404 + *pError = rv; 1.1405 + return to; 1.1406 + } 1.1407 + if (!firstAvailable && to->IsAvailable()) 1.1408 + firstAvailable = to; 1.1409 + } 1.1410 + } 1.1411 + 1.1412 + to = firstAvailable; 1.1413 + 1.1414 + if (!to) { 1.1415 + auto newChunk = new XPCWrappedNativeTearOffChunk(); 1.1416 + lastChunk->mNextChunk = newChunk; 1.1417 + to = newChunk->mTearOffs; 1.1418 + } 1.1419 + 1.1420 + { 1.1421 + // Scope keeps |tearoff| from leaking across the rest of the function. 1.1422 + AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to); 1.1423 + rv = InitTearOff(to, aInterface, needJSObject); 1.1424 + // During shutdown, we don't sweep tearoffs. So make sure to unmark 1.1425 + // manually in case the auto-marker marked us. We shouldn't ever be 1.1426 + // getting here _during_ our Mark/Sweep cycle, so this should be safe. 1.1427 + to->Unmark(); 1.1428 + if (NS_FAILED(rv)) 1.1429 + to = nullptr; 1.1430 + } 1.1431 + 1.1432 + if (pError) 1.1433 + *pError = rv; 1.1434 + return to; 1.1435 +} 1.1436 + 1.1437 +nsresult 1.1438 +XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff, 1.1439 + XPCNativeInterface* aInterface, 1.1440 + bool needJSObject) 1.1441 +{ 1.1442 + AutoJSContext cx; 1.1443 + 1.1444 + // Determine if the object really does this interface... 1.1445 + 1.1446 + const nsIID* iid = aInterface->GetIID(); 1.1447 + nsISupports* identity = GetIdentityObject(); 1.1448 + nsISupports* obj; 1.1449 + 1.1450 + // If the scriptable helper forbids us from reflecting additional 1.1451 + // interfaces, then don't even try the QI, just fail. 1.1452 + if (mScriptableInfo && 1.1453 + mScriptableInfo->GetFlags().ClassInfoInterfacesOnly() && 1.1454 + !mSet->HasInterface(aInterface) && 1.1455 + !mSet->HasInterfaceWithAncestor(aInterface)) { 1.1456 + return NS_ERROR_NO_INTERFACE; 1.1457 + } 1.1458 + 1.1459 + // We are about to call out to other code. 1.1460 + // So protect our intended tearoff. 1.1461 + 1.1462 + aTearOff->SetReserved(); 1.1463 + 1.1464 + if (NS_FAILED(identity->QueryInterface(*iid, (void**)&obj)) || !obj) { 1.1465 + aTearOff->SetInterface(nullptr); 1.1466 + return NS_ERROR_NO_INTERFACE; 1.1467 + } 1.1468 + 1.1469 + // Guard against trying to build a tearoff for a shared nsIClassInfo. 1.1470 + if (iid->Equals(NS_GET_IID(nsIClassInfo))) { 1.1471 + nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(obj)); 1.1472 + if (alternate_identity.get() != identity) { 1.1473 + NS_RELEASE(obj); 1.1474 + aTearOff->SetInterface(nullptr); 1.1475 + return NS_ERROR_NO_INTERFACE; 1.1476 + } 1.1477 + } 1.1478 + 1.1479 + // Guard against trying to build a tearoff for an interface that is 1.1480 + // aggregated and is implemented as a nsIXPConnectWrappedJS using this 1.1481 + // self-same JSObject. The XBL system does this. If we mutate the set 1.1482 + // of this wrapper then we will shadow the method that XBL has added to 1.1483 + // the JSObject that it has inserted in the JS proto chain between our 1.1484 + // JSObject and our XPCWrappedNativeProto's JSObject. If we let this 1.1485 + // set mutation happen then the interface's methods will be added to 1.1486 + // our JSObject, but calls on those methods will get routed up to 1.1487 + // native code and into the wrappedJS - which will do a method lookup 1.1488 + // on *our* JSObject and find the same method and make another call 1.1489 + // into an infinite loop. 1.1490 + // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725 1.1491 + 1.1492 + // The code in this block also does a check for the double wrapped 1.1493 + // nsIPropertyBag case. 1.1494 + 1.1495 + nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(obj)); 1.1496 + if (wrappedJS) { 1.1497 + RootedObject jso(cx, wrappedJS->GetJSObject()); 1.1498 + if (jso == mFlatJSObject) { 1.1499 + // The implementing JSObject is the same as ours! Just say OK 1.1500 + // without actually extending the set. 1.1501 + // 1.1502 + // XXX It is a little cheesy to have FindTearOff return an 1.1503 + // 'empty' tearoff. But this is the centralized place to do the 1.1504 + // QI activities on the underlying object. *And* most caller to 1.1505 + // FindTearOff only look for a non-null result and ignore the 1.1506 + // actual tearoff returned. The only callers that do use the 1.1507 + // returned tearoff make sure to check for either a non-null 1.1508 + // JSObject or a matching Interface before proceeding. 1.1509 + // I think we can get away with this bit of ugliness. 1.1510 + 1.1511 + NS_RELEASE(obj); 1.1512 + aTearOff->SetInterface(nullptr); 1.1513 + return NS_OK; 1.1514 + } 1.1515 + 1.1516 + // Decide whether or not to expose nsIPropertyBag to calling 1.1517 + // JS code in the double wrapped case. 1.1518 + // 1.1519 + // Our rule here is that when JSObjects are double wrapped and 1.1520 + // exposed to other JSObjects then the nsIPropertyBag interface 1.1521 + // is only exposed on an 'opt-in' basis; i.e. if the underlying 1.1522 + // JSObject wants other JSObjects to be able to see this interface 1.1523 + // then it must implement QueryInterface and not throw an exception 1.1524 + // when asked for nsIPropertyBag. It need not actually *implement* 1.1525 + // nsIPropertyBag - xpconnect will do that work. 1.1526 + 1.1527 + if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso) { 1.1528 + nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, *iid); 1.1529 + if (clasp) { 1.1530 + RootedObject answer(cx, clasp->CallQueryInterfaceOnJSObject(cx, jso, *iid)); 1.1531 + 1.1532 + if (!answer) { 1.1533 + NS_RELEASE(obj); 1.1534 + aTearOff->SetInterface(nullptr); 1.1535 + return NS_ERROR_NO_INTERFACE; 1.1536 + } 1.1537 + } 1.1538 + } 1.1539 + } 1.1540 + 1.1541 + nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager(); 1.1542 + if (sm && NS_FAILED(sm-> 1.1543 + CanCreateWrapper(cx, *iid, identity, 1.1544 + GetClassInfo()))) { 1.1545 + // the security manager vetoed. It should have set an exception. 1.1546 + NS_RELEASE(obj); 1.1547 + aTearOff->SetInterface(nullptr); 1.1548 + return NS_ERROR_XPC_SECURITY_MANAGER_VETO; 1.1549 + } 1.1550 + 1.1551 + // If this is not already in our set we need to extend our set. 1.1552 + // Note: we do not cache the result of the previous call to HasInterface() 1.1553 + // because we unlocked and called out in the interim and the result of the 1.1554 + // previous call might not be correct anymore. 1.1555 + 1.1556 + if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) { 1.1557 + NS_RELEASE(obj); 1.1558 + aTearOff->SetInterface(nullptr); 1.1559 + return NS_ERROR_NO_INTERFACE; 1.1560 + } 1.1561 + 1.1562 + aTearOff->SetInterface(aInterface); 1.1563 + aTearOff->SetNative(obj); 1.1564 + if (needJSObject && !InitTearOffJSObject(aTearOff)) 1.1565 + return NS_ERROR_OUT_OF_MEMORY; 1.1566 + 1.1567 + return NS_OK; 1.1568 +} 1.1569 + 1.1570 +bool 1.1571 +XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to) 1.1572 +{ 1.1573 + AutoJSContext cx; 1.1574 + 1.1575 + RootedObject parent(cx, mFlatJSObject); 1.1576 + RootedObject proto(cx, JS_GetObjectPrototype(cx, parent)); 1.1577 + JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass), 1.1578 + proto, parent); 1.1579 + if (!obj) 1.1580 + return false; 1.1581 + 1.1582 + JS_SetPrivate(obj, to); 1.1583 + to->SetJSObject(obj); 1.1584 + return true; 1.1585 +} 1.1586 + 1.1587 +/***************************************************************************/ 1.1588 + 1.1589 +static bool Throw(nsresult errNum, XPCCallContext& ccx) 1.1590 +{ 1.1591 + XPCThrower::Throw(errNum, ccx); 1.1592 + return false; 1.1593 +} 1.1594 + 1.1595 +/***************************************************************************/ 1.1596 + 1.1597 +class MOZ_STACK_CLASS CallMethodHelper 1.1598 +{ 1.1599 + XPCCallContext& mCallContext; 1.1600 + // We wait to call SetLastResult(mInvokeResult) until ~CallMethodHelper(), 1.1601 + // so that XPCWN-implemented functions like XPCComponents::GetLastResult() 1.1602 + // can still access the previous result. 1.1603 + nsresult mInvokeResult; 1.1604 + nsIInterfaceInfo* const mIFaceInfo; 1.1605 + const nsXPTMethodInfo* mMethodInfo; 1.1606 + nsISupports* const mCallee; 1.1607 + const uint16_t mVTableIndex; 1.1608 + HandleId mIdxValueId; 1.1609 + 1.1610 + nsAutoTArray<nsXPTCVariant, 8> mDispatchParams; 1.1611 + uint8_t mJSContextIndex; // TODO make const 1.1612 + uint8_t mOptArgcIndex; // TODO make const 1.1613 + 1.1614 + jsval* const mArgv; 1.1615 + const uint32_t mArgc; 1.1616 + 1.1617 + MOZ_ALWAYS_INLINE bool 1.1618 + GetArraySizeFromParam(uint8_t paramIndex, uint32_t* result) const; 1.1619 + 1.1620 + MOZ_ALWAYS_INLINE bool 1.1621 + GetInterfaceTypeFromParam(uint8_t paramIndex, 1.1622 + const nsXPTType& datum_type, 1.1623 + nsID* result) const; 1.1624 + 1.1625 + MOZ_ALWAYS_INLINE bool 1.1626 + GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const; 1.1627 + 1.1628 + MOZ_ALWAYS_INLINE bool 1.1629 + GatherAndConvertResults(); 1.1630 + 1.1631 + MOZ_ALWAYS_INLINE bool 1.1632 + QueryInterfaceFastPath(); 1.1633 + 1.1634 + nsXPTCVariant* 1.1635 + GetDispatchParam(uint8_t paramIndex) 1.1636 + { 1.1637 + if (paramIndex >= mJSContextIndex) 1.1638 + paramIndex += 1; 1.1639 + if (paramIndex >= mOptArgcIndex) 1.1640 + paramIndex += 1; 1.1641 + return &mDispatchParams[paramIndex]; 1.1642 + } 1.1643 + const nsXPTCVariant* 1.1644 + GetDispatchParam(uint8_t paramIndex) const 1.1645 + { 1.1646 + return const_cast<CallMethodHelper*>(this)->GetDispatchParam(paramIndex); 1.1647 + } 1.1648 + 1.1649 + MOZ_ALWAYS_INLINE bool InitializeDispatchParams(); 1.1650 + 1.1651 + MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam); 1.1652 + MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i); 1.1653 + MOZ_ALWAYS_INLINE bool ConvertDependentParams(); 1.1654 + MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i); 1.1655 + 1.1656 + MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type); 1.1657 + 1.1658 + MOZ_ALWAYS_INLINE bool HandleDipperParam(nsXPTCVariant* dp, 1.1659 + const nsXPTParamInfo& paramInfo); 1.1660 + 1.1661 + MOZ_ALWAYS_INLINE nsresult Invoke(); 1.1662 + 1.1663 +public: 1.1664 + 1.1665 + CallMethodHelper(XPCCallContext& ccx) 1.1666 + : mCallContext(ccx) 1.1667 + , mInvokeResult(NS_ERROR_UNEXPECTED) 1.1668 + , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo()) 1.1669 + , mMethodInfo(nullptr) 1.1670 + , mCallee(ccx.GetTearOff()->GetNative()) 1.1671 + , mVTableIndex(ccx.GetMethodIndex()) 1.1672 + , mIdxValueId(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_VALUE)) 1.1673 + , mJSContextIndex(UINT8_MAX) 1.1674 + , mOptArgcIndex(UINT8_MAX) 1.1675 + , mArgv(ccx.GetArgv()) 1.1676 + , mArgc(ccx.GetArgc()) 1.1677 + 1.1678 + { 1.1679 + // Success checked later. 1.1680 + mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo); 1.1681 + } 1.1682 + 1.1683 + ~CallMethodHelper(); 1.1684 + 1.1685 + MOZ_ALWAYS_INLINE bool Call(); 1.1686 + 1.1687 +}; 1.1688 + 1.1689 +// static 1.1690 +bool 1.1691 +XPCWrappedNative::CallMethod(XPCCallContext& ccx, 1.1692 + CallMode mode /*= CALL_METHOD */) 1.1693 +{ 1.1694 + MOZ_ASSERT(ccx.GetXPCContext()->CallerTypeIsJavaScript(), 1.1695 + "Native caller for XPCWrappedNative::CallMethod?"); 1.1696 + 1.1697 + nsresult rv = ccx.CanCallNow(); 1.1698 + if (NS_FAILED(rv)) { 1.1699 + return Throw(rv, ccx); 1.1700 + } 1.1701 + 1.1702 + return CallMethodHelper(ccx).Call(); 1.1703 +} 1.1704 + 1.1705 +bool 1.1706 +CallMethodHelper::Call() 1.1707 +{ 1.1708 + mCallContext.SetRetVal(JSVAL_VOID); 1.1709 + 1.1710 + XPCJSRuntime::Get()->SetPendingException(nullptr); 1.1711 + 1.1712 + if (mVTableIndex == 0) { 1.1713 + return QueryInterfaceFastPath(); 1.1714 + } 1.1715 + 1.1716 + if (!mMethodInfo) { 1.1717 + Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext); 1.1718 + return false; 1.1719 + } 1.1720 + 1.1721 + if (!InitializeDispatchParams()) 1.1722 + return false; 1.1723 + 1.1724 + // Iterate through the params doing conversions of independent params only. 1.1725 + // When we later convert the dependent params (if any) we will know that 1.1726 + // the params upon which they depend will have already been converted - 1.1727 + // regardless of ordering. 1.1728 + bool foundDependentParam = false; 1.1729 + if (!ConvertIndependentParams(&foundDependentParam)) 1.1730 + return false; 1.1731 + 1.1732 + if (foundDependentParam && !ConvertDependentParams()) 1.1733 + return false; 1.1734 + 1.1735 + mInvokeResult = Invoke(); 1.1736 + 1.1737 + if (JS_IsExceptionPending(mCallContext)) { 1.1738 + return false; 1.1739 + } 1.1740 + 1.1741 + if (NS_FAILED(mInvokeResult)) { 1.1742 + ThrowBadResult(mInvokeResult, mCallContext); 1.1743 + return false; 1.1744 + } 1.1745 + 1.1746 + return GatherAndConvertResults(); 1.1747 +} 1.1748 + 1.1749 +CallMethodHelper::~CallMethodHelper() 1.1750 +{ 1.1751 + uint8_t paramCount = mMethodInfo->GetParamCount(); 1.1752 + if (mDispatchParams.Length()) { 1.1753 + for (uint8_t i = 0; i < paramCount; i++) { 1.1754 + nsXPTCVariant* dp = GetDispatchParam(i); 1.1755 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); 1.1756 + 1.1757 + if (paramInfo.GetType().IsArray()) { 1.1758 + void* p = dp->val.p; 1.1759 + if (!p) 1.1760 + continue; 1.1761 + 1.1762 + // Clean up the array contents if necessary. 1.1763 + if (dp->DoesValNeedCleanup()) { 1.1764 + // We need some basic information to properly destroy the array. 1.1765 + uint32_t array_count = 0; 1.1766 + nsXPTType datum_type; 1.1767 + if (!GetArraySizeFromParam(i, &array_count) || 1.1768 + !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex, 1.1769 + ¶mInfo, 1.1770 + 1, &datum_type))) { 1.1771 + // XXXbholley - I'm not convinced that the above calls will 1.1772 + // ever fail. 1.1773 + NS_ERROR("failed to get array information, we'll leak here"); 1.1774 + continue; 1.1775 + } 1.1776 + 1.1777 + // Loop over the array contents. For each one, we create a 1.1778 + // dummy 'val' and pass it to the cleanup helper. 1.1779 + for (uint32_t k = 0; k < array_count; k++) { 1.1780 + nsXPTCMiniVariant v; 1.1781 + v.val.p = static_cast<void**>(p)[k]; 1.1782 + CleanupParam(v, datum_type); 1.1783 + } 1.1784 + } 1.1785 + 1.1786 + // always free the array itself 1.1787 + nsMemory::Free(p); 1.1788 + } else { 1.1789 + // Clean up single parameters (if requested). 1.1790 + if (dp->DoesValNeedCleanup()) 1.1791 + CleanupParam(*dp, dp->type); 1.1792 + } 1.1793 + } 1.1794 + } 1.1795 + 1.1796 + mCallContext.GetXPCContext()->SetLastResult(mInvokeResult); 1.1797 +} 1.1798 + 1.1799 +bool 1.1800 +CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex, 1.1801 + uint32_t* result) const 1.1802 +{ 1.1803 + nsresult rv; 1.1804 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex); 1.1805 + 1.1806 + // TODO fixup the various exceptions that are thrown 1.1807 + 1.1808 + rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, ¶mInfo, 0, ¶mIndex); 1.1809 + if (NS_FAILED(rv)) 1.1810 + return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext); 1.1811 + 1.1812 + *result = GetDispatchParam(paramIndex)->val.u32; 1.1813 + 1.1814 + return true; 1.1815 +} 1.1816 + 1.1817 +bool 1.1818 +CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex, 1.1819 + const nsXPTType& datum_type, 1.1820 + nsID* result) const 1.1821 +{ 1.1822 + nsresult rv; 1.1823 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex); 1.1824 + uint8_t tag = datum_type.TagPart(); 1.1825 + 1.1826 + // TODO fixup the various exceptions that are thrown 1.1827 + 1.1828 + if (tag == nsXPTType::T_INTERFACE) { 1.1829 + rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, ¶mInfo, result); 1.1830 + if (NS_FAILED(rv)) 1.1831 + return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, 1.1832 + paramIndex, mCallContext); 1.1833 + } else if (tag == nsXPTType::T_INTERFACE_IS) { 1.1834 + rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, ¶mInfo, 1.1835 + ¶mIndex); 1.1836 + if (NS_FAILED(rv)) 1.1837 + return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext); 1.1838 + 1.1839 + nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p; 1.1840 + if (!p) 1.1841 + return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, 1.1842 + paramIndex, mCallContext); 1.1843 + *result = *p; 1.1844 + } 1.1845 + return true; 1.1846 +} 1.1847 + 1.1848 +bool 1.1849 +CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const 1.1850 +{ 1.1851 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex); 1.1852 + 1.1853 + if ((paramInfo.IsOut() || paramInfo.IsDipper()) && 1.1854 + !paramInfo.IsRetval()) { 1.1855 + MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(), 1.1856 + "Expected either enough arguments or an optional argument"); 1.1857 + jsval arg = paramIndex < mArgc ? mArgv[paramIndex] : JSVAL_NULL; 1.1858 + if (paramIndex < mArgc) { 1.1859 + RootedObject obj(mCallContext); 1.1860 + if (!arg.isPrimitive()) 1.1861 + obj = &arg.toObject(); 1.1862 + if (!obj || !JS_GetPropertyById(mCallContext, obj, mIdxValueId, srcp)) { 1.1863 + // Explicitly passed in unusable value for out param. Note 1.1864 + // that if i >= mArgc we already know that |arg| is JSVAL_NULL, 1.1865 + // and that's ok. 1.1866 + ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, paramIndex, 1.1867 + mCallContext); 1.1868 + return false; 1.1869 + } 1.1870 + } 1.1871 + } 1.1872 + 1.1873 + return true; 1.1874 +} 1.1875 + 1.1876 +bool 1.1877 +CallMethodHelper::GatherAndConvertResults() 1.1878 +{ 1.1879 + // now we iterate through the native params to gather and convert results 1.1880 + uint8_t paramCount = mMethodInfo->GetParamCount(); 1.1881 + for (uint8_t i = 0; i < paramCount; i++) { 1.1882 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); 1.1883 + if (!paramInfo.IsOut() && !paramInfo.IsDipper()) 1.1884 + continue; 1.1885 + 1.1886 + const nsXPTType& type = paramInfo.GetType(); 1.1887 + nsXPTCVariant* dp = GetDispatchParam(i); 1.1888 + RootedValue v(mCallContext, NullValue()); 1.1889 + uint32_t array_count = 0; 1.1890 + nsXPTType datum_type; 1.1891 + bool isArray = type.IsArray(); 1.1892 + bool isSizedString = isArray ? 1.1893 + false : 1.1894 + type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || 1.1895 + type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS; 1.1896 + 1.1897 + if (isArray) { 1.1898 + if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, ¶mInfo, 1, 1.1899 + &datum_type))) { 1.1900 + Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext); 1.1901 + return false; 1.1902 + } 1.1903 + } else 1.1904 + datum_type = type; 1.1905 + 1.1906 + if (isArray || isSizedString) { 1.1907 + if (!GetArraySizeFromParam(i, &array_count)) 1.1908 + return false; 1.1909 + } 1.1910 + 1.1911 + nsID param_iid; 1.1912 + if (datum_type.IsInterfacePointer() && 1.1913 + !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid)) 1.1914 + return false; 1.1915 + 1.1916 + nsresult err; 1.1917 + if (isArray) { 1.1918 + if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val, 1.1919 + datum_type, ¶m_iid, 1.1920 + array_count, &err)) { 1.1921 + // XXX need exception scheme for arrays to indicate bad element 1.1922 + ThrowBadParam(err, i, mCallContext); 1.1923 + return false; 1.1924 + } 1.1925 + } else if (isSizedString) { 1.1926 + if (!XPCConvert::NativeStringWithSize2JS(&v, 1.1927 + (const void*)&dp->val, 1.1928 + datum_type, 1.1929 + array_count, &err)) { 1.1930 + ThrowBadParam(err, i, mCallContext); 1.1931 + return false; 1.1932 + } 1.1933 + } else { 1.1934 + if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type, 1.1935 + ¶m_iid, &err)) { 1.1936 + ThrowBadParam(err, i, mCallContext); 1.1937 + return false; 1.1938 + } 1.1939 + } 1.1940 + 1.1941 + if (paramInfo.IsRetval()) { 1.1942 + mCallContext.SetRetVal(v); 1.1943 + } else if (i < mArgc) { 1.1944 + // we actually assured this before doing the invoke 1.1945 + MOZ_ASSERT(mArgv[i].isObject(), "out var is not object"); 1.1946 + RootedObject obj(mCallContext, &mArgv[i].toObject()); 1.1947 + if (!JS_SetPropertyById(mCallContext, obj, mIdxValueId, v)) { 1.1948 + ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, i, mCallContext); 1.1949 + return false; 1.1950 + } 1.1951 + } else { 1.1952 + MOZ_ASSERT(paramInfo.IsOptional(), 1.1953 + "Expected either enough arguments or an optional argument"); 1.1954 + } 1.1955 + } 1.1956 + 1.1957 + return true; 1.1958 +} 1.1959 + 1.1960 +bool 1.1961 +CallMethodHelper::QueryInterfaceFastPath() 1.1962 +{ 1.1963 + MOZ_ASSERT(mVTableIndex == 0, 1.1964 + "Using the QI fast-path for a method other than QueryInterface"); 1.1965 + 1.1966 + if (mArgc < 1) { 1.1967 + Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext); 1.1968 + return false; 1.1969 + } 1.1970 + 1.1971 + if (!mArgv[0].isObject()) { 1.1972 + ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext); 1.1973 + return false; 1.1974 + } 1.1975 + 1.1976 + const nsID* iid = xpc_JSObjectToID(mCallContext, &mArgv[0].toObject()); 1.1977 + if (!iid) { 1.1978 + ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext); 1.1979 + return false; 1.1980 + } 1.1981 + 1.1982 + nsISupports* qiresult = nullptr; 1.1983 + mInvokeResult = mCallee->QueryInterface(*iid, (void**) &qiresult); 1.1984 + 1.1985 + if (NS_FAILED(mInvokeResult)) { 1.1986 + ThrowBadResult(mInvokeResult, mCallContext); 1.1987 + return false; 1.1988 + } 1.1989 + 1.1990 + RootedValue v(mCallContext, NullValue()); 1.1991 + nsresult err; 1.1992 + bool success = 1.1993 + XPCConvert::NativeData2JS(&v, &qiresult, 1.1994 + nsXPTType::T_INTERFACE_IS, 1.1995 + iid, &err); 1.1996 + NS_IF_RELEASE(qiresult); 1.1997 + 1.1998 + if (!success) { 1.1999 + ThrowBadParam(err, 0, mCallContext); 1.2000 + return false; 1.2001 + } 1.2002 + 1.2003 + mCallContext.SetRetVal(v); 1.2004 + return true; 1.2005 +} 1.2006 + 1.2007 +bool 1.2008 +CallMethodHelper::InitializeDispatchParams() 1.2009 +{ 1.2010 + const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0; 1.2011 + const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0; 1.2012 + const uint8_t paramCount = mMethodInfo->GetParamCount(); 1.2013 + uint8_t requiredArgs = paramCount; 1.2014 + uint8_t hasRetval = 0; 1.2015 + 1.2016 + // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this. 1.2017 + if (paramCount && mMethodInfo->GetParam(paramCount-1).IsRetval()) { 1.2018 + hasRetval = 1; 1.2019 + requiredArgs--; 1.2020 + } 1.2021 + 1.2022 + if (mArgc < requiredArgs || wantsOptArgc) { 1.2023 + if (wantsOptArgc) 1.2024 + mOptArgcIndex = requiredArgs; 1.2025 + 1.2026 + // skip over any optional arguments 1.2027 + while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional()) 1.2028 + requiredArgs--; 1.2029 + 1.2030 + if (mArgc < requiredArgs) { 1.2031 + Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext); 1.2032 + return false; 1.2033 + } 1.2034 + } 1.2035 + 1.2036 + if (wantsJSContext) { 1.2037 + if (wantsOptArgc) 1.2038 + // Need to bump mOptArgcIndex up one here. 1.2039 + mJSContextIndex = mOptArgcIndex++; 1.2040 + else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter()) 1.2041 + // For attributes, we always put the JSContext* first. 1.2042 + mJSContextIndex = 0; 1.2043 + else 1.2044 + mJSContextIndex = paramCount - hasRetval; 1.2045 + } 1.2046 + 1.2047 + // iterate through the params to clear flags (for safe cleanup later) 1.2048 + for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) { 1.2049 + nsXPTCVariant* dp = mDispatchParams.AppendElement(); 1.2050 + dp->ClearFlags(); 1.2051 + dp->val.p = nullptr; 1.2052 + } 1.2053 + 1.2054 + // Fill in the JSContext argument 1.2055 + if (wantsJSContext) { 1.2056 + nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex]; 1.2057 + dp->type = nsXPTType::T_VOID; 1.2058 + dp->val.p = mCallContext; 1.2059 + } 1.2060 + 1.2061 + // Fill in the optional_argc argument 1.2062 + if (wantsOptArgc) { 1.2063 + nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex]; 1.2064 + dp->type = nsXPTType::T_U8; 1.2065 + dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs; 1.2066 + } 1.2067 + 1.2068 + return true; 1.2069 +} 1.2070 + 1.2071 +bool 1.2072 +CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam) 1.2073 +{ 1.2074 + const uint8_t paramCount = mMethodInfo->GetParamCount(); 1.2075 + for (uint8_t i = 0; i < paramCount; i++) { 1.2076 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); 1.2077 + 1.2078 + if (paramInfo.GetType().IsDependent()) 1.2079 + *foundDependentParam = true; 1.2080 + else if (!ConvertIndependentParam(i)) 1.2081 + return false; 1.2082 + 1.2083 + } 1.2084 + 1.2085 + return true; 1.2086 +} 1.2087 + 1.2088 +bool 1.2089 +CallMethodHelper::ConvertIndependentParam(uint8_t i) 1.2090 +{ 1.2091 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); 1.2092 + const nsXPTType& type = paramInfo.GetType(); 1.2093 + uint8_t type_tag = type.TagPart(); 1.2094 + nsXPTCVariant* dp = GetDispatchParam(i); 1.2095 + dp->type = type; 1.2096 + MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!"); 1.2097 + 1.2098 + // Handle dipper types separately. 1.2099 + if (paramInfo.IsDipper()) 1.2100 + return HandleDipperParam(dp, paramInfo); 1.2101 + 1.2102 + // Specify the correct storage/calling semantics. 1.2103 + if (paramInfo.IsIndirect()) 1.2104 + dp->SetIndirect(); 1.2105 + 1.2106 + // The JSVal proper is always stored within the 'val' union and passed 1.2107 + // indirectly, regardless of in/out-ness. 1.2108 + if (type_tag == nsXPTType::T_JSVAL) { 1.2109 + // Root the value. 1.2110 + dp->val.j = JSVAL_VOID; 1.2111 + if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param")) 1.2112 + return false; 1.2113 + } 1.2114 + 1.2115 + // Flag cleanup for anything that isn't self-contained. 1.2116 + if (!type.IsArithmetic()) 1.2117 + dp->SetValNeedsCleanup(); 1.2118 + 1.2119 + // Even if there's nothing to convert, we still need to examine the 1.2120 + // JSObject container for out-params. If it's null or otherwise invalid, 1.2121 + // we want to know before the call, rather than after. 1.2122 + // 1.2123 + // This is a no-op for 'in' params. 1.2124 + RootedValue src(mCallContext); 1.2125 + if (!GetOutParamSource(i, &src)) 1.2126 + return false; 1.2127 + 1.2128 + // All that's left to do is value conversion. Bail early if we don't need 1.2129 + // to do that. 1.2130 + if (!paramInfo.IsIn()) 1.2131 + return true; 1.2132 + 1.2133 + // We're definitely some variety of 'in' now, so there's something to 1.2134 + // convert. The source value for conversion depends on whether we're 1.2135 + // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above, 1.2136 + // so all that's left is 'in'. 1.2137 + if (!paramInfo.IsOut()) { 1.2138 + // Handle the 'in' case. 1.2139 + MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(), 1.2140 + "Expected either enough arguments or an optional argument"); 1.2141 + if (i < mArgc) 1.2142 + src = mArgv[i]; 1.2143 + else if (type_tag == nsXPTType::T_JSVAL) 1.2144 + src = JSVAL_VOID; 1.2145 + else 1.2146 + src = JSVAL_NULL; 1.2147 + } 1.2148 + 1.2149 + nsID param_iid; 1.2150 + if (type_tag == nsXPTType::T_INTERFACE && 1.2151 + NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, ¶mInfo, 1.2152 + ¶m_iid))) { 1.2153 + ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext); 1.2154 + return false; 1.2155 + } 1.2156 + 1.2157 + nsresult err; 1.2158 + if (!XPCConvert::JSData2Native(&dp->val, src, type, true, ¶m_iid, &err)) { 1.2159 + ThrowBadParam(err, i, mCallContext); 1.2160 + return false; 1.2161 + } 1.2162 + 1.2163 + return true; 1.2164 +} 1.2165 + 1.2166 +bool 1.2167 +CallMethodHelper::ConvertDependentParams() 1.2168 +{ 1.2169 + const uint8_t paramCount = mMethodInfo->GetParamCount(); 1.2170 + for (uint8_t i = 0; i < paramCount; i++) { 1.2171 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); 1.2172 + 1.2173 + if (!paramInfo.GetType().IsDependent()) 1.2174 + continue; 1.2175 + if (!ConvertDependentParam(i)) 1.2176 + return false; 1.2177 + } 1.2178 + 1.2179 + return true; 1.2180 +} 1.2181 + 1.2182 +bool 1.2183 +CallMethodHelper::ConvertDependentParam(uint8_t i) 1.2184 +{ 1.2185 + const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); 1.2186 + const nsXPTType& type = paramInfo.GetType(); 1.2187 + nsXPTType datum_type; 1.2188 + uint32_t array_count = 0; 1.2189 + bool isArray = type.IsArray(); 1.2190 + 1.2191 + bool isSizedString = isArray ? 1.2192 + false : 1.2193 + type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || 1.2194 + type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS; 1.2195 + 1.2196 + nsXPTCVariant* dp = GetDispatchParam(i); 1.2197 + dp->type = type; 1.2198 + 1.2199 + if (isArray) { 1.2200 + if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, ¶mInfo, 1, 1.2201 + &datum_type))) { 1.2202 + Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext); 1.2203 + return false; 1.2204 + } 1.2205 + MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL, 1.2206 + "Arrays of JSVals not currently supported - see bug 693337."); 1.2207 + } else { 1.2208 + datum_type = type; 1.2209 + } 1.2210 + 1.2211 + // Specify the correct storage/calling semantics. 1.2212 + if (paramInfo.IsIndirect()) 1.2213 + dp->SetIndirect(); 1.2214 + 1.2215 + // We have 3 possible type of dependent parameters: Arrays, Sized Strings, 1.2216 + // and iid_is Interface pointers. The latter two always need cleanup, and 1.2217 + // arrays need cleanup for all non-arithmetic types. Since the latter two 1.2218 + // cases also happen to be non-arithmetic, we can just inspect datum_type 1.2219 + // here. 1.2220 + if (!datum_type.IsArithmetic()) 1.2221 + dp->SetValNeedsCleanup(); 1.2222 + 1.2223 + // Even if there's nothing to convert, we still need to examine the 1.2224 + // JSObject container for out-params. If it's null or otherwise invalid, 1.2225 + // we want to know before the call, rather than after. 1.2226 + // 1.2227 + // This is a no-op for 'in' params. 1.2228 + RootedValue src(mCallContext); 1.2229 + if (!GetOutParamSource(i, &src)) 1.2230 + return false; 1.2231 + 1.2232 + // All that's left to do is value conversion. Bail early if we don't need 1.2233 + // to do that. 1.2234 + if (!paramInfo.IsIn()) 1.2235 + return true; 1.2236 + 1.2237 + // We're definitely some variety of 'in' now, so there's something to 1.2238 + // convert. The source value for conversion depends on whether we're 1.2239 + // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above, 1.2240 + // so all that's left is 'in'. 1.2241 + if (!paramInfo.IsOut()) { 1.2242 + // Handle the 'in' case. 1.2243 + MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(), 1.2244 + "Expected either enough arguments or an optional argument"); 1.2245 + src = i < mArgc ? mArgv[i] : JSVAL_NULL; 1.2246 + } 1.2247 + 1.2248 + nsID param_iid; 1.2249 + if (datum_type.IsInterfacePointer() && 1.2250 + !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid)) 1.2251 + return false; 1.2252 + 1.2253 + nsresult err; 1.2254 + 1.2255 + if (isArray || isSizedString) { 1.2256 + if (!GetArraySizeFromParam(i, &array_count)) 1.2257 + return false; 1.2258 + 1.2259 + if (isArray) { 1.2260 + if (array_count && 1.2261 + !XPCConvert::JSArray2Native((void**)&dp->val, src, 1.2262 + array_count, datum_type, ¶m_iid, 1.2263 + &err)) { 1.2264 + // XXX need exception scheme for arrays to indicate bad element 1.2265 + ThrowBadParam(err, i, mCallContext); 1.2266 + return false; 1.2267 + } 1.2268 + } else // if (isSizedString) 1.2269 + { 1.2270 + if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val, 1.2271 + src, array_count, 1.2272 + datum_type, &err)) { 1.2273 + ThrowBadParam(err, i, mCallContext); 1.2274 + return false; 1.2275 + } 1.2276 + } 1.2277 + } else { 1.2278 + if (!XPCConvert::JSData2Native(&dp->val, src, type, true, 1.2279 + ¶m_iid, &err)) { 1.2280 + ThrowBadParam(err, i, mCallContext); 1.2281 + return false; 1.2282 + } 1.2283 + } 1.2284 + 1.2285 + return true; 1.2286 +} 1.2287 + 1.2288 +// Performs all necessary teardown on a parameter after method invocation. 1.2289 +// 1.2290 +// This method should only be called if the value in question was flagged 1.2291 +// for cleanup (ie, if dp->DoesValNeedCleanup()). 1.2292 +void 1.2293 +CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type) 1.2294 +{ 1.2295 + // We handle array elements, but not the arrays themselves. 1.2296 + MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays."); 1.2297 + 1.2298 + // Pointers may sometimes be null even if cleanup was requested. Combine 1.2299 + // the null checking for all the different types into one check here. 1.2300 + if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr) 1.2301 + return; 1.2302 + 1.2303 + switch (type.TagPart()) { 1.2304 + case nsXPTType::T_JSVAL: 1.2305 + js::RemoveRawValueRoot(mCallContext, (jsval*)¶m.val); 1.2306 + break; 1.2307 + case nsXPTType::T_INTERFACE: 1.2308 + case nsXPTType::T_INTERFACE_IS: 1.2309 + ((nsISupports*)param.val.p)->Release(); 1.2310 + break; 1.2311 + case nsXPTType::T_ASTRING: 1.2312 + case nsXPTType::T_DOMSTRING: 1.2313 + nsXPConnect::GetRuntimeInstance()->DeleteShortLivedString((nsString*)param.val.p); 1.2314 + break; 1.2315 + case nsXPTType::T_UTF8STRING: 1.2316 + case nsXPTType::T_CSTRING: 1.2317 + { 1.2318 + nsCString* rs = (nsCString*)param.val.p; 1.2319 + if (rs != &EmptyCString() && rs != &NullCString()) 1.2320 + delete rs; 1.2321 + } 1.2322 + break; 1.2323 + default: 1.2324 + MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type."); 1.2325 + nsMemory::Free(param.val.p); 1.2326 + break; 1.2327 + } 1.2328 +} 1.2329 + 1.2330 +// Handle parameters with dipper types. 1.2331 +// 1.2332 +// Dipper types are one of the more inscrutable aspects of xpidl. In a 1.2333 +// nutshell, dippers are empty container objects, created and passed by 1.2334 +// the caller, and filled by the callee. The callee receives a 1.2335 +// fully-formed object, and thus does not have to construct anything. But 1.2336 +// the object is functionally empty, and the callee is responsible for 1.2337 +// putting something useful inside of it. 1.2338 +// 1.2339 +// XPIDL decides which types to make dippers. The list of these types 1.2340 +// is given in the isDipperType() function in typelib.py, and is currently 1.2341 +// limited to 4 string types. 1.2342 +// 1.2343 +// When a dipper type is declared as an 'out' parameter, xpidl internally 1.2344 +// converts it to an 'in', and sets the XPT_PD_DIPPER flag on it. For this 1.2345 +// reason, dipper types are sometimes referred to as 'out parameters 1.2346 +// masquerading as in'. The burden of maintaining this illusion falls mostly 1.2347 +// on XPConnect - we create the empty containers, and harvest the results 1.2348 +// after the call. 1.2349 +// 1.2350 +// This method creates these empty containers. 1.2351 +bool 1.2352 +CallMethodHelper::HandleDipperParam(nsXPTCVariant* dp, 1.2353 + const nsXPTParamInfo& paramInfo) 1.2354 +{ 1.2355 + // Get something we can make comparisons with. 1.2356 + uint8_t type_tag = paramInfo.GetType().TagPart(); 1.2357 + 1.2358 + // Dippers always have the 'in' and 'dipper' flags set. Never 'out'. 1.2359 + MOZ_ASSERT(!paramInfo.IsOut(), "Dipper has unexpected flags."); 1.2360 + 1.2361 + // xpidl.h specifies that dipper types will be used in exactly four 1.2362 + // cases, all strings. Verify that here. 1.2363 + MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING || 1.2364 + type_tag == nsXPTType::T_DOMSTRING || 1.2365 + type_tag == nsXPTType::T_UTF8STRING || 1.2366 + type_tag == nsXPTType::T_CSTRING, 1.2367 + "Unexpected dipper type!"); 1.2368 + 1.2369 + // ASTRING and DOMSTRING are very similar, and both use nsString. 1.2370 + // UTF8_STRING and CSTRING are also quite similar, and both use nsCString. 1.2371 + if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING) 1.2372 + dp->val.p = nsXPConnect::GetRuntimeInstance()->NewShortLivedString(); 1.2373 + else 1.2374 + dp->val.p = new nsCString(); 1.2375 + 1.2376 + // Check for OOM, in either case. 1.2377 + if (!dp->val.p) { 1.2378 + JS_ReportOutOfMemory(mCallContext); 1.2379 + return false; 1.2380 + } 1.2381 + 1.2382 + // We allocated, so we need to deallocate after the method call completes. 1.2383 + dp->SetValNeedsCleanup(); 1.2384 + 1.2385 + return true; 1.2386 +} 1.2387 + 1.2388 +nsresult 1.2389 +CallMethodHelper::Invoke() 1.2390 +{ 1.2391 + uint32_t argc = mDispatchParams.Length(); 1.2392 + nsXPTCVariant* argv = mDispatchParams.Elements(); 1.2393 + 1.2394 + return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv); 1.2395 +} 1.2396 + 1.2397 +/***************************************************************************/ 1.2398 +// interface methods 1.2399 + 1.2400 +/* JSObjectPtr GetJSObject(); */ 1.2401 +JSObject* 1.2402 +XPCWrappedNative::GetJSObject() 1.2403 +{ 1.2404 + return GetFlatJSObject(); 1.2405 +} 1.2406 + 1.2407 +/* readonly attribute nsISupports Native; */ 1.2408 +NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative) 1.2409 +{ 1.2410 + // No need to QI here, we already have the correct nsISupports 1.2411 + // vtable. 1.2412 + nsCOMPtr<nsISupports> rval = mIdentity; 1.2413 + rval.forget(aNative); 1.2414 + return NS_OK; 1.2415 +} 1.2416 + 1.2417 +/* reaonly attribute JSObjectPtr JSObjectPrototype; */ 1.2418 +NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype) 1.2419 +{ 1.2420 + *aJSObjectPrototype = HasProto() ? 1.2421 + GetProto()->GetJSProtoObject() : GetFlatJSObject(); 1.2422 + return NS_OK; 1.2423 +} 1.2424 + 1.2425 +nsIPrincipal* 1.2426 +XPCWrappedNative::GetObjectPrincipal() const 1.2427 +{ 1.2428 + nsIPrincipal* principal = GetScope()->GetPrincipal(); 1.2429 +#ifdef DEBUG 1.2430 + // Because of inner window reuse, we can have objects with one principal 1.2431 + // living in a scope with a different (but same-origin) principal. So 1.2432 + // just check same-origin here. 1.2433 + nsCOMPtr<nsIScriptObjectPrincipal> objPrin(do_QueryInterface(mIdentity)); 1.2434 + if (objPrin) { 1.2435 + bool equal; 1.2436 + if (!principal) 1.2437 + equal = !objPrin->GetPrincipal(); 1.2438 + else 1.2439 + principal->Equals(objPrin->GetPrincipal(), &equal); 1.2440 + MOZ_ASSERT(equal, "Principal mismatch. Expect bad things to happen"); 1.2441 + } 1.2442 +#endif 1.2443 + return principal; 1.2444 +} 1.2445 + 1.2446 +/* XPCNativeInterface FindInterfaceWithMember (in JSHandleId name); */ 1.2447 +NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name, 1.2448 + nsIInterfaceInfo * *_retval) 1.2449 +{ 1.2450 + XPCNativeInterface* iface; 1.2451 + XPCNativeMember* member; 1.2452 + 1.2453 + if (GetSet()->FindMember(name, &member, &iface) && iface) { 1.2454 + nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo(); 1.2455 + temp.forget(_retval); 1.2456 + } else 1.2457 + *_retval = nullptr; 1.2458 + return NS_OK; 1.2459 +} 1.2460 + 1.2461 +/* XPCNativeInterface FindInterfaceWithName (in JSHandleId name); */ 1.2462 +NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name, 1.2463 + nsIInterfaceInfo * *_retval) 1.2464 +{ 1.2465 + XPCNativeInterface* iface = GetSet()->FindNamedInterface(name); 1.2466 + if (iface) { 1.2467 + nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo(); 1.2468 + temp.forget(_retval); 1.2469 + } else 1.2470 + *_retval = nullptr; 1.2471 + return NS_OK; 1.2472 +} 1.2473 + 1.2474 +/* [notxpcom] bool HasNativeMember (in JSHandleId name); */ 1.2475 +NS_IMETHODIMP_(bool) 1.2476 +XPCWrappedNative::HasNativeMember(HandleId name) 1.2477 +{ 1.2478 + XPCNativeMember *member = nullptr; 1.2479 + uint16_t ignored; 1.2480 + return GetSet()->FindMember(name, &member, &ignored) && !!member; 1.2481 +} 1.2482 + 1.2483 +/* void finishInitForWrappedGlobal (); */ 1.2484 +NS_IMETHODIMP XPCWrappedNative::FinishInitForWrappedGlobal() 1.2485 +{ 1.2486 + // We can only be called under certain conditions. 1.2487 + MOZ_ASSERT(mScriptableInfo); 1.2488 + MOZ_ASSERT(mScriptableInfo->GetFlags().IsGlobalObject()); 1.2489 + MOZ_ASSERT(HasProto()); 1.2490 + 1.2491 + // Call PostCreateProrotype. 1.2492 + bool success = GetProto()->CallPostCreatePrototype(); 1.2493 + if (!success) 1.2494 + return NS_ERROR_FAILURE; 1.2495 + 1.2496 + return NS_OK; 1.2497 +} 1.2498 + 1.2499 +/* void debugDump (in short depth); */ 1.2500 +NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth) 1.2501 +{ 1.2502 +#ifdef DEBUG 1.2503 + depth-- ; 1.2504 + XPC_LOG_ALWAYS(("XPCWrappedNative @ %x with mRefCnt = %d", this, mRefCnt.get())); 1.2505 + XPC_LOG_INDENT(); 1.2506 + 1.2507 + if (HasProto()) { 1.2508 + XPCWrappedNativeProto* proto = GetProto(); 1.2509 + if (depth && proto) 1.2510 + proto->DebugDump(depth); 1.2511 + else 1.2512 + XPC_LOG_ALWAYS(("mMaybeProto @ %x", proto)); 1.2513 + } else 1.2514 + XPC_LOG_ALWAYS(("Scope @ %x", GetScope())); 1.2515 + 1.2516 + if (depth && mSet) 1.2517 + mSet->DebugDump(depth); 1.2518 + else 1.2519 + XPC_LOG_ALWAYS(("mSet @ %x", mSet)); 1.2520 + 1.2521 + XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.getPtr())); 1.2522 + XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity)); 1.2523 + XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo)); 1.2524 + 1.2525 + if (depth && mScriptableInfo) { 1.2526 + XPC_LOG_INDENT(); 1.2527 + XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptableInfo->GetCallback())); 1.2528 + XPC_LOG_ALWAYS(("mFlags of %x", (uint32_t)mScriptableInfo->GetFlags())); 1.2529 + XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass())); 1.2530 + XPC_LOG_OUTDENT(); 1.2531 + } 1.2532 + XPC_LOG_OUTDENT(); 1.2533 +#endif 1.2534 + return NS_OK; 1.2535 +} 1.2536 + 1.2537 +/***************************************************************************/ 1.2538 + 1.2539 +char* 1.2540 +XPCWrappedNative::ToString(XPCWrappedNativeTearOff* to /* = nullptr */ ) const 1.2541 +{ 1.2542 +#ifdef DEBUG 1.2543 +# define FMT_ADDR " @ 0x%p" 1.2544 +# define FMT_STR(str) str 1.2545 +# define PARAM_ADDR(w) , w 1.2546 +#else 1.2547 +# define FMT_ADDR "" 1.2548 +# define FMT_STR(str) 1.2549 +# define PARAM_ADDR(w) 1.2550 +#endif 1.2551 + 1.2552 + char* sz = nullptr; 1.2553 + char* name = nullptr; 1.2554 + 1.2555 + XPCNativeScriptableInfo* si = GetScriptableInfo(); 1.2556 + if (si) 1.2557 + name = JS_smprintf("%s", si->GetJSClass()->name); 1.2558 + if (to) { 1.2559 + const char* fmt = name ? " (%s)" : "%s"; 1.2560 + name = JS_sprintf_append(name, fmt, 1.2561 + to->GetInterface()->GetNameString()); 1.2562 + } else if (!name) { 1.2563 + XPCNativeSet* set = GetSet(); 1.2564 + XPCNativeInterface** array = set->GetInterfaceArray(); 1.2565 + uint16_t count = set->GetInterfaceCount(); 1.2566 + 1.2567 + if (count == 1) 1.2568 + name = JS_sprintf_append(name, "%s", array[0]->GetNameString()); 1.2569 + else if (count == 2 && 1.2570 + array[0] == XPCNativeInterface::GetISupports()) { 1.2571 + name = JS_sprintf_append(name, "%s", array[1]->GetNameString()); 1.2572 + } else { 1.2573 + for (uint16_t i = 0; i < count; i++) { 1.2574 + const char* fmt = (i == 0) ? 1.2575 + "(%s" : (i == count-1) ? 1.2576 + ", %s)" : ", %s"; 1.2577 + name = JS_sprintf_append(name, fmt, 1.2578 + array[i]->GetNameString()); 1.2579 + } 1.2580 + } 1.2581 + } 1.2582 + 1.2583 + if (!name) { 1.2584 + return nullptr; 1.2585 + } 1.2586 + const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native") 1.2587 + FMT_ADDR FMT_STR(")") "]"; 1.2588 + if (si) { 1.2589 + fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]"; 1.2590 + } 1.2591 + sz = JS_smprintf(fmt, name PARAM_ADDR(this) PARAM_ADDR(mIdentity)); 1.2592 + 1.2593 + JS_smprintf_free(name); 1.2594 + 1.2595 + 1.2596 + return sz; 1.2597 + 1.2598 +#undef FMT_ADDR 1.2599 +#undef PARAM_ADDR 1.2600 +} 1.2601 + 1.2602 +/***************************************************************************/ 1.2603 + 1.2604 +#ifdef XPC_CHECK_CLASSINFO_CLAIMS 1.2605 +static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper) 1.2606 +{ 1.2607 + if (!wrapper || !wrapper->GetClassInfo()) 1.2608 + return; 1.2609 + 1.2610 + nsISupports* obj = wrapper->GetIdentityObject(); 1.2611 + XPCNativeSet* set = wrapper->GetSet(); 1.2612 + uint16_t count = set->GetInterfaceCount(); 1.2613 + for (uint16_t i = 0; i < count; i++) { 1.2614 + nsIClassInfo* clsInfo = wrapper->GetClassInfo(); 1.2615 + XPCNativeInterface* iface = set->GetInterfaceAt(i); 1.2616 + nsIInterfaceInfo* info = iface->GetInterfaceInfo(); 1.2617 + const nsIID* iid; 1.2618 + nsISupports* ptr; 1.2619 + 1.2620 + info->GetIIDShared(&iid); 1.2621 + nsresult rv = obj->QueryInterface(*iid, (void**)&ptr); 1.2622 + if (NS_SUCCEEDED(rv)) { 1.2623 + NS_RELEASE(ptr); 1.2624 + continue; 1.2625 + } 1.2626 + if (rv == NS_ERROR_OUT_OF_MEMORY) 1.2627 + continue; 1.2628 + 1.2629 + // Houston, We have a problem... 1.2630 + 1.2631 + char* className = nullptr; 1.2632 + char* contractID = nullptr; 1.2633 + const char* interfaceName; 1.2634 + 1.2635 + info->GetNameShared(&interfaceName); 1.2636 + clsInfo->GetContractID(&contractID); 1.2637 + if (wrapper->GetScriptableInfo()) { 1.2638 + wrapper->GetScriptableInfo()->GetCallback()-> 1.2639 + GetClassName(&className); 1.2640 + } 1.2641 + 1.2642 + 1.2643 + printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n" 1.2644 + " classname: %s \n" 1.2645 + " contractid: %s \n" 1.2646 + " unimplemented interface name: %s\n\n", 1.2647 + className ? className : "<unknown>", 1.2648 + contractID ? contractID : "<unknown>", 1.2649 + interfaceName); 1.2650 + 1.2651 + if (className) 1.2652 + nsMemory::Free(className); 1.2653 + if (contractID) 1.2654 + nsMemory::Free(contractID); 1.2655 + } 1.2656 +} 1.2657 +#endif 1.2658 + 1.2659 +NS_IMPL_ISUPPORTS(XPCJSObjectHolder, nsIXPConnectJSObjectHolder) 1.2660 + 1.2661 +JSObject* 1.2662 +XPCJSObjectHolder::GetJSObject() 1.2663 +{ 1.2664 + NS_PRECONDITION(mJSObj, "bad object state"); 1.2665 + return mJSObj; 1.2666 +} 1.2667 + 1.2668 +XPCJSObjectHolder::XPCJSObjectHolder(JSObject* obj) 1.2669 + : mJSObj(obj) 1.2670 +{ 1.2671 + XPCJSRuntime::Get()->AddObjectHolderRoot(this); 1.2672 +} 1.2673 + 1.2674 +XPCJSObjectHolder::~XPCJSObjectHolder() 1.2675 +{ 1.2676 + RemoveFromRootSet(); 1.2677 +} 1.2678 + 1.2679 +void 1.2680 +XPCJSObjectHolder::TraceJS(JSTracer *trc) 1.2681 +{ 1.2682 + trc->setTracingDetails(GetTraceName, this, 0); 1.2683 + JS_CallHeapObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj"); 1.2684 +} 1.2685 + 1.2686 +// static 1.2687 +void 1.2688 +XPCJSObjectHolder::GetTraceName(JSTracer* trc, char *buf, size_t bufsize) 1.2689 +{ 1.2690 + JS_snprintf(buf, bufsize, "XPCJSObjectHolder[0x%p].mJSObj", 1.2691 + trc->debugPrintArg()); 1.2692 +} 1.2693 + 1.2694 +// static 1.2695 +XPCJSObjectHolder* 1.2696 +XPCJSObjectHolder::newHolder(JSObject* obj) 1.2697 +{ 1.2698 + if (!obj) { 1.2699 + NS_ERROR("bad param"); 1.2700 + return nullptr; 1.2701 + } 1.2702 + return new XPCJSObjectHolder(obj); 1.2703 +}