Line data Source code
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 "mozilla/jsipc/CrossProcessObjectWrappers.h"
11 : #include "nsWrapperCacheInlines.h"
12 : #include "XPCLog.h"
13 : #include "jsprf.h"
14 : #include "jsfriendapi.h"
15 : #include "AccessCheck.h"
16 : #include "WrapperFactory.h"
17 : #include "XrayWrapper.h"
18 :
19 : #include "nsContentUtils.h"
20 : #include "nsCycleCollectionNoteRootCallback.h"
21 :
22 : #include <stdint.h>
23 : #include "mozilla/DeferredFinalize.h"
24 : #include "mozilla/Likely.h"
25 : #include "mozilla/Unused.h"
26 : #include "mozilla/Sprintf.h"
27 : #include "mozilla/dom/BindingUtils.h"
28 : #include <algorithm>
29 :
30 : using namespace xpc;
31 : using namespace mozilla;
32 : using namespace mozilla::dom;
33 : using namespace JS;
34 :
35 : /***************************************************************************/
36 :
37 : NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
38 :
39 : // No need to unlink the JS objects: if the XPCWrappedNative is cycle
40 : // collected then its mFlatJSObject will be cycle collected too and
41 : // finalization of the mFlatJSObject will unlink the JS objects (see
42 : // XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
43 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCWrappedNative)
44 0 : tmp->ExpireWrapper();
45 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
46 :
47 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(XPCWrappedNative)
48 0 : if (!tmp->IsValid())
49 0 : return NS_OK;
50 :
51 0 : if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
52 : char name[72];
53 0 : nsCOMPtr<nsIXPCScriptable> scr = tmp->GetScriptable();
54 0 : if (scr)
55 : SprintfLiteral(name, "XPCWrappedNative (%s)",
56 0 : scr->GetJSClass()->name);
57 : else
58 0 : SprintfLiteral(name, "XPCWrappedNative");
59 :
60 0 : cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
61 : } else {
62 0 : NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get())
63 : }
64 :
65 0 : if (tmp->HasExternalReference()) {
66 :
67 : // If our refcount is > 1, our reference to the flat JS object is
68 : // considered "strong", and we're going to traverse it.
69 : //
70 : // If our refcount is <= 1, our reference to the flat JS object is
71 : // considered "weak", and we're *not* going to traverse it.
72 : //
73 : // This reasoning is in line with the slightly confusing lifecycle rules
74 : // for XPCWrappedNatives, described in a larger comment below and also
75 : // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
76 :
77 0 : JSObject* obj = tmp->GetFlatJSObjectPreserveColor();
78 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject");
79 0 : cb.NoteJSChild(JS::GCCellPtr(obj));
80 : }
81 :
82 : // XPCWrappedNative keeps its native object alive.
83 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity");
84 0 : cb.NoteXPCOMChild(tmp->GetIdentityObject());
85 :
86 0 : tmp->NoteTearoffs(cb);
87 :
88 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
89 :
90 : void
91 0 : XPCWrappedNative::Suspect(nsCycleCollectionNoteRootCallback& cb)
92 : {
93 0 : if (!IsValid() || IsWrapperExpired())
94 0 : return;
95 :
96 0 : MOZ_ASSERT(NS_IsMainThread(),
97 : "Suspecting wrapped natives from non-main thread");
98 :
99 : // Only record objects that might be part of a cycle as roots, unless
100 : // the callback wants all traces (a debug feature). Do this even if
101 : // the XPCWN doesn't own the JS reflector object in case the reflector
102 : // keeps alive other C++ things. This is safe because if the reflector
103 : // had died the reference from the XPCWN to it would have been cleared.
104 0 : JSObject* obj = GetFlatJSObjectPreserveColor();
105 0 : if (JS::ObjectIsMarkedGray(obj) || cb.WantAllTraces())
106 0 : cb.NoteJSRoot(obj);
107 : }
108 :
109 : void
110 0 : XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
111 : {
112 : // Tearoffs hold their native object alive. If their JS object hasn't been
113 : // finalized yet we'll note the edge between the JS object and the native
114 : // (see nsXPConnect::Traverse), but if their JS object has been finalized
115 : // then the tearoff is only reachable through the XPCWrappedNative, so we
116 : // record an edge here.
117 0 : for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
118 0 : JSObject* jso = to->GetJSObjectPreserveColor();
119 0 : if (!jso) {
120 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
121 0 : cb.NoteXPCOMChild(to->GetNative());
122 : }
123 : }
124 0 : }
125 :
126 : #ifdef XPC_CHECK_CLASSINFO_CLAIMS
127 : static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper);
128 : #else
129 : #define DEBUG_CheckClassInfoClaims(wrapper) ((void)0)
130 : #endif
131 :
132 : /***************************************************************************/
133 : static nsresult
134 : FinishCreate(XPCWrappedNativeScope* Scope,
135 : XPCNativeInterface* Interface,
136 : nsWrapperCache* cache,
137 : XPCWrappedNative* inWrapper,
138 : XPCWrappedNative** resultWrapper);
139 :
140 : // static
141 : //
142 : // This method handles the special case of wrapping a new global object.
143 : //
144 : // The normal code path for wrapping natives goes through
145 : // XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed,
146 : // and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
147 : // very early on that we have an XPCWrappedNativeScope and corresponding global
148 : // JS object, which are the very things we need to create here. So we special-
149 : // case the logic and do some things in a different order.
150 : nsresult
151 260 : XPCWrappedNative::WrapNewGlobal(xpcObjectHelper& nativeHelper,
152 : nsIPrincipal* principal,
153 : bool initStandardClasses,
154 : JS::CompartmentOptions& aOptions,
155 : XPCWrappedNative** wrappedGlobal)
156 : {
157 520 : AutoJSContext cx;
158 260 : nsISupports* identity = nativeHelper.GetCanonical();
159 :
160 : // The object should specify that it's meant to be global.
161 260 : MOZ_ASSERT(nativeHelper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT);
162 :
163 : // We shouldn't be reusing globals.
164 260 : MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
165 : !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
166 :
167 : // Get the nsIXPCScriptable. This will tell us the JSClass of the object
168 : // we're going to create.
169 520 : nsCOMPtr<nsIXPCScriptable> scrProto;
170 520 : nsCOMPtr<nsIXPCScriptable> scrWrapper;
171 520 : GatherScriptable(identity, nativeHelper.GetClassInfo(),
172 780 : getter_AddRefs(scrProto), getter_AddRefs(scrWrapper));
173 260 : MOZ_ASSERT(scrWrapper);
174 :
175 : // Finally, we get to the JSClass.
176 260 : const JSClass* clasp = scrWrapper->GetJSClass();
177 260 : MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
178 :
179 : // Create the global.
180 260 : aOptions.creationOptions().setTrace(XPCWrappedNative::Trace);
181 260 : if (xpc::SharedMemoryEnabled())
182 260 : aOptions.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
183 520 : RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
184 260 : if (!global)
185 0 : return NS_ERROR_FAILURE;
186 260 : XPCWrappedNativeScope* scope = CompartmentPrivate::Get(global)->scope;
187 :
188 : // Immediately enter the global's compartment, so that everything else we
189 : // create ends up there.
190 520 : JSAutoCompartment ac(cx, global);
191 :
192 : // If requested, initialize the standard classes on the global.
193 260 : if (initStandardClasses && ! JS_InitStandardClasses(cx, global))
194 0 : return NS_ERROR_FAILURE;
195 :
196 : // Make a proto.
197 : XPCWrappedNativeProto* proto =
198 260 : XPCWrappedNativeProto::GetNewOrUsed(scope,
199 : nativeHelper.GetClassInfo(),
200 : scrProto,
201 260 : /* callPostCreatePrototype = */ false);
202 260 : if (!proto)
203 0 : return NS_ERROR_FAILURE;
204 :
205 : // Set up the prototype on the global.
206 260 : MOZ_ASSERT(proto->GetJSProtoObject());
207 520 : RootedObject protoObj(cx, proto->GetJSProtoObject());
208 260 : bool success = JS_SplicePrototype(cx, global, protoObj);
209 260 : if (!success)
210 0 : return NS_ERROR_FAILURE;
211 :
212 : // Construct the wrapper, which takes over the strong reference to the
213 : // native object.
214 : RefPtr<XPCWrappedNative> wrapper =
215 780 : new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
216 :
217 : //
218 : // We don't call ::Init() on this wrapper, because our setup requirements
219 : // are different for globals. We do our setup inline here, instead.
220 : //
221 :
222 260 : wrapper->mScriptable = scrWrapper;
223 :
224 : // Set the JS object to the global we already created.
225 260 : wrapper->mFlatJSObject = global;
226 260 : wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
227 :
228 : // Set the private to the XPCWrappedNative.
229 260 : JS_SetPrivate(global, wrapper);
230 :
231 : // There are dire comments elsewhere in the code about how a GC can
232 : // happen somewhere after wrapper initialization but before the wrapper is
233 : // added to the hashtable in FinishCreate(). It's not clear if that can
234 : // happen here, but let's just be safe for now.
235 520 : AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
236 :
237 : // Call the common Init finish routine. This mainly just does an AddRef
238 : // on behalf of XPConnect (the corresponding Release is in the finalizer
239 : // hook), but it does some other miscellaneous things too, so we don't
240 : // inline it.
241 260 : success = wrapper->FinishInit();
242 260 : MOZ_ASSERT(success);
243 :
244 : // Go through some extra work to find the tearoff. This is kind of silly
245 : // on a conceptual level: the point of tearoffs is to cache the results
246 : // of QI-ing mIdentity to different interfaces, and we don't need that
247 : // since we're dealing with nsISupports. But lots of code expects tearoffs
248 : // to exist for everything, so we just follow along.
249 520 : RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&NS_GET_IID(nsISupports));
250 260 : MOZ_ASSERT(iface);
251 : nsresult status;
252 260 : success = wrapper->FindTearOff(iface, false, &status);
253 260 : if (!success)
254 0 : return status;
255 :
256 : // Call the common creation finish routine. This does all of the bookkeeping
257 : // like inserting the wrapper into the wrapper map and setting up the wrapper
258 : // cache.
259 260 : nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(),
260 260 : wrapper, wrappedGlobal);
261 260 : NS_ENSURE_SUCCESS(rv, rv);
262 :
263 260 : return NS_OK;
264 : }
265 :
266 : // static
267 : nsresult
268 8007 : XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
269 : XPCWrappedNativeScope* Scope,
270 : XPCNativeInterface* Interface,
271 : XPCWrappedNative** resultWrapper)
272 : {
273 8007 : MOZ_ASSERT(Interface);
274 16014 : AutoJSContext cx;
275 8007 : nsWrapperCache* cache = helper.GetWrapperCache();
276 :
277 8007 : MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
278 : "We assume the caller already checked if it could get the "
279 : "wrapper from the cache.");
280 :
281 : nsresult rv;
282 :
283 8007 : MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
284 : "XPCWrappedNative::GetNewOrUsed called during GC");
285 :
286 8007 : nsISupports* identity = helper.GetCanonical();
287 :
288 8007 : if (!identity) {
289 0 : NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
290 0 : return NS_ERROR_FAILURE;
291 : }
292 :
293 16014 : RefPtr<XPCWrappedNative> wrapper;
294 :
295 8007 : Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
296 : // Some things are nsWrapperCache subclasses but never use the cache, so go
297 : // ahead and check our map even if we have a cache and it has no existing
298 : // wrapper: we might have an XPCWrappedNative anyway.
299 8007 : wrapper = map->Find(identity);
300 :
301 8007 : if (wrapper) {
302 2556 : if (!wrapper->FindTearOff(Interface, false, &rv)) {
303 0 : MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
304 0 : return rv;
305 : }
306 2556 : wrapper.forget(resultWrapper);
307 2556 : return NS_OK;
308 : }
309 :
310 : // There is a chance that the object wants to have the self-same JSObject
311 : // reflection regardless of the scope into which we are reflecting it.
312 : // Many DOM objects require this. The scriptable helper specifies this
313 : // in preCreate by indicating a 'parent' of a particular scope.
314 : //
315 : // To handle this we need to get the scriptable helper early and ask it.
316 : // It is possible that we will then end up forwarding this entire call
317 : // to this same function but with a different scope.
318 :
319 : // If we are making a wrapper for an nsIClassInfo singleton then
320 : // We *don't* want to have it use the prototype meant for instances
321 : // of that class.
322 : uint32_t classInfoFlags;
323 5451 : bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() &&
324 0 : NS_SUCCEEDED(helper.GetClassInfo()
325 5451 : ->GetFlags(&classInfoFlags)) &&
326 5451 : (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO);
327 :
328 5451 : nsIClassInfo* info = helper.GetClassInfo();
329 :
330 10902 : nsCOMPtr<nsIXPCScriptable> scrProto;
331 10902 : nsCOMPtr<nsIXPCScriptable> scrWrapper;
332 :
333 : // Gather scriptable create info if we are wrapping something
334 : // other than an nsIClassInfo object. We need to not do this for
335 : // nsIClassInfo objects because often nsIClassInfo implementations
336 : // are also nsIXPCScriptable helper implementations, but the helper
337 : // code is obviously intended for the implementation of the class
338 : // described by the nsIClassInfo, not for the class info object
339 : // itself.
340 5451 : if (!isClassInfoSingleton)
341 10902 : GatherScriptable(identity, info, getter_AddRefs(scrProto),
342 16353 : getter_AddRefs(scrWrapper));
343 :
344 10902 : RootedObject parent(cx, Scope->GetGlobalJSObject());
345 :
346 10902 : mozilla::Maybe<JSAutoCompartment> ac;
347 :
348 5451 : if (scrWrapper && scrWrapper->WantPreCreate()) {
349 664 : RootedObject plannedParent(cx, parent);
350 : nsresult rv =
351 332 : scrWrapper->PreCreate(identity, cx, parent, parent.address());
352 332 : if (NS_FAILED(rv))
353 0 : return rv;
354 332 : rv = NS_OK;
355 :
356 332 : MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
357 : "Xray wrapper being used to parent XPCWrappedNative?");
358 :
359 332 : MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(parent) == parent,
360 : "Non-global being used to parent XPCWrappedNative?");
361 :
362 332 : ac.emplace(static_cast<JSContext*>(cx), parent);
363 :
364 332 : if (parent != plannedParent) {
365 0 : XPCWrappedNativeScope* betterScope = ObjectScope(parent);
366 0 : MOZ_ASSERT(betterScope != Scope,
367 : "How can we have the same scope for two different globals?");
368 0 : return GetNewOrUsed(helper, betterScope, Interface, resultWrapper);
369 : }
370 :
371 : // Take the performance hit of checking the hashtable again in case
372 : // the preCreate call caused the wrapper to get created through some
373 : // interesting path (the DOM code tends to make this happen sometimes).
374 :
375 332 : if (cache) {
376 0 : RootedObject cached(cx, cache->GetWrapper());
377 0 : if (cached)
378 0 : wrapper = XPCWrappedNative::Get(cached);
379 : } else {
380 332 : wrapper = map->Find(identity);
381 : }
382 :
383 332 : if (wrapper) {
384 0 : if (wrapper->FindTearOff(Interface, false, &rv)) {
385 0 : MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
386 0 : return rv;
387 : }
388 0 : wrapper.forget(resultWrapper);
389 0 : return NS_OK;
390 : }
391 : } else {
392 5119 : ac.emplace(static_cast<JSContext*>(cx), parent);
393 : }
394 :
395 10902 : AutoMarkingWrappedNativeProtoPtr proto(cx);
396 :
397 : // If there is ClassInfo (and we are not building a wrapper for the
398 : // nsIClassInfo interface) then we use a wrapper that needs a prototype.
399 :
400 : // Note that the security check happens inside FindTearOff - after the
401 : // wrapper is actually created, but before JS code can see it.
402 :
403 5451 : if (info && !isClassInfoSingleton) {
404 4118 : proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, scrProto);
405 4118 : if (!proto)
406 0 : return NS_ERROR_FAILURE;
407 :
408 8236 : wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto);
409 : } else {
410 2666 : RefPtr<XPCNativeInterface> iface = Interface;
411 1333 : if (!iface)
412 0 : iface = XPCNativeInterface::GetISupports();
413 :
414 2666 : XPCNativeSetKey key(iface);
415 : RefPtr<XPCNativeSet> set =
416 2666 : XPCNativeSet::GetNewOrUsed(&key);
417 :
418 1333 : if (!set)
419 0 : return NS_ERROR_FAILURE;
420 :
421 2666 : wrapper = new XPCWrappedNative(helper.forgetCanonical(), Scope,
422 2666 : set.forget());
423 : }
424 :
425 5451 : MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
426 : "Xray wrapper being used to parent XPCWrappedNative?");
427 :
428 : // We use an AutoMarkingPtr here because it is possible for JS gc to happen
429 : // after we have Init'd the wrapper but *before* we add it to the hashtable.
430 : // This would cause the mSet to get collected and we'd later crash. I've
431 : // *seen* this happen.
432 10902 : AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
433 :
434 5451 : if (!wrapper->Init(scrWrapper))
435 0 : return NS_ERROR_FAILURE;
436 :
437 5451 : if (!wrapper->FindTearOff(Interface, false, &rv)) {
438 0 : MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
439 0 : return rv;
440 : }
441 :
442 5451 : return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper);
443 : }
444 :
445 : static nsresult
446 5711 : FinishCreate(XPCWrappedNativeScope* Scope,
447 : XPCNativeInterface* Interface,
448 : nsWrapperCache* cache,
449 : XPCWrappedNative* inWrapper,
450 : XPCWrappedNative** resultWrapper)
451 : {
452 11422 : AutoJSContext cx;
453 5711 : MOZ_ASSERT(inWrapper);
454 :
455 5711 : Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
456 :
457 11422 : RefPtr<XPCWrappedNative> wrapper;
458 : // Deal with the case where the wrapper got created as a side effect
459 : // of one of our calls out of this code. Add() returns the (possibly
460 : // pre-existing) wrapper that ultimately ends up in the map, which is
461 : // what we want.
462 5711 : wrapper = map->Add(inWrapper);
463 5711 : if (!wrapper)
464 0 : return NS_ERROR_FAILURE;
465 :
466 5711 : if (wrapper == inWrapper) {
467 5711 : JSObject* flat = wrapper->GetFlatJSObject();
468 5711 : MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() ||
469 : flat == cache->GetWrapperPreserveColor(),
470 : "This object has a cached wrapper that's different from "
471 : "the JSObject held by its native wrapper?");
472 :
473 5711 : if (cache && !cache->GetWrapperPreserveColor())
474 5 : cache->SetWrapper(flat);
475 : }
476 :
477 : DEBUG_CheckClassInfoClaims(wrapper);
478 5711 : wrapper.forget(resultWrapper);
479 5711 : return NS_OK;
480 : }
481 :
482 : // static
483 : nsresult
484 0 : XPCWrappedNative::GetUsedOnly(nsISupports* Object,
485 : XPCWrappedNativeScope* Scope,
486 : XPCNativeInterface* Interface,
487 : XPCWrappedNative** resultWrapper)
488 : {
489 0 : AutoJSContext cx;
490 0 : MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object");
491 0 : MOZ_ASSERT(Interface);
492 :
493 0 : RefPtr<XPCWrappedNative> wrapper;
494 0 : nsWrapperCache* cache = nullptr;
495 0 : CallQueryInterface(Object, &cache);
496 0 : if (cache) {
497 0 : RootedObject flat(cx, cache->GetWrapper());
498 0 : if (!flat) {
499 0 : *resultWrapper = nullptr;
500 0 : return NS_OK;
501 : }
502 0 : wrapper = XPCWrappedNative::Get(flat);
503 : } else {
504 0 : nsCOMPtr<nsISupports> identity = do_QueryInterface(Object);
505 :
506 0 : if (!identity) {
507 0 : NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
508 0 : return NS_ERROR_FAILURE;
509 : }
510 :
511 0 : Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
512 :
513 0 : wrapper = map->Find(identity);
514 0 : if (!wrapper) {
515 0 : *resultWrapper = nullptr;
516 0 : return NS_OK;
517 : }
518 : }
519 :
520 : nsresult rv;
521 0 : if (!wrapper->FindTearOff(Interface, false, &rv)) {
522 0 : MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
523 0 : return rv;
524 : }
525 :
526 0 : wrapper.forget(resultWrapper);
527 0 : return NS_OK;
528 : }
529 :
530 : // This ctor is used if this object will have a proto.
531 4378 : XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
532 4378 : XPCWrappedNativeProto* aProto)
533 : : mMaybeProto(aProto),
534 4378 : mSet(aProto->GetSet())
535 : {
536 4378 : MOZ_ASSERT(NS_IsMainThread());
537 :
538 4378 : mIdentity = aIdentity;
539 4378 : mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
540 :
541 4378 : MOZ_ASSERT(mMaybeProto, "bad ctor param");
542 4378 : MOZ_ASSERT(mSet, "bad ctor param");
543 4378 : }
544 :
545 : // This ctor is used if this object will NOT have a proto.
546 1333 : XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
547 : XPCWrappedNativeScope* aScope,
548 1333 : already_AddRefed<XPCNativeSet>&& aSet)
549 :
550 1333 : : mMaybeScope(TagScope(aScope)),
551 2666 : mSet(aSet)
552 : {
553 1333 : MOZ_ASSERT(NS_IsMainThread());
554 :
555 1333 : mIdentity = aIdentity;
556 1333 : mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
557 :
558 1333 : MOZ_ASSERT(aScope, "bad ctor param");
559 1333 : MOZ_ASSERT(mSet, "bad ctor param");
560 1333 : }
561 :
562 0 : XPCWrappedNative::~XPCWrappedNative()
563 : {
564 0 : Destroy();
565 0 : }
566 :
567 : void
568 0 : XPCWrappedNative::Destroy()
569 : {
570 0 : mScriptable = nullptr;
571 :
572 : #ifdef DEBUG
573 : // Check that this object has already been swept from the map.
574 0 : XPCWrappedNativeScope* scope = GetScope();
575 0 : if (scope) {
576 0 : Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
577 0 : MOZ_ASSERT(map->Find(GetIdentityObject()) != this);
578 : }
579 : #endif
580 :
581 0 : if (mIdentity) {
582 0 : XPCJSRuntime* rt = GetRuntime();
583 0 : if (rt && rt->GetDoingFinalization()) {
584 0 : DeferredFinalize(mIdentity.forget().take());
585 : } else {
586 0 : mIdentity = nullptr;
587 : }
588 : }
589 :
590 0 : mMaybeScope = nullptr;
591 0 : }
592 :
593 : // This is factored out so that it can be called publicly.
594 : // static
595 : nsIXPCScriptable*
596 4378 : XPCWrappedNative::GatherProtoScriptable(nsIClassInfo* classInfo)
597 : {
598 4378 : MOZ_ASSERT(classInfo, "bad param");
599 :
600 4378 : nsXPCClassInfo* classInfoHelper = nullptr;
601 4378 : CallQueryInterface(classInfo, &classInfoHelper);
602 4378 : if (classInfoHelper) {
603 : nsCOMPtr<nsIXPCScriptable> helper =
604 116 : dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper));
605 58 : return helper;
606 : }
607 :
608 8640 : nsCOMPtr<nsIXPCScriptable> helper;
609 4320 : nsresult rv = classInfo->GetScriptableHelper(getter_AddRefs(helper));
610 4320 : if (NS_SUCCEEDED(rv) && helper) {
611 1541 : return helper;
612 : }
613 :
614 2779 : return nullptr;
615 : }
616 :
617 : // static
618 : void
619 5711 : XPCWrappedNative::GatherScriptable(nsISupports* aObj,
620 : nsIClassInfo* aClassInfo,
621 : nsIXPCScriptable** aScrProto,
622 : nsIXPCScriptable** aScrWrapper)
623 : {
624 5711 : MOZ_ASSERT(!*aScrProto, "bad param");
625 5711 : MOZ_ASSERT(!*aScrWrapper, "bad param");
626 :
627 11364 : nsCOMPtr<nsIXPCScriptable> scrProto;
628 11364 : nsCOMPtr<nsIXPCScriptable> scrWrapper;
629 :
630 : // Get the class scriptable helper (if present)
631 5711 : if (aClassInfo) {
632 4378 : scrProto = GatherProtoScriptable(aClassInfo);
633 :
634 4378 : if (scrProto && scrProto->DontAskInstanceForScriptable()) {
635 58 : scrWrapper = scrProto;
636 58 : scrProto.forget(aScrProto);
637 58 : scrWrapper.forget(aScrWrapper);
638 58 : return;
639 : }
640 : }
641 :
642 : // Do the same for the wrapper specific scriptable
643 5653 : scrWrapper = do_QueryInterface(aObj);
644 5653 : if (scrWrapper) {
645 : // A whole series of assertions to catch bad uses of scriptable flags on
646 : // the scrWrapper...
647 :
648 : // Can't set WANT_PRECREATE on an instance scriptable without also
649 : // setting it on the class scriptable.
650 2396 : MOZ_ASSERT_IF(scrWrapper->WantPreCreate(),
651 : scrProto && scrProto->WantPreCreate());
652 :
653 : // Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable
654 : // without also setting it on the class scriptable (if present).
655 2396 : MOZ_ASSERT_IF(scrWrapper->DontEnumQueryInterface() && scrProto,
656 : scrProto->DontEnumQueryInterface());
657 :
658 : // Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable
659 : // without also setting it on the class scriptable.
660 2396 : MOZ_ASSERT_IF(scrWrapper->DontAskInstanceForScriptable(),
661 : scrProto && scrProto->DontAskInstanceForScriptable());
662 :
663 : // Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable
664 : // without also setting it on the class scriptable (if present).
665 2396 : MOZ_ASSERT_IF(scrWrapper->ClassInfoInterfacesOnly() && scrProto,
666 : scrProto->ClassInfoInterfacesOnly());
667 :
668 : // Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable
669 : // without also setting it on the class scriptable (if present).
670 2396 : MOZ_ASSERT_IF(scrWrapper->AllowPropModsDuringResolve() && scrProto,
671 : scrProto->AllowPropModsDuringResolve());
672 :
673 : // Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable
674 : // without also setting it on the class scriptable (if present).
675 2396 : MOZ_ASSERT_IF(scrWrapper->AllowPropModsToPrototype() && scrProto,
676 : scrProto->AllowPropModsToPrototype());
677 : } else {
678 3257 : scrWrapper = scrProto;
679 : }
680 :
681 5653 : scrProto.forget(aScrProto);
682 5653 : scrWrapper.forget(aScrWrapper);
683 : }
684 :
685 : bool
686 5451 : XPCWrappedNative::Init(nsIXPCScriptable* aScriptable)
687 : {
688 10902 : AutoJSContext cx;
689 :
690 : // Setup our scriptable...
691 5451 : MOZ_ASSERT(!mScriptable);
692 5451 : mScriptable = aScriptable;
693 :
694 : // create our flatJSObject
695 :
696 : const JSClass* jsclazz = mScriptable
697 5451 : ? mScriptable->GetJSClass()
698 10902 : : Jsvalify(&XPC_WN_NoHelper_JSClass);
699 :
700 : // We should have the global jsclass flag if and only if we're a global.
701 5451 : MOZ_ASSERT_IF(mScriptable, !!mScriptable->IsGlobalObject() ==
702 : !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
703 :
704 5451 : MOZ_ASSERT(jsclazz &&
705 : jsclazz->name &&
706 : jsclazz->flags &&
707 : jsclazz->getResolve() &&
708 : jsclazz->hasFinalize(), "bad class");
709 :
710 : // XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
711 : // that this object is same-compartment with cx, which means it could just
712 : // use the cx global...
713 10902 : RootedObject global(cx, CurrentGlobalOrNull(cx));
714 17686 : RootedObject protoJSObject(cx, HasProto() ?
715 4118 : GetProto()->GetJSProtoObject() :
716 19019 : JS_GetObjectPrototype(cx, global));
717 5451 : if (!protoJSObject) {
718 0 : return false;
719 : }
720 :
721 5451 : mFlatJSObject = JS_NewObjectWithGivenProto(cx, jsclazz, protoJSObject);
722 5451 : if (!mFlatJSObject) {
723 0 : mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
724 0 : return false;
725 : }
726 :
727 5451 : mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
728 5451 : JS_SetPrivate(mFlatJSObject, this);
729 :
730 5451 : return FinishInit();
731 : }
732 :
733 : bool
734 5711 : XPCWrappedNative::FinishInit()
735 : {
736 11422 : AutoJSContext cx;
737 :
738 : // This reference will be released when mFlatJSObject is finalized.
739 : // Since this reference will push the refcount to 2 it will also root
740 : // mFlatJSObject;
741 5711 : MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
742 5711 : NS_ADDREF(this);
743 :
744 : // A hack for bug 517665, increase the probability for GC.
745 5711 : JS_updateMallocCounter(cx, 2 * sizeof(XPCWrappedNative));
746 :
747 11422 : return true;
748 : }
749 :
750 :
751 9968 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative)
752 4257 : NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative)
753 264 : NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder)
754 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative)
755 0 : NS_INTERFACE_MAP_END
756 :
757 24025 : NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCWrappedNative)
758 :
759 : // Release calls Destroy() immediately when the refcount drops to 0 to
760 : // clear the weak references nsXPConnect has to XPCWNs and to ensure there
761 : // are no pointers to dying protos.
762 18312 : NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(XPCWrappedNative, Destroy())
763 :
764 : /*
765 : * Wrapped Native lifetime management is messy!
766 : *
767 : * - At creation we push the refcount to 2 (only one of which is owned by
768 : * the native caller that caused the wrapper creation).
769 : * - During the JS GC Mark phase we mark any wrapper with a refcount > 1.
770 : * - The *only* thing that can make the wrapper get destroyed is the
771 : * finalization of mFlatJSObject. And *that* should only happen if the only
772 : * reference is the single extra (internal) reference we hold.
773 : *
774 : * - The wrapper has a pointer to the nsISupports 'view' of the wrapped native
775 : * object i.e... mIdentity. This is held until the wrapper's refcount goes
776 : * to zero and the wrapper is released, or until an expired wrapper (i.e.,
777 : * one unlinked by the cycle collector) has had its JS object finalized.
778 : *
779 : * - The wrapper also has 'tearoffs'. It has one tearoff for each interface
780 : * that is actually used on the native object. 'Used' means we have either
781 : * needed to QueryInterface to verify the availability of that interface
782 : * of that we've had to QueryInterface in order to actually make a call
783 : * into the wrapped object via the pointer for the given interface.
784 : *
785 : * - Each tearoff's 'mNative' member (if non-null) indicates one reference
786 : * held by our wrapper on the wrapped native for the given interface
787 : * associated with the tearoff. If we release that reference then we set
788 : * the tearoff's 'mNative' to null.
789 : *
790 : * - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END
791 : * event to scan the tearoffs of all wrappers for non-null mNative members
792 : * that represent unused references. We can tell that a given tearoff's
793 : * mNative is unused by noting that no live XPCCallContexts hold a pointer
794 : * to the tearoff.
795 : *
796 : * - As a time/space tradeoff we may decide to not do this scanning on
797 : * *every* JavaScript GC. We *do* want to do this *sometimes* because
798 : * we want to allow for wrapped native's to do their own tearoff patterns.
799 : * So, we want to avoid holding references to interfaces that we don't need.
800 : * At the same time, we don't want to be bracketing every call into a
801 : * wrapped native object with a QueryInterface/Release pair. And we *never*
802 : * make a call into the object except via the correct interface for which
803 : * we've QI'd.
804 : *
805 : * - Each tearoff *can* have a mJSObject whose lazily resolved properties
806 : * represent the methods/attributes/constants of that specific interface.
807 : * This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo"
808 : * is the name of mFlatJSObject and "nsIFoo" is the name of the given
809 : * interface associated with the tearoff. When we create the tearoff's
810 : * mJSObject we set it's parent to be mFlatJSObject. This way we know that
811 : * when mFlatJSObject get's collected there are no outstanding reachable
812 : * tearoff mJSObjects. Note that we must clear the private of any lingering
813 : * mJSObjects at this point because we have no guarentee of the *order* of
814 : * finalization within a given gc cycle.
815 : */
816 :
817 : void
818 0 : XPCWrappedNative::FlatJSObjectFinalized()
819 : {
820 0 : if (!IsValid())
821 0 : return;
822 :
823 : // Iterate the tearoffs and null out each of their JSObject's privates.
824 : // This will keep them from trying to access their pointers to the
825 : // dying tearoff object. We can safely assume that those remaining
826 : // JSObjects are about to be finalized too.
827 :
828 0 : for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
829 0 : JSObject* jso = to->GetJSObjectPreserveColor();
830 0 : if (jso) {
831 0 : JS_SetPrivate(jso, nullptr);
832 : #ifdef DEBUG
833 0 : JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
834 0 : MOZ_ASSERT(!jso);
835 : #endif
836 0 : to->JSObjectFinalized();
837 : }
838 :
839 : // We also need to release any native pointers held...
840 0 : RefPtr<nsISupports> native = to->TakeNative();
841 0 : if (native && GetRuntime()) {
842 0 : DeferredFinalize(native.forget().take());
843 : }
844 :
845 0 : to->SetInterface(nullptr);
846 : }
847 :
848 0 : nsWrapperCache* cache = nullptr;
849 0 : CallQueryInterface(mIdentity, &cache);
850 0 : if (cache)
851 0 : cache->ClearWrapper(mFlatJSObject.unbarrieredGetPtr());
852 :
853 0 : mFlatJSObject = nullptr;
854 0 : mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
855 :
856 0 : MOZ_ASSERT(mIdentity, "bad pointer!");
857 : #ifdef XP_WIN
858 : // Try to detect free'd pointer
859 : MOZ_ASSERT(*(int*)mIdentity.get() != 0xdddddddd, "bad pointer!");
860 : MOZ_ASSERT(*(int*)mIdentity.get() != 0, "bad pointer!");
861 : #endif
862 :
863 0 : if (IsWrapperExpired()) {
864 0 : Destroy();
865 : }
866 :
867 : // Note that it's not safe to touch mNativeWrapper here since it's
868 : // likely that it has already been finalized.
869 :
870 0 : Release();
871 : }
872 :
873 : void
874 0 : XPCWrappedNative::FlatJSObjectMoved(JSObject* obj, const JSObject* old)
875 : {
876 0 : JS::AutoAssertGCCallback inCallback;
877 0 : MOZ_ASSERT(mFlatJSObject == old);
878 :
879 0 : nsWrapperCache* cache = nullptr;
880 0 : CallQueryInterface(mIdentity, &cache);
881 0 : if (cache)
882 0 : cache->UpdateWrapper(obj, old);
883 :
884 0 : mFlatJSObject = obj;
885 0 : }
886 :
887 : void
888 0 : XPCWrappedNative::SystemIsBeingShutDown()
889 : {
890 0 : if (!IsValid())
891 0 : return;
892 :
893 : // The long standing strategy is to leak some objects still held at shutdown.
894 : // The general problem is that propagating release out of xpconnect at
895 : // shutdown time causes a world of problems.
896 :
897 : // We leak mIdentity (see above).
898 :
899 : // Short circuit future finalization.
900 0 : JS_SetPrivate(mFlatJSObject, nullptr);
901 0 : mFlatJSObject = nullptr;
902 0 : mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
903 :
904 0 : XPCWrappedNativeProto* proto = GetProto();
905 :
906 0 : if (HasProto())
907 0 : proto->SystemIsBeingShutDown();
908 :
909 : // We don't clear mScriptable here. The destructor will do it.
910 :
911 : // Cleanup the tearoffs.
912 0 : for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
913 0 : if (JSObject* jso = to->GetJSObjectPreserveColor()) {
914 0 : JS_SetPrivate(jso, nullptr);
915 0 : to->SetJSObject(nullptr);
916 : }
917 : // We leak the tearoff mNative
918 : // (for the same reason we leak mIdentity - see above).
919 0 : Unused << to->TakeNative().take();
920 0 : to->SetInterface(nullptr);
921 : }
922 : }
923 :
924 : /***************************************************************************/
925 :
926 : // Dynamically ensure that two objects don't end up with the same private.
927 : class MOZ_STACK_CLASS AutoClonePrivateGuard {
928 : public:
929 : AutoClonePrivateGuard(JSContext* cx, JSObject* aOld, JSObject* aNew)
930 : : mOldReflector(cx, aOld), mNewReflector(cx, aNew)
931 : {
932 : MOZ_ASSERT(JS_GetPrivate(aOld) == JS_GetPrivate(aNew));
933 : }
934 :
935 : ~AutoClonePrivateGuard()
936 : {
937 : if (JS_GetPrivate(mOldReflector)) {
938 : JS_SetPrivate(mNewReflector, nullptr);
939 : }
940 : }
941 :
942 : private:
943 : RootedObject mOldReflector;
944 : RootedObject mNewReflector;
945 : };
946 :
947 : bool
948 233 : XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
949 : {
950 233 : if (!mSet->HasInterface(aInterface)) {
951 466 : XPCNativeSetKey key(mSet, aInterface);
952 : RefPtr<XPCNativeSet> newSet =
953 466 : XPCNativeSet::GetNewOrUsed(&key);
954 233 : if (!newSet)
955 0 : return false;
956 :
957 233 : mSet = newSet.forget();
958 : }
959 233 : return true;
960 : }
961 :
962 : XPCWrappedNativeTearOff*
963 20332 : XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface,
964 : bool needJSObject /* = false */,
965 : nsresult* pError /* = nullptr */)
966 : {
967 40664 : AutoJSContext cx;
968 20332 : nsresult rv = NS_OK;
969 : XPCWrappedNativeTearOff* to;
970 20332 : XPCWrappedNativeTearOff* firstAvailable = nullptr;
971 :
972 : XPCWrappedNativeTearOff* lastTearOff;
973 38425 : for (lastTearOff = to = &mFirstTearOff;
974 38425 : to;
975 18093 : lastTearOff = to, to = to->GetNextTearOff()) {
976 30706 : if (to->GetInterface() == aInterface) {
977 12613 : if (needJSObject && !to->GetJSObjectPreserveColor()) {
978 0 : AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
979 0 : bool ok = InitTearOffJSObject(to);
980 : // During shutdown, we don't sweep tearoffs. So make sure
981 : // to unmark manually in case the auto-marker marked us.
982 : // We shouldn't ever be getting here _during_ our
983 : // Mark/Sweep cycle, so this should be safe.
984 0 : to->Unmark();
985 0 : if (!ok) {
986 0 : to = nullptr;
987 0 : rv = NS_ERROR_OUT_OF_MEMORY;
988 : }
989 : }
990 12613 : if (pError)
991 12574 : *pError = rv;
992 12613 : return to;
993 : }
994 18093 : if (!firstAvailable && to->IsAvailable())
995 5714 : firstAvailable = to;
996 : }
997 :
998 7719 : to = firstAvailable;
999 :
1000 7719 : if (!to) {
1001 2005 : to = lastTearOff->AddTearOff();
1002 : }
1003 :
1004 : {
1005 : // Scope keeps |tearoff| from leaking across the rest of the function.
1006 15438 : AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
1007 7719 : rv = InitTearOff(to, aInterface, needJSObject);
1008 : // During shutdown, we don't sweep tearoffs. So make sure to unmark
1009 : // manually in case the auto-marker marked us. We shouldn't ever be
1010 : // getting here _during_ our Mark/Sweep cycle, so this should be safe.
1011 7719 : to->Unmark();
1012 7719 : if (NS_FAILED(rv))
1013 12 : to = nullptr;
1014 : }
1015 :
1016 7719 : if (pError)
1017 7689 : *pError = rv;
1018 7719 : return to;
1019 : }
1020 :
1021 : XPCWrappedNativeTearOff*
1022 69 : XPCWrappedNative::FindTearOff(const nsIID& iid) {
1023 138 : RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&iid);
1024 138 : return iface ? FindTearOff(iface) : nullptr;
1025 : }
1026 :
1027 : nsresult
1028 7719 : XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
1029 : XPCNativeInterface* aInterface,
1030 : bool needJSObject)
1031 : {
1032 15438 : AutoJSContext cx;
1033 :
1034 : // Determine if the object really does this interface...
1035 :
1036 7719 : const nsIID* iid = aInterface->GetIID();
1037 7719 : nsISupports* identity = GetIdentityObject();
1038 :
1039 : // This is an nsRefPtr instead of an nsCOMPtr because it may not be the
1040 : // canonical nsISupports for this object.
1041 15438 : RefPtr<nsISupports> qiResult;
1042 :
1043 : // If the scriptable helper forbids us from reflecting additional
1044 : // interfaces, then don't even try the QI, just fail.
1045 11160 : if (mScriptable &&
1046 11296 : mScriptable->ClassInfoInterfacesOnly() &&
1047 7858 : !mSet->HasInterface(aInterface) &&
1048 3 : !mSet->HasInterfaceWithAncestor(aInterface)) {
1049 3 : return NS_ERROR_NO_INTERFACE;
1050 : }
1051 :
1052 : // We are about to call out to other code.
1053 : // So protect our intended tearoff.
1054 :
1055 7716 : aTearOff->SetReserved();
1056 :
1057 7716 : if (NS_FAILED(identity->QueryInterface(*iid, getter_AddRefs(qiResult))) || !qiResult) {
1058 9 : aTearOff->SetInterface(nullptr);
1059 9 : return NS_ERROR_NO_INTERFACE;
1060 : }
1061 :
1062 : // Guard against trying to build a tearoff for a shared nsIClassInfo.
1063 7707 : if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
1064 0 : nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(qiResult));
1065 0 : if (alternate_identity.get() != identity) {
1066 0 : aTearOff->SetInterface(nullptr);
1067 0 : return NS_ERROR_NO_INTERFACE;
1068 : }
1069 : }
1070 :
1071 : // Guard against trying to build a tearoff for an interface that is
1072 : // aggregated and is implemented as a nsIXPConnectWrappedJS using this
1073 : // self-same JSObject. The XBL system does this. If we mutate the set
1074 : // of this wrapper then we will shadow the method that XBL has added to
1075 : // the JSObject that it has inserted in the JS proto chain between our
1076 : // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
1077 : // set mutation happen then the interface's methods will be added to
1078 : // our JSObject, but calls on those methods will get routed up to
1079 : // native code and into the wrappedJS - which will do a method lookup
1080 : // on *our* JSObject and find the same method and make another call
1081 : // into an infinite loop.
1082 : // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
1083 :
1084 : // The code in this block also does a check for the double wrapped
1085 : // nsIPropertyBag case.
1086 :
1087 15414 : nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(qiResult));
1088 7707 : if (wrappedJS) {
1089 166 : RootedObject jso(cx, wrappedJS->GetJSObject());
1090 83 : if (jso == mFlatJSObject) {
1091 : // The implementing JSObject is the same as ours! Just say OK
1092 : // without actually extending the set.
1093 : //
1094 : // XXX It is a little cheesy to have FindTearOff return an
1095 : // 'empty' tearoff. But this is the centralized place to do the
1096 : // QI activities on the underlying object. *And* most caller to
1097 : // FindTearOff only look for a non-null result and ignore the
1098 : // actual tearoff returned. The only callers that do use the
1099 : // returned tearoff make sure to check for either a non-null
1100 : // JSObject or a matching Interface before proceeding.
1101 : // I think we can get away with this bit of ugliness.
1102 :
1103 0 : aTearOff->SetInterface(nullptr);
1104 0 : return NS_OK;
1105 : }
1106 :
1107 : // Decide whether or not to expose nsIPropertyBag to calling
1108 : // JS code in the double wrapped case.
1109 : //
1110 : // Our rule here is that when JSObjects are double wrapped and
1111 : // exposed to other JSObjects then the nsIPropertyBag interface
1112 : // is only exposed on an 'opt-in' basis; i.e. if the underlying
1113 : // JSObject wants other JSObjects to be able to see this interface
1114 : // then it must implement QueryInterface and not throw an exception
1115 : // when asked for nsIPropertyBag. It need not actually *implement*
1116 : // nsIPropertyBag - xpconnect will do that work.
1117 :
1118 83 : if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso) {
1119 0 : RefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, *iid);
1120 0 : if (clasp) {
1121 0 : RootedObject answer(cx, clasp->CallQueryInterfaceOnJSObject(cx, jso, *iid));
1122 :
1123 0 : if (!answer) {
1124 0 : aTearOff->SetInterface(nullptr);
1125 0 : return NS_ERROR_NO_INTERFACE;
1126 : }
1127 : }
1128 : }
1129 : }
1130 :
1131 7707 : if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateWrapper(cx, *iid, identity,
1132 : GetClassInfo()))) {
1133 : // the security manager vetoed. It should have set an exception.
1134 0 : aTearOff->SetInterface(nullptr);
1135 0 : return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
1136 : }
1137 :
1138 : // If this is not already in our set we need to extend our set.
1139 : // Note: we do not cache the result of the previous call to HasInterface()
1140 : // because we unlocked and called out in the interim and the result of the
1141 : // previous call might not be correct anymore.
1142 :
1143 7707 : if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) {
1144 0 : aTearOff->SetInterface(nullptr);
1145 0 : return NS_ERROR_NO_INTERFACE;
1146 : }
1147 :
1148 7707 : aTearOff->SetInterface(aInterface);
1149 7707 : aTearOff->SetNative(qiResult);
1150 7707 : if (needJSObject && !InitTearOffJSObject(aTearOff))
1151 0 : return NS_ERROR_OUT_OF_MEMORY;
1152 :
1153 7707 : return NS_OK;
1154 : }
1155 :
1156 : bool
1157 0 : XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to)
1158 : {
1159 0 : AutoJSContext cx;
1160 :
1161 0 : JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass));
1162 0 : if (!obj)
1163 0 : return false;
1164 :
1165 0 : JS_SetPrivate(obj, to);
1166 0 : to->SetJSObject(obj);
1167 :
1168 : js::SetReservedSlot(obj, XPC_WN_TEAROFF_FLAT_OBJECT_SLOT,
1169 0 : JS::ObjectValue(*mFlatJSObject));
1170 0 : return true;
1171 : }
1172 :
1173 : /***************************************************************************/
1174 :
1175 0 : static bool Throw(nsresult errNum, XPCCallContext& ccx)
1176 : {
1177 0 : XPCThrower::Throw(errNum, ccx);
1178 0 : return false;
1179 : }
1180 :
1181 : /***************************************************************************/
1182 :
1183 : class MOZ_STACK_CLASS CallMethodHelper
1184 : {
1185 : XPCCallContext& mCallContext;
1186 : nsresult mInvokeResult;
1187 : nsIInterfaceInfo* const mIFaceInfo;
1188 : const nsXPTMethodInfo* mMethodInfo;
1189 : nsISupports* const mCallee;
1190 : const uint16_t mVTableIndex;
1191 : HandleId mIdxValueId;
1192 :
1193 : AutoTArray<nsXPTCVariant, 8> mDispatchParams;
1194 : uint8_t mJSContextIndex; // TODO make const
1195 : uint8_t mOptArgcIndex; // TODO make const
1196 :
1197 : Value* const mArgv;
1198 : const uint32_t mArgc;
1199 :
1200 : MOZ_ALWAYS_INLINE bool
1201 : GetArraySizeFromParam(uint8_t paramIndex, HandleValue maybeArray, uint32_t* result);
1202 :
1203 : MOZ_ALWAYS_INLINE bool
1204 : GetInterfaceTypeFromParam(uint8_t paramIndex,
1205 : const nsXPTType& datum_type,
1206 : nsID* result) const;
1207 :
1208 : MOZ_ALWAYS_INLINE bool
1209 : GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
1210 :
1211 : MOZ_ALWAYS_INLINE bool
1212 : GatherAndConvertResults();
1213 :
1214 : MOZ_ALWAYS_INLINE bool
1215 : QueryInterfaceFastPath();
1216 :
1217 : nsXPTCVariant*
1218 58682 : GetDispatchParam(uint8_t paramIndex)
1219 : {
1220 58682 : if (paramIndex >= mJSContextIndex)
1221 4209 : paramIndex += 1;
1222 58682 : if (paramIndex >= mOptArgcIndex)
1223 5264 : paramIndex += 1;
1224 58682 : return &mDispatchParams[paramIndex];
1225 : }
1226 : const nsXPTCVariant*
1227 53 : GetDispatchParam(uint8_t paramIndex) const
1228 : {
1229 53 : return const_cast<CallMethodHelper*>(this)->GetDispatchParam(paramIndex);
1230 : }
1231 :
1232 : MOZ_ALWAYS_INLINE bool InitializeDispatchParams();
1233 :
1234 : MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam);
1235 : MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i);
1236 : MOZ_ALWAYS_INLINE bool ConvertDependentParams();
1237 : MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i);
1238 :
1239 : MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type);
1240 :
1241 : MOZ_ALWAYS_INLINE bool AllocateStringClass(nsXPTCVariant* dp,
1242 : const nsXPTParamInfo& paramInfo);
1243 :
1244 : MOZ_ALWAYS_INLINE nsresult Invoke();
1245 :
1246 : public:
1247 :
1248 11996 : explicit CallMethodHelper(XPCCallContext& ccx)
1249 11996 : : mCallContext(ccx)
1250 : , mInvokeResult(NS_ERROR_UNEXPECTED)
1251 11996 : , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
1252 : , mMethodInfo(nullptr)
1253 11996 : , mCallee(ccx.GetTearOff()->GetNative())
1254 11996 : , mVTableIndex(ccx.GetMethodIndex())
1255 11996 : , mIdxValueId(ccx.GetContext()->GetStringID(XPCJSContext::IDX_VALUE))
1256 : , mJSContextIndex(UINT8_MAX)
1257 : , mOptArgcIndex(UINT8_MAX)
1258 11996 : , mArgv(ccx.GetArgv())
1259 71976 : , mArgc(ccx.GetArgc())
1260 :
1261 : {
1262 : // Success checked later.
1263 11996 : mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
1264 11996 : }
1265 :
1266 : ~CallMethodHelper();
1267 :
1268 : MOZ_ALWAYS_INLINE bool Call();
1269 :
1270 : };
1271 :
1272 : // static
1273 : bool
1274 11996 : XPCWrappedNative::CallMethod(XPCCallContext& ccx,
1275 : CallMode mode /*= CALL_METHOD */)
1276 : {
1277 11996 : nsresult rv = ccx.CanCallNow();
1278 11996 : if (NS_FAILED(rv)) {
1279 0 : return Throw(rv, ccx);
1280 : }
1281 :
1282 11996 : return CallMethodHelper(ccx).Call();
1283 : }
1284 :
1285 : bool
1286 11996 : CallMethodHelper::Call()
1287 : {
1288 11996 : mCallContext.SetRetVal(JS::UndefinedValue());
1289 :
1290 11996 : mCallContext.GetContext()->SetPendingException(nullptr);
1291 :
1292 11996 : if (mVTableIndex == 0) {
1293 129 : return QueryInterfaceFastPath();
1294 : }
1295 :
1296 11867 : if (!mMethodInfo) {
1297 0 : Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext);
1298 0 : return false;
1299 : }
1300 :
1301 11867 : if (!InitializeDispatchParams())
1302 0 : return false;
1303 :
1304 : // Iterate through the params doing conversions of independent params only.
1305 : // When we later convert the dependent params (if any) we will know that
1306 : // the params upon which they depend will have already been converted -
1307 : // regardless of ordering.
1308 11867 : bool foundDependentParam = false;
1309 11867 : if (!ConvertIndependentParams(&foundDependentParam))
1310 0 : return false;
1311 :
1312 11867 : if (foundDependentParam && !ConvertDependentParams())
1313 0 : return false;
1314 :
1315 11867 : mInvokeResult = Invoke();
1316 :
1317 11866 : if (JS_IsExceptionPending(mCallContext)) {
1318 0 : return false;
1319 : }
1320 :
1321 11866 : if (NS_FAILED(mInvokeResult)) {
1322 1978 : ThrowBadResult(mInvokeResult, mCallContext);
1323 1978 : return false;
1324 : }
1325 :
1326 9888 : return GatherAndConvertResults();
1327 : }
1328 :
1329 23990 : CallMethodHelper::~CallMethodHelper()
1330 : {
1331 11995 : uint8_t paramCount = mMethodInfo->GetParamCount();
1332 11995 : if (mDispatchParams.Length()) {
1333 36882 : for (uint8_t i = 0; i < paramCount; i++) {
1334 25032 : nsXPTCVariant* dp = GetDispatchParam(i);
1335 25032 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
1336 :
1337 25032 : if (paramInfo.GetType().IsArray()) {
1338 32 : void* p = dp->val.p;
1339 32 : if (!p)
1340 5 : continue;
1341 :
1342 : // Clean up the array contents if necessary.
1343 27 : if (dp->DoesValNeedCleanup()) {
1344 : // We need some basic information to properly destroy the array.
1345 27 : uint32_t array_count = 0;
1346 27 : nsXPTType datum_type;
1347 54 : if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count) ||
1348 27 : !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
1349 : ¶mInfo,
1350 : 1, &datum_type))) {
1351 : // XXXbholley - I'm not convinced that the above calls will
1352 : // ever fail.
1353 0 : NS_ERROR("failed to get array information, we'll leak here");
1354 0 : continue;
1355 : }
1356 :
1357 : // Loop over the array contents. For each one, we create a
1358 : // dummy 'val' and pass it to the cleanup helper.
1359 369 : for (uint32_t k = 0; k < array_count; k++) {
1360 342 : nsXPTCMiniVariant v;
1361 342 : v.val.p = static_cast<void**>(p)[k];
1362 342 : CleanupParam(v, datum_type);
1363 : }
1364 : }
1365 :
1366 : // always free the array itself
1367 27 : free(p);
1368 : } else {
1369 : // Clean up single parameters (if requested).
1370 25000 : if (dp->DoesValNeedCleanup())
1371 18348 : CleanupParam(*dp, dp->type);
1372 : }
1373 : }
1374 : }
1375 11995 : }
1376 :
1377 : bool
1378 59 : CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
1379 : HandleValue maybeArray,
1380 : uint32_t* result)
1381 : {
1382 : nsresult rv;
1383 59 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
1384 :
1385 : // TODO fixup the various exceptions that are thrown
1386 :
1387 59 : rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, ¶mInfo, 0, ¶mIndex);
1388 59 : if (NS_FAILED(rv))
1389 0 : return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
1390 :
1391 : // If the array length wasn't passed, it might have been listed as optional.
1392 : // When converting arguments from JS to C++, we pass the array as |maybeArray|,
1393 : // and give ourselves the chance to infer the length. Once we have it, we stick
1394 : // it in the right slot so that we can find it again when cleaning up the params.
1395 : // from the array.
1396 59 : if (paramIndex >= mArgc && maybeArray.isObject()) {
1397 0 : MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional());
1398 0 : RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject()
1399 0 : : nullptr);
1400 :
1401 : bool isArray;
1402 0 : if (!JS_IsArrayObject(mCallContext, maybeArray, &isArray) ||
1403 0 : !isArray ||
1404 0 : !JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32))
1405 : {
1406 0 : return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext);
1407 : }
1408 : }
1409 :
1410 59 : *result = GetDispatchParam(paramIndex)->val.u32;
1411 :
1412 59 : return true;
1413 : }
1414 :
1415 : bool
1416 2240 : CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
1417 : const nsXPTType& datum_type,
1418 : nsID* result) const
1419 : {
1420 : nsresult rv;
1421 2240 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
1422 2240 : uint8_t tag = datum_type.TagPart();
1423 :
1424 : // TODO fixup the various exceptions that are thrown
1425 :
1426 2240 : if (tag == nsXPTType::T_INTERFACE) {
1427 2187 : rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, ¶mInfo, result);
1428 2187 : if (NS_FAILED(rv))
1429 0 : return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
1430 0 : paramIndex, mCallContext);
1431 53 : } else if (tag == nsXPTType::T_INTERFACE_IS) {
1432 106 : rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, ¶mInfo,
1433 106 : ¶mIndex);
1434 53 : if (NS_FAILED(rv))
1435 0 : return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
1436 :
1437 53 : nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p;
1438 53 : if (!p)
1439 0 : return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
1440 0 : paramIndex, mCallContext);
1441 53 : *result = *p;
1442 : }
1443 2240 : return true;
1444 : }
1445 :
1446 : bool
1447 24638 : CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const
1448 : {
1449 24638 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
1450 :
1451 24638 : MOZ_ASSERT(!paramInfo.IsDipper(), "Dipper params are handled separately");
1452 24638 : if (paramInfo.IsOut() && !paramInfo.IsRetval()) {
1453 37 : MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(),
1454 : "Expected either enough arguments or an optional argument");
1455 37 : Value arg = paramIndex < mArgc ? mArgv[paramIndex] : JS::NullValue();
1456 37 : if (paramIndex < mArgc) {
1457 18 : RootedObject obj(mCallContext);
1458 9 : if (!arg.isPrimitive())
1459 9 : obj = &arg.toObject();
1460 9 : if (!obj || !JS_GetPropertyById(mCallContext, obj, mIdxValueId, srcp)) {
1461 : // Explicitly passed in unusable value for out param. Note
1462 : // that if i >= mArgc we already know that |arg| is JS::NullValue(),
1463 : // and that's ok.
1464 0 : ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, paramIndex,
1465 0 : mCallContext);
1466 0 : return false;
1467 : }
1468 : }
1469 : }
1470 :
1471 24638 : return true;
1472 : }
1473 :
1474 : bool
1475 9888 : CallMethodHelper::GatherAndConvertResults()
1476 : {
1477 : // now we iterate through the native params to gather and convert results
1478 9888 : uint8_t paramCount = mMethodInfo->GetParamCount();
1479 29065 : for (uint8_t i = 0; i < paramCount; i++) {
1480 19177 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
1481 19177 : if (!paramInfo.IsOut() && !paramInfo.IsDipper())
1482 10671 : continue;
1483 :
1484 8506 : const nsXPTType& type = paramInfo.GetType();
1485 8506 : nsXPTCVariant* dp = GetDispatchParam(i);
1486 17012 : RootedValue v(mCallContext, NullValue());
1487 8506 : uint32_t array_count = 0;
1488 8506 : nsXPTType datum_type;
1489 8506 : bool isArray = type.IsArray();
1490 16987 : bool isSizedString = isArray ?
1491 : false :
1492 16962 : type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
1493 16987 : type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
1494 :
1495 8506 : if (isArray) {
1496 25 : if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, ¶mInfo, 1,
1497 : &datum_type))) {
1498 0 : Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
1499 0 : return false;
1500 : }
1501 : } else
1502 8481 : datum_type = type;
1503 :
1504 8506 : if (isArray || isSizedString) {
1505 25 : if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count))
1506 0 : return false;
1507 : }
1508 :
1509 : nsID param_iid;
1510 10743 : if (datum_type.IsInterfacePointer() &&
1511 2237 : !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid))
1512 0 : return false;
1513 :
1514 : nsresult err;
1515 8506 : if (isArray) {
1516 25 : if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val,
1517 : datum_type, ¶m_iid,
1518 : array_count, &err)) {
1519 : // XXX need exception scheme for arrays to indicate bad element
1520 0 : ThrowBadParam(err, i, mCallContext);
1521 0 : return false;
1522 : }
1523 8481 : } else if (isSizedString) {
1524 0 : if (!XPCConvert::NativeStringWithSize2JS(&v,
1525 0 : (const void*)&dp->val,
1526 : datum_type,
1527 : array_count, &err)) {
1528 0 : ThrowBadParam(err, i, mCallContext);
1529 0 : return false;
1530 : }
1531 : } else {
1532 8481 : if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type,
1533 : ¶m_iid, &err)) {
1534 0 : ThrowBadParam(err, i, mCallContext);
1535 0 : return false;
1536 : }
1537 : }
1538 :
1539 8506 : if (paramInfo.IsRetval()) {
1540 8466 : mCallContext.SetRetVal(v);
1541 40 : } else if (i < mArgc) {
1542 : // we actually assured this before doing the invoke
1543 12 : MOZ_ASSERT(mArgv[i].isObject(), "out var is not object");
1544 24 : RootedObject obj(mCallContext, &mArgv[i].toObject());
1545 12 : if (!JS_SetPropertyById(mCallContext, obj, mIdxValueId, v)) {
1546 0 : ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, i, mCallContext);
1547 0 : return false;
1548 : }
1549 : } else {
1550 28 : MOZ_ASSERT(paramInfo.IsOptional(),
1551 : "Expected either enough arguments or an optional argument");
1552 : }
1553 : }
1554 :
1555 9888 : return true;
1556 : }
1557 :
1558 : bool
1559 129 : CallMethodHelper::QueryInterfaceFastPath()
1560 : {
1561 129 : MOZ_ASSERT(mVTableIndex == 0,
1562 : "Using the QI fast-path for a method other than QueryInterface");
1563 :
1564 129 : if (mArgc < 1) {
1565 0 : Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
1566 0 : return false;
1567 : }
1568 :
1569 129 : if (!mArgv[0].isObject()) {
1570 0 : ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
1571 0 : return false;
1572 : }
1573 :
1574 129 : const nsID* iid = xpc_JSObjectToID(mCallContext, &mArgv[0].toObject());
1575 129 : if (!iid) {
1576 0 : ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
1577 0 : return false;
1578 : }
1579 :
1580 129 : nsISupports* qiresult = nullptr;
1581 129 : mInvokeResult = mCallee->QueryInterface(*iid, (void**) &qiresult);
1582 :
1583 129 : if (NS_FAILED(mInvokeResult)) {
1584 0 : ThrowBadResult(mInvokeResult, mCallContext);
1585 0 : return false;
1586 : }
1587 :
1588 258 : RootedValue v(mCallContext, NullValue());
1589 : nsresult err;
1590 : bool success =
1591 258 : XPCConvert::NativeData2JS(&v, &qiresult,
1592 : nsXPTType::T_INTERFACE_IS,
1593 129 : iid, &err);
1594 129 : NS_IF_RELEASE(qiresult);
1595 :
1596 129 : if (!success) {
1597 0 : ThrowBadParam(err, 0, mCallContext);
1598 0 : return false;
1599 : }
1600 :
1601 129 : mCallContext.SetRetVal(v);
1602 129 : return true;
1603 : }
1604 :
1605 : bool
1606 11867 : CallMethodHelper::InitializeDispatchParams()
1607 : {
1608 11867 : const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0;
1609 11867 : const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0;
1610 11867 : const uint8_t paramCount = mMethodInfo->GetParamCount();
1611 11867 : uint8_t requiredArgs = paramCount;
1612 11867 : uint8_t hasRetval = 0;
1613 :
1614 : // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
1615 11867 : if (paramCount && mMethodInfo->GetParam(paramCount-1).IsRetval()) {
1616 10404 : hasRetval = 1;
1617 10404 : requiredArgs--;
1618 : }
1619 :
1620 11867 : if (mArgc < requiredArgs || wantsOptArgc) {
1621 2802 : if (wantsOptArgc)
1622 1925 : mOptArgcIndex = requiredArgs;
1623 :
1624 : // skip over any optional arguments
1625 9138 : while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional())
1626 3168 : requiredArgs--;
1627 :
1628 2802 : if (mArgc < requiredArgs) {
1629 0 : Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
1630 0 : return false;
1631 : }
1632 : }
1633 :
1634 11867 : if (wantsJSContext) {
1635 1476 : if (wantsOptArgc)
1636 : // Need to bump mOptArgcIndex up one here.
1637 1377 : mJSContextIndex = mOptArgcIndex++;
1638 99 : else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter())
1639 : // For attributes, we always put the JSContext* first.
1640 12 : mJSContextIndex = 0;
1641 : else
1642 87 : mJSContextIndex = paramCount - hasRetval;
1643 : }
1644 :
1645 : // iterate through the params to clear flags (for safe cleanup later)
1646 40300 : for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
1647 28433 : nsXPTCVariant* dp = mDispatchParams.AppendElement();
1648 28433 : dp->ClearFlags();
1649 28433 : dp->val.p = nullptr;
1650 : }
1651 :
1652 : // Fill in the JSContext argument
1653 11867 : if (wantsJSContext) {
1654 1476 : nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex];
1655 1476 : dp->type = nsXPTType::T_VOID;
1656 1476 : dp->val.p = mCallContext;
1657 : }
1658 :
1659 : // Fill in the optional_argc argument
1660 11867 : if (wantsOptArgc) {
1661 1925 : nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex];
1662 1925 : dp->type = nsXPTType::T_U8;
1663 1925 : dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
1664 : }
1665 :
1666 11867 : return true;
1667 : }
1668 :
1669 : bool
1670 11867 : CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam)
1671 : {
1672 11867 : const uint8_t paramCount = mMethodInfo->GetParamCount();
1673 36899 : for (uint8_t i = 0; i < paramCount; i++) {
1674 25032 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
1675 :
1676 25032 : if (paramInfo.GetType().IsDependent())
1677 91 : *foundDependentParam = true;
1678 24941 : else if (!ConvertIndependentParam(i))
1679 0 : return false;
1680 :
1681 : }
1682 :
1683 11867 : return true;
1684 : }
1685 :
1686 : bool
1687 24941 : CallMethodHelper::ConvertIndependentParam(uint8_t i)
1688 : {
1689 24941 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
1690 24941 : const nsXPTType& type = paramInfo.GetType();
1691 24941 : uint8_t type_tag = type.TagPart();
1692 24941 : nsXPTCVariant* dp = GetDispatchParam(i);
1693 24941 : dp->type = type;
1694 24941 : MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
1695 :
1696 : // String classes are always "in" - those that are marked "out" are converted
1697 : // by the XPIDL compiler to "in+dipper". See the note above IsDipper() in
1698 : // xptinfo.h.
1699 : //
1700 : // Also note that the fact that we bail out early for dipper parameters means
1701 : // that "inout" dipper parameters don't work - see bug 687612.
1702 24941 : if (paramInfo.IsStringClass()) {
1703 2481 : if (!AllocateStringClass(dp, paramInfo))
1704 0 : return false;
1705 2481 : if (paramInfo.IsDipper()) {
1706 : // We've allocated our string class explicitly, so we don't need
1707 : // to do any conversions on the incoming argument. However, we still
1708 : // need to verify that it's an object, so that we don't get surprised
1709 : // later on when trying to assign the result to .value.
1710 394 : if (i < mArgc && !mArgv[i].isObject()) {
1711 0 : ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, i, mCallContext);
1712 0 : return false;
1713 : }
1714 394 : return true;
1715 : }
1716 : }
1717 :
1718 : // Specify the correct storage/calling semantics.
1719 24547 : if (paramInfo.IsIndirect())
1720 11505 : dp->SetIndirect();
1721 :
1722 : // The JSVal proper is always stored within the 'val' union and passed
1723 : // indirectly, regardless of in/out-ness.
1724 24547 : if (type_tag == nsXPTType::T_JSVAL) {
1725 : // Root the value.
1726 2947 : dp->val.j.setUndefined();
1727 2947 : if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param"))
1728 0 : return false;
1729 : }
1730 :
1731 : // Flag cleanup for anything that isn't self-contained.
1732 24547 : if (!type.IsArithmetic())
1733 17895 : dp->SetValNeedsCleanup();
1734 :
1735 : // Even if there's nothing to convert, we still need to examine the
1736 : // JSObject container for out-params. If it's null or otherwise invalid,
1737 : // we want to know before the call, rather than after.
1738 : //
1739 : // This is a no-op for 'in' params.
1740 49094 : RootedValue src(mCallContext);
1741 24547 : if (!GetOutParamSource(i, &src))
1742 0 : return false;
1743 :
1744 : // All that's left to do is value conversion. Bail early if we don't need
1745 : // to do that.
1746 24547 : if (!paramInfo.IsIn())
1747 9966 : return true;
1748 :
1749 : // We're definitely some variety of 'in' now, so there's something to
1750 : // convert. The source value for conversion depends on whether we're
1751 : // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
1752 : // so all that's left is 'in'.
1753 14581 : if (!paramInfo.IsOut()) {
1754 : // Handle the 'in' case.
1755 14581 : MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
1756 : "Expected either enough arguments or an optional argument");
1757 14581 : if (i < mArgc)
1758 12550 : src = mArgv[i];
1759 2031 : else if (type_tag == nsXPTType::T_JSVAL)
1760 693 : src.setUndefined();
1761 : else
1762 1338 : src.setNull();
1763 : }
1764 :
1765 : nsID param_iid;
1766 17441 : if (type_tag == nsXPTType::T_INTERFACE &&
1767 2860 : NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, ¶mInfo,
1768 : ¶m_iid))) {
1769 0 : ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext);
1770 0 : return false;
1771 : }
1772 :
1773 : // Don't allow CPOWs to be passed to native code (in case they try to cast
1774 : // to a concrete type).
1775 35124 : if (src.isObject() &&
1776 5962 : jsipc::IsWrappedCPOW(&src.toObject()) &&
1777 14581 : type_tag == nsXPTType::T_INTERFACE &&
1778 0 : !param_iid.Equals(NS_GET_IID(nsISupports)))
1779 : {
1780 : // Allow passing CPOWs to XPCWrappedJS.
1781 0 : nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(mCallee));
1782 0 : if (!wrappedJS) {
1783 0 : ThrowBadParam(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, i, mCallContext);
1784 0 : return false;
1785 : }
1786 : }
1787 :
1788 : nsresult err;
1789 14581 : if (!XPCConvert::JSData2Native(&dp->val, src, type, ¶m_iid, &err)) {
1790 0 : ThrowBadParam(err, i, mCallContext);
1791 0 : return false;
1792 : }
1793 :
1794 14581 : return true;
1795 : }
1796 :
1797 : bool
1798 91 : CallMethodHelper::ConvertDependentParams()
1799 : {
1800 91 : const uint8_t paramCount = mMethodInfo->GetParamCount();
1801 350 : for (uint8_t i = 0; i < paramCount; i++) {
1802 259 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
1803 :
1804 259 : if (!paramInfo.GetType().IsDependent())
1805 168 : continue;
1806 91 : if (!ConvertDependentParam(i))
1807 0 : return false;
1808 : }
1809 :
1810 91 : return true;
1811 : }
1812 :
1813 : bool
1814 91 : CallMethodHelper::ConvertDependentParam(uint8_t i)
1815 : {
1816 91 : const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
1817 91 : const nsXPTType& type = paramInfo.GetType();
1818 91 : nsXPTType datum_type;
1819 91 : uint32_t array_count = 0;
1820 91 : bool isArray = type.IsArray();
1821 :
1822 150 : bool isSizedString = isArray ?
1823 : false :
1824 118 : type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
1825 150 : type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
1826 :
1827 91 : nsXPTCVariant* dp = GetDispatchParam(i);
1828 91 : dp->type = type;
1829 :
1830 91 : if (isArray) {
1831 32 : if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, ¶mInfo, 1,
1832 : &datum_type))) {
1833 0 : Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
1834 0 : return false;
1835 : }
1836 32 : MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL,
1837 : "Arrays of JSVals not currently supported - see bug 693337.");
1838 : } else {
1839 59 : datum_type = type;
1840 : }
1841 :
1842 : // Specify the correct storage/calling semantics.
1843 91 : if (paramInfo.IsIndirect())
1844 84 : dp->SetIndirect();
1845 :
1846 : // We have 3 possible type of dependent parameters: Arrays, Sized Strings,
1847 : // and iid_is Interface pointers. The latter two always need cleanup, and
1848 : // arrays need cleanup for all non-arithmetic types. Since the latter two
1849 : // cases also happen to be non-arithmetic, we can just inspect datum_type
1850 : // here.
1851 91 : if (!datum_type.IsArithmetic())
1852 91 : dp->SetValNeedsCleanup();
1853 :
1854 : // Even if there's nothing to convert, we still need to examine the
1855 : // JSObject container for out-params. If it's null or otherwise invalid,
1856 : // we want to know before the call, rather than after.
1857 : //
1858 : // This is a no-op for 'in' params.
1859 182 : RootedValue src(mCallContext);
1860 91 : if (!GetOutParamSource(i, &src))
1861 0 : return false;
1862 :
1863 : // All that's left to do is value conversion. Bail early if we don't need
1864 : // to do that.
1865 91 : if (!paramInfo.IsIn())
1866 84 : return true;
1867 :
1868 : // We're definitely some variety of 'in' now, so there's something to
1869 : // convert. The source value for conversion depends on whether we're
1870 : // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
1871 : // so all that's left is 'in'.
1872 7 : if (!paramInfo.IsOut()) {
1873 : // Handle the 'in' case.
1874 7 : MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
1875 : "Expected either enough arguments or an optional argument");
1876 7 : src = i < mArgc ? mArgv[i] : JS::NullValue();
1877 : }
1878 :
1879 : nsID param_iid;
1880 10 : if (datum_type.IsInterfacePointer() &&
1881 3 : !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid))
1882 0 : return false;
1883 :
1884 : nsresult err;
1885 :
1886 7 : if (isArray || isSizedString) {
1887 7 : if (!GetArraySizeFromParam(i, src, &array_count))
1888 0 : return false;
1889 :
1890 14 : if (isArray) {
1891 35 : if (array_count &&
1892 28 : !XPCConvert::JSArray2Native((void**)&dp->val, src,
1893 : array_count, datum_type, ¶m_iid,
1894 7 : &err)) {
1895 : // XXX need exception scheme for arrays to indicate bad element
1896 0 : ThrowBadParam(err, i, mCallContext);
1897 0 : return false;
1898 : }
1899 : } else // if (isSizedString)
1900 : {
1901 0 : if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val,
1902 : src, array_count,
1903 : datum_type, &err)) {
1904 0 : ThrowBadParam(err, i, mCallContext);
1905 0 : return false;
1906 : }
1907 : }
1908 : } else {
1909 0 : if (!XPCConvert::JSData2Native(&dp->val, src, type,
1910 : ¶m_iid, &err)) {
1911 0 : ThrowBadParam(err, i, mCallContext);
1912 0 : return false;
1913 : }
1914 : }
1915 :
1916 7 : return true;
1917 : }
1918 :
1919 : // Performs all necessary teardown on a parameter after method invocation.
1920 : //
1921 : // This method should only be called if the value in question was flagged
1922 : // for cleanup (ie, if dp->DoesValNeedCleanup()).
1923 : void
1924 18690 : CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
1925 : {
1926 : // We handle array elements, but not the arrays themselves.
1927 18690 : MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays.");
1928 :
1929 : // Pointers may sometimes be null even if cleanup was requested. Combine
1930 : // the null checking for all the different types into one check here.
1931 18690 : if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr)
1932 303 : return;
1933 :
1934 18387 : switch (type.TagPart()) {
1935 : case nsXPTType::T_JSVAL:
1936 2947 : js::RemoveRawValueRoot(mCallContext, (Value*)¶m.val);
1937 2947 : break;
1938 : case nsXPTType::T_INTERFACE:
1939 : case nsXPTType::T_INTERFACE_IS:
1940 4918 : ((nsISupports*)param.val.p)->Release();
1941 4918 : break;
1942 : case nsXPTType::T_ASTRING:
1943 : case nsXPTType::T_DOMSTRING:
1944 1018 : mCallContext.GetContext()->mScratchStrings.Destroy((nsString*)param.val.p);
1945 1018 : break;
1946 : case nsXPTType::T_UTF8STRING:
1947 : case nsXPTType::T_CSTRING:
1948 1463 : mCallContext.GetContext()->mScratchCStrings.Destroy((nsCString*)param.val.p);
1949 1463 : break;
1950 : default:
1951 8041 : MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
1952 8041 : free(param.val.p);
1953 8041 : break;
1954 : }
1955 : }
1956 :
1957 : bool
1958 2481 : CallMethodHelper::AllocateStringClass(nsXPTCVariant* dp,
1959 : const nsXPTParamInfo& paramInfo)
1960 : {
1961 : // Get something we can make comparisons with.
1962 2481 : uint8_t type_tag = paramInfo.GetType().TagPart();
1963 :
1964 : // There should be 4 cases, all strings. Verify that here.
1965 2481 : MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING ||
1966 : type_tag == nsXPTType::T_DOMSTRING ||
1967 : type_tag == nsXPTType::T_UTF8STRING ||
1968 : type_tag == nsXPTType::T_CSTRING,
1969 : "Unexpected string class type!");
1970 :
1971 : // ASTRING and DOMSTRING are very similar, and both use nsString.
1972 : // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
1973 2481 : if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
1974 1018 : dp->val.p = mCallContext.GetContext()->mScratchStrings.Create();
1975 : else
1976 1463 : dp->val.p = mCallContext.GetContext()->mScratchCStrings.Create();
1977 :
1978 : // Check for OOM, in either case.
1979 2481 : if (!dp->val.p) {
1980 0 : JS_ReportOutOfMemory(mCallContext);
1981 0 : return false;
1982 : }
1983 :
1984 : // We allocated, so we need to deallocate after the method call completes.
1985 2481 : dp->SetValNeedsCleanup();
1986 :
1987 2481 : return true;
1988 : }
1989 :
1990 : nsresult
1991 11867 : CallMethodHelper::Invoke()
1992 : {
1993 11867 : uint32_t argc = mDispatchParams.Length();
1994 11867 : nsXPTCVariant* argv = mDispatchParams.Elements();
1995 :
1996 11867 : return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv);
1997 : }
1998 :
1999 : /***************************************************************************/
2000 : // interface methods
2001 :
2002 : JSObject*
2003 266 : XPCWrappedNative::GetJSObject()
2004 : {
2005 266 : return GetFlatJSObject();
2006 : }
2007 :
2008 0 : NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
2009 : {
2010 : // No need to QI here, we already have the correct nsISupports
2011 : // vtable.
2012 0 : nsCOMPtr<nsISupports> rval = mIdentity;
2013 0 : rval.forget(aNative);
2014 0 : return NS_OK;
2015 : }
2016 :
2017 0 : NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
2018 : {
2019 0 : *aJSObjectPrototype = HasProto() ?
2020 0 : GetProto()->GetJSProtoObject() : GetFlatJSObject();
2021 0 : return NS_OK;
2022 : }
2023 :
2024 : nsIPrincipal*
2025 0 : XPCWrappedNative::GetObjectPrincipal() const
2026 : {
2027 0 : nsIPrincipal* principal = GetScope()->GetPrincipal();
2028 : #ifdef DEBUG
2029 : // Because of inner window reuse, we can have objects with one principal
2030 : // living in a scope with a different (but same-origin) principal. So
2031 : // just check same-origin here.
2032 0 : nsCOMPtr<nsIScriptObjectPrincipal> objPrin(do_QueryInterface(mIdentity));
2033 0 : if (objPrin) {
2034 : bool equal;
2035 0 : if (!principal)
2036 0 : equal = !objPrin->GetPrincipal();
2037 : else
2038 0 : principal->Equals(objPrin->GetPrincipal(), &equal);
2039 0 : MOZ_ASSERT(equal, "Principal mismatch. Expect bad things to happen");
2040 : }
2041 : #endif
2042 0 : return principal;
2043 : }
2044 :
2045 0 : NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name,
2046 : nsIInterfaceInfo * *_retval)
2047 : {
2048 0 : RefPtr<XPCNativeInterface> iface;
2049 : XPCNativeMember* member;
2050 :
2051 0 : if (GetSet()->FindMember(name, &member, &iface) && iface) {
2052 0 : nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
2053 0 : temp.forget(_retval);
2054 : } else
2055 0 : *_retval = nullptr;
2056 0 : return NS_OK;
2057 : }
2058 :
2059 0 : NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name,
2060 : nsIInterfaceInfo * *_retval)
2061 : {
2062 0 : XPCNativeInterface* iface = GetSet()->FindNamedInterface(name);
2063 0 : if (iface) {
2064 0 : nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
2065 0 : temp.forget(_retval);
2066 : } else
2067 0 : *_retval = nullptr;
2068 0 : return NS_OK;
2069 : }
2070 :
2071 : NS_IMETHODIMP_(bool)
2072 0 : XPCWrappedNative::HasNativeMember(HandleId name)
2073 : {
2074 0 : XPCNativeMember* member = nullptr;
2075 : uint16_t ignored;
2076 0 : return GetSet()->FindMember(name, &member, &ignored) && !!member;
2077 : }
2078 :
2079 0 : NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
2080 : {
2081 : #ifdef DEBUG
2082 0 : depth-- ;
2083 0 : XPC_LOG_ALWAYS(("XPCWrappedNative @ %p with mRefCnt = %" PRIuPTR, this, mRefCnt.get()));
2084 0 : XPC_LOG_INDENT();
2085 :
2086 0 : if (HasProto()) {
2087 0 : XPCWrappedNativeProto* proto = GetProto();
2088 0 : if (depth && proto)
2089 0 : proto->DebugDump(depth);
2090 : else
2091 0 : XPC_LOG_ALWAYS(("mMaybeProto @ %p", proto));
2092 : } else
2093 0 : XPC_LOG_ALWAYS(("Scope @ %p", GetScope()));
2094 :
2095 0 : if (depth && mSet)
2096 0 : mSet->DebugDump(depth);
2097 : else
2098 0 : XPC_LOG_ALWAYS(("mSet @ %p", mSet.get()));
2099 :
2100 0 : XPC_LOG_ALWAYS(("mFlatJSObject of %p", mFlatJSObject.unbarrieredGetPtr()));
2101 0 : XPC_LOG_ALWAYS(("mIdentity of %p", mIdentity.get()));
2102 0 : XPC_LOG_ALWAYS(("mScriptable @ %p", mScriptable.get()));
2103 :
2104 0 : if (depth && mScriptable) {
2105 0 : XPC_LOG_INDENT();
2106 0 : XPC_LOG_ALWAYS(("mFlags of %x", mScriptable->GetScriptableFlags()));
2107 0 : XPC_LOG_ALWAYS(("mJSClass @ %p", mScriptable->GetJSClass()));
2108 0 : XPC_LOG_OUTDENT();
2109 : }
2110 0 : XPC_LOG_OUTDENT();
2111 : #endif
2112 0 : return NS_OK;
2113 : }
2114 :
2115 : /***************************************************************************/
2116 :
2117 : char*
2118 8 : XPCWrappedNative::ToString(XPCWrappedNativeTearOff* to /* = nullptr */ ) const
2119 : {
2120 : #ifdef DEBUG
2121 : # define FMT_ADDR " @ 0x%p"
2122 : # define FMT_STR(str) str
2123 : # define PARAM_ADDR(w) , w
2124 : #else
2125 : # define FMT_ADDR ""
2126 : # define FMT_STR(str)
2127 : # define PARAM_ADDR(w)
2128 : #endif
2129 :
2130 16 : UniqueChars sz;
2131 16 : UniqueChars name;
2132 :
2133 16 : nsCOMPtr<nsIXPCScriptable> scr = GetScriptable();
2134 8 : if (scr)
2135 1 : name = JS_smprintf("%s", scr->GetJSClass()->name);
2136 8 : if (to) {
2137 0 : const char* fmt = name ? " (%s)" : "%s";
2138 0 : name = JS_sprintf_append(Move(name), fmt,
2139 0 : to->GetInterface()->GetNameString());
2140 8 : } else if (!name) {
2141 7 : XPCNativeSet* set = GetSet();
2142 7 : XPCNativeInterface** array = set->GetInterfaceArray();
2143 14 : RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
2144 7 : uint16_t count = set->GetInterfaceCount();
2145 :
2146 7 : if (count == 1)
2147 0 : name = JS_sprintf_append(Move(name), "%s", array[0]->GetNameString());
2148 7 : else if (count == 2 && array[0] == isupp) {
2149 7 : name = JS_sprintf_append(Move(name), "%s", array[1]->GetNameString());
2150 : } else {
2151 0 : for (uint16_t i = 0; i < count; i++) {
2152 0 : const char* fmt = (i == 0) ?
2153 0 : "(%s" : (i == count-1) ?
2154 0 : ", %s)" : ", %s";
2155 0 : name = JS_sprintf_append(Move(name), fmt,
2156 0 : array[i]->GetNameString());
2157 : }
2158 : }
2159 : }
2160 :
2161 8 : if (!name) {
2162 0 : return nullptr;
2163 : }
2164 : const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
2165 8 : FMT_ADDR FMT_STR(")") "]";
2166 8 : if (scr) {
2167 1 : fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
2168 : }
2169 8 : sz = JS_smprintf(fmt, name.get() PARAM_ADDR(this) PARAM_ADDR(mIdentity.get()));
2170 :
2171 8 : return sz.release();
2172 :
2173 : #undef FMT_ADDR
2174 : #undef PARAM_ADDR
2175 : }
2176 :
2177 : /***************************************************************************/
2178 :
2179 : #ifdef XPC_CHECK_CLASSINFO_CLAIMS
2180 : static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper)
2181 : {
2182 : if (!wrapper || !wrapper->GetClassInfo())
2183 : return;
2184 :
2185 : nsISupports* obj = wrapper->GetIdentityObject();
2186 : XPCNativeSet* set = wrapper->GetSet();
2187 : uint16_t count = set->GetInterfaceCount();
2188 : for (uint16_t i = 0; i < count; i++) {
2189 : nsIClassInfo* clsInfo = wrapper->GetClassInfo();
2190 : XPCNativeInterface* iface = set->GetInterfaceAt(i);
2191 : nsIInterfaceInfo* info = iface->GetInterfaceInfo();
2192 : const nsIID* iid;
2193 : nsISupports* ptr;
2194 :
2195 : info->GetIIDShared(&iid);
2196 : nsresult rv = obj->QueryInterface(*iid, (void**)&ptr);
2197 : if (NS_SUCCEEDED(rv)) {
2198 : NS_RELEASE(ptr);
2199 : continue;
2200 : }
2201 : if (rv == NS_ERROR_OUT_OF_MEMORY)
2202 : continue;
2203 :
2204 : // Houston, We have a problem...
2205 :
2206 : char* className = nullptr;
2207 : char* contractID = nullptr;
2208 : const char* interfaceName;
2209 :
2210 : info->GetNameShared(&interfaceName);
2211 : clsInfo->GetContractID(&contractID);
2212 : if (wrapper->GetScriptable()) {
2213 : wrapper->GetScriptable()->GetClassName(&className);
2214 : }
2215 :
2216 : printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n"
2217 : " classname: %s \n"
2218 : " contractid: %s \n"
2219 : " unimplemented interface name: %s\n\n",
2220 : className ? className : "<unknown>",
2221 : contractID ? contractID : "<unknown>",
2222 : interfaceName);
2223 :
2224 : if (className)
2225 : free(className);
2226 : if (contractID)
2227 : free(contractID);
2228 : }
2229 : }
2230 : #endif
2231 :
2232 0 : NS_IMPL_ISUPPORTS(XPCJSObjectHolder, nsIXPConnectJSObjectHolder)
2233 :
2234 : JSObject*
2235 0 : XPCJSObjectHolder::GetJSObject()
2236 : {
2237 0 : NS_PRECONDITION(mJSObj, "bad object state");
2238 0 : return mJSObj;
2239 : }
2240 :
2241 0 : XPCJSObjectHolder::XPCJSObjectHolder(JSContext* cx, JSObject* obj)
2242 0 : : mJSObj(cx, obj)
2243 : {
2244 0 : MOZ_ASSERT(obj);
2245 0 : }
|