|
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 ¶mInfo, |
|
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, ¶mInfo, 0, ¶mIndex); |
|
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, ¶mInfo, 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, ¶mInfo, |
|
1832 ¶mIndex); |
|
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, ¶mInfo, 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, ¶m_iid)) |
|
1911 return false; |
|
1912 |
|
1913 nsresult err; |
|
1914 if (isArray) { |
|
1915 if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val, |
|
1916 datum_type, ¶m_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 ¶m_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, ¶mInfo, |
|
2149 ¶m_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, ¶m_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, ¶mInfo, 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, ¶m_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, ¶m_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 ¶m_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*)¶m.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 } |