js/xpconnect/src/XPCWrappedNative.cpp

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

mercurial