js/xpconnect/src/XPCWrappedNative.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* Wrapper object for reflecting native xpcom objects into JavaScript. */
michael@0 8
michael@0 9 #include "xpcprivate.h"
michael@0 10 #include "nsWrapperCacheInlines.h"
michael@0 11 #include "XPCLog.h"
michael@0 12 #include "jsprf.h"
michael@0 13 #include "AccessCheck.h"
michael@0 14 #include "WrapperFactory.h"
michael@0 15 #include "XrayWrapper.h"
michael@0 16
michael@0 17 #include "nsContentUtils.h"
michael@0 18 #include "nsCxPusher.h"
michael@0 19
michael@0 20 #include <stdint.h>
michael@0 21 #include "mozilla/Likely.h"
michael@0 22 #include "mozilla/dom/BindingUtils.h"
michael@0 23 #include <algorithm>
michael@0 24
michael@0 25 using namespace xpc;
michael@0 26 using namespace mozilla;
michael@0 27 using namespace mozilla::dom;
michael@0 28 using namespace JS;
michael@0 29
michael@0 30 /***************************************************************************/
michael@0 31
michael@0 32 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
michael@0 33
michael@0 34 // No need to unlink the JS objects: if the XPCWrappedNative is cycle
michael@0 35 // collected then its mFlatJSObject will be cycle collected too and
michael@0 36 // finalization of the mFlatJSObject will unlink the JS objects (see
michael@0 37 // XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
michael@0 38 NS_IMETHODIMP_(void)
michael@0 39 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(void *p)
michael@0 40 {
michael@0 41 XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
michael@0 42 tmp->ExpireWrapper();
michael@0 43 }
michael@0 44
michael@0 45 NS_IMETHODIMP
michael@0 46 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse
michael@0 47 (void *p, nsCycleCollectionTraversalCallback &cb)
michael@0 48 {
michael@0 49 XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
michael@0 50 if (!tmp->IsValid())
michael@0 51 return NS_OK;
michael@0 52
michael@0 53 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
michael@0 54 char name[72];
michael@0 55 XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
michael@0 56 if (si)
michael@0 57 JS_snprintf(name, sizeof(name), "XPCWrappedNative (%s)",
michael@0 58 si->GetJSClass()->name);
michael@0 59 else
michael@0 60 JS_snprintf(name, sizeof(name), "XPCWrappedNative");
michael@0 61
michael@0 62 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
michael@0 63 } else {
michael@0 64 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get())
michael@0 65 }
michael@0 66
michael@0 67 if (tmp->mRefCnt.get() > 1) {
michael@0 68
michael@0 69 // If our refcount is > 1, our reference to the flat JS object is
michael@0 70 // considered "strong", and we're going to traverse it.
michael@0 71 //
michael@0 72 // If our refcount is <= 1, our reference to the flat JS object is
michael@0 73 // considered "weak", and we're *not* going to traverse it.
michael@0 74 //
michael@0 75 // This reasoning is in line with the slightly confusing lifecycle rules
michael@0 76 // for XPCWrappedNatives, described in a larger comment below and also
michael@0 77 // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
michael@0 78
michael@0 79 JSObject *obj = tmp->GetFlatJSObjectPreserveColor();
michael@0 80 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject");
michael@0 81 cb.NoteJSChild(obj);
michael@0 82 }
michael@0 83
michael@0 84 // XPCWrappedNative keeps its native object alive.
michael@0 85 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity");
michael@0 86 cb.NoteXPCOMChild(tmp->GetIdentityObject());
michael@0 87
michael@0 88 tmp->NoteTearoffs(cb);
michael@0 89
michael@0 90 return NS_OK;
michael@0 91 }
michael@0 92
michael@0 93 void
michael@0 94 XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
michael@0 95 {
michael@0 96 // Tearoffs hold their native object alive. If their JS object hasn't been
michael@0 97 // finalized yet we'll note the edge between the JS object and the native
michael@0 98 // (see nsXPConnect::Traverse), but if their JS object has been finalized
michael@0 99 // then the tearoff is only reachable through the XPCWrappedNative, so we
michael@0 100 // record an edge here.
michael@0 101 XPCWrappedNativeTearOffChunk* chunk;
michael@0 102 for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
michael@0 103 XPCWrappedNativeTearOff* to = chunk->mTearOffs;
michael@0 104 for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
michael@0 105 JSObject* jso = to->GetJSObjectPreserveColor();
michael@0 106 if (!jso) {
michael@0 107 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
michael@0 108 cb.NoteXPCOMChild(to->GetNative());
michael@0 109 }
michael@0 110 }
michael@0 111 }
michael@0 112 }
michael@0 113
michael@0 114 #ifdef XPC_CHECK_CLASSINFO_CLAIMS
michael@0 115 static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper);
michael@0 116 #else
michael@0 117 #define DEBUG_CheckClassInfoClaims(wrapper) ((void)0)
michael@0 118 #endif
michael@0 119
michael@0 120 /***************************************************************************/
michael@0 121 static nsresult
michael@0 122 FinishCreate(XPCWrappedNativeScope* Scope,
michael@0 123 XPCNativeInterface* Interface,
michael@0 124 nsWrapperCache *cache,
michael@0 125 XPCWrappedNative* inWrapper,
michael@0 126 XPCWrappedNative** resultWrapper);
michael@0 127
michael@0 128 // static
michael@0 129 //
michael@0 130 // This method handles the special case of wrapping a new global object.
michael@0 131 //
michael@0 132 // The normal code path for wrapping natives goes through
michael@0 133 // XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed,
michael@0 134 // and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
michael@0 135 // very early on that we have an XPCWrappedNativeScope and corresponding global
michael@0 136 // JS object, which are the very things we need to create here. So we special-
michael@0 137 // case the logic and do some things in a different order.
michael@0 138 nsresult
michael@0 139 XPCWrappedNative::WrapNewGlobal(xpcObjectHelper &nativeHelper,
michael@0 140 nsIPrincipal *principal,
michael@0 141 bool initStandardClasses,
michael@0 142 JS::CompartmentOptions& aOptions,
michael@0 143 XPCWrappedNative **wrappedGlobal)
michael@0 144 {
michael@0 145 AutoJSContext cx;
michael@0 146 nsISupports *identity = nativeHelper.GetCanonical();
michael@0 147
michael@0 148 // The object should specify that it's meant to be global.
michael@0 149 MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
michael@0 150
michael@0 151 // We shouldn't be reusing globals.
michael@0 152 MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
michael@0 153 !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
michael@0 154
michael@0 155 // Put together the ScriptableCreateInfo...
michael@0 156 XPCNativeScriptableCreateInfo sciProto;
michael@0 157 XPCNativeScriptableCreateInfo sciMaybe;
michael@0 158 const XPCNativeScriptableCreateInfo& sciWrapper =
michael@0 159 GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(),
michael@0 160 sciProto, sciMaybe);
michael@0 161
michael@0 162 // ...and then ScriptableInfo. We need all this stuff now because it's going
michael@0 163 // to tell us the JSClass of the object we're going to create.
michael@0 164 AutoMarkingNativeScriptableInfoPtr si(cx, XPCNativeScriptableInfo::Construct(&sciWrapper));
michael@0 165 MOZ_ASSERT(si.get());
michael@0 166
michael@0 167 // Finally, we get to the JSClass.
michael@0 168 const JSClass *clasp = si->GetJSClass();
michael@0 169 MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
michael@0 170
michael@0 171 // Create the global.
michael@0 172 aOptions.setTrace(XPCWrappedNative::Trace);
michael@0 173 RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
michael@0 174 if (!global)
michael@0 175 return NS_ERROR_FAILURE;
michael@0 176 XPCWrappedNativeScope *scope = GetCompartmentPrivate(global)->scope;
michael@0 177
michael@0 178 // Immediately enter the global's compartment, so that everything else we
michael@0 179 // create ends up there.
michael@0 180 JSAutoCompartment ac(cx, global);
michael@0 181
michael@0 182 // If requested, initialize the standard classes on the global.
michael@0 183 if (initStandardClasses && ! JS_InitStandardClasses(cx, global))
michael@0 184 return NS_ERROR_FAILURE;
michael@0 185
michael@0 186 // Make a proto.
michael@0 187 XPCWrappedNativeProto *proto =
michael@0 188 XPCWrappedNativeProto::GetNewOrUsed(scope,
michael@0 189 nativeHelper.GetClassInfo(), &sciProto,
michael@0 190 /* callPostCreatePrototype = */ false);
michael@0 191 if (!proto)
michael@0 192 return NS_ERROR_FAILURE;
michael@0 193
michael@0 194 // Set up the prototype on the global.
michael@0 195 MOZ_ASSERT(proto->GetJSProtoObject());
michael@0 196 RootedObject protoObj(cx, proto->GetJSProtoObject());
michael@0 197 bool success = JS_SplicePrototype(cx, global, protoObj);
michael@0 198 if (!success)
michael@0 199 return NS_ERROR_FAILURE;
michael@0 200
michael@0 201 // Construct the wrapper, which takes over the strong reference to the
michael@0 202 // native object.
michael@0 203 nsRefPtr<XPCWrappedNative> wrapper =
michael@0 204 new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
michael@0 205
michael@0 206 //
michael@0 207 // We don't call ::Init() on this wrapper, because our setup requirements
michael@0 208 // are different for globals. We do our setup inline here, instead.
michael@0 209 //
michael@0 210
michael@0 211 // Share mScriptableInfo with the proto.
michael@0 212 //
michael@0 213 // This is probably more trouble than it's worth, since we've already created
michael@0 214 // an XPCNativeScriptableInfo for ourselves. Moreover, most of that class is
michael@0 215 // shared internally via XPCNativeScriptableInfoShared, so the memory
michael@0 216 // savings are negligible. Nevertheless, this is what ::Init() does, and we
michael@0 217 // want to be as consistent as possible with that code.
michael@0 218 XPCNativeScriptableInfo* siProto = proto->GetScriptableInfo();
michael@0 219 if (siProto && siProto->GetCallback() == sciWrapper.GetCallback()) {
michael@0 220 wrapper->mScriptableInfo = siProto;
michael@0 221 // XPCNativeScriptableShared instances live in a map, and are
michael@0 222 // GCed, but XPCNativeScriptableInfo is per-instance and must be
michael@0 223 // manually managed. If we're switching over to that of the proto, we
michael@0 224 // need to destroy the one we've allocated, and also null out the
michael@0 225 // AutoMarkingPtr, so that it doesn't try to mark garbage data.
michael@0 226 delete si;
michael@0 227 si = nullptr;
michael@0 228 } else {
michael@0 229 wrapper->mScriptableInfo = si;
michael@0 230 }
michael@0 231
michael@0 232 // Set the JS object to the global we already created.
michael@0 233 wrapper->mFlatJSObject = global;
michael@0 234 wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
michael@0 235
michael@0 236 // Set the private to the XPCWrappedNative.
michael@0 237 JS_SetPrivate(global, wrapper);
michael@0 238
michael@0 239 // There are dire comments elsewhere in the code about how a GC can
michael@0 240 // happen somewhere after wrapper initialization but before the wrapper is
michael@0 241 // added to the hashtable in FinishCreate(). It's not clear if that can
michael@0 242 // happen here, but let's just be safe for now.
michael@0 243 AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
michael@0 244
michael@0 245 // Call the common Init finish routine. This mainly just does an AddRef
michael@0 246 // on behalf of XPConnect (the corresponding Release is in the finalizer
michael@0 247 // hook), but it does some other miscellaneous things too, so we don't
michael@0 248 // inline it.
michael@0 249 success = wrapper->FinishInit();
michael@0 250 MOZ_ASSERT(success);
michael@0 251
michael@0 252 // Go through some extra work to find the tearoff. This is kind of silly
michael@0 253 // on a conceptual level: the point of tearoffs is to cache the results
michael@0 254 // of QI-ing mIdentity to different interfaces, and we don't need that
michael@0 255 // since we're dealing with nsISupports. But lots of code expects tearoffs
michael@0 256 // to exist for everything, so we just follow along.
michael@0 257 XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(&NS_GET_IID(nsISupports));
michael@0 258 MOZ_ASSERT(iface);
michael@0 259 nsresult status;
michael@0 260 success = wrapper->FindTearOff(iface, false, &status);
michael@0 261 if (!success)
michael@0 262 return status;
michael@0 263
michael@0 264 // Call the common creation finish routine. This does all of the bookkeeping
michael@0 265 // like inserting the wrapper into the wrapper map and setting up the wrapper
michael@0 266 // cache.
michael@0 267 nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(),
michael@0 268 wrapper, wrappedGlobal);
michael@0 269 NS_ENSURE_SUCCESS(rv, rv);
michael@0 270
michael@0 271 return NS_OK;
michael@0 272 }
michael@0 273
michael@0 274 // static
michael@0 275 nsresult
michael@0 276 XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
michael@0 277 XPCWrappedNativeScope* Scope,
michael@0 278 XPCNativeInterface* Interface,
michael@0 279 XPCWrappedNative** resultWrapper)
michael@0 280 {
michael@0 281 MOZ_ASSERT(Interface);
michael@0 282 AutoJSContext cx;
michael@0 283 nsWrapperCache *cache = helper.GetWrapperCache();
michael@0 284
michael@0 285 MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
michael@0 286 "We assume the caller already checked if it could get the "
michael@0 287 "wrapper from the cache.");
michael@0 288
michael@0 289 nsresult rv;
michael@0 290
michael@0 291 MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
michael@0 292 "XPCWrappedNative::GetNewOrUsed called during GC");
michael@0 293
michael@0 294 nsISupports *identity = helper.GetCanonical();
michael@0 295
michael@0 296 if (!identity) {
michael@0 297 NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
michael@0 298 return NS_ERROR_FAILURE;
michael@0 299 }
michael@0 300
michael@0 301 nsRefPtr<XPCWrappedNative> wrapper;
michael@0 302
michael@0 303 Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
michael@0 304 // Some things are nsWrapperCache subclasses but never use the cache, so go
michael@0 305 // ahead and check our map even if we have a cache and it has no existing
michael@0 306 // wrapper: we might have an XPCWrappedNative anyway.
michael@0 307 wrapper = map->Find(identity);
michael@0 308
michael@0 309 if (wrapper) {
michael@0 310 if (!wrapper->FindTearOff(Interface, false, &rv)) {
michael@0 311 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
michael@0 312 return rv;
michael@0 313 }
michael@0 314 wrapper.forget(resultWrapper);
michael@0 315 return NS_OK;
michael@0 316 }
michael@0 317
michael@0 318 // There is a chance that the object wants to have the self-same JSObject
michael@0 319 // reflection regardless of the scope into which we are reflecting it.
michael@0 320 // Many DOM objects require this. The scriptable helper specifies this
michael@0 321 // in preCreate by indicating a 'parent' of a particular scope.
michael@0 322 //
michael@0 323 // To handle this we need to get the scriptable helper early and ask it.
michael@0 324 // It is possible that we will then end up forwarding this entire call
michael@0 325 // to this same function but with a different scope.
michael@0 326
michael@0 327 // If we are making a wrapper for the nsIClassInfo interface then
michael@0 328 // We *don't* want to have it use the prototype meant for instances
michael@0 329 // of that class.
michael@0 330 bool iidIsClassInfo = Interface->GetIID()->Equals(NS_GET_IID(nsIClassInfo));
michael@0 331 uint32_t classInfoFlags;
michael@0 332 bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() &&
michael@0 333 NS_SUCCEEDED(helper.GetClassInfo()
michael@0 334 ->GetFlags(&classInfoFlags)) &&
michael@0 335 (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO);
michael@0 336 bool isClassInfo = iidIsClassInfo || isClassInfoSingleton;
michael@0 337
michael@0 338 nsIClassInfo *info = helper.GetClassInfo();
michael@0 339
michael@0 340 XPCNativeScriptableCreateInfo sciProto;
michael@0 341 XPCNativeScriptableCreateInfo sci;
michael@0 342
michael@0 343 // Gather scriptable create info if we are wrapping something
michael@0 344 // other than an nsIClassInfo object. We need to not do this for
michael@0 345 // nsIClassInfo objects because often nsIClassInfo implementations
michael@0 346 // are also nsIXPCScriptable helper implementations, but the helper
michael@0 347 // code is obviously intended for the implementation of the class
michael@0 348 // described by the nsIClassInfo, not for the class info object
michael@0 349 // itself.
michael@0 350 const XPCNativeScriptableCreateInfo& sciWrapper =
michael@0 351 isClassInfo ? sci :
michael@0 352 GatherScriptableCreateInfo(identity, info, sciProto, sci);
michael@0 353
michael@0 354 RootedObject parent(cx, Scope->GetGlobalJSObject());
michael@0 355
michael@0 356 RootedValue newParentVal(cx, NullValue());
michael@0 357
michael@0 358 mozilla::Maybe<JSAutoCompartment> ac;
michael@0 359
michael@0 360 if (sciWrapper.GetFlags().WantPreCreate()) {
michael@0 361 // PreCreate may touch dead compartments.
michael@0 362 js::AutoMaybeTouchDeadZones agc(parent);
michael@0 363
michael@0 364 RootedObject plannedParent(cx, parent);
michael@0 365 nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx,
michael@0 366 parent, parent.address());
michael@0 367 if (NS_FAILED(rv))
michael@0 368 return rv;
michael@0 369 rv = NS_OK;
michael@0 370
michael@0 371 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
michael@0 372 "Xray wrapper being used to parent XPCWrappedNative?");
michael@0 373
michael@0 374 ac.construct(static_cast<JSContext*>(cx), parent);
michael@0 375
michael@0 376 if (parent != plannedParent) {
michael@0 377 XPCWrappedNativeScope* betterScope = GetObjectScope(parent);
michael@0 378 if (betterScope != Scope)
michael@0 379 return GetNewOrUsed(helper, betterScope, Interface, resultWrapper);
michael@0 380
michael@0 381 newParentVal = OBJECT_TO_JSVAL(parent);
michael@0 382 }
michael@0 383
michael@0 384 // Take the performance hit of checking the hashtable again in case
michael@0 385 // the preCreate call caused the wrapper to get created through some
michael@0 386 // interesting path (the DOM code tends to make this happen sometimes).
michael@0 387
michael@0 388 if (cache) {
michael@0 389 RootedObject cached(cx, cache->GetWrapper());
michael@0 390 if (cached)
michael@0 391 wrapper = XPCWrappedNative::Get(cached);
michael@0 392 } else {
michael@0 393 wrapper = map->Find(identity);
michael@0 394 }
michael@0 395
michael@0 396 if (wrapper) {
michael@0 397 if (wrapper->FindTearOff(Interface, false, &rv)) {
michael@0 398 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
michael@0 399 return rv;
michael@0 400 }
michael@0 401 wrapper.forget(resultWrapper);
michael@0 402 return NS_OK;
michael@0 403 }
michael@0 404 } else {
michael@0 405 ac.construct(static_cast<JSContext*>(cx), parent);
michael@0 406 }
michael@0 407
michael@0 408 AutoMarkingWrappedNativeProtoPtr proto(cx);
michael@0 409
michael@0 410 // If there is ClassInfo (and we are not building a wrapper for the
michael@0 411 // nsIClassInfo interface) then we use a wrapper that needs a prototype.
michael@0 412
michael@0 413 // Note that the security check happens inside FindTearOff - after the
michael@0 414 // wrapper is actually created, but before JS code can see it.
michael@0 415
michael@0 416 if (info && !isClassInfo) {
michael@0 417 proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, &sciProto);
michael@0 418 if (!proto)
michael@0 419 return NS_ERROR_FAILURE;
michael@0 420
michael@0 421 wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto);
michael@0 422 } else {
michael@0 423 AutoMarkingNativeInterfacePtr iface(cx, Interface);
michael@0 424 if (!iface)
michael@0 425 iface = XPCNativeInterface::GetISupports();
michael@0 426
michael@0 427 AutoMarkingNativeSetPtr set(cx);
michael@0 428 set = XPCNativeSet::GetNewOrUsed(nullptr, iface, 0);
michael@0 429
michael@0 430 if (!set)
michael@0 431 return NS_ERROR_FAILURE;
michael@0 432
michael@0 433 wrapper =
michael@0 434 new XPCWrappedNative(helper.forgetCanonical(), Scope, set);
michael@0 435 }
michael@0 436
michael@0 437 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
michael@0 438 "Xray wrapper being used to parent XPCWrappedNative?");
michael@0 439
michael@0 440 // We use an AutoMarkingPtr here because it is possible for JS gc to happen
michael@0 441 // after we have Init'd the wrapper but *before* we add it to the hashtable.
michael@0 442 // This would cause the mSet to get collected and we'd later crash. I've
michael@0 443 // *seen* this happen.
michael@0 444 AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
michael@0 445
michael@0 446 if (!wrapper->Init(parent, &sciWrapper))
michael@0 447 return NS_ERROR_FAILURE;
michael@0 448
michael@0 449 if (!wrapper->FindTearOff(Interface, false, &rv)) {
michael@0 450 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
michael@0 451 return rv;
michael@0 452 }
michael@0 453
michael@0 454 return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper);
michael@0 455 }
michael@0 456
michael@0 457 static nsresult
michael@0 458 FinishCreate(XPCWrappedNativeScope* Scope,
michael@0 459 XPCNativeInterface* Interface,
michael@0 460 nsWrapperCache *cache,
michael@0 461 XPCWrappedNative* inWrapper,
michael@0 462 XPCWrappedNative** resultWrapper)
michael@0 463 {
michael@0 464 AutoJSContext cx;
michael@0 465 MOZ_ASSERT(inWrapper);
michael@0 466
michael@0 467 Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
michael@0 468
michael@0 469 nsRefPtr<XPCWrappedNative> wrapper;
michael@0 470 // Deal with the case where the wrapper got created as a side effect
michael@0 471 // of one of our calls out of this code. Add() returns the (possibly
michael@0 472 // pre-existing) wrapper that ultimately ends up in the map, which is
michael@0 473 // what we want.
michael@0 474 wrapper = map->Add(inWrapper);
michael@0 475 if (!wrapper)
michael@0 476 return NS_ERROR_FAILURE;
michael@0 477
michael@0 478 if (wrapper == inWrapper) {
michael@0 479 JSObject *flat = wrapper->GetFlatJSObject();
michael@0 480 MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() ||
michael@0 481 flat == cache->GetWrapperPreserveColor(),
michael@0 482 "This object has a cached wrapper that's different from "
michael@0 483 "the JSObject held by its native wrapper?");
michael@0 484
michael@0 485 if (cache && !cache->GetWrapperPreserveColor())
michael@0 486 cache->SetWrapper(flat);
michael@0 487
michael@0 488 // Our newly created wrapper is the one that we just added to the table.
michael@0 489 // All is well. Call PostCreate as necessary.
michael@0 490 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
michael@0 491 if (si && si->GetFlags().WantPostCreate()) {
michael@0 492 nsresult rv = si->GetCallback()->PostCreate(wrapper, cx, flat);
michael@0 493 if (NS_FAILED(rv)) {
michael@0 494 // PostCreate failed and that's Very Bad. We'll remove it from
michael@0 495 // the map and mark it as invalid, but the PostCreate function
michael@0 496 // may have handed the partially-constructed-and-now-invalid
michael@0 497 // wrapper to someone before failing. Or, perhaps worse, the
michael@0 498 // PostCreate call could have triggered code that reentered
michael@0 499 // XPConnect and tried to wrap the same object. In that case
michael@0 500 // *we* hand out the invalid wrapper since it is already in our
michael@0 501 // map :(
michael@0 502 NS_ERROR("PostCreate failed! This is known to cause "
michael@0 503 "inconsistent state for some class types and may even "
michael@0 504 "cause a crash in combination with a JS GC. Fix the "
michael@0 505 "failing PostCreate ASAP!");
michael@0 506
michael@0 507 map->Remove(wrapper);
michael@0 508
michael@0 509 // This would be a good place to tell the wrapper not to remove
michael@0 510 // itself from the map when it dies... See bug 429442.
michael@0 511
michael@0 512 if (cache)
michael@0 513 cache->ClearWrapper();
michael@0 514 wrapper->Release();
michael@0 515 return rv;
michael@0 516 }
michael@0 517 }
michael@0 518 }
michael@0 519
michael@0 520 DEBUG_CheckClassInfoClaims(wrapper);
michael@0 521 wrapper.forget(resultWrapper);
michael@0 522 return NS_OK;
michael@0 523 }
michael@0 524
michael@0 525 // static
michael@0 526 nsresult
michael@0 527 XPCWrappedNative::GetUsedOnly(nsISupports* Object,
michael@0 528 XPCWrappedNativeScope* Scope,
michael@0 529 XPCNativeInterface* Interface,
michael@0 530 XPCWrappedNative** resultWrapper)
michael@0 531 {
michael@0 532 AutoJSContext cx;
michael@0 533 MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object");
michael@0 534 MOZ_ASSERT(Interface);
michael@0 535
michael@0 536 nsRefPtr<XPCWrappedNative> wrapper;
michael@0 537 nsWrapperCache* cache = nullptr;
michael@0 538 CallQueryInterface(Object, &cache);
michael@0 539 if (cache) {
michael@0 540 RootedObject flat(cx, cache->GetWrapper());
michael@0 541 if (!flat) {
michael@0 542 *resultWrapper = nullptr;
michael@0 543 return NS_OK;
michael@0 544 }
michael@0 545 wrapper = XPCWrappedNative::Get(flat);
michael@0 546 } else {
michael@0 547 nsCOMPtr<nsISupports> identity = do_QueryInterface(Object);
michael@0 548
michael@0 549 if (!identity) {
michael@0 550 NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
michael@0 551 return NS_ERROR_FAILURE;
michael@0 552 }
michael@0 553
michael@0 554 Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
michael@0 555
michael@0 556 wrapper = map->Find(identity);
michael@0 557 if (!wrapper) {
michael@0 558 *resultWrapper = nullptr;
michael@0 559 return NS_OK;
michael@0 560 }
michael@0 561 }
michael@0 562
michael@0 563 nsresult rv;
michael@0 564 if (!wrapper->FindTearOff(Interface, false, &rv)) {
michael@0 565 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
michael@0 566 return rv;
michael@0 567 }
michael@0 568
michael@0 569 wrapper.forget(resultWrapper);
michael@0 570 return NS_OK;
michael@0 571 }
michael@0 572
michael@0 573 // This ctor is used if this object will have a proto.
michael@0 574 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
michael@0 575 XPCWrappedNativeProto* aProto)
michael@0 576 : mMaybeProto(aProto),
michael@0 577 mSet(aProto->GetSet()),
michael@0 578 mScriptableInfo(nullptr)
michael@0 579 {
michael@0 580 MOZ_ASSERT(NS_IsMainThread());
michael@0 581
michael@0 582 mIdentity = aIdentity.take();
michael@0 583 mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
michael@0 584
michael@0 585 MOZ_ASSERT(mMaybeProto, "bad ctor param");
michael@0 586 MOZ_ASSERT(mSet, "bad ctor param");
michael@0 587 }
michael@0 588
michael@0 589 // This ctor is used if this object will NOT have a proto.
michael@0 590 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
michael@0 591 XPCWrappedNativeScope* aScope,
michael@0 592 XPCNativeSet* aSet)
michael@0 593
michael@0 594 : mMaybeScope(TagScope(aScope)),
michael@0 595 mSet(aSet),
michael@0 596 mScriptableInfo(nullptr)
michael@0 597 {
michael@0 598 MOZ_ASSERT(NS_IsMainThread());
michael@0 599
michael@0 600 mIdentity = aIdentity.take();
michael@0 601 mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
michael@0 602
michael@0 603 MOZ_ASSERT(aScope, "bad ctor param");
michael@0 604 MOZ_ASSERT(aSet, "bad ctor param");
michael@0 605 }
michael@0 606
michael@0 607 XPCWrappedNative::~XPCWrappedNative()
michael@0 608 {
michael@0 609 Destroy();
michael@0 610 }
michael@0 611
michael@0 612 void
michael@0 613 XPCWrappedNative::Destroy()
michael@0 614 {
michael@0 615 XPCWrappedNativeProto* proto = GetProto();
michael@0 616
michael@0 617 if (mScriptableInfo &&
michael@0 618 (!HasProto() ||
michael@0 619 (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
michael@0 620 delete mScriptableInfo;
michael@0 621 mScriptableInfo = nullptr;
michael@0 622 }
michael@0 623
michael@0 624 XPCWrappedNativeScope *scope = GetScope();
michael@0 625 if (scope) {
michael@0 626 Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
michael@0 627
michael@0 628 // Post-1.9 we should not remove this wrapper from the map if it is
michael@0 629 // uninitialized.
michael@0 630 map->Remove(this);
michael@0 631 }
michael@0 632
michael@0 633 if (mIdentity) {
michael@0 634 XPCJSRuntime* rt = GetRuntime();
michael@0 635 if (rt && rt->GetDoingFinalization()) {
michael@0 636 nsContentUtils::DeferredFinalize(mIdentity);
michael@0 637 mIdentity = nullptr;
michael@0 638 } else {
michael@0 639 NS_RELEASE(mIdentity);
michael@0 640 }
michael@0 641 }
michael@0 642
michael@0 643 mMaybeScope = nullptr;
michael@0 644 }
michael@0 645
michael@0 646 void
michael@0 647 XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si)
michael@0 648 {
michael@0 649 MOZ_ASSERT(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
michael@0 650
michael@0 651 // Write barrier for incremental GC.
michael@0 652 JSRuntime* rt = GetRuntime()->Runtime();
michael@0 653 if (IsIncrementalBarrierNeeded(rt))
michael@0 654 mScriptableInfo->Mark();
michael@0 655
michael@0 656 mScriptableInfo = si;
michael@0 657 }
michael@0 658
michael@0 659 void
michael@0 660 XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
michael@0 661 {
michael@0 662 MOZ_ASSERT(!IsWrapperExpired(), "bad ptr!");
michael@0 663
michael@0 664 MOZ_ASSERT(HasProto());
michael@0 665
michael@0 666 // Write barrier for incremental GC.
michael@0 667 JSRuntime* rt = GetRuntime()->Runtime();
michael@0 668 GetProto()->WriteBarrierPre(rt);
michael@0 669
michael@0 670 mMaybeProto = p;
michael@0 671 }
michael@0 672
michael@0 673 // This is factored out so that it can be called publicly
michael@0 674 // static
michael@0 675 void
michael@0 676 XPCWrappedNative::GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
michael@0 677 XPCNativeScriptableCreateInfo& sciProto)
michael@0 678 {
michael@0 679 MOZ_ASSERT(classInfo, "bad param");
michael@0 680 MOZ_ASSERT(!sciProto.GetCallback(), "bad param");
michael@0 681
michael@0 682 nsXPCClassInfo *classInfoHelper = nullptr;
michael@0 683 CallQueryInterface(classInfo, &classInfoHelper);
michael@0 684 if (classInfoHelper) {
michael@0 685 nsCOMPtr<nsIXPCScriptable> helper =
michael@0 686 dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper));
michael@0 687 uint32_t flags = classInfoHelper->GetScriptableFlags();
michael@0 688 sciProto.SetCallback(helper.forget());
michael@0 689 sciProto.SetFlags(flags);
michael@0 690 sciProto.SetInterfacesBitmap(classInfoHelper->GetInterfacesBitmap());
michael@0 691
michael@0 692 return;
michael@0 693 }
michael@0 694
michael@0 695 nsCOMPtr<nsISupports> possibleHelper;
michael@0 696 nsresult rv = classInfo->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT,
michael@0 697 getter_AddRefs(possibleHelper));
michael@0 698 if (NS_SUCCEEDED(rv) && possibleHelper) {
michael@0 699 nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(possibleHelper));
michael@0 700 if (helper) {
michael@0 701 uint32_t flags = helper->GetScriptableFlags();
michael@0 702 sciProto.SetCallback(helper.forget());
michael@0 703 sciProto.SetFlags(flags);
michael@0 704 }
michael@0 705 }
michael@0 706 }
michael@0 707
michael@0 708 // static
michael@0 709 const XPCNativeScriptableCreateInfo&
michael@0 710 XPCWrappedNative::GatherScriptableCreateInfo(nsISupports* obj,
michael@0 711 nsIClassInfo* classInfo,
michael@0 712 XPCNativeScriptableCreateInfo& sciProto,
michael@0 713 XPCNativeScriptableCreateInfo& sciWrapper)
michael@0 714 {
michael@0 715 MOZ_ASSERT(!sciWrapper.GetCallback(), "bad param");
michael@0 716
michael@0 717 // Get the class scriptable helper (if present)
michael@0 718 if (classInfo) {
michael@0 719 GatherProtoScriptableCreateInfo(classInfo, sciProto);
michael@0 720
michael@0 721 if (sciProto.GetFlags().DontAskInstanceForScriptable())
michael@0 722 return sciProto;
michael@0 723 }
michael@0 724
michael@0 725 // Do the same for the wrapper specific scriptable
michael@0 726 nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(obj));
michael@0 727 if (helper) {
michael@0 728 uint32_t flags = helper->GetScriptableFlags();
michael@0 729 sciWrapper.SetCallback(helper.forget());
michael@0 730 sciWrapper.SetFlags(flags);
michael@0 731
michael@0 732 // A whole series of assertions to catch bad uses of scriptable flags on
michael@0 733 // the siWrapper...
michael@0 734
michael@0 735 MOZ_ASSERT(!(sciWrapper.GetFlags().WantPreCreate() &&
michael@0 736 !sciProto.GetFlags().WantPreCreate()),
michael@0 737 "Can't set WANT_PRECREATE on an instance scriptable "
michael@0 738 "without also setting it on the class scriptable");
michael@0 739
michael@0 740 MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumStaticProps() &&
michael@0 741 !sciProto.GetFlags().DontEnumStaticProps() &&
michael@0 742 sciProto.GetCallback()),
michael@0 743 "Can't set DONT_ENUM_STATIC_PROPS on an instance scriptable "
michael@0 744 "without also setting it on the class scriptable (if present and shared)");
michael@0 745
michael@0 746 MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumQueryInterface() &&
michael@0 747 !sciProto.GetFlags().DontEnumQueryInterface() &&
michael@0 748 sciProto.GetCallback()),
michael@0 749 "Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable "
michael@0 750 "without also setting it on the class scriptable (if present and shared)");
michael@0 751
michael@0 752 MOZ_ASSERT(!(sciWrapper.GetFlags().DontAskInstanceForScriptable() &&
michael@0 753 !sciProto.GetFlags().DontAskInstanceForScriptable()),
michael@0 754 "Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable "
michael@0 755 "without also setting it on the class scriptable");
michael@0 756
michael@0 757 MOZ_ASSERT(!(sciWrapper.GetFlags().ClassInfoInterfacesOnly() &&
michael@0 758 !sciProto.GetFlags().ClassInfoInterfacesOnly() &&
michael@0 759 sciProto.GetCallback()),
michael@0 760 "Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable "
michael@0 761 "without also setting it on the class scriptable (if present and shared)");
michael@0 762
michael@0 763 MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsDuringResolve() &&
michael@0 764 !sciProto.GetFlags().AllowPropModsDuringResolve() &&
michael@0 765 sciProto.GetCallback()),
michael@0 766 "Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable "
michael@0 767 "without also setting it on the class scriptable (if present and shared)");
michael@0 768
michael@0 769 MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsToPrototype() &&
michael@0 770 !sciProto.GetFlags().AllowPropModsToPrototype() &&
michael@0 771 sciProto.GetCallback()),
michael@0 772 "Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable "
michael@0 773 "without also setting it on the class scriptable (if present and shared)");
michael@0 774
michael@0 775 return sciWrapper;
michael@0 776 }
michael@0 777
michael@0 778 return sciProto;
michael@0 779 }
michael@0 780
michael@0 781 bool
michael@0 782 XPCWrappedNative::Init(HandleObject parent,
michael@0 783 const XPCNativeScriptableCreateInfo* sci)
michael@0 784 {
michael@0 785 AutoJSContext cx;
michael@0 786 // setup our scriptable info...
michael@0 787
michael@0 788 if (sci->GetCallback()) {
michael@0 789 if (HasProto()) {
michael@0 790 XPCNativeScriptableInfo* siProto = GetProto()->GetScriptableInfo();
michael@0 791 if (siProto && siProto->GetCallback() == sci->GetCallback())
michael@0 792 mScriptableInfo = siProto;
michael@0 793 }
michael@0 794 if (!mScriptableInfo) {
michael@0 795 mScriptableInfo =
michael@0 796 XPCNativeScriptableInfo::Construct(sci);
michael@0 797
michael@0 798 if (!mScriptableInfo)
michael@0 799 return false;
michael@0 800 }
michael@0 801 }
michael@0 802 XPCNativeScriptableInfo* si = mScriptableInfo;
michael@0 803
michael@0 804 // create our flatJSObject
michael@0 805
michael@0 806 const JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass.base);
michael@0 807
michael@0 808 // We should have the global jsclass flag if and only if we're a global.
michael@0 809 MOZ_ASSERT_IF(si, !!si->GetFlags().IsGlobalObject() == !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
michael@0 810
michael@0 811 MOZ_ASSERT(jsclazz &&
michael@0 812 jsclazz->name &&
michael@0 813 jsclazz->flags &&
michael@0 814 jsclazz->addProperty &&
michael@0 815 jsclazz->delProperty &&
michael@0 816 jsclazz->getProperty &&
michael@0 817 jsclazz->setProperty &&
michael@0 818 jsclazz->enumerate &&
michael@0 819 jsclazz->resolve &&
michael@0 820 jsclazz->convert &&
michael@0 821 jsclazz->finalize, "bad class");
michael@0 822
michael@0 823 RootedObject protoJSObject(cx, HasProto() ?
michael@0 824 GetProto()->GetJSProtoObject() :
michael@0 825 JS_GetObjectPrototype(cx, parent));
michael@0 826 if (!protoJSObject) {
michael@0 827 return false;
michael@0 828 }
michael@0 829
michael@0 830 mFlatJSObject = JS_NewObject(cx, jsclazz, protoJSObject, parent);
michael@0 831 if (!mFlatJSObject) {
michael@0 832 mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
michael@0 833 return false;
michael@0 834 }
michael@0 835
michael@0 836 mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
michael@0 837 JS_SetPrivate(mFlatJSObject, this);
michael@0 838
michael@0 839 return FinishInit();
michael@0 840 }
michael@0 841
michael@0 842 bool
michael@0 843 XPCWrappedNative::FinishInit()
michael@0 844 {
michael@0 845 AutoJSContext cx;
michael@0 846
michael@0 847 // This reference will be released when mFlatJSObject is finalized.
michael@0 848 // Since this reference will push the refcount to 2 it will also root
michael@0 849 // mFlatJSObject;
michael@0 850 MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
michael@0 851 NS_ADDREF(this);
michael@0 852
michael@0 853 if (mScriptableInfo && mScriptableInfo->GetFlags().WantCreate() &&
michael@0 854 NS_FAILED(mScriptableInfo->GetCallback()->Create(this, cx,
michael@0 855 mFlatJSObject))) {
michael@0 856 return false;
michael@0 857 }
michael@0 858
michael@0 859 // A hack for bug 517665, increase the probability for GC.
michael@0 860 JS_updateMallocCounter(cx, 2 * sizeof(XPCWrappedNative));
michael@0 861
michael@0 862 return true;
michael@0 863 }
michael@0 864
michael@0 865
michael@0 866 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative)
michael@0 867 NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative)
michael@0 868 NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder)
michael@0 869 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative)
michael@0 870 NS_INTERFACE_MAP_END
michael@0 871
michael@0 872 NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCWrappedNative)
michael@0 873
michael@0 874 // Release calls Destroy() immediately when the refcount drops to 0 to
michael@0 875 // clear the weak references nsXPConnect has to XPCWNs and to ensure there
michael@0 876 // are no pointers to dying protos.
michael@0 877 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(XPCWrappedNative, Destroy())
michael@0 878
michael@0 879 /*
michael@0 880 * Wrapped Native lifetime management is messy!
michael@0 881 *
michael@0 882 * - At creation we push the refcount to 2 (only one of which is owned by
michael@0 883 * the native caller that caused the wrapper creation).
michael@0 884 * - During the JS GC Mark phase we mark any wrapper with a refcount > 1.
michael@0 885 * - The *only* thing that can make the wrapper get destroyed is the
michael@0 886 * finalization of mFlatJSObject. And *that* should only happen if the only
michael@0 887 * reference is the single extra (internal) reference we hold.
michael@0 888 *
michael@0 889 * - The wrapper has a pointer to the nsISupports 'view' of the wrapped native
michael@0 890 * object i.e... mIdentity. This is held until the wrapper's refcount goes
michael@0 891 * to zero and the wrapper is released, or until an expired wrapper (i.e.,
michael@0 892 * one unlinked by the cycle collector) has had its JS object finalized.
michael@0 893 *
michael@0 894 * - The wrapper also has 'tearoffs'. It has one tearoff for each interface
michael@0 895 * that is actually used on the native object. 'Used' means we have either
michael@0 896 * needed to QueryInterface to verify the availability of that interface
michael@0 897 * of that we've had to QueryInterface in order to actually make a call
michael@0 898 * into the wrapped object via the pointer for the given interface.
michael@0 899 *
michael@0 900 * - Each tearoff's 'mNative' member (if non-null) indicates one reference
michael@0 901 * held by our wrapper on the wrapped native for the given interface
michael@0 902 * associated with the tearoff. If we release that reference then we set
michael@0 903 * the tearoff's 'mNative' to null.
michael@0 904 *
michael@0 905 * - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END
michael@0 906 * event to scan the tearoffs of all wrappers for non-null mNative members
michael@0 907 * that represent unused references. We can tell that a given tearoff's
michael@0 908 * mNative is unused by noting that no live XPCCallContexts hold a pointer
michael@0 909 * to the tearoff.
michael@0 910 *
michael@0 911 * - As a time/space tradeoff we may decide to not do this scanning on
michael@0 912 * *every* JavaScript GC. We *do* want to do this *sometimes* because
michael@0 913 * we want to allow for wrapped native's to do their own tearoff patterns.
michael@0 914 * So, we want to avoid holding references to interfaces that we don't need.
michael@0 915 * At the same time, we don't want to be bracketing every call into a
michael@0 916 * wrapped native object with a QueryInterface/Release pair. And we *never*
michael@0 917 * make a call into the object except via the correct interface for which
michael@0 918 * we've QI'd.
michael@0 919 *
michael@0 920 * - Each tearoff *can* have a mJSObject whose lazily resolved properties
michael@0 921 * represent the methods/attributes/constants of that specific interface.
michael@0 922 * This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo"
michael@0 923 * is the name of mFlatJSObject and "nsIFoo" is the name of the given
michael@0 924 * interface associated with the tearoff. When we create the tearoff's
michael@0 925 * mJSObject we set it's parent to be mFlatJSObject. This way we know that
michael@0 926 * when mFlatJSObject get's collected there are no outstanding reachable
michael@0 927 * tearoff mJSObjects. Note that we must clear the private of any lingering
michael@0 928 * mJSObjects at this point because we have no guarentee of the *order* of
michael@0 929 * finalization within a given gc cycle.
michael@0 930 */
michael@0 931
michael@0 932 void
michael@0 933 XPCWrappedNative::FlatJSObjectFinalized()
michael@0 934 {
michael@0 935 if (!IsValid())
michael@0 936 return;
michael@0 937
michael@0 938 // Iterate the tearoffs and null out each of their JSObject's privates.
michael@0 939 // This will keep them from trying to access their pointers to the
michael@0 940 // dying tearoff object. We can safely assume that those remaining
michael@0 941 // JSObjects are about to be finalized too.
michael@0 942
michael@0 943 XPCWrappedNativeTearOffChunk* chunk;
michael@0 944 for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
michael@0 945 XPCWrappedNativeTearOff* to = chunk->mTearOffs;
michael@0 946 for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
michael@0 947 JSObject* jso = to->GetJSObjectPreserveColor();
michael@0 948 if (jso) {
michael@0 949 MOZ_ASSERT(JS_IsAboutToBeFinalizedUnbarriered(&jso));
michael@0 950 JS_SetPrivate(jso, nullptr);
michael@0 951 to->JSObjectFinalized();
michael@0 952 }
michael@0 953
michael@0 954 // We also need to release any native pointers held...
michael@0 955 nsISupports* obj = to->GetNative();
michael@0 956 if (obj) {
michael@0 957 #ifdef XP_WIN
michael@0 958 // Try to detect free'd pointer
michael@0 959 MOZ_ASSERT(*(int*)obj != 0xdddddddd, "bad pointer!");
michael@0 960 MOZ_ASSERT(*(int*)obj != 0, "bad pointer!");
michael@0 961 #endif
michael@0 962 XPCJSRuntime* rt = GetRuntime();
michael@0 963 if (rt) {
michael@0 964 nsContentUtils::DeferredFinalize(obj);
michael@0 965 } else {
michael@0 966 obj->Release();
michael@0 967 }
michael@0 968 to->SetNative(nullptr);
michael@0 969 }
michael@0 970
michael@0 971 to->SetInterface(nullptr);
michael@0 972 }
michael@0 973 }
michael@0 974
michael@0 975 nsWrapperCache *cache = nullptr;
michael@0 976 CallQueryInterface(mIdentity, &cache);
michael@0 977 if (cache)
michael@0 978 cache->ClearWrapper();
michael@0 979
michael@0 980 mFlatJSObject = nullptr;
michael@0 981 mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
michael@0 982
michael@0 983 MOZ_ASSERT(mIdentity, "bad pointer!");
michael@0 984 #ifdef XP_WIN
michael@0 985 // Try to detect free'd pointer
michael@0 986 MOZ_ASSERT(*(int*)mIdentity != 0xdddddddd, "bad pointer!");
michael@0 987 MOZ_ASSERT(*(int*)mIdentity != 0, "bad pointer!");
michael@0 988 #endif
michael@0 989
michael@0 990 if (IsWrapperExpired()) {
michael@0 991 Destroy();
michael@0 992 }
michael@0 993
michael@0 994 // Note that it's not safe to touch mNativeWrapper here since it's
michael@0 995 // likely that it has already been finalized.
michael@0 996
michael@0 997 Release();
michael@0 998 }
michael@0 999
michael@0 1000 void
michael@0 1001 XPCWrappedNative::SystemIsBeingShutDown()
michael@0 1002 {
michael@0 1003 if (!IsValid())
michael@0 1004 return;
michael@0 1005
michael@0 1006 // The long standing strategy is to leak some objects still held at shutdown.
michael@0 1007 // The general problem is that propagating release out of xpconnect at
michael@0 1008 // shutdown time causes a world of problems.
michael@0 1009
michael@0 1010 // We leak mIdentity (see above).
michael@0 1011
michael@0 1012 // short circuit future finalization
michael@0 1013 JS_SetPrivate(mFlatJSObject, nullptr);
michael@0 1014 mFlatJSObject = nullptr;
michael@0 1015 mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
michael@0 1016
michael@0 1017 XPCWrappedNativeProto* proto = GetProto();
michael@0 1018
michael@0 1019 if (HasProto())
michael@0 1020 proto->SystemIsBeingShutDown();
michael@0 1021
michael@0 1022 if (mScriptableInfo &&
michael@0 1023 (!HasProto() ||
michael@0 1024 (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
michael@0 1025 delete mScriptableInfo;
michael@0 1026 }
michael@0 1027
michael@0 1028 // cleanup the tearoffs...
michael@0 1029
michael@0 1030 XPCWrappedNativeTearOffChunk* chunk;
michael@0 1031 for (chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk) {
michael@0 1032 XPCWrappedNativeTearOff* to = chunk->mTearOffs;
michael@0 1033 for (int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++) {
michael@0 1034 if (JSObject *jso = to->GetJSObjectPreserveColor()) {
michael@0 1035 JS_SetPrivate(jso, nullptr);
michael@0 1036 to->SetJSObject(nullptr);
michael@0 1037 }
michael@0 1038 // We leak the tearoff mNative
michael@0 1039 // (for the same reason we leak mIdentity - see above).
michael@0 1040 to->SetNative(nullptr);
michael@0 1041 to->SetInterface(nullptr);
michael@0 1042 }
michael@0 1043 }
michael@0 1044
michael@0 1045 if (mFirstChunk.mNextChunk) {
michael@0 1046 delete mFirstChunk.mNextChunk;
michael@0 1047 mFirstChunk.mNextChunk = nullptr;
michael@0 1048 }
michael@0 1049 }
michael@0 1050
michael@0 1051 /***************************************************************************/
michael@0 1052
michael@0 1053 // Dynamically ensure that two objects don't end up with the same private.
michael@0 1054 class MOZ_STACK_CLASS AutoClonePrivateGuard {
michael@0 1055 public:
michael@0 1056 AutoClonePrivateGuard(JSContext *cx, JSObject *aOld, JSObject *aNew)
michael@0 1057 : mOldReflector(cx, aOld), mNewReflector(cx, aNew)
michael@0 1058 {
michael@0 1059 MOZ_ASSERT(JS_GetPrivate(aOld) == JS_GetPrivate(aNew));
michael@0 1060 }
michael@0 1061
michael@0 1062 ~AutoClonePrivateGuard()
michael@0 1063 {
michael@0 1064 if (JS_GetPrivate(mOldReflector)) {
michael@0 1065 JS_SetPrivate(mNewReflector, nullptr);
michael@0 1066 }
michael@0 1067 }
michael@0 1068
michael@0 1069 private:
michael@0 1070 RootedObject mOldReflector;
michael@0 1071 RootedObject mNewReflector;
michael@0 1072 };
michael@0 1073
michael@0 1074 // static
michael@0 1075 nsresult
michael@0 1076 XPCWrappedNative::ReparentWrapperIfFound(XPCWrappedNativeScope* aOldScope,
michael@0 1077 XPCWrappedNativeScope* aNewScope,
michael@0 1078 HandleObject aNewParent,
michael@0 1079 nsISupports* aCOMObj)
michael@0 1080 {
michael@0 1081 // Check if we're near the stack limit before we get anywhere near the
michael@0 1082 // transplanting code.
michael@0 1083 AutoJSContext cx;
michael@0 1084 JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
michael@0 1085
michael@0 1086 XPCNativeInterface* iface = XPCNativeInterface::GetISupports();
michael@0 1087 if (!iface)
michael@0 1088 return NS_ERROR_FAILURE;
michael@0 1089
michael@0 1090 nsresult rv;
michael@0 1091
michael@0 1092 nsRefPtr<XPCWrappedNative> wrapper;
michael@0 1093 RootedObject flat(cx);
michael@0 1094 nsWrapperCache* cache = nullptr;
michael@0 1095 CallQueryInterface(aCOMObj, &cache);
michael@0 1096 if (cache) {
michael@0 1097 flat = cache->GetWrapper();
michael@0 1098 if (flat) {
michael@0 1099 wrapper = XPCWrappedNative::Get(flat);
michael@0 1100 MOZ_ASSERT(wrapper->GetScope() == aOldScope,
michael@0 1101 "Incorrect scope passed");
michael@0 1102 }
michael@0 1103 } else {
michael@0 1104 rv = XPCWrappedNative::GetUsedOnly(aCOMObj, aOldScope, iface,
michael@0 1105 getter_AddRefs(wrapper));
michael@0 1106 if (NS_FAILED(rv))
michael@0 1107 return rv;
michael@0 1108
michael@0 1109 if (wrapper)
michael@0 1110 flat = wrapper->GetFlatJSObject();
michael@0 1111 }
michael@0 1112
michael@0 1113 if (!flat)
michael@0 1114 return NS_OK;
michael@0 1115
michael@0 1116 JSAutoCompartment ac(cx, aNewScope->GetGlobalJSObject());
michael@0 1117
michael@0 1118 if (aOldScope != aNewScope) {
michael@0 1119 // Oh, so now we need to move the wrapper to a different scope.
michael@0 1120 AutoMarkingWrappedNativeProtoPtr oldProto(cx);
michael@0 1121 AutoMarkingWrappedNativeProtoPtr newProto(cx);
michael@0 1122
michael@0 1123 // Cross-scope means cross-compartment.
michael@0 1124 MOZ_ASSERT(js::GetObjectCompartment(aOldScope->GetGlobalJSObject()) !=
michael@0 1125 js::GetObjectCompartment(aNewScope->GetGlobalJSObject()));
michael@0 1126 MOZ_ASSERT(aNewParent, "won't be able to find the new parent");
michael@0 1127
michael@0 1128 if (wrapper->HasProto()) {
michael@0 1129 oldProto = wrapper->GetProto();
michael@0 1130 XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
michael@0 1131 XPCNativeScriptableCreateInfo ci(*info);
michael@0 1132 newProto =
michael@0 1133 XPCWrappedNativeProto::GetNewOrUsed(aNewScope,
michael@0 1134 oldProto->GetClassInfo(),
michael@0 1135 &ci);
michael@0 1136 if (!newProto) {
michael@0 1137 return NS_ERROR_FAILURE;
michael@0 1138 }
michael@0 1139 }
michael@0 1140
michael@0 1141 // First, the clone of the reflector, get a copy of its
michael@0 1142 // properties and clone its expando chain. The only part that is
michael@0 1143 // dangerous here if we have to return early is that we must avoid
michael@0 1144 // ending up with two reflectors pointing to the same WN. Other than
michael@0 1145 // that, the objects we create will just go away if we return early.
michael@0 1146
michael@0 1147 RootedObject proto(cx, newProto->GetJSProtoObject());
michael@0 1148 RootedObject newobj(cx, JS_CloneObject(cx, flat, proto, aNewParent));
michael@0 1149 if (!newobj)
michael@0 1150 return NS_ERROR_FAILURE;
michael@0 1151
michael@0 1152 // At this point, both |flat| and |newobj| point to the same wrapped
michael@0 1153 // native, which is bad, because one of them will end up finalizing
michael@0 1154 // a wrapped native it does not own. |cloneGuard| ensures that if we
michael@0 1155 // exit before calling clearing |flat|'s private the private of
michael@0 1156 // |newobj| will be set to nullptr. |flat| will go away soon, because
michael@0 1157 // we swap it with another object during the transplant and let that
michael@0 1158 // object die.
michael@0 1159 RootedObject propertyHolder(cx);
michael@0 1160 {
michael@0 1161 AutoClonePrivateGuard cloneGuard(cx, flat, newobj);
michael@0 1162
michael@0 1163 propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(),
michael@0 1164 aNewParent);
michael@0 1165 if (!propertyHolder)
michael@0 1166 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1167 if (!JS_CopyPropertiesFrom(cx, propertyHolder, flat))
michael@0 1168 return NS_ERROR_FAILURE;
michael@0 1169
michael@0 1170 // Expandos from other compartments are attached to the target JS object.
michael@0 1171 // Copy them over, and let the old ones die a natural death.
michael@0 1172 if (!XrayUtils::CloneExpandoChain(cx, newobj, flat))
michael@0 1173 return NS_ERROR_FAILURE;
michael@0 1174
michael@0 1175 // We've set up |newobj|, so we make it own the WN by nulling out
michael@0 1176 // the private of |flat|.
michael@0 1177 //
michael@0 1178 // NB: It's important to do this _after_ copying the properties to
michael@0 1179 // propertyHolder. Otherwise, an object with |foo.x === foo| will
michael@0 1180 // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
michael@0 1181 JS_SetPrivate(flat, nullptr);
michael@0 1182 }
michael@0 1183
michael@0 1184 // Update scope maps. This section modifies global state, so from
michael@0 1185 // here on out we crash if anything fails.
michael@0 1186 Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
michael@0 1187 Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
michael@0 1188
michael@0 1189 oldMap->Remove(wrapper);
michael@0 1190
michael@0 1191 if (wrapper->HasProto())
michael@0 1192 wrapper->SetProto(newProto);
michael@0 1193
michael@0 1194 // If the wrapper has no scriptable or it has a non-shared
michael@0 1195 // scriptable, then we don't need to mess with it.
michael@0 1196 // Otherwise...
michael@0 1197
michael@0 1198 if (wrapper->mScriptableInfo &&
michael@0 1199 wrapper->mScriptableInfo == oldProto->GetScriptableInfo()) {
michael@0 1200 // The new proto had better have the same JSClass stuff as
michael@0 1201 // the old one! We maintain a runtime wide unique map of
michael@0 1202 // this stuff. So, if these don't match then the caller is
michael@0 1203 // doing something bad here.
michael@0 1204
michael@0 1205 MOZ_ASSERT(oldProto->GetScriptableInfo()->GetScriptableShared() ==
michael@0 1206 newProto->GetScriptableInfo()->GetScriptableShared(),
michael@0 1207 "Changing proto is also changing JSObject Classname or "
michael@0 1208 "helper's nsIXPScriptable flags. This is not allowed!");
michael@0 1209
michael@0 1210 wrapper->UpdateScriptableInfo(newProto->GetScriptableInfo());
michael@0 1211 }
michael@0 1212
michael@0 1213 // Crash if the wrapper is already in the new scope.
michael@0 1214 if (newMap->Find(wrapper->GetIdentityObject()))
michael@0 1215 MOZ_CRASH();
michael@0 1216
michael@0 1217 if (!newMap->Add(wrapper))
michael@0 1218 MOZ_CRASH();
michael@0 1219
michael@0 1220 flat = xpc::TransplantObject(cx, flat, newobj);
michael@0 1221 if (!flat)
michael@0 1222 MOZ_CRASH();
michael@0 1223
michael@0 1224 MOZ_ASSERT(flat);
michael@0 1225 wrapper->mFlatJSObject = flat;
michael@0 1226 wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
michael@0 1227
michael@0 1228 if (cache) {
michael@0 1229 bool preserving = cache->PreservingWrapper();
michael@0 1230 cache->SetPreservingWrapper(false);
michael@0 1231 cache->SetWrapper(flat);
michael@0 1232 cache->SetPreservingWrapper(preserving);
michael@0 1233 }
michael@0 1234 if (!JS_CopyPropertiesFrom(cx, flat, propertyHolder))
michael@0 1235 MOZ_CRASH();
michael@0 1236
michael@0 1237 // Call the scriptable hook to indicate that we transplanted.
michael@0 1238 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
michael@0 1239 if (si->GetFlags().WantPostCreate())
michael@0 1240 (void) si->GetCallback()->PostTransplant(wrapper, cx, flat);
michael@0 1241 }
michael@0 1242
michael@0 1243 // Now we can just fix up the parent and return the wrapper
michael@0 1244
michael@0 1245 if (aNewParent) {
michael@0 1246 if (!JS_SetParent(cx, flat, aNewParent))
michael@0 1247 MOZ_CRASH();
michael@0 1248 }
michael@0 1249
michael@0 1250 return NS_OK;
michael@0 1251 }
michael@0 1252
michael@0 1253 // Orphans are sad little things - If only we could treat them better. :-(
michael@0 1254 //
michael@0 1255 // When a wrapper gets reparented to another scope (for example, when calling
michael@0 1256 // adoptNode), it's entirely possible that it previously served as the parent for
michael@0 1257 // other wrappers (via PreCreate hooks). When it moves, the old mFlatJSObject is
michael@0 1258 // replaced by a cross-compartment wrapper. Its descendants really _should_ move
michael@0 1259 // too, but we have no way of locating them short of a compartment-wide sweep
michael@0 1260 // (which we believe to be prohibitively expensive).
michael@0 1261 //
michael@0 1262 // So we just leave them behind. In practice, the only time this turns out to
michael@0 1263 // be a problem is during subsequent wrapper reparenting. When this happens, we
michael@0 1264 // call into the below fixup code at the last minute and straighten things out
michael@0 1265 // before proceeding.
michael@0 1266 //
michael@0 1267 // See bug 751995 for more information.
michael@0 1268
michael@0 1269 static nsresult
michael@0 1270 RescueOrphans(HandleObject obj)
michael@0 1271 {
michael@0 1272 AutoJSContext cx;
michael@0 1273 //
michael@0 1274 // Even if we're not an orphan at the moment, one of our ancestors might
michael@0 1275 // be. If so, we need to recursively rescue up the parent chain.
michael@0 1276 //
michael@0 1277
michael@0 1278 // First, get the parent object. If we're currently an orphan, the parent
michael@0 1279 // object is a cross-compartment wrapper. Follow the parent into its own
michael@0 1280 // compartment and fix it up there. We'll fix up |this| afterwards.
michael@0 1281 //
michael@0 1282 // NB: We pass stopAtOuter=false during the unwrap because Location objects
michael@0 1283 // are parented to outer window proxies.
michael@0 1284 nsresult rv;
michael@0 1285 RootedObject parentObj(cx, js::GetObjectParent(obj));
michael@0 1286 if (!parentObj)
michael@0 1287 return NS_OK; // Global object. We're done.
michael@0 1288 parentObj = js::UncheckedUnwrap(parentObj, /* stopAtOuter = */ false);
michael@0 1289
michael@0 1290 // PreCreate may touch dead compartments.
michael@0 1291 js::AutoMaybeTouchDeadZones agc(parentObj);
michael@0 1292
michael@0 1293 // Recursively fix up orphans on the parent chain.
michael@0 1294 rv = RescueOrphans(parentObj);
michael@0 1295 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1296
michael@0 1297 // Now that we know our parent is in the right place, determine if we've
michael@0 1298 // been orphaned. If not, we have nothing to do.
michael@0 1299 if (!js::IsCrossCompartmentWrapper(parentObj))
michael@0 1300 return NS_OK;
michael@0 1301
michael@0 1302 // We've been orphaned. Find where our parent went, and follow it.
michael@0 1303 if (IS_WN_REFLECTOR(obj)) {
michael@0 1304 RootedObject realParent(cx, js::UncheckedUnwrap(parentObj));
michael@0 1305 XPCWrappedNative *wn =
michael@0 1306 static_cast<XPCWrappedNative*>(js::GetObjectPrivate(obj));
michael@0 1307 return wn->ReparentWrapperIfFound(GetObjectScope(parentObj),
michael@0 1308 GetObjectScope(realParent),
michael@0 1309 realParent, wn->GetIdentityObject());
michael@0 1310 }
michael@0 1311
michael@0 1312 JSAutoCompartment ac(cx, obj);
michael@0 1313 return ReparentWrapper(cx, obj);
michael@0 1314 }
michael@0 1315
michael@0 1316 // Recursively fix up orphans on the parent chain of a wrapper. Note that this
michael@0 1317 // can cause a wrapper to move even if it is not an orphan, since its parent
michael@0 1318 // might be an orphan and fixing the parent causes this wrapper to become an
michael@0 1319 // orphan.
michael@0 1320 nsresult
michael@0 1321 XPCWrappedNative::RescueOrphans()
michael@0 1322 {
michael@0 1323 AutoJSContext cx;
michael@0 1324 RootedObject flatJSObject(cx, mFlatJSObject);
michael@0 1325 return ::RescueOrphans(flatJSObject);
michael@0 1326 }
michael@0 1327
michael@0 1328 bool
michael@0 1329 XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
michael@0 1330 {
michael@0 1331 AutoJSContext cx;
michael@0 1332
michael@0 1333 if (!mSet->HasInterface(aInterface)) {
michael@0 1334 AutoMarkingNativeSetPtr newSet(cx);
michael@0 1335 newSet = XPCNativeSet::GetNewOrUsed(mSet, aInterface,
michael@0 1336 mSet->GetInterfaceCount());
michael@0 1337 if (!newSet)
michael@0 1338 return false;
michael@0 1339
michael@0 1340 mSet = newSet;
michael@0 1341 }
michael@0 1342 return true;
michael@0 1343 }
michael@0 1344
michael@0 1345 XPCWrappedNativeTearOff*
michael@0 1346 XPCWrappedNative::LocateTearOff(XPCNativeInterface* aInterface)
michael@0 1347 {
michael@0 1348 for (XPCWrappedNativeTearOffChunk* chunk = &mFirstChunk;
michael@0 1349 chunk != nullptr;
michael@0 1350 chunk = chunk->mNextChunk) {
michael@0 1351 XPCWrappedNativeTearOff* tearOff = chunk->mTearOffs;
michael@0 1352 XPCWrappedNativeTearOff* const end = tearOff +
michael@0 1353 XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK;
michael@0 1354 for (tearOff = chunk->mTearOffs;
michael@0 1355 tearOff < end;
michael@0 1356 tearOff++) {
michael@0 1357 if (tearOff->GetInterface() == aInterface) {
michael@0 1358 return tearOff;
michael@0 1359 }
michael@0 1360 }
michael@0 1361 }
michael@0 1362 return nullptr;
michael@0 1363 }
michael@0 1364
michael@0 1365 XPCWrappedNativeTearOff*
michael@0 1366 XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface,
michael@0 1367 bool needJSObject /* = false */,
michael@0 1368 nsresult* pError /* = nullptr */)
michael@0 1369 {
michael@0 1370 AutoJSContext cx;
michael@0 1371 nsresult rv = NS_OK;
michael@0 1372 XPCWrappedNativeTearOff* to;
michael@0 1373 XPCWrappedNativeTearOff* firstAvailable = nullptr;
michael@0 1374
michael@0 1375 XPCWrappedNativeTearOffChunk* lastChunk;
michael@0 1376 XPCWrappedNativeTearOffChunk* chunk;
michael@0 1377 for (lastChunk = chunk = &mFirstChunk;
michael@0 1378 chunk;
michael@0 1379 lastChunk = chunk, chunk = chunk->mNextChunk) {
michael@0 1380 to = chunk->mTearOffs;
michael@0 1381 XPCWrappedNativeTearOff* const end = chunk->mTearOffs +
michael@0 1382 XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK;
michael@0 1383 for (to = chunk->mTearOffs;
michael@0 1384 to < end;
michael@0 1385 to++) {
michael@0 1386 if (to->GetInterface() == aInterface) {
michael@0 1387 if (needJSObject && !to->GetJSObjectPreserveColor()) {
michael@0 1388 AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
michael@0 1389 bool ok = InitTearOffJSObject(to);
michael@0 1390 // During shutdown, we don't sweep tearoffs. So make sure
michael@0 1391 // to unmark manually in case the auto-marker marked us.
michael@0 1392 // We shouldn't ever be getting here _during_ our
michael@0 1393 // Mark/Sweep cycle, so this should be safe.
michael@0 1394 to->Unmark();
michael@0 1395 if (!ok) {
michael@0 1396 to = nullptr;
michael@0 1397 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 1398 }
michael@0 1399 }
michael@0 1400 if (pError)
michael@0 1401 *pError = rv;
michael@0 1402 return to;
michael@0 1403 }
michael@0 1404 if (!firstAvailable && to->IsAvailable())
michael@0 1405 firstAvailable = to;
michael@0 1406 }
michael@0 1407 }
michael@0 1408
michael@0 1409 to = firstAvailable;
michael@0 1410
michael@0 1411 if (!to) {
michael@0 1412 auto newChunk = new XPCWrappedNativeTearOffChunk();
michael@0 1413 lastChunk->mNextChunk = newChunk;
michael@0 1414 to = newChunk->mTearOffs;
michael@0 1415 }
michael@0 1416
michael@0 1417 {
michael@0 1418 // Scope keeps |tearoff| from leaking across the rest of the function.
michael@0 1419 AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
michael@0 1420 rv = InitTearOff(to, aInterface, needJSObject);
michael@0 1421 // During shutdown, we don't sweep tearoffs. So make sure to unmark
michael@0 1422 // manually in case the auto-marker marked us. We shouldn't ever be
michael@0 1423 // getting here _during_ our Mark/Sweep cycle, so this should be safe.
michael@0 1424 to->Unmark();
michael@0 1425 if (NS_FAILED(rv))
michael@0 1426 to = nullptr;
michael@0 1427 }
michael@0 1428
michael@0 1429 if (pError)
michael@0 1430 *pError = rv;
michael@0 1431 return to;
michael@0 1432 }
michael@0 1433
michael@0 1434 nsresult
michael@0 1435 XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
michael@0 1436 XPCNativeInterface* aInterface,
michael@0 1437 bool needJSObject)
michael@0 1438 {
michael@0 1439 AutoJSContext cx;
michael@0 1440
michael@0 1441 // Determine if the object really does this interface...
michael@0 1442
michael@0 1443 const nsIID* iid = aInterface->GetIID();
michael@0 1444 nsISupports* identity = GetIdentityObject();
michael@0 1445 nsISupports* obj;
michael@0 1446
michael@0 1447 // If the scriptable helper forbids us from reflecting additional
michael@0 1448 // interfaces, then don't even try the QI, just fail.
michael@0 1449 if (mScriptableInfo &&
michael@0 1450 mScriptableInfo->GetFlags().ClassInfoInterfacesOnly() &&
michael@0 1451 !mSet->HasInterface(aInterface) &&
michael@0 1452 !mSet->HasInterfaceWithAncestor(aInterface)) {
michael@0 1453 return NS_ERROR_NO_INTERFACE;
michael@0 1454 }
michael@0 1455
michael@0 1456 // We are about to call out to other code.
michael@0 1457 // So protect our intended tearoff.
michael@0 1458
michael@0 1459 aTearOff->SetReserved();
michael@0 1460
michael@0 1461 if (NS_FAILED(identity->QueryInterface(*iid, (void**)&obj)) || !obj) {
michael@0 1462 aTearOff->SetInterface(nullptr);
michael@0 1463 return NS_ERROR_NO_INTERFACE;
michael@0 1464 }
michael@0 1465
michael@0 1466 // Guard against trying to build a tearoff for a shared nsIClassInfo.
michael@0 1467 if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
michael@0 1468 nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(obj));
michael@0 1469 if (alternate_identity.get() != identity) {
michael@0 1470 NS_RELEASE(obj);
michael@0 1471 aTearOff->SetInterface(nullptr);
michael@0 1472 return NS_ERROR_NO_INTERFACE;
michael@0 1473 }
michael@0 1474 }
michael@0 1475
michael@0 1476 // Guard against trying to build a tearoff for an interface that is
michael@0 1477 // aggregated and is implemented as a nsIXPConnectWrappedJS using this
michael@0 1478 // self-same JSObject. The XBL system does this. If we mutate the set
michael@0 1479 // of this wrapper then we will shadow the method that XBL has added to
michael@0 1480 // the JSObject that it has inserted in the JS proto chain between our
michael@0 1481 // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
michael@0 1482 // set mutation happen then the interface's methods will be added to
michael@0 1483 // our JSObject, but calls on those methods will get routed up to
michael@0 1484 // native code and into the wrappedJS - which will do a method lookup
michael@0 1485 // on *our* JSObject and find the same method and make another call
michael@0 1486 // into an infinite loop.
michael@0 1487 // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
michael@0 1488
michael@0 1489 // The code in this block also does a check for the double wrapped
michael@0 1490 // nsIPropertyBag case.
michael@0 1491
michael@0 1492 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(obj));
michael@0 1493 if (wrappedJS) {
michael@0 1494 RootedObject jso(cx, wrappedJS->GetJSObject());
michael@0 1495 if (jso == mFlatJSObject) {
michael@0 1496 // The implementing JSObject is the same as ours! Just say OK
michael@0 1497 // without actually extending the set.
michael@0 1498 //
michael@0 1499 // XXX It is a little cheesy to have FindTearOff return an
michael@0 1500 // 'empty' tearoff. But this is the centralized place to do the
michael@0 1501 // QI activities on the underlying object. *And* most caller to
michael@0 1502 // FindTearOff only look for a non-null result and ignore the
michael@0 1503 // actual tearoff returned. The only callers that do use the
michael@0 1504 // returned tearoff make sure to check for either a non-null
michael@0 1505 // JSObject or a matching Interface before proceeding.
michael@0 1506 // I think we can get away with this bit of ugliness.
michael@0 1507
michael@0 1508 NS_RELEASE(obj);
michael@0 1509 aTearOff->SetInterface(nullptr);
michael@0 1510 return NS_OK;
michael@0 1511 }
michael@0 1512
michael@0 1513 // Decide whether or not to expose nsIPropertyBag to calling
michael@0 1514 // JS code in the double wrapped case.
michael@0 1515 //
michael@0 1516 // Our rule here is that when JSObjects are double wrapped and
michael@0 1517 // exposed to other JSObjects then the nsIPropertyBag interface
michael@0 1518 // is only exposed on an 'opt-in' basis; i.e. if the underlying
michael@0 1519 // JSObject wants other JSObjects to be able to see this interface
michael@0 1520 // then it must implement QueryInterface and not throw an exception
michael@0 1521 // when asked for nsIPropertyBag. It need not actually *implement*
michael@0 1522 // nsIPropertyBag - xpconnect will do that work.
michael@0 1523
michael@0 1524 if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso) {
michael@0 1525 nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, *iid);
michael@0 1526 if (clasp) {
michael@0 1527 RootedObject answer(cx, clasp->CallQueryInterfaceOnJSObject(cx, jso, *iid));
michael@0 1528
michael@0 1529 if (!answer) {
michael@0 1530 NS_RELEASE(obj);
michael@0 1531 aTearOff->SetInterface(nullptr);
michael@0 1532 return NS_ERROR_NO_INTERFACE;
michael@0 1533 }
michael@0 1534 }
michael@0 1535 }
michael@0 1536 }
michael@0 1537
michael@0 1538 nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
michael@0 1539 if (sm && NS_FAILED(sm->
michael@0 1540 CanCreateWrapper(cx, *iid, identity,
michael@0 1541 GetClassInfo()))) {
michael@0 1542 // the security manager vetoed. It should have set an exception.
michael@0 1543 NS_RELEASE(obj);
michael@0 1544 aTearOff->SetInterface(nullptr);
michael@0 1545 return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
michael@0 1546 }
michael@0 1547
michael@0 1548 // If this is not already in our set we need to extend our set.
michael@0 1549 // Note: we do not cache the result of the previous call to HasInterface()
michael@0 1550 // because we unlocked and called out in the interim and the result of the
michael@0 1551 // previous call might not be correct anymore.
michael@0 1552
michael@0 1553 if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) {
michael@0 1554 NS_RELEASE(obj);
michael@0 1555 aTearOff->SetInterface(nullptr);
michael@0 1556 return NS_ERROR_NO_INTERFACE;
michael@0 1557 }
michael@0 1558
michael@0 1559 aTearOff->SetInterface(aInterface);
michael@0 1560 aTearOff->SetNative(obj);
michael@0 1561 if (needJSObject && !InitTearOffJSObject(aTearOff))
michael@0 1562 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1563
michael@0 1564 return NS_OK;
michael@0 1565 }
michael@0 1566
michael@0 1567 bool
michael@0 1568 XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to)
michael@0 1569 {
michael@0 1570 AutoJSContext cx;
michael@0 1571
michael@0 1572 RootedObject parent(cx, mFlatJSObject);
michael@0 1573 RootedObject proto(cx, JS_GetObjectPrototype(cx, parent));
michael@0 1574 JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass),
michael@0 1575 proto, parent);
michael@0 1576 if (!obj)
michael@0 1577 return false;
michael@0 1578
michael@0 1579 JS_SetPrivate(obj, to);
michael@0 1580 to->SetJSObject(obj);
michael@0 1581 return true;
michael@0 1582 }
michael@0 1583
michael@0 1584 /***************************************************************************/
michael@0 1585
michael@0 1586 static bool Throw(nsresult errNum, XPCCallContext& ccx)
michael@0 1587 {
michael@0 1588 XPCThrower::Throw(errNum, ccx);
michael@0 1589 return false;
michael@0 1590 }
michael@0 1591
michael@0 1592 /***************************************************************************/
michael@0 1593
michael@0 1594 class MOZ_STACK_CLASS CallMethodHelper
michael@0 1595 {
michael@0 1596 XPCCallContext& mCallContext;
michael@0 1597 // We wait to call SetLastResult(mInvokeResult) until ~CallMethodHelper(),
michael@0 1598 // so that XPCWN-implemented functions like XPCComponents::GetLastResult()
michael@0 1599 // can still access the previous result.
michael@0 1600 nsresult mInvokeResult;
michael@0 1601 nsIInterfaceInfo* const mIFaceInfo;
michael@0 1602 const nsXPTMethodInfo* mMethodInfo;
michael@0 1603 nsISupports* const mCallee;
michael@0 1604 const uint16_t mVTableIndex;
michael@0 1605 HandleId mIdxValueId;
michael@0 1606
michael@0 1607 nsAutoTArray<nsXPTCVariant, 8> mDispatchParams;
michael@0 1608 uint8_t mJSContextIndex; // TODO make const
michael@0 1609 uint8_t mOptArgcIndex; // TODO make const
michael@0 1610
michael@0 1611 jsval* const mArgv;
michael@0 1612 const uint32_t mArgc;
michael@0 1613
michael@0 1614 MOZ_ALWAYS_INLINE bool
michael@0 1615 GetArraySizeFromParam(uint8_t paramIndex, uint32_t* result) const;
michael@0 1616
michael@0 1617 MOZ_ALWAYS_INLINE bool
michael@0 1618 GetInterfaceTypeFromParam(uint8_t paramIndex,
michael@0 1619 const nsXPTType& datum_type,
michael@0 1620 nsID* result) const;
michael@0 1621
michael@0 1622 MOZ_ALWAYS_INLINE bool
michael@0 1623 GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
michael@0 1624
michael@0 1625 MOZ_ALWAYS_INLINE bool
michael@0 1626 GatherAndConvertResults();
michael@0 1627
michael@0 1628 MOZ_ALWAYS_INLINE bool
michael@0 1629 QueryInterfaceFastPath();
michael@0 1630
michael@0 1631 nsXPTCVariant*
michael@0 1632 GetDispatchParam(uint8_t paramIndex)
michael@0 1633 {
michael@0 1634 if (paramIndex >= mJSContextIndex)
michael@0 1635 paramIndex += 1;
michael@0 1636 if (paramIndex >= mOptArgcIndex)
michael@0 1637 paramIndex += 1;
michael@0 1638 return &mDispatchParams[paramIndex];
michael@0 1639 }
michael@0 1640 const nsXPTCVariant*
michael@0 1641 GetDispatchParam(uint8_t paramIndex) const
michael@0 1642 {
michael@0 1643 return const_cast<CallMethodHelper*>(this)->GetDispatchParam(paramIndex);
michael@0 1644 }
michael@0 1645
michael@0 1646 MOZ_ALWAYS_INLINE bool InitializeDispatchParams();
michael@0 1647
michael@0 1648 MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam);
michael@0 1649 MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i);
michael@0 1650 MOZ_ALWAYS_INLINE bool ConvertDependentParams();
michael@0 1651 MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i);
michael@0 1652
michael@0 1653 MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type);
michael@0 1654
michael@0 1655 MOZ_ALWAYS_INLINE bool HandleDipperParam(nsXPTCVariant* dp,
michael@0 1656 const nsXPTParamInfo& paramInfo);
michael@0 1657
michael@0 1658 MOZ_ALWAYS_INLINE nsresult Invoke();
michael@0 1659
michael@0 1660 public:
michael@0 1661
michael@0 1662 CallMethodHelper(XPCCallContext& ccx)
michael@0 1663 : mCallContext(ccx)
michael@0 1664 , mInvokeResult(NS_ERROR_UNEXPECTED)
michael@0 1665 , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
michael@0 1666 , mMethodInfo(nullptr)
michael@0 1667 , mCallee(ccx.GetTearOff()->GetNative())
michael@0 1668 , mVTableIndex(ccx.GetMethodIndex())
michael@0 1669 , mIdxValueId(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_VALUE))
michael@0 1670 , mJSContextIndex(UINT8_MAX)
michael@0 1671 , mOptArgcIndex(UINT8_MAX)
michael@0 1672 , mArgv(ccx.GetArgv())
michael@0 1673 , mArgc(ccx.GetArgc())
michael@0 1674
michael@0 1675 {
michael@0 1676 // Success checked later.
michael@0 1677 mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
michael@0 1678 }
michael@0 1679
michael@0 1680 ~CallMethodHelper();
michael@0 1681
michael@0 1682 MOZ_ALWAYS_INLINE bool Call();
michael@0 1683
michael@0 1684 };
michael@0 1685
michael@0 1686 // static
michael@0 1687 bool
michael@0 1688 XPCWrappedNative::CallMethod(XPCCallContext& ccx,
michael@0 1689 CallMode mode /*= CALL_METHOD */)
michael@0 1690 {
michael@0 1691 MOZ_ASSERT(ccx.GetXPCContext()->CallerTypeIsJavaScript(),
michael@0 1692 "Native caller for XPCWrappedNative::CallMethod?");
michael@0 1693
michael@0 1694 nsresult rv = ccx.CanCallNow();
michael@0 1695 if (NS_FAILED(rv)) {
michael@0 1696 return Throw(rv, ccx);
michael@0 1697 }
michael@0 1698
michael@0 1699 return CallMethodHelper(ccx).Call();
michael@0 1700 }
michael@0 1701
michael@0 1702 bool
michael@0 1703 CallMethodHelper::Call()
michael@0 1704 {
michael@0 1705 mCallContext.SetRetVal(JSVAL_VOID);
michael@0 1706
michael@0 1707 XPCJSRuntime::Get()->SetPendingException(nullptr);
michael@0 1708
michael@0 1709 if (mVTableIndex == 0) {
michael@0 1710 return QueryInterfaceFastPath();
michael@0 1711 }
michael@0 1712
michael@0 1713 if (!mMethodInfo) {
michael@0 1714 Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext);
michael@0 1715 return false;
michael@0 1716 }
michael@0 1717
michael@0 1718 if (!InitializeDispatchParams())
michael@0 1719 return false;
michael@0 1720
michael@0 1721 // Iterate through the params doing conversions of independent params only.
michael@0 1722 // When we later convert the dependent params (if any) we will know that
michael@0 1723 // the params upon which they depend will have already been converted -
michael@0 1724 // regardless of ordering.
michael@0 1725 bool foundDependentParam = false;
michael@0 1726 if (!ConvertIndependentParams(&foundDependentParam))
michael@0 1727 return false;
michael@0 1728
michael@0 1729 if (foundDependentParam && !ConvertDependentParams())
michael@0 1730 return false;
michael@0 1731
michael@0 1732 mInvokeResult = Invoke();
michael@0 1733
michael@0 1734 if (JS_IsExceptionPending(mCallContext)) {
michael@0 1735 return false;
michael@0 1736 }
michael@0 1737
michael@0 1738 if (NS_FAILED(mInvokeResult)) {
michael@0 1739 ThrowBadResult(mInvokeResult, mCallContext);
michael@0 1740 return false;
michael@0 1741 }
michael@0 1742
michael@0 1743 return GatherAndConvertResults();
michael@0 1744 }
michael@0 1745
michael@0 1746 CallMethodHelper::~CallMethodHelper()
michael@0 1747 {
michael@0 1748 uint8_t paramCount = mMethodInfo->GetParamCount();
michael@0 1749 if (mDispatchParams.Length()) {
michael@0 1750 for (uint8_t i = 0; i < paramCount; i++) {
michael@0 1751 nsXPTCVariant* dp = GetDispatchParam(i);
michael@0 1752 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
michael@0 1753
michael@0 1754 if (paramInfo.GetType().IsArray()) {
michael@0 1755 void* p = dp->val.p;
michael@0 1756 if (!p)
michael@0 1757 continue;
michael@0 1758
michael@0 1759 // Clean up the array contents if necessary.
michael@0 1760 if (dp->DoesValNeedCleanup()) {
michael@0 1761 // We need some basic information to properly destroy the array.
michael@0 1762 uint32_t array_count = 0;
michael@0 1763 nsXPTType datum_type;
michael@0 1764 if (!GetArraySizeFromParam(i, &array_count) ||
michael@0 1765 !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
michael@0 1766 &paramInfo,
michael@0 1767 1, &datum_type))) {
michael@0 1768 // XXXbholley - I'm not convinced that the above calls will
michael@0 1769 // ever fail.
michael@0 1770 NS_ERROR("failed to get array information, we'll leak here");
michael@0 1771 continue;
michael@0 1772 }
michael@0 1773
michael@0 1774 // Loop over the array contents. For each one, we create a
michael@0 1775 // dummy 'val' and pass it to the cleanup helper.
michael@0 1776 for (uint32_t k = 0; k < array_count; k++) {
michael@0 1777 nsXPTCMiniVariant v;
michael@0 1778 v.val.p = static_cast<void**>(p)[k];
michael@0 1779 CleanupParam(v, datum_type);
michael@0 1780 }
michael@0 1781 }
michael@0 1782
michael@0 1783 // always free the array itself
michael@0 1784 nsMemory::Free(p);
michael@0 1785 } else {
michael@0 1786 // Clean up single parameters (if requested).
michael@0 1787 if (dp->DoesValNeedCleanup())
michael@0 1788 CleanupParam(*dp, dp->type);
michael@0 1789 }
michael@0 1790 }
michael@0 1791 }
michael@0 1792
michael@0 1793 mCallContext.GetXPCContext()->SetLastResult(mInvokeResult);
michael@0 1794 }
michael@0 1795
michael@0 1796 bool
michael@0 1797 CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
michael@0 1798 uint32_t* result) const
michael@0 1799 {
michael@0 1800 nsresult rv;
michael@0 1801 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
michael@0 1802
michael@0 1803 // TODO fixup the various exceptions that are thrown
michael@0 1804
michael@0 1805 rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, &paramInfo, 0, &paramIndex);
michael@0 1806 if (NS_FAILED(rv))
michael@0 1807 return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
michael@0 1808
michael@0 1809 *result = GetDispatchParam(paramIndex)->val.u32;
michael@0 1810
michael@0 1811 return true;
michael@0 1812 }
michael@0 1813
michael@0 1814 bool
michael@0 1815 CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
michael@0 1816 const nsXPTType& datum_type,
michael@0 1817 nsID* result) const
michael@0 1818 {
michael@0 1819 nsresult rv;
michael@0 1820 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
michael@0 1821 uint8_t tag = datum_type.TagPart();
michael@0 1822
michael@0 1823 // TODO fixup the various exceptions that are thrown
michael@0 1824
michael@0 1825 if (tag == nsXPTType::T_INTERFACE) {
michael@0 1826 rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo, result);
michael@0 1827 if (NS_FAILED(rv))
michael@0 1828 return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
michael@0 1829 paramIndex, mCallContext);
michael@0 1830 } else if (tag == nsXPTType::T_INTERFACE_IS) {
michael@0 1831 rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, &paramInfo,
michael@0 1832 &paramIndex);
michael@0 1833 if (NS_FAILED(rv))
michael@0 1834 return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
michael@0 1835
michael@0 1836 nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p;
michael@0 1837 if (!p)
michael@0 1838 return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
michael@0 1839 paramIndex, mCallContext);
michael@0 1840 *result = *p;
michael@0 1841 }
michael@0 1842 return true;
michael@0 1843 }
michael@0 1844
michael@0 1845 bool
michael@0 1846 CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const
michael@0 1847 {
michael@0 1848 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
michael@0 1849
michael@0 1850 if ((paramInfo.IsOut() || paramInfo.IsDipper()) &&
michael@0 1851 !paramInfo.IsRetval()) {
michael@0 1852 MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(),
michael@0 1853 "Expected either enough arguments or an optional argument");
michael@0 1854 jsval arg = paramIndex < mArgc ? mArgv[paramIndex] : JSVAL_NULL;
michael@0 1855 if (paramIndex < mArgc) {
michael@0 1856 RootedObject obj(mCallContext);
michael@0 1857 if (!arg.isPrimitive())
michael@0 1858 obj = &arg.toObject();
michael@0 1859 if (!obj || !JS_GetPropertyById(mCallContext, obj, mIdxValueId, srcp)) {
michael@0 1860 // Explicitly passed in unusable value for out param. Note
michael@0 1861 // that if i >= mArgc we already know that |arg| is JSVAL_NULL,
michael@0 1862 // and that's ok.
michael@0 1863 ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, paramIndex,
michael@0 1864 mCallContext);
michael@0 1865 return false;
michael@0 1866 }
michael@0 1867 }
michael@0 1868 }
michael@0 1869
michael@0 1870 return true;
michael@0 1871 }
michael@0 1872
michael@0 1873 bool
michael@0 1874 CallMethodHelper::GatherAndConvertResults()
michael@0 1875 {
michael@0 1876 // now we iterate through the native params to gather and convert results
michael@0 1877 uint8_t paramCount = mMethodInfo->GetParamCount();
michael@0 1878 for (uint8_t i = 0; i < paramCount; i++) {
michael@0 1879 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
michael@0 1880 if (!paramInfo.IsOut() && !paramInfo.IsDipper())
michael@0 1881 continue;
michael@0 1882
michael@0 1883 const nsXPTType& type = paramInfo.GetType();
michael@0 1884 nsXPTCVariant* dp = GetDispatchParam(i);
michael@0 1885 RootedValue v(mCallContext, NullValue());
michael@0 1886 uint32_t array_count = 0;
michael@0 1887 nsXPTType datum_type;
michael@0 1888 bool isArray = type.IsArray();
michael@0 1889 bool isSizedString = isArray ?
michael@0 1890 false :
michael@0 1891 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
michael@0 1892 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
michael@0 1893
michael@0 1894 if (isArray) {
michael@0 1895 if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
michael@0 1896 &datum_type))) {
michael@0 1897 Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
michael@0 1898 return false;
michael@0 1899 }
michael@0 1900 } else
michael@0 1901 datum_type = type;
michael@0 1902
michael@0 1903 if (isArray || isSizedString) {
michael@0 1904 if (!GetArraySizeFromParam(i, &array_count))
michael@0 1905 return false;
michael@0 1906 }
michael@0 1907
michael@0 1908 nsID param_iid;
michael@0 1909 if (datum_type.IsInterfacePointer() &&
michael@0 1910 !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
michael@0 1911 return false;
michael@0 1912
michael@0 1913 nsresult err;
michael@0 1914 if (isArray) {
michael@0 1915 if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val,
michael@0 1916 datum_type, &param_iid,
michael@0 1917 array_count, &err)) {
michael@0 1918 // XXX need exception scheme for arrays to indicate bad element
michael@0 1919 ThrowBadParam(err, i, mCallContext);
michael@0 1920 return false;
michael@0 1921 }
michael@0 1922 } else if (isSizedString) {
michael@0 1923 if (!XPCConvert::NativeStringWithSize2JS(&v,
michael@0 1924 (const void*)&dp->val,
michael@0 1925 datum_type,
michael@0 1926 array_count, &err)) {
michael@0 1927 ThrowBadParam(err, i, mCallContext);
michael@0 1928 return false;
michael@0 1929 }
michael@0 1930 } else {
michael@0 1931 if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type,
michael@0 1932 &param_iid, &err)) {
michael@0 1933 ThrowBadParam(err, i, mCallContext);
michael@0 1934 return false;
michael@0 1935 }
michael@0 1936 }
michael@0 1937
michael@0 1938 if (paramInfo.IsRetval()) {
michael@0 1939 mCallContext.SetRetVal(v);
michael@0 1940 } else if (i < mArgc) {
michael@0 1941 // we actually assured this before doing the invoke
michael@0 1942 MOZ_ASSERT(mArgv[i].isObject(), "out var is not object");
michael@0 1943 RootedObject obj(mCallContext, &mArgv[i].toObject());
michael@0 1944 if (!JS_SetPropertyById(mCallContext, obj, mIdxValueId, v)) {
michael@0 1945 ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, i, mCallContext);
michael@0 1946 return false;
michael@0 1947 }
michael@0 1948 } else {
michael@0 1949 MOZ_ASSERT(paramInfo.IsOptional(),
michael@0 1950 "Expected either enough arguments or an optional argument");
michael@0 1951 }
michael@0 1952 }
michael@0 1953
michael@0 1954 return true;
michael@0 1955 }
michael@0 1956
michael@0 1957 bool
michael@0 1958 CallMethodHelper::QueryInterfaceFastPath()
michael@0 1959 {
michael@0 1960 MOZ_ASSERT(mVTableIndex == 0,
michael@0 1961 "Using the QI fast-path for a method other than QueryInterface");
michael@0 1962
michael@0 1963 if (mArgc < 1) {
michael@0 1964 Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
michael@0 1965 return false;
michael@0 1966 }
michael@0 1967
michael@0 1968 if (!mArgv[0].isObject()) {
michael@0 1969 ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
michael@0 1970 return false;
michael@0 1971 }
michael@0 1972
michael@0 1973 const nsID* iid = xpc_JSObjectToID(mCallContext, &mArgv[0].toObject());
michael@0 1974 if (!iid) {
michael@0 1975 ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
michael@0 1976 return false;
michael@0 1977 }
michael@0 1978
michael@0 1979 nsISupports* qiresult = nullptr;
michael@0 1980 mInvokeResult = mCallee->QueryInterface(*iid, (void**) &qiresult);
michael@0 1981
michael@0 1982 if (NS_FAILED(mInvokeResult)) {
michael@0 1983 ThrowBadResult(mInvokeResult, mCallContext);
michael@0 1984 return false;
michael@0 1985 }
michael@0 1986
michael@0 1987 RootedValue v(mCallContext, NullValue());
michael@0 1988 nsresult err;
michael@0 1989 bool success =
michael@0 1990 XPCConvert::NativeData2JS(&v, &qiresult,
michael@0 1991 nsXPTType::T_INTERFACE_IS,
michael@0 1992 iid, &err);
michael@0 1993 NS_IF_RELEASE(qiresult);
michael@0 1994
michael@0 1995 if (!success) {
michael@0 1996 ThrowBadParam(err, 0, mCallContext);
michael@0 1997 return false;
michael@0 1998 }
michael@0 1999
michael@0 2000 mCallContext.SetRetVal(v);
michael@0 2001 return true;
michael@0 2002 }
michael@0 2003
michael@0 2004 bool
michael@0 2005 CallMethodHelper::InitializeDispatchParams()
michael@0 2006 {
michael@0 2007 const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0;
michael@0 2008 const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0;
michael@0 2009 const uint8_t paramCount = mMethodInfo->GetParamCount();
michael@0 2010 uint8_t requiredArgs = paramCount;
michael@0 2011 uint8_t hasRetval = 0;
michael@0 2012
michael@0 2013 // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
michael@0 2014 if (paramCount && mMethodInfo->GetParam(paramCount-1).IsRetval()) {
michael@0 2015 hasRetval = 1;
michael@0 2016 requiredArgs--;
michael@0 2017 }
michael@0 2018
michael@0 2019 if (mArgc < requiredArgs || wantsOptArgc) {
michael@0 2020 if (wantsOptArgc)
michael@0 2021 mOptArgcIndex = requiredArgs;
michael@0 2022
michael@0 2023 // skip over any optional arguments
michael@0 2024 while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional())
michael@0 2025 requiredArgs--;
michael@0 2026
michael@0 2027 if (mArgc < requiredArgs) {
michael@0 2028 Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
michael@0 2029 return false;
michael@0 2030 }
michael@0 2031 }
michael@0 2032
michael@0 2033 if (wantsJSContext) {
michael@0 2034 if (wantsOptArgc)
michael@0 2035 // Need to bump mOptArgcIndex up one here.
michael@0 2036 mJSContextIndex = mOptArgcIndex++;
michael@0 2037 else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter())
michael@0 2038 // For attributes, we always put the JSContext* first.
michael@0 2039 mJSContextIndex = 0;
michael@0 2040 else
michael@0 2041 mJSContextIndex = paramCount - hasRetval;
michael@0 2042 }
michael@0 2043
michael@0 2044 // iterate through the params to clear flags (for safe cleanup later)
michael@0 2045 for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
michael@0 2046 nsXPTCVariant* dp = mDispatchParams.AppendElement();
michael@0 2047 dp->ClearFlags();
michael@0 2048 dp->val.p = nullptr;
michael@0 2049 }
michael@0 2050
michael@0 2051 // Fill in the JSContext argument
michael@0 2052 if (wantsJSContext) {
michael@0 2053 nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex];
michael@0 2054 dp->type = nsXPTType::T_VOID;
michael@0 2055 dp->val.p = mCallContext;
michael@0 2056 }
michael@0 2057
michael@0 2058 // Fill in the optional_argc argument
michael@0 2059 if (wantsOptArgc) {
michael@0 2060 nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex];
michael@0 2061 dp->type = nsXPTType::T_U8;
michael@0 2062 dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
michael@0 2063 }
michael@0 2064
michael@0 2065 return true;
michael@0 2066 }
michael@0 2067
michael@0 2068 bool
michael@0 2069 CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam)
michael@0 2070 {
michael@0 2071 const uint8_t paramCount = mMethodInfo->GetParamCount();
michael@0 2072 for (uint8_t i = 0; i < paramCount; i++) {
michael@0 2073 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
michael@0 2074
michael@0 2075 if (paramInfo.GetType().IsDependent())
michael@0 2076 *foundDependentParam = true;
michael@0 2077 else if (!ConvertIndependentParam(i))
michael@0 2078 return false;
michael@0 2079
michael@0 2080 }
michael@0 2081
michael@0 2082 return true;
michael@0 2083 }
michael@0 2084
michael@0 2085 bool
michael@0 2086 CallMethodHelper::ConvertIndependentParam(uint8_t i)
michael@0 2087 {
michael@0 2088 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
michael@0 2089 const nsXPTType& type = paramInfo.GetType();
michael@0 2090 uint8_t type_tag = type.TagPart();
michael@0 2091 nsXPTCVariant* dp = GetDispatchParam(i);
michael@0 2092 dp->type = type;
michael@0 2093 MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
michael@0 2094
michael@0 2095 // Handle dipper types separately.
michael@0 2096 if (paramInfo.IsDipper())
michael@0 2097 return HandleDipperParam(dp, paramInfo);
michael@0 2098
michael@0 2099 // Specify the correct storage/calling semantics.
michael@0 2100 if (paramInfo.IsIndirect())
michael@0 2101 dp->SetIndirect();
michael@0 2102
michael@0 2103 // The JSVal proper is always stored within the 'val' union and passed
michael@0 2104 // indirectly, regardless of in/out-ness.
michael@0 2105 if (type_tag == nsXPTType::T_JSVAL) {
michael@0 2106 // Root the value.
michael@0 2107 dp->val.j = JSVAL_VOID;
michael@0 2108 if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param"))
michael@0 2109 return false;
michael@0 2110 }
michael@0 2111
michael@0 2112 // Flag cleanup for anything that isn't self-contained.
michael@0 2113 if (!type.IsArithmetic())
michael@0 2114 dp->SetValNeedsCleanup();
michael@0 2115
michael@0 2116 // Even if there's nothing to convert, we still need to examine the
michael@0 2117 // JSObject container for out-params. If it's null or otherwise invalid,
michael@0 2118 // we want to know before the call, rather than after.
michael@0 2119 //
michael@0 2120 // This is a no-op for 'in' params.
michael@0 2121 RootedValue src(mCallContext);
michael@0 2122 if (!GetOutParamSource(i, &src))
michael@0 2123 return false;
michael@0 2124
michael@0 2125 // All that's left to do is value conversion. Bail early if we don't need
michael@0 2126 // to do that.
michael@0 2127 if (!paramInfo.IsIn())
michael@0 2128 return true;
michael@0 2129
michael@0 2130 // We're definitely some variety of 'in' now, so there's something to
michael@0 2131 // convert. The source value for conversion depends on whether we're
michael@0 2132 // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
michael@0 2133 // so all that's left is 'in'.
michael@0 2134 if (!paramInfo.IsOut()) {
michael@0 2135 // Handle the 'in' case.
michael@0 2136 MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
michael@0 2137 "Expected either enough arguments or an optional argument");
michael@0 2138 if (i < mArgc)
michael@0 2139 src = mArgv[i];
michael@0 2140 else if (type_tag == nsXPTType::T_JSVAL)
michael@0 2141 src = JSVAL_VOID;
michael@0 2142 else
michael@0 2143 src = JSVAL_NULL;
michael@0 2144 }
michael@0 2145
michael@0 2146 nsID param_iid;
michael@0 2147 if (type_tag == nsXPTType::T_INTERFACE &&
michael@0 2148 NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo,
michael@0 2149 &param_iid))) {
michael@0 2150 ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext);
michael@0 2151 return false;
michael@0 2152 }
michael@0 2153
michael@0 2154 nsresult err;
michael@0 2155 if (!XPCConvert::JSData2Native(&dp->val, src, type, true, &param_iid, &err)) {
michael@0 2156 ThrowBadParam(err, i, mCallContext);
michael@0 2157 return false;
michael@0 2158 }
michael@0 2159
michael@0 2160 return true;
michael@0 2161 }
michael@0 2162
michael@0 2163 bool
michael@0 2164 CallMethodHelper::ConvertDependentParams()
michael@0 2165 {
michael@0 2166 const uint8_t paramCount = mMethodInfo->GetParamCount();
michael@0 2167 for (uint8_t i = 0; i < paramCount; i++) {
michael@0 2168 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
michael@0 2169
michael@0 2170 if (!paramInfo.GetType().IsDependent())
michael@0 2171 continue;
michael@0 2172 if (!ConvertDependentParam(i))
michael@0 2173 return false;
michael@0 2174 }
michael@0 2175
michael@0 2176 return true;
michael@0 2177 }
michael@0 2178
michael@0 2179 bool
michael@0 2180 CallMethodHelper::ConvertDependentParam(uint8_t i)
michael@0 2181 {
michael@0 2182 const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
michael@0 2183 const nsXPTType& type = paramInfo.GetType();
michael@0 2184 nsXPTType datum_type;
michael@0 2185 uint32_t array_count = 0;
michael@0 2186 bool isArray = type.IsArray();
michael@0 2187
michael@0 2188 bool isSizedString = isArray ?
michael@0 2189 false :
michael@0 2190 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
michael@0 2191 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
michael@0 2192
michael@0 2193 nsXPTCVariant* dp = GetDispatchParam(i);
michael@0 2194 dp->type = type;
michael@0 2195
michael@0 2196 if (isArray) {
michael@0 2197 if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
michael@0 2198 &datum_type))) {
michael@0 2199 Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
michael@0 2200 return false;
michael@0 2201 }
michael@0 2202 MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL,
michael@0 2203 "Arrays of JSVals not currently supported - see bug 693337.");
michael@0 2204 } else {
michael@0 2205 datum_type = type;
michael@0 2206 }
michael@0 2207
michael@0 2208 // Specify the correct storage/calling semantics.
michael@0 2209 if (paramInfo.IsIndirect())
michael@0 2210 dp->SetIndirect();
michael@0 2211
michael@0 2212 // We have 3 possible type of dependent parameters: Arrays, Sized Strings,
michael@0 2213 // and iid_is Interface pointers. The latter two always need cleanup, and
michael@0 2214 // arrays need cleanup for all non-arithmetic types. Since the latter two
michael@0 2215 // cases also happen to be non-arithmetic, we can just inspect datum_type
michael@0 2216 // here.
michael@0 2217 if (!datum_type.IsArithmetic())
michael@0 2218 dp->SetValNeedsCleanup();
michael@0 2219
michael@0 2220 // Even if there's nothing to convert, we still need to examine the
michael@0 2221 // JSObject container for out-params. If it's null or otherwise invalid,
michael@0 2222 // we want to know before the call, rather than after.
michael@0 2223 //
michael@0 2224 // This is a no-op for 'in' params.
michael@0 2225 RootedValue src(mCallContext);
michael@0 2226 if (!GetOutParamSource(i, &src))
michael@0 2227 return false;
michael@0 2228
michael@0 2229 // All that's left to do is value conversion. Bail early if we don't need
michael@0 2230 // to do that.
michael@0 2231 if (!paramInfo.IsIn())
michael@0 2232 return true;
michael@0 2233
michael@0 2234 // We're definitely some variety of 'in' now, so there's something to
michael@0 2235 // convert. The source value for conversion depends on whether we're
michael@0 2236 // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
michael@0 2237 // so all that's left is 'in'.
michael@0 2238 if (!paramInfo.IsOut()) {
michael@0 2239 // Handle the 'in' case.
michael@0 2240 MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
michael@0 2241 "Expected either enough arguments or an optional argument");
michael@0 2242 src = i < mArgc ? mArgv[i] : JSVAL_NULL;
michael@0 2243 }
michael@0 2244
michael@0 2245 nsID param_iid;
michael@0 2246 if (datum_type.IsInterfacePointer() &&
michael@0 2247 !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
michael@0 2248 return false;
michael@0 2249
michael@0 2250 nsresult err;
michael@0 2251
michael@0 2252 if (isArray || isSizedString) {
michael@0 2253 if (!GetArraySizeFromParam(i, &array_count))
michael@0 2254 return false;
michael@0 2255
michael@0 2256 if (isArray) {
michael@0 2257 if (array_count &&
michael@0 2258 !XPCConvert::JSArray2Native((void**)&dp->val, src,
michael@0 2259 array_count, datum_type, &param_iid,
michael@0 2260 &err)) {
michael@0 2261 // XXX need exception scheme for arrays to indicate bad element
michael@0 2262 ThrowBadParam(err, i, mCallContext);
michael@0 2263 return false;
michael@0 2264 }
michael@0 2265 } else // if (isSizedString)
michael@0 2266 {
michael@0 2267 if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val,
michael@0 2268 src, array_count,
michael@0 2269 datum_type, &err)) {
michael@0 2270 ThrowBadParam(err, i, mCallContext);
michael@0 2271 return false;
michael@0 2272 }
michael@0 2273 }
michael@0 2274 } else {
michael@0 2275 if (!XPCConvert::JSData2Native(&dp->val, src, type, true,
michael@0 2276 &param_iid, &err)) {
michael@0 2277 ThrowBadParam(err, i, mCallContext);
michael@0 2278 return false;
michael@0 2279 }
michael@0 2280 }
michael@0 2281
michael@0 2282 return true;
michael@0 2283 }
michael@0 2284
michael@0 2285 // Performs all necessary teardown on a parameter after method invocation.
michael@0 2286 //
michael@0 2287 // This method should only be called if the value in question was flagged
michael@0 2288 // for cleanup (ie, if dp->DoesValNeedCleanup()).
michael@0 2289 void
michael@0 2290 CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
michael@0 2291 {
michael@0 2292 // We handle array elements, but not the arrays themselves.
michael@0 2293 MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays.");
michael@0 2294
michael@0 2295 // Pointers may sometimes be null even if cleanup was requested. Combine
michael@0 2296 // the null checking for all the different types into one check here.
michael@0 2297 if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr)
michael@0 2298 return;
michael@0 2299
michael@0 2300 switch (type.TagPart()) {
michael@0 2301 case nsXPTType::T_JSVAL:
michael@0 2302 js::RemoveRawValueRoot(mCallContext, (jsval*)&param.val);
michael@0 2303 break;
michael@0 2304 case nsXPTType::T_INTERFACE:
michael@0 2305 case nsXPTType::T_INTERFACE_IS:
michael@0 2306 ((nsISupports*)param.val.p)->Release();
michael@0 2307 break;
michael@0 2308 case nsXPTType::T_ASTRING:
michael@0 2309 case nsXPTType::T_DOMSTRING:
michael@0 2310 nsXPConnect::GetRuntimeInstance()->DeleteShortLivedString((nsString*)param.val.p);
michael@0 2311 break;
michael@0 2312 case nsXPTType::T_UTF8STRING:
michael@0 2313 case nsXPTType::T_CSTRING:
michael@0 2314 {
michael@0 2315 nsCString* rs = (nsCString*)param.val.p;
michael@0 2316 if (rs != &EmptyCString() && rs != &NullCString())
michael@0 2317 delete rs;
michael@0 2318 }
michael@0 2319 break;
michael@0 2320 default:
michael@0 2321 MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
michael@0 2322 nsMemory::Free(param.val.p);
michael@0 2323 break;
michael@0 2324 }
michael@0 2325 }
michael@0 2326
michael@0 2327 // Handle parameters with dipper types.
michael@0 2328 //
michael@0 2329 // Dipper types are one of the more inscrutable aspects of xpidl. In a
michael@0 2330 // nutshell, dippers are empty container objects, created and passed by
michael@0 2331 // the caller, and filled by the callee. The callee receives a
michael@0 2332 // fully-formed object, and thus does not have to construct anything. But
michael@0 2333 // the object is functionally empty, and the callee is responsible for
michael@0 2334 // putting something useful inside of it.
michael@0 2335 //
michael@0 2336 // XPIDL decides which types to make dippers. The list of these types
michael@0 2337 // is given in the isDipperType() function in typelib.py, and is currently
michael@0 2338 // limited to 4 string types.
michael@0 2339 //
michael@0 2340 // When a dipper type is declared as an 'out' parameter, xpidl internally
michael@0 2341 // converts it to an 'in', and sets the XPT_PD_DIPPER flag on it. For this
michael@0 2342 // reason, dipper types are sometimes referred to as 'out parameters
michael@0 2343 // masquerading as in'. The burden of maintaining this illusion falls mostly
michael@0 2344 // on XPConnect - we create the empty containers, and harvest the results
michael@0 2345 // after the call.
michael@0 2346 //
michael@0 2347 // This method creates these empty containers.
michael@0 2348 bool
michael@0 2349 CallMethodHelper::HandleDipperParam(nsXPTCVariant* dp,
michael@0 2350 const nsXPTParamInfo& paramInfo)
michael@0 2351 {
michael@0 2352 // Get something we can make comparisons with.
michael@0 2353 uint8_t type_tag = paramInfo.GetType().TagPart();
michael@0 2354
michael@0 2355 // Dippers always have the 'in' and 'dipper' flags set. Never 'out'.
michael@0 2356 MOZ_ASSERT(!paramInfo.IsOut(), "Dipper has unexpected flags.");
michael@0 2357
michael@0 2358 // xpidl.h specifies that dipper types will be used in exactly four
michael@0 2359 // cases, all strings. Verify that here.
michael@0 2360 MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING ||
michael@0 2361 type_tag == nsXPTType::T_DOMSTRING ||
michael@0 2362 type_tag == nsXPTType::T_UTF8STRING ||
michael@0 2363 type_tag == nsXPTType::T_CSTRING,
michael@0 2364 "Unexpected dipper type!");
michael@0 2365
michael@0 2366 // ASTRING and DOMSTRING are very similar, and both use nsString.
michael@0 2367 // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
michael@0 2368 if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
michael@0 2369 dp->val.p = nsXPConnect::GetRuntimeInstance()->NewShortLivedString();
michael@0 2370 else
michael@0 2371 dp->val.p = new nsCString();
michael@0 2372
michael@0 2373 // Check for OOM, in either case.
michael@0 2374 if (!dp->val.p) {
michael@0 2375 JS_ReportOutOfMemory(mCallContext);
michael@0 2376 return false;
michael@0 2377 }
michael@0 2378
michael@0 2379 // We allocated, so we need to deallocate after the method call completes.
michael@0 2380 dp->SetValNeedsCleanup();
michael@0 2381
michael@0 2382 return true;
michael@0 2383 }
michael@0 2384
michael@0 2385 nsresult
michael@0 2386 CallMethodHelper::Invoke()
michael@0 2387 {
michael@0 2388 uint32_t argc = mDispatchParams.Length();
michael@0 2389 nsXPTCVariant* argv = mDispatchParams.Elements();
michael@0 2390
michael@0 2391 return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv);
michael@0 2392 }
michael@0 2393
michael@0 2394 /***************************************************************************/
michael@0 2395 // interface methods
michael@0 2396
michael@0 2397 /* JSObjectPtr GetJSObject(); */
michael@0 2398 JSObject*
michael@0 2399 XPCWrappedNative::GetJSObject()
michael@0 2400 {
michael@0 2401 return GetFlatJSObject();
michael@0 2402 }
michael@0 2403
michael@0 2404 /* readonly attribute nsISupports Native; */
michael@0 2405 NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
michael@0 2406 {
michael@0 2407 // No need to QI here, we already have the correct nsISupports
michael@0 2408 // vtable.
michael@0 2409 nsCOMPtr<nsISupports> rval = mIdentity;
michael@0 2410 rval.forget(aNative);
michael@0 2411 return NS_OK;
michael@0 2412 }
michael@0 2413
michael@0 2414 /* reaonly attribute JSObjectPtr JSObjectPrototype; */
michael@0 2415 NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
michael@0 2416 {
michael@0 2417 *aJSObjectPrototype = HasProto() ?
michael@0 2418 GetProto()->GetJSProtoObject() : GetFlatJSObject();
michael@0 2419 return NS_OK;
michael@0 2420 }
michael@0 2421
michael@0 2422 nsIPrincipal*
michael@0 2423 XPCWrappedNative::GetObjectPrincipal() const
michael@0 2424 {
michael@0 2425 nsIPrincipal* principal = GetScope()->GetPrincipal();
michael@0 2426 #ifdef DEBUG
michael@0 2427 // Because of inner window reuse, we can have objects with one principal
michael@0 2428 // living in a scope with a different (but same-origin) principal. So
michael@0 2429 // just check same-origin here.
michael@0 2430 nsCOMPtr<nsIScriptObjectPrincipal> objPrin(do_QueryInterface(mIdentity));
michael@0 2431 if (objPrin) {
michael@0 2432 bool equal;
michael@0 2433 if (!principal)
michael@0 2434 equal = !objPrin->GetPrincipal();
michael@0 2435 else
michael@0 2436 principal->Equals(objPrin->GetPrincipal(), &equal);
michael@0 2437 MOZ_ASSERT(equal, "Principal mismatch. Expect bad things to happen");
michael@0 2438 }
michael@0 2439 #endif
michael@0 2440 return principal;
michael@0 2441 }
michael@0 2442
michael@0 2443 /* XPCNativeInterface FindInterfaceWithMember (in JSHandleId name); */
michael@0 2444 NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name,
michael@0 2445 nsIInterfaceInfo * *_retval)
michael@0 2446 {
michael@0 2447 XPCNativeInterface* iface;
michael@0 2448 XPCNativeMember* member;
michael@0 2449
michael@0 2450 if (GetSet()->FindMember(name, &member, &iface) && iface) {
michael@0 2451 nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
michael@0 2452 temp.forget(_retval);
michael@0 2453 } else
michael@0 2454 *_retval = nullptr;
michael@0 2455 return NS_OK;
michael@0 2456 }
michael@0 2457
michael@0 2458 /* XPCNativeInterface FindInterfaceWithName (in JSHandleId name); */
michael@0 2459 NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name,
michael@0 2460 nsIInterfaceInfo * *_retval)
michael@0 2461 {
michael@0 2462 XPCNativeInterface* iface = GetSet()->FindNamedInterface(name);
michael@0 2463 if (iface) {
michael@0 2464 nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
michael@0 2465 temp.forget(_retval);
michael@0 2466 } else
michael@0 2467 *_retval = nullptr;
michael@0 2468 return NS_OK;
michael@0 2469 }
michael@0 2470
michael@0 2471 /* [notxpcom] bool HasNativeMember (in JSHandleId name); */
michael@0 2472 NS_IMETHODIMP_(bool)
michael@0 2473 XPCWrappedNative::HasNativeMember(HandleId name)
michael@0 2474 {
michael@0 2475 XPCNativeMember *member = nullptr;
michael@0 2476 uint16_t ignored;
michael@0 2477 return GetSet()->FindMember(name, &member, &ignored) && !!member;
michael@0 2478 }
michael@0 2479
michael@0 2480 /* void finishInitForWrappedGlobal (); */
michael@0 2481 NS_IMETHODIMP XPCWrappedNative::FinishInitForWrappedGlobal()
michael@0 2482 {
michael@0 2483 // We can only be called under certain conditions.
michael@0 2484 MOZ_ASSERT(mScriptableInfo);
michael@0 2485 MOZ_ASSERT(mScriptableInfo->GetFlags().IsGlobalObject());
michael@0 2486 MOZ_ASSERT(HasProto());
michael@0 2487
michael@0 2488 // Call PostCreateProrotype.
michael@0 2489 bool success = GetProto()->CallPostCreatePrototype();
michael@0 2490 if (!success)
michael@0 2491 return NS_ERROR_FAILURE;
michael@0 2492
michael@0 2493 return NS_OK;
michael@0 2494 }
michael@0 2495
michael@0 2496 /* void debugDump (in short depth); */
michael@0 2497 NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
michael@0 2498 {
michael@0 2499 #ifdef DEBUG
michael@0 2500 depth-- ;
michael@0 2501 XPC_LOG_ALWAYS(("XPCWrappedNative @ %x with mRefCnt = %d", this, mRefCnt.get()));
michael@0 2502 XPC_LOG_INDENT();
michael@0 2503
michael@0 2504 if (HasProto()) {
michael@0 2505 XPCWrappedNativeProto* proto = GetProto();
michael@0 2506 if (depth && proto)
michael@0 2507 proto->DebugDump(depth);
michael@0 2508 else
michael@0 2509 XPC_LOG_ALWAYS(("mMaybeProto @ %x", proto));
michael@0 2510 } else
michael@0 2511 XPC_LOG_ALWAYS(("Scope @ %x", GetScope()));
michael@0 2512
michael@0 2513 if (depth && mSet)
michael@0 2514 mSet->DebugDump(depth);
michael@0 2515 else
michael@0 2516 XPC_LOG_ALWAYS(("mSet @ %x", mSet));
michael@0 2517
michael@0 2518 XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.getPtr()));
michael@0 2519 XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity));
michael@0 2520 XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));
michael@0 2521
michael@0 2522 if (depth && mScriptableInfo) {
michael@0 2523 XPC_LOG_INDENT();
michael@0 2524 XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptableInfo->GetCallback()));
michael@0 2525 XPC_LOG_ALWAYS(("mFlags of %x", (uint32_t)mScriptableInfo->GetFlags()));
michael@0 2526 XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass()));
michael@0 2527 XPC_LOG_OUTDENT();
michael@0 2528 }
michael@0 2529 XPC_LOG_OUTDENT();
michael@0 2530 #endif
michael@0 2531 return NS_OK;
michael@0 2532 }
michael@0 2533
michael@0 2534 /***************************************************************************/
michael@0 2535
michael@0 2536 char*
michael@0 2537 XPCWrappedNative::ToString(XPCWrappedNativeTearOff* to /* = nullptr */ ) const
michael@0 2538 {
michael@0 2539 #ifdef DEBUG
michael@0 2540 # define FMT_ADDR " @ 0x%p"
michael@0 2541 # define FMT_STR(str) str
michael@0 2542 # define PARAM_ADDR(w) , w
michael@0 2543 #else
michael@0 2544 # define FMT_ADDR ""
michael@0 2545 # define FMT_STR(str)
michael@0 2546 # define PARAM_ADDR(w)
michael@0 2547 #endif
michael@0 2548
michael@0 2549 char* sz = nullptr;
michael@0 2550 char* name = nullptr;
michael@0 2551
michael@0 2552 XPCNativeScriptableInfo* si = GetScriptableInfo();
michael@0 2553 if (si)
michael@0 2554 name = JS_smprintf("%s", si->GetJSClass()->name);
michael@0 2555 if (to) {
michael@0 2556 const char* fmt = name ? " (%s)" : "%s";
michael@0 2557 name = JS_sprintf_append(name, fmt,
michael@0 2558 to->GetInterface()->GetNameString());
michael@0 2559 } else if (!name) {
michael@0 2560 XPCNativeSet* set = GetSet();
michael@0 2561 XPCNativeInterface** array = set->GetInterfaceArray();
michael@0 2562 uint16_t count = set->GetInterfaceCount();
michael@0 2563
michael@0 2564 if (count == 1)
michael@0 2565 name = JS_sprintf_append(name, "%s", array[0]->GetNameString());
michael@0 2566 else if (count == 2 &&
michael@0 2567 array[0] == XPCNativeInterface::GetISupports()) {
michael@0 2568 name = JS_sprintf_append(name, "%s", array[1]->GetNameString());
michael@0 2569 } else {
michael@0 2570 for (uint16_t i = 0; i < count; i++) {
michael@0 2571 const char* fmt = (i == 0) ?
michael@0 2572 "(%s" : (i == count-1) ?
michael@0 2573 ", %s)" : ", %s";
michael@0 2574 name = JS_sprintf_append(name, fmt,
michael@0 2575 array[i]->GetNameString());
michael@0 2576 }
michael@0 2577 }
michael@0 2578 }
michael@0 2579
michael@0 2580 if (!name) {
michael@0 2581 return nullptr;
michael@0 2582 }
michael@0 2583 const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
michael@0 2584 FMT_ADDR FMT_STR(")") "]";
michael@0 2585 if (si) {
michael@0 2586 fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
michael@0 2587 }
michael@0 2588 sz = JS_smprintf(fmt, name PARAM_ADDR(this) PARAM_ADDR(mIdentity));
michael@0 2589
michael@0 2590 JS_smprintf_free(name);
michael@0 2591
michael@0 2592
michael@0 2593 return sz;
michael@0 2594
michael@0 2595 #undef FMT_ADDR
michael@0 2596 #undef PARAM_ADDR
michael@0 2597 }
michael@0 2598
michael@0 2599 /***************************************************************************/
michael@0 2600
michael@0 2601 #ifdef XPC_CHECK_CLASSINFO_CLAIMS
michael@0 2602 static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper)
michael@0 2603 {
michael@0 2604 if (!wrapper || !wrapper->GetClassInfo())
michael@0 2605 return;
michael@0 2606
michael@0 2607 nsISupports* obj = wrapper->GetIdentityObject();
michael@0 2608 XPCNativeSet* set = wrapper->GetSet();
michael@0 2609 uint16_t count = set->GetInterfaceCount();
michael@0 2610 for (uint16_t i = 0; i < count; i++) {
michael@0 2611 nsIClassInfo* clsInfo = wrapper->GetClassInfo();
michael@0 2612 XPCNativeInterface* iface = set->GetInterfaceAt(i);
michael@0 2613 nsIInterfaceInfo* info = iface->GetInterfaceInfo();
michael@0 2614 const nsIID* iid;
michael@0 2615 nsISupports* ptr;
michael@0 2616
michael@0 2617 info->GetIIDShared(&iid);
michael@0 2618 nsresult rv = obj->QueryInterface(*iid, (void**)&ptr);
michael@0 2619 if (NS_SUCCEEDED(rv)) {
michael@0 2620 NS_RELEASE(ptr);
michael@0 2621 continue;
michael@0 2622 }
michael@0 2623 if (rv == NS_ERROR_OUT_OF_MEMORY)
michael@0 2624 continue;
michael@0 2625
michael@0 2626 // Houston, We have a problem...
michael@0 2627
michael@0 2628 char* className = nullptr;
michael@0 2629 char* contractID = nullptr;
michael@0 2630 const char* interfaceName;
michael@0 2631
michael@0 2632 info->GetNameShared(&interfaceName);
michael@0 2633 clsInfo->GetContractID(&contractID);
michael@0 2634 if (wrapper->GetScriptableInfo()) {
michael@0 2635 wrapper->GetScriptableInfo()->GetCallback()->
michael@0 2636 GetClassName(&className);
michael@0 2637 }
michael@0 2638
michael@0 2639
michael@0 2640 printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n"
michael@0 2641 " classname: %s \n"
michael@0 2642 " contractid: %s \n"
michael@0 2643 " unimplemented interface name: %s\n\n",
michael@0 2644 className ? className : "<unknown>",
michael@0 2645 contractID ? contractID : "<unknown>",
michael@0 2646 interfaceName);
michael@0 2647
michael@0 2648 if (className)
michael@0 2649 nsMemory::Free(className);
michael@0 2650 if (contractID)
michael@0 2651 nsMemory::Free(contractID);
michael@0 2652 }
michael@0 2653 }
michael@0 2654 #endif
michael@0 2655
michael@0 2656 NS_IMPL_ISUPPORTS(XPCJSObjectHolder, nsIXPConnectJSObjectHolder)
michael@0 2657
michael@0 2658 JSObject*
michael@0 2659 XPCJSObjectHolder::GetJSObject()
michael@0 2660 {
michael@0 2661 NS_PRECONDITION(mJSObj, "bad object state");
michael@0 2662 return mJSObj;
michael@0 2663 }
michael@0 2664
michael@0 2665 XPCJSObjectHolder::XPCJSObjectHolder(JSObject* obj)
michael@0 2666 : mJSObj(obj)
michael@0 2667 {
michael@0 2668 XPCJSRuntime::Get()->AddObjectHolderRoot(this);
michael@0 2669 }
michael@0 2670
michael@0 2671 XPCJSObjectHolder::~XPCJSObjectHolder()
michael@0 2672 {
michael@0 2673 RemoveFromRootSet();
michael@0 2674 }
michael@0 2675
michael@0 2676 void
michael@0 2677 XPCJSObjectHolder::TraceJS(JSTracer *trc)
michael@0 2678 {
michael@0 2679 trc->setTracingDetails(GetTraceName, this, 0);
michael@0 2680 JS_CallHeapObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
michael@0 2681 }
michael@0 2682
michael@0 2683 // static
michael@0 2684 void
michael@0 2685 XPCJSObjectHolder::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
michael@0 2686 {
michael@0 2687 JS_snprintf(buf, bufsize, "XPCJSObjectHolder[0x%p].mJSObj",
michael@0 2688 trc->debugPrintArg());
michael@0 2689 }
michael@0 2690
michael@0 2691 // static
michael@0 2692 XPCJSObjectHolder*
michael@0 2693 XPCJSObjectHolder::newHolder(JSObject* obj)
michael@0 2694 {
michael@0 2695 if (!obj) {
michael@0 2696 NS_ERROR("bad param");
michael@0 2697 return nullptr;
michael@0 2698 }
michael@0 2699 return new XPCJSObjectHolder(obj);
michael@0 2700 }

mercurial