|
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 /* Sharable code and data for wrapper around JSObjects. */ |
|
8 |
|
9 #include "xpcprivate.h" |
|
10 #include "jsprf.h" |
|
11 #include "nsArrayEnumerator.h" |
|
12 #include "nsContentUtils.h" |
|
13 #include "nsWrapperCache.h" |
|
14 #include "AccessCheck.h" |
|
15 #include "nsJSUtils.h" |
|
16 #include "mozilla/Attributes.h" |
|
17 #include "mozilla/dom/BindingUtils.h" |
|
18 #include "mozilla/dom/DOMException.h" |
|
19 #include "mozilla/dom/DOMExceptionBinding.h" |
|
20 |
|
21 #include "jsapi.h" |
|
22 #include "jsfriendapi.h" |
|
23 |
|
24 using namespace xpc; |
|
25 using namespace JS; |
|
26 using namespace mozilla; |
|
27 |
|
28 NS_IMPL_ISUPPORTS(nsXPCWrappedJSClass, nsIXPCWrappedJSClass) |
|
29 |
|
30 // the value of this variable is never used - we use its address as a sentinel |
|
31 static uint32_t zero_methods_descriptor; |
|
32 |
|
33 bool AutoScriptEvaluate::StartEvaluating(HandleObject scope, JSErrorReporter errorReporter) |
|
34 { |
|
35 NS_PRECONDITION(!mEvaluated, "AutoScriptEvaluate::Evaluate should only be called once"); |
|
36 |
|
37 if (!mJSContext) |
|
38 return true; |
|
39 |
|
40 mEvaluated = true; |
|
41 if (!JS_GetErrorReporter(mJSContext)) { |
|
42 JS_SetErrorReporter(mJSContext, errorReporter); |
|
43 mErrorReporterSet = true; |
|
44 } |
|
45 |
|
46 JS_BeginRequest(mJSContext); |
|
47 mAutoCompartment.construct(mJSContext, scope); |
|
48 |
|
49 // Saving the exception state keeps us from interfering with another script |
|
50 // that may also be running on this context. This occurred first with the |
|
51 // js debugger, as described in |
|
52 // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could |
|
53 // show up in any situation where a script calls into a wrapped js component |
|
54 // on the same context, while the context has a nonzero exception state. |
|
55 mState.construct(mJSContext); |
|
56 |
|
57 return true; |
|
58 } |
|
59 |
|
60 AutoScriptEvaluate::~AutoScriptEvaluate() |
|
61 { |
|
62 if (!mJSContext || !mEvaluated) |
|
63 return; |
|
64 mState.ref().restore(); |
|
65 |
|
66 JS_EndRequest(mJSContext); |
|
67 |
|
68 if (mErrorReporterSet) |
|
69 JS_SetErrorReporter(mJSContext, nullptr); |
|
70 } |
|
71 |
|
72 // It turns out that some errors may be not worth reporting. So, this |
|
73 // function is factored out to manage that. |
|
74 bool xpc_IsReportableErrorCode(nsresult code) |
|
75 { |
|
76 if (NS_SUCCEEDED(code)) |
|
77 return false; |
|
78 |
|
79 switch (code) { |
|
80 // Error codes that we don't want to report as errors... |
|
81 // These generally indicate bad interface design AFAIC. |
|
82 case NS_ERROR_FACTORY_REGISTER_AGAIN: |
|
83 case NS_BASE_STREAM_WOULD_BLOCK: |
|
84 return false; |
|
85 default: |
|
86 return true; |
|
87 } |
|
88 } |
|
89 |
|
90 // static |
|
91 already_AddRefed<nsXPCWrappedJSClass> |
|
92 nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID) |
|
93 { |
|
94 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); |
|
95 IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap(); |
|
96 nsRefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID); |
|
97 |
|
98 if (!clasp) { |
|
99 nsCOMPtr<nsIInterfaceInfo> info; |
|
100 nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info)); |
|
101 if (info) { |
|
102 bool canScript, isBuiltin; |
|
103 if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && canScript && |
|
104 NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin && |
|
105 nsXPConnect::IsISupportsDescendant(info)) |
|
106 { |
|
107 clasp = new nsXPCWrappedJSClass(cx, aIID, info); |
|
108 if (!clasp->mDescriptors) |
|
109 clasp = nullptr; |
|
110 } |
|
111 } |
|
112 } |
|
113 return clasp.forget(); |
|
114 } |
|
115 |
|
116 nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID, |
|
117 nsIInterfaceInfo* aInfo) |
|
118 : mRuntime(nsXPConnect::GetRuntimeInstance()), |
|
119 mInfo(aInfo), |
|
120 mName(nullptr), |
|
121 mIID(aIID), |
|
122 mDescriptors(nullptr) |
|
123 { |
|
124 mRuntime->GetWrappedJSClassMap()->Add(this); |
|
125 |
|
126 uint16_t methodCount; |
|
127 if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) { |
|
128 if (methodCount) { |
|
129 int wordCount = (methodCount/32)+1; |
|
130 if (nullptr != (mDescriptors = new uint32_t[wordCount])) { |
|
131 int i; |
|
132 // init flags to 0; |
|
133 for (i = wordCount-1; i >= 0; i--) |
|
134 mDescriptors[i] = 0; |
|
135 |
|
136 for (i = 0; i < methodCount; i++) { |
|
137 const nsXPTMethodInfo* info; |
|
138 if (NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info))) |
|
139 SetReflectable(i, XPCConvert::IsMethodReflectable(*info)); |
|
140 else { |
|
141 delete [] mDescriptors; |
|
142 mDescriptors = nullptr; |
|
143 break; |
|
144 } |
|
145 } |
|
146 } |
|
147 } else { |
|
148 mDescriptors = &zero_methods_descriptor; |
|
149 } |
|
150 } |
|
151 } |
|
152 |
|
153 nsXPCWrappedJSClass::~nsXPCWrappedJSClass() |
|
154 { |
|
155 if (mDescriptors && mDescriptors != &zero_methods_descriptor) |
|
156 delete [] mDescriptors; |
|
157 if (mRuntime) |
|
158 mRuntime->GetWrappedJSClassMap()->Remove(this); |
|
159 |
|
160 if (mName) |
|
161 nsMemory::Free(mName); |
|
162 } |
|
163 |
|
164 JSObject* |
|
165 nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx, |
|
166 JSObject* jsobjArg, |
|
167 REFNSIID aIID) |
|
168 { |
|
169 RootedObject jsobj(cx, jsobjArg); |
|
170 JSObject* id; |
|
171 RootedValue retval(cx); |
|
172 RootedObject retObj(cx); |
|
173 bool success = false; |
|
174 RootedValue fun(cx); |
|
175 |
|
176 // Don't call the actual function on a content object. We'll determine |
|
177 // whether or not a content object is capable of implementing the |
|
178 // interface (i.e. whether the interface is scriptable) and most content |
|
179 // objects don't have QI implementations anyway. Also see bug 503926. |
|
180 if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(jsobj))) { |
|
181 return nullptr; |
|
182 } |
|
183 |
|
184 // OK, it looks like we'll be calling into JS code. |
|
185 AutoScriptEvaluate scriptEval(cx); |
|
186 |
|
187 // XXX we should install an error reporter that will send reports to |
|
188 // the JS error console service. |
|
189 if (!scriptEval.StartEvaluating(jsobj)) |
|
190 return nullptr; |
|
191 |
|
192 // check upfront for the existence of the function property |
|
193 HandleId funid = mRuntime->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE); |
|
194 if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || JSVAL_IS_PRIMITIVE(fun)) |
|
195 return nullptr; |
|
196 |
|
197 // Ensure that we are asking for a scriptable interface. |
|
198 // NB: It's important for security that this check is here rather |
|
199 // than later, since it prevents untrusted objects from implementing |
|
200 // some interfaces in JS and aggregating a trusted object to |
|
201 // implement intentionally (for security) unscriptable interfaces. |
|
202 // We so often ask for nsISupports that we can short-circuit the test... |
|
203 if (!aIID.Equals(NS_GET_IID(nsISupports))) { |
|
204 nsCOMPtr<nsIInterfaceInfo> info; |
|
205 nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info)); |
|
206 if (!info) |
|
207 return nullptr; |
|
208 bool canScript, isBuiltin; |
|
209 if (NS_FAILED(info->IsScriptable(&canScript)) || !canScript || |
|
210 NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin) |
|
211 return nullptr; |
|
212 } |
|
213 |
|
214 id = xpc_NewIDObject(cx, jsobj, aIID); |
|
215 if (id) { |
|
216 // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It |
|
217 // is not an exception that is ever worth reporting, but we don't want |
|
218 // to eat all exceptions either. |
|
219 |
|
220 { |
|
221 AutoSaveContextOptions asco(cx); |
|
222 ContextOptionsRef(cx).setDontReportUncaught(true); |
|
223 RootedValue arg(cx, JS::ObjectValue(*id)); |
|
224 success = JS_CallFunctionValue(cx, jsobj, fun, arg, &retval); |
|
225 } |
|
226 |
|
227 if (!success && JS_IsExceptionPending(cx)) { |
|
228 RootedValue jsexception(cx, NullValue()); |
|
229 |
|
230 if (JS_GetPendingException(cx, &jsexception)) { |
|
231 nsresult rv; |
|
232 if (jsexception.isObject()) { |
|
233 // XPConnect may have constructed an object to represent a |
|
234 // C++ QI failure. See if that is the case. |
|
235 using namespace mozilla::dom; |
|
236 Exception *e = nullptr; |
|
237 UNWRAP_OBJECT(Exception, &jsexception.toObject(), e); |
|
238 |
|
239 if (e && |
|
240 NS_SUCCEEDED(e->GetResult(&rv)) && |
|
241 rv == NS_NOINTERFACE) { |
|
242 JS_ClearPendingException(cx); |
|
243 } |
|
244 } else if (JSVAL_IS_NUMBER(jsexception)) { |
|
245 // JS often throws an nsresult. |
|
246 if (JSVAL_IS_DOUBLE(jsexception)) |
|
247 // Visual Studio 9 doesn't allow casting directly from |
|
248 // a double to an enumeration type, contrary to |
|
249 // 5.2.9(10) of C++11, so add an intermediate cast. |
|
250 rv = (nsresult)(uint32_t)(JSVAL_TO_DOUBLE(jsexception)); |
|
251 else |
|
252 rv = (nsresult)(JSVAL_TO_INT(jsexception)); |
|
253 |
|
254 if (rv == NS_NOINTERFACE) |
|
255 JS_ClearPendingException(cx); |
|
256 } |
|
257 } |
|
258 |
|
259 // Don't report if reporting was disabled by someone else. |
|
260 if (!ContextOptionsRef(cx).dontReportUncaught()) |
|
261 JS_ReportPendingException(cx); |
|
262 } else if (!success) { |
|
263 NS_WARNING("QI hook ran OOMed - this is probably a bug!"); |
|
264 } |
|
265 } |
|
266 |
|
267 if (success) |
|
268 success = JS_ValueToObject(cx, retval, &retObj); |
|
269 |
|
270 return success ? retObj.get() : nullptr; |
|
271 } |
|
272 |
|
273 /***************************************************************************/ |
|
274 |
|
275 static bool |
|
276 GetNamedPropertyAsVariantRaw(XPCCallContext& ccx, |
|
277 HandleObject aJSObj, |
|
278 HandleId aName, |
|
279 nsIVariant** aResult, |
|
280 nsresult* pErr) |
|
281 { |
|
282 nsXPTType type = nsXPTType((uint8_t)TD_INTERFACE_TYPE); |
|
283 RootedValue val(ccx); |
|
284 |
|
285 return JS_GetPropertyById(ccx, aJSObj, aName, &val) && |
|
286 // Note that this always takes the T_INTERFACE path through |
|
287 // JSData2Native, so the value passed for useAllocator |
|
288 // doesn't really matter. We pass true for consistency. |
|
289 XPCConvert::JSData2Native(aResult, val, type, true, |
|
290 &NS_GET_IID(nsIVariant), pErr); |
|
291 } |
|
292 |
|
293 // static |
|
294 nsresult |
|
295 nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx, |
|
296 JSObject* aJSObjArg, |
|
297 const nsAString& aName, |
|
298 nsIVariant** aResult) |
|
299 { |
|
300 JSContext* cx = ccx.GetJSContext(); |
|
301 RootedObject aJSObj(cx, aJSObjArg); |
|
302 |
|
303 AutoScriptEvaluate scriptEval(cx); |
|
304 if (!scriptEval.StartEvaluating(aJSObj)) |
|
305 return NS_ERROR_FAILURE; |
|
306 |
|
307 // Wrap the string in a jsval after the AutoScriptEvaluate, so that the |
|
308 // resulting value ends up in the correct compartment. |
|
309 nsStringBuffer* buf; |
|
310 RootedValue value(cx); |
|
311 if (!XPCStringConvert::ReadableToJSVal(ccx, aName, &buf, &value)) |
|
312 return NS_ERROR_OUT_OF_MEMORY; |
|
313 if (buf) |
|
314 buf->AddRef(); |
|
315 |
|
316 RootedId id(cx); |
|
317 nsresult rv = NS_OK; |
|
318 if (!JS_ValueToId(cx, value, &id) || |
|
319 !GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv)) { |
|
320 if (NS_FAILED(rv)) |
|
321 return rv; |
|
322 return NS_ERROR_FAILURE; |
|
323 } |
|
324 return NS_OK; |
|
325 } |
|
326 |
|
327 /***************************************************************************/ |
|
328 |
|
329 // static |
|
330 nsresult |
|
331 nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx, |
|
332 JSObject* aJSObjArg, |
|
333 nsISimpleEnumerator** aEnumerate) |
|
334 { |
|
335 JSContext* cx = ccx.GetJSContext(); |
|
336 RootedObject aJSObj(cx, aJSObjArg); |
|
337 |
|
338 AutoScriptEvaluate scriptEval(cx); |
|
339 if (!scriptEval.StartEvaluating(aJSObj)) |
|
340 return NS_ERROR_FAILURE; |
|
341 |
|
342 AutoIdArray idArray(cx, JS_Enumerate(cx, aJSObj)); |
|
343 if (!idArray) |
|
344 return NS_ERROR_FAILURE; |
|
345 |
|
346 nsCOMArray<nsIProperty> propertyArray(idArray.length()); |
|
347 RootedId idName(cx); |
|
348 for (size_t i = 0; i < idArray.length(); i++) { |
|
349 idName = idArray[i]; |
|
350 |
|
351 nsCOMPtr<nsIVariant> value; |
|
352 nsresult rv; |
|
353 if (!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName, |
|
354 getter_AddRefs(value), &rv)) { |
|
355 if (NS_FAILED(rv)) |
|
356 return rv; |
|
357 return NS_ERROR_FAILURE; |
|
358 } |
|
359 |
|
360 RootedValue jsvalName(cx); |
|
361 if (!JS_IdToValue(cx, idName, &jsvalName)) |
|
362 return NS_ERROR_FAILURE; |
|
363 |
|
364 JSString* name = ToString(cx, jsvalName); |
|
365 if (!name) |
|
366 return NS_ERROR_FAILURE; |
|
367 |
|
368 size_t length; |
|
369 const jschar *chars = JS_GetStringCharsAndLength(cx, name, &length); |
|
370 if (!chars) |
|
371 return NS_ERROR_FAILURE; |
|
372 |
|
373 nsCOMPtr<nsIProperty> property = |
|
374 new xpcProperty(chars, (uint32_t) length, value); |
|
375 |
|
376 if (!propertyArray.AppendObject(property)) |
|
377 return NS_ERROR_FAILURE; |
|
378 } |
|
379 |
|
380 return NS_NewArrayEnumerator(aEnumerate, propertyArray); |
|
381 } |
|
382 |
|
383 /***************************************************************************/ |
|
384 |
|
385 NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty) |
|
386 |
|
387 xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen, |
|
388 nsIVariant* aValue) |
|
389 : mName(aName, aNameLen), mValue(aValue) |
|
390 { |
|
391 } |
|
392 |
|
393 /* readonly attribute AString name; */ |
|
394 NS_IMETHODIMP xpcProperty::GetName(nsAString & aName) |
|
395 { |
|
396 aName.Assign(mName); |
|
397 return NS_OK; |
|
398 } |
|
399 |
|
400 /* readonly attribute nsIVariant value; */ |
|
401 NS_IMETHODIMP xpcProperty::GetValue(nsIVariant * *aValue) |
|
402 { |
|
403 nsCOMPtr<nsIVariant> rval = mValue; |
|
404 rval.forget(aValue); |
|
405 return NS_OK; |
|
406 } |
|
407 |
|
408 /***************************************************************************/ |
|
409 // This 'WrappedJSIdentity' class and singleton allow us to figure out if |
|
410 // any given nsISupports* is implemented by a WrappedJS object. This is done |
|
411 // using a QueryInterface call on the interface pointer with our ID. If |
|
412 // that call returns NS_OK and the pointer is to our singleton, then the |
|
413 // interface must be implemented by a WrappedJS object. NOTE: the |
|
414 // 'WrappedJSIdentity' object is not a real XPCOM object and should not be |
|
415 // used for anything else (hence it is declared in this implementation file). |
|
416 |
|
417 // {5C5C3BB0-A9BA-11d2-BA64-00805F8A5DD7} |
|
418 #define NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID \ |
|
419 { 0x5c5c3bb0, 0xa9ba, 0x11d2, \ |
|
420 { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } |
|
421 |
|
422 class WrappedJSIdentity |
|
423 { |
|
424 // no instance methods... |
|
425 public: |
|
426 NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID) |
|
427 |
|
428 static void* GetSingleton() |
|
429 { |
|
430 static WrappedJSIdentity* singleton = nullptr; |
|
431 if (!singleton) |
|
432 singleton = new WrappedJSIdentity(); |
|
433 return (void*) singleton; |
|
434 } |
|
435 }; |
|
436 |
|
437 NS_DEFINE_STATIC_IID_ACCESSOR(WrappedJSIdentity, |
|
438 NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID) |
|
439 |
|
440 /***************************************************************************/ |
|
441 |
|
442 // static |
|
443 bool |
|
444 nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr) |
|
445 { |
|
446 void* result; |
|
447 NS_PRECONDITION(aPtr, "null pointer"); |
|
448 return aPtr && |
|
449 NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) && |
|
450 result == WrappedJSIdentity::GetSingleton(); |
|
451 } |
|
452 |
|
453 // NB: This will return the top JSContext on the JSContext stack if there is one, |
|
454 // before attempting to get the context from the wrapped JS object. |
|
455 static JSContext * |
|
456 GetContextFromObjectOrDefault(nsXPCWrappedJS* wrapper) |
|
457 { |
|
458 // First, try the cx stack. |
|
459 XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack(); |
|
460 if (stack->Peek()) |
|
461 return stack->Peek(); |
|
462 |
|
463 // If the cx stack is empty, try the wrapper's JSObject. |
|
464 JSCompartment *c = js::GetObjectCompartment(wrapper->GetJSObject()); |
|
465 XPCContext *xpcc = EnsureCompartmentPrivate(c)->scope->GetContext(); |
|
466 if (xpcc) { |
|
467 JSContext *cx = xpcc->GetJSContext(); |
|
468 JS_AbortIfWrongThread(JS_GetRuntime(cx)); |
|
469 return cx; |
|
470 } |
|
471 |
|
472 // Fall back to the safe JSContext. |
|
473 return stack->GetSafeJSContext(); |
|
474 } |
|
475 |
|
476 NS_IMETHODIMP |
|
477 nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self, |
|
478 REFNSIID aIID, |
|
479 void** aInstancePtr) |
|
480 { |
|
481 if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) { |
|
482 NS_ADDREF(self); |
|
483 *aInstancePtr = (void*) static_cast<nsIXPConnectJSObjectHolder*>(self); |
|
484 return NS_OK; |
|
485 } |
|
486 |
|
487 // Objects internal to xpconnect are the only objects that even know *how* |
|
488 // to ask for this iid. And none of them bother refcounting the thing. |
|
489 if (aIID.Equals(NS_GET_IID(WrappedJSIdentity))) { |
|
490 // asking to find out if this is a wrapper object |
|
491 *aInstancePtr = WrappedJSIdentity::GetSingleton(); |
|
492 return NS_OK; |
|
493 } |
|
494 |
|
495 if (aIID.Equals(NS_GET_IID(nsIPropertyBag))) { |
|
496 // We only want to expose one implementation from our aggregate. |
|
497 nsXPCWrappedJS* root = self->GetRootWrapper(); |
|
498 |
|
499 if (!root->IsValid()) { |
|
500 *aInstancePtr = nullptr; |
|
501 return NS_NOINTERFACE; |
|
502 } |
|
503 |
|
504 NS_ADDREF(root); |
|
505 *aInstancePtr = (void*) static_cast<nsIPropertyBag*>(root); |
|
506 return NS_OK; |
|
507 } |
|
508 |
|
509 // We can't have a cached wrapper. |
|
510 if (aIID.Equals(NS_GET_IID(nsWrapperCache))) { |
|
511 *aInstancePtr = nullptr; |
|
512 return NS_NOINTERFACE; |
|
513 } |
|
514 |
|
515 AutoPushJSContext context(GetContextFromObjectOrDefault(self)); |
|
516 XPCCallContext ccx(NATIVE_CALLER, context); |
|
517 if (!ccx.IsValid()) { |
|
518 *aInstancePtr = nullptr; |
|
519 return NS_NOINTERFACE; |
|
520 } |
|
521 |
|
522 // We support nsISupportsWeakReference iff the root wrapped JSObject |
|
523 // claims to support it in its QueryInterface implementation. |
|
524 if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) { |
|
525 // We only want to expose one implementation from our aggregate. |
|
526 nsXPCWrappedJS* root = self->GetRootWrapper(); |
|
527 |
|
528 // Fail if JSObject doesn't claim support for nsISupportsWeakReference |
|
529 if (!root->IsValid() || |
|
530 !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID)) { |
|
531 *aInstancePtr = nullptr; |
|
532 return NS_NOINTERFACE; |
|
533 } |
|
534 |
|
535 NS_ADDREF(root); |
|
536 *aInstancePtr = (void*) static_cast<nsISupportsWeakReference*>(root); |
|
537 return NS_OK; |
|
538 } |
|
539 |
|
540 // Checks for any existing wrapper explicitly constructed for this iid. |
|
541 // This includes the current 'self' wrapper. This also deals with the |
|
542 // nsISupports case (for which it returns mRoot). |
|
543 // Also check if asking for an interface from which one of our wrappers inherits. |
|
544 if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) { |
|
545 NS_ADDREF(sibling); |
|
546 *aInstancePtr = sibling->GetXPTCStub(); |
|
547 return NS_OK; |
|
548 } |
|
549 |
|
550 // else we do the more expensive stuff... |
|
551 |
|
552 // check if the JSObject claims to implement this interface |
|
553 RootedObject jsobj(ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(), |
|
554 aIID)); |
|
555 if (jsobj) { |
|
556 // We can't use XPConvert::JSObject2NativeInterface() here |
|
557 // since that can find a XPCWrappedNative directly on the |
|
558 // proto chain, and we don't want that here. We need to find |
|
559 // the actual JS object that claimed it supports the interface |
|
560 // we're looking for or we'll potentially bypass security |
|
561 // checks etc by calling directly through to a native found on |
|
562 // the prototype chain. |
|
563 // |
|
564 // Instead, simply do the nsXPCWrappedJS part of |
|
565 // XPConvert::JSObject2NativeInterface() here to make sure we |
|
566 // get a new (or used) nsXPCWrappedJS. |
|
567 nsXPCWrappedJS* wrapper; |
|
568 nsresult rv = nsXPCWrappedJS::GetNewOrUsed(jsobj, aIID, &wrapper); |
|
569 if (NS_SUCCEEDED(rv) && wrapper) { |
|
570 // We need to go through the QueryInterface logic to make |
|
571 // this return the right thing for the various 'special' |
|
572 // interfaces; e.g. nsIPropertyBag. |
|
573 rv = wrapper->QueryInterface(aIID, aInstancePtr); |
|
574 NS_RELEASE(wrapper); |
|
575 return rv; |
|
576 } |
|
577 } |
|
578 |
|
579 // else... |
|
580 // no can do |
|
581 *aInstancePtr = nullptr; |
|
582 return NS_NOINTERFACE; |
|
583 } |
|
584 |
|
585 JSObject* |
|
586 nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg) |
|
587 { |
|
588 RootedObject aJSObj(cx, aJSObjArg); |
|
589 JSObject* result = CallQueryInterfaceOnJSObject(cx, aJSObj, |
|
590 NS_GET_IID(nsISupports)); |
|
591 if (!result) |
|
592 return aJSObj; |
|
593 JSObject* inner = js::UncheckedUnwrap(result); |
|
594 if (inner) |
|
595 return inner; |
|
596 return result; |
|
597 } |
|
598 |
|
599 void |
|
600 xpcWrappedJSErrorReporter(JSContext *cx, const char *message, |
|
601 JSErrorReport *report) |
|
602 { |
|
603 if (report) { |
|
604 // If it is an exception report, then we can just deal with the |
|
605 // exception later (if not caught in the JS code). |
|
606 if (JSREPORT_IS_EXCEPTION(report->flags)) { |
|
607 // XXX We have a problem with error reports from uncaught exceptions. |
|
608 // |
|
609 // http://bugzilla.mozilla.org/show_bug.cgi?id=66453 |
|
610 // |
|
611 // The issue is... |
|
612 // |
|
613 // We can't assume that the exception will *stay* uncaught. So, if |
|
614 // we build an nsIXPCException here and the underlying exception |
|
615 // really is caught before our script is done running then we blow |
|
616 // it by returning failure to our caller when the script didn't |
|
617 // really fail. However, This report contains error location info |
|
618 // that is no longer available after the script is done. So, if the |
|
619 // exception really is not caught (and is a non-engine exception) |
|
620 // then we've lost the oportunity to capture the script location |
|
621 // info that we *could* have captured here. |
|
622 // |
|
623 // This is expecially an issue with nested evaluations. |
|
624 // |
|
625 // Perhaps we could capture an expception here and store it as |
|
626 // 'provisional' and then later if there is a pending exception |
|
627 // when the script is done then we could maybe compare that in some |
|
628 // way with the 'provisional' one in which we captured location info. |
|
629 // We would not want to assume that the one discovered here is the |
|
630 // same one that is later detected. This could cause us to lie. |
|
631 // |
|
632 // The thing is. we do not currently store the right stuff to compare |
|
633 // these two nsIXPCExceptions (triggered by the same exception jsval |
|
634 // in the engine). Maybe we should store the jsval and compare that? |
|
635 // Maybe without even rooting it since we will not dereference it. |
|
636 // This is inexact, but maybe the right thing to do? |
|
637 // |
|
638 // if (report->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)) ... |
|
639 // |
|
640 |
|
641 return; |
|
642 } |
|
643 |
|
644 if (JSREPORT_IS_WARNING(report->flags)) { |
|
645 // XXX printf the warning (#ifdef DEBUG only!). |
|
646 // XXX send the warning to the console service. |
|
647 return; |
|
648 } |
|
649 } |
|
650 |
|
651 XPCCallContext ccx(NATIVE_CALLER, cx); |
|
652 if (!ccx.IsValid()) |
|
653 return; |
|
654 |
|
655 nsCOMPtr<nsIException> e; |
|
656 XPCConvert::JSErrorToXPCException(message, nullptr, nullptr, report, |
|
657 getter_AddRefs(e)); |
|
658 if (e) |
|
659 ccx.GetXPCContext()->SetException(e); |
|
660 } |
|
661 |
|
662 bool |
|
663 nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx, |
|
664 const XPTMethodDescriptor* method, |
|
665 const nsXPTParamInfo& param, |
|
666 uint16_t methodIndex, |
|
667 uint8_t paramIndex, |
|
668 nsXPTCMiniVariant* nativeParams, |
|
669 uint32_t* result) |
|
670 { |
|
671 uint8_t argnum; |
|
672 nsresult rv; |
|
673 |
|
674 rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, ¶m, 0, &argnum); |
|
675 if (NS_FAILED(rv)) |
|
676 return false; |
|
677 |
|
678 const nsXPTParamInfo& arg_param = method->params[argnum]; |
|
679 |
|
680 // This should be enforced by the xpidl compiler, but it's not. |
|
681 // See bug 695235. |
|
682 MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32, |
|
683 "size_is references parameter of invalid type."); |
|
684 |
|
685 if (arg_param.IsIndirect()) |
|
686 *result = *(uint32_t*)nativeParams[argnum].val.p; |
|
687 else |
|
688 *result = nativeParams[argnum].val.u32; |
|
689 |
|
690 return true; |
|
691 } |
|
692 |
|
693 bool |
|
694 nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx, |
|
695 const XPTMethodDescriptor* method, |
|
696 const nsXPTParamInfo& param, |
|
697 uint16_t methodIndex, |
|
698 const nsXPTType& type, |
|
699 nsXPTCMiniVariant* nativeParams, |
|
700 nsID* result) |
|
701 { |
|
702 uint8_t type_tag = type.TagPart(); |
|
703 |
|
704 if (type_tag == nsXPTType::T_INTERFACE) { |
|
705 if (NS_SUCCEEDED(GetInterfaceInfo()-> |
|
706 GetIIDForParamNoAlloc(methodIndex, ¶m, result))) { |
|
707 return true; |
|
708 } |
|
709 } else if (type_tag == nsXPTType::T_INTERFACE_IS) { |
|
710 uint8_t argnum; |
|
711 nsresult rv; |
|
712 rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex, |
|
713 ¶m, &argnum); |
|
714 if (NS_FAILED(rv)) |
|
715 return false; |
|
716 |
|
717 const nsXPTParamInfo& arg_param = method->params[argnum]; |
|
718 const nsXPTType& arg_type = arg_param.GetType(); |
|
719 |
|
720 if (arg_type.TagPart() == nsXPTType::T_IID) { |
|
721 if (arg_param.IsIndirect()) { |
|
722 nsID** p = (nsID**) nativeParams[argnum].val.p; |
|
723 if (!p || !*p) |
|
724 return false; |
|
725 *result = **p; |
|
726 } else { |
|
727 nsID* p = (nsID*) nativeParams[argnum].val.p; |
|
728 if (!p) |
|
729 return false; |
|
730 *result = *p; |
|
731 } |
|
732 return true; |
|
733 } |
|
734 } |
|
735 return false; |
|
736 } |
|
737 |
|
738 void |
|
739 nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type, |
|
740 uint32_t array_count, |
|
741 void** arrayp) |
|
742 { |
|
743 if (datum_type.IsInterfacePointer()) { |
|
744 nsISupports** pp = (nsISupports**) arrayp; |
|
745 for (uint32_t k = 0; k < array_count; k++) { |
|
746 nsISupports* p = pp[k]; |
|
747 NS_IF_RELEASE(p); |
|
748 } |
|
749 } else { |
|
750 void** pp = (void**) arrayp; |
|
751 for (uint32_t k = 0; k < array_count; k++) { |
|
752 void* p = pp[k]; |
|
753 if (p) nsMemory::Free(p); |
|
754 } |
|
755 } |
|
756 } |
|
757 |
|
758 void |
|
759 nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type, |
|
760 void** pp) |
|
761 { |
|
762 MOZ_ASSERT(pp,"null pointer"); |
|
763 if (type.IsInterfacePointer()) { |
|
764 nsISupports* p = *((nsISupports**)pp); |
|
765 if (p) p->Release(); |
|
766 } else { |
|
767 void* p = *((void**)pp); |
|
768 if (p) nsMemory::Free(p); |
|
769 } |
|
770 } |
|
771 |
|
772 class AutoClearPendingException |
|
773 { |
|
774 public: |
|
775 AutoClearPendingException(JSContext *cx) : mCx(cx) { } |
|
776 ~AutoClearPendingException() { JS_ClearPendingException(mCx); } |
|
777 private: |
|
778 JSContext* mCx; |
|
779 }; |
|
780 |
|
781 nsresult |
|
782 nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, |
|
783 const char * aPropertyName, |
|
784 const char * anInterfaceName, |
|
785 bool aForceReport) |
|
786 { |
|
787 XPCContext * xpcc = ccx.GetXPCContext(); |
|
788 JSContext * cx = ccx.GetJSContext(); |
|
789 nsCOMPtr<nsIException> xpc_exception; |
|
790 /* this one would be set by our error reporter */ |
|
791 |
|
792 xpcc->GetException(getter_AddRefs(xpc_exception)); |
|
793 if (xpc_exception) |
|
794 xpcc->SetException(nullptr); |
|
795 |
|
796 // get this right away in case we do something below to cause JS code |
|
797 // to run on this JSContext |
|
798 nsresult pending_result = xpcc->GetPendingResult(); |
|
799 |
|
800 RootedValue js_exception(cx); |
|
801 bool is_js_exception = JS_GetPendingException(cx, &js_exception); |
|
802 |
|
803 /* JS might throw an expection whether the reporter was called or not */ |
|
804 if (is_js_exception) { |
|
805 if (!xpc_exception) |
|
806 XPCConvert::JSValToXPCException(&js_exception, anInterfaceName, |
|
807 aPropertyName, |
|
808 getter_AddRefs(xpc_exception)); |
|
809 |
|
810 /* cleanup and set failed even if we can't build an exception */ |
|
811 if (!xpc_exception) { |
|
812 XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary? |
|
813 } |
|
814 } |
|
815 |
|
816 AutoClearPendingException acpe(cx); |
|
817 |
|
818 if (xpc_exception) { |
|
819 nsresult e_result; |
|
820 if (NS_SUCCEEDED(xpc_exception->GetResult(&e_result))) { |
|
821 // Figure out whether or not we should report this exception. |
|
822 bool reportable = xpc_IsReportableErrorCode(e_result); |
|
823 if (reportable) { |
|
824 // Always want to report forced exceptions and XPConnect's own |
|
825 // errors. |
|
826 reportable = aForceReport || |
|
827 NS_ERROR_GET_MODULE(e_result) == NS_ERROR_MODULE_XPCONNECT; |
|
828 |
|
829 // See if an environment variable was set or someone has told us |
|
830 // that a user pref was set indicating that we should report all |
|
831 // exceptions. |
|
832 if (!reportable) |
|
833 reportable = nsXPConnect::ReportAllJSExceptions(); |
|
834 |
|
835 // Finally, check to see if this is the last JS frame on the |
|
836 // stack. If so then we always want to report it. |
|
837 if (!reportable) |
|
838 reportable = !JS::DescribeScriptedCaller(cx); |
|
839 |
|
840 // Ugly special case for GetInterface. It's "special" in the |
|
841 // same way as QueryInterface in that a failure is not |
|
842 // exceptional and shouldn't be reported. We have to do this |
|
843 // check here instead of in xpcwrappedjs (like we do for QI) to |
|
844 // avoid adding extra code to all xpcwrappedjs objects. |
|
845 if (reportable && e_result == NS_ERROR_NO_INTERFACE && |
|
846 !strcmp(anInterfaceName, "nsIInterfaceRequestor") && |
|
847 !strcmp(aPropertyName, "getInterface")) { |
|
848 reportable = false; |
|
849 } |
|
850 |
|
851 // More special case, see bug 877760. |
|
852 if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) { |
|
853 reportable = false; |
|
854 } |
|
855 } |
|
856 |
|
857 // Try to use the error reporter set on the context to handle this |
|
858 // error if it came from a JS exception. |
|
859 if (reportable && is_js_exception && |
|
860 JS_GetErrorReporter(cx) != xpcWrappedJSErrorReporter) |
|
861 { |
|
862 // If the error reporter ignores the error, it will call |
|
863 // xpc->MarkErrorUnreported(). |
|
864 xpcc->ClearUnreportedError(); |
|
865 reportable = !JS_ReportPendingException(cx); |
|
866 if (!xpcc->WasErrorReported()) |
|
867 reportable = true; |
|
868 } |
|
869 |
|
870 if (reportable) { |
|
871 if (nsContentUtils::DOMWindowDumpEnabled()) { |
|
872 static const char line[] = |
|
873 "************************************************************\n"; |
|
874 static const char preamble[] = |
|
875 "* Call to xpconnect wrapped JSObject produced this error: *\n"; |
|
876 static const char cant_get_text[] = |
|
877 "FAILED TO GET TEXT FROM EXCEPTION\n"; |
|
878 |
|
879 fputs(line, stdout); |
|
880 fputs(preamble, stdout); |
|
881 nsCString text; |
|
882 if (NS_SUCCEEDED(xpc_exception->ToString(text)) && |
|
883 !text.IsEmpty()) { |
|
884 fputs(text.get(), stdout); |
|
885 fputs("\n", stdout); |
|
886 } else |
|
887 fputs(cant_get_text, stdout); |
|
888 fputs(line, stdout); |
|
889 } |
|
890 |
|
891 // Log the exception to the JS Console, so that users can do |
|
892 // something with it. |
|
893 nsCOMPtr<nsIConsoleService> consoleService |
|
894 (do_GetService(XPC_CONSOLE_CONTRACTID)); |
|
895 if (nullptr != consoleService) { |
|
896 nsresult rv; |
|
897 nsCOMPtr<nsIScriptError> scriptError; |
|
898 nsCOMPtr<nsISupports> errorData; |
|
899 rv = xpc_exception->GetData(getter_AddRefs(errorData)); |
|
900 if (NS_SUCCEEDED(rv)) |
|
901 scriptError = do_QueryInterface(errorData); |
|
902 |
|
903 if (nullptr == scriptError) { |
|
904 // No luck getting one from the exception, so |
|
905 // try to cook one up. |
|
906 scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID); |
|
907 if (nullptr != scriptError) { |
|
908 nsCString newMessage; |
|
909 rv = xpc_exception->ToString(newMessage); |
|
910 if (NS_SUCCEEDED(rv)) { |
|
911 // try to get filename, lineno from the first |
|
912 // stack frame location. |
|
913 int32_t lineNumber = 0; |
|
914 nsString sourceName; |
|
915 |
|
916 nsCOMPtr<nsIStackFrame> location; |
|
917 xpc_exception-> |
|
918 GetLocation(getter_AddRefs(location)); |
|
919 if (location) { |
|
920 // Get line number w/o checking; 0 is ok. |
|
921 location->GetLineNumber(&lineNumber); |
|
922 |
|
923 // get a filename. |
|
924 rv = location->GetFilename(sourceName); |
|
925 } |
|
926 |
|
927 rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(newMessage), |
|
928 sourceName, |
|
929 EmptyString(), |
|
930 lineNumber, 0, 0, |
|
931 "XPConnect JavaScript", |
|
932 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx)); |
|
933 if (NS_FAILED(rv)) |
|
934 scriptError = nullptr; |
|
935 } |
|
936 } |
|
937 } |
|
938 if (nullptr != scriptError) |
|
939 consoleService->LogMessage(scriptError); |
|
940 } |
|
941 } |
|
942 // Whether or not it passes the 'reportable' test, it might |
|
943 // still be an error and we have to do the right thing here... |
|
944 if (NS_FAILED(e_result)) { |
|
945 XPCJSRuntime::Get()->SetPendingException(xpc_exception); |
|
946 return e_result; |
|
947 } |
|
948 } |
|
949 } else { |
|
950 // see if JS code signaled failure result without throwing exception |
|
951 if (NS_FAILED(pending_result)) { |
|
952 return pending_result; |
|
953 } |
|
954 } |
|
955 return NS_ERROR_FAILURE; |
|
956 } |
|
957 |
|
958 NS_IMETHODIMP |
|
959 nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, |
|
960 const XPTMethodDescriptor* info_, |
|
961 nsXPTCMiniVariant* nativeParams) |
|
962 { |
|
963 jsval* sp = nullptr; |
|
964 jsval* argv = nullptr; |
|
965 uint8_t i; |
|
966 nsresult retval = NS_ERROR_FAILURE; |
|
967 nsresult pending_result = NS_OK; |
|
968 bool success; |
|
969 bool readyToDoTheCall = false; |
|
970 nsID param_iid; |
|
971 const nsXPTMethodInfo* info = static_cast<const nsXPTMethodInfo*>(info_); |
|
972 const char* name = info->name; |
|
973 bool foundDependentParam; |
|
974 |
|
975 // Make sure not to set the callee on ccx until after we've gone through |
|
976 // the whole nsIXPCFunctionThisTranslator bit. That code uses ccx to |
|
977 // convert natives to JSObjects, but we do NOT plan to pass those JSObjects |
|
978 // to our real callee. |
|
979 AutoPushJSContext context(GetContextFromObjectOrDefault(wrapper)); |
|
980 XPCCallContext ccx(NATIVE_CALLER, context); |
|
981 if (!ccx.IsValid()) |
|
982 return retval; |
|
983 |
|
984 XPCContext *xpcc = ccx.GetXPCContext(); |
|
985 JSContext *cx = ccx.GetJSContext(); |
|
986 |
|
987 if (!cx || !xpcc || !IsReflectable(methodIndex)) |
|
988 return NS_ERROR_FAILURE; |
|
989 |
|
990 // [implicit_jscontext] and [optional_argc] have a different calling |
|
991 // convention, which we don't support for JS-implemented components. |
|
992 if (info->WantsOptArgc() || info->WantsContext()) { |
|
993 const char *str = "IDL methods marked with [implicit_jscontext] " |
|
994 "or [optional_argc] may not be implemented in JS"; |
|
995 // Throw and warn for good measure. |
|
996 JS_ReportError(cx, str); |
|
997 NS_WARNING(str); |
|
998 return NS_ERROR_FAILURE; |
|
999 } |
|
1000 |
|
1001 RootedValue fval(cx); |
|
1002 RootedObject obj(cx, wrapper->GetJSObject()); |
|
1003 RootedObject thisObj(cx, obj); |
|
1004 |
|
1005 JSAutoCompartment ac(cx, obj); |
|
1006 |
|
1007 AutoValueVector args(cx); |
|
1008 AutoScriptEvaluate scriptEval(cx); |
|
1009 |
|
1010 // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this. |
|
1011 uint8_t paramCount = info->num_args; |
|
1012 uint8_t argc = paramCount - |
|
1013 (paramCount && XPT_PD_IS_RETVAL(info->params[paramCount-1].flags) ? 1 : 0); |
|
1014 |
|
1015 if (!scriptEval.StartEvaluating(obj, xpcWrappedJSErrorReporter)) |
|
1016 goto pre_call_clean_up; |
|
1017 |
|
1018 xpcc->SetPendingResult(pending_result); |
|
1019 xpcc->SetException(nullptr); |
|
1020 XPCJSRuntime::Get()->SetPendingException(nullptr); |
|
1021 |
|
1022 // We use js_Invoke so that the gcthings we use as args will be rooted by |
|
1023 // the engine as we do conversions and prepare to do the function call. |
|
1024 |
|
1025 // setup stack |
|
1026 |
|
1027 // if this isn't a function call then we don't need to push extra stuff |
|
1028 if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags))) { |
|
1029 // We get fval before allocating the stack to avoid gc badness that can |
|
1030 // happen if the GetProperty call leaves our request and the gc runs |
|
1031 // while the stack we allocate contains garbage. |
|
1032 |
|
1033 // If the interface is marked as a [function] then we will assume that |
|
1034 // our JSObject is a function and not an object with a named method. |
|
1035 |
|
1036 bool isFunction; |
|
1037 if (NS_FAILED(mInfo->IsFunction(&isFunction))) |
|
1038 goto pre_call_clean_up; |
|
1039 |
|
1040 // In the xpidl [function] case we are making sure now that the |
|
1041 // JSObject is callable. If it is *not* callable then we silently |
|
1042 // fallback to looking up the named property... |
|
1043 // (because jst says he thinks this fallback is 'The Right Thing'.) |
|
1044 // |
|
1045 // In the normal (non-function) case we just lookup the property by |
|
1046 // name and as long as the object has such a named property we go ahead |
|
1047 // and try to make the call. If it turns out the named property is not |
|
1048 // a callable object then the JS engine will throw an error and we'll |
|
1049 // pass this along to the caller as an exception/result code. |
|
1050 |
|
1051 fval = ObjectValue(*obj); |
|
1052 if (isFunction && |
|
1053 JS_TypeOfValue(ccx, fval) == JSTYPE_FUNCTION) { |
|
1054 |
|
1055 // We may need to translate the 'this' for the function object. |
|
1056 |
|
1057 if (paramCount) { |
|
1058 const nsXPTParamInfo& firstParam = info->params[0]; |
|
1059 if (firstParam.IsIn()) { |
|
1060 const nsXPTType& firstType = firstParam.GetType(); |
|
1061 |
|
1062 if (firstType.IsInterfacePointer()) { |
|
1063 nsIXPCFunctionThisTranslator* translator; |
|
1064 |
|
1065 IID2ThisTranslatorMap* map = |
|
1066 mRuntime->GetThisTranslatorMap(); |
|
1067 |
|
1068 translator = map->Find(mIID); |
|
1069 |
|
1070 if (translator) { |
|
1071 nsCOMPtr<nsISupports> newThis; |
|
1072 if (NS_FAILED(translator-> |
|
1073 TranslateThis((nsISupports*)nativeParams[0].val.p, |
|
1074 getter_AddRefs(newThis)))) { |
|
1075 goto pre_call_clean_up; |
|
1076 } |
|
1077 if (newThis) { |
|
1078 RootedValue v(cx); |
|
1079 xpcObjectHelper helper(newThis); |
|
1080 bool ok = |
|
1081 XPCConvert::NativeInterface2JSObject( |
|
1082 &v, nullptr, helper, nullptr, |
|
1083 nullptr, false, nullptr); |
|
1084 if (!ok) { |
|
1085 goto pre_call_clean_up; |
|
1086 } |
|
1087 thisObj = JSVAL_TO_OBJECT(v); |
|
1088 if (!JS_WrapObject(cx, &thisObj)) |
|
1089 goto pre_call_clean_up; |
|
1090 } |
|
1091 } |
|
1092 } |
|
1093 } |
|
1094 } |
|
1095 } else { |
|
1096 if (!JS_GetProperty(cx, obj, name, &fval)) |
|
1097 goto pre_call_clean_up; |
|
1098 // XXX We really want to factor out the error reporting better and |
|
1099 // specifically report the failure to find a function with this name. |
|
1100 // This is what we do below if the property is found but is not a |
|
1101 // function. We just need to factor better so we can get to that |
|
1102 // reporting path from here. |
|
1103 |
|
1104 thisObj = obj; |
|
1105 } |
|
1106 } |
|
1107 |
|
1108 if (!args.resize(argc)) { |
|
1109 retval = NS_ERROR_OUT_OF_MEMORY; |
|
1110 goto pre_call_clean_up; |
|
1111 } |
|
1112 |
|
1113 argv = args.begin(); |
|
1114 sp = argv; |
|
1115 |
|
1116 // build the args |
|
1117 // NB: This assignment *looks* wrong because we haven't yet called our |
|
1118 // function. However, we *have* already entered the compartmen that we're |
|
1119 // about to call, and that's the global that we want here. In other words: |
|
1120 // we're trusting the JS engine to come up with a good global to use for |
|
1121 // our object (whatever it was). |
|
1122 for (i = 0; i < argc; i++) { |
|
1123 const nsXPTParamInfo& param = info->params[i]; |
|
1124 const nsXPTType& type = param.GetType(); |
|
1125 nsXPTType datum_type; |
|
1126 uint32_t array_count; |
|
1127 bool isArray = type.IsArray(); |
|
1128 RootedValue val(cx, NullValue()); |
|
1129 bool isSizedString = isArray ? |
|
1130 false : |
|
1131 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || |
|
1132 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS; |
|
1133 |
|
1134 |
|
1135 // verify that null was not passed for 'out' param |
|
1136 if (param.IsOut() && !nativeParams[i].val.p) { |
|
1137 retval = NS_ERROR_INVALID_ARG; |
|
1138 goto pre_call_clean_up; |
|
1139 } |
|
1140 |
|
1141 if (isArray) { |
|
1142 if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, ¶m, 1, |
|
1143 &datum_type))) |
|
1144 goto pre_call_clean_up; |
|
1145 } else |
|
1146 datum_type = type; |
|
1147 |
|
1148 if (param.IsIn()) { |
|
1149 nsXPTCMiniVariant* pv; |
|
1150 |
|
1151 if (param.IsIndirect()) |
|
1152 pv = (nsXPTCMiniVariant*) nativeParams[i].val.p; |
|
1153 else |
|
1154 pv = &nativeParams[i]; |
|
1155 |
|
1156 if (datum_type.IsInterfacePointer() && |
|
1157 !GetInterfaceTypeFromParam(cx, info, param, methodIndex, |
|
1158 datum_type, nativeParams, |
|
1159 ¶m_iid)) |
|
1160 goto pre_call_clean_up; |
|
1161 |
|
1162 if (isArray || isSizedString) { |
|
1163 if (!GetArraySizeFromParam(cx, info, param, methodIndex, |
|
1164 i, nativeParams, &array_count)) |
|
1165 goto pre_call_clean_up; |
|
1166 } |
|
1167 |
|
1168 if (isArray) { |
|
1169 if (!XPCConvert::NativeArray2JS(&val, |
|
1170 (const void**)&pv->val, |
|
1171 datum_type, ¶m_iid, |
|
1172 array_count, nullptr)) |
|
1173 goto pre_call_clean_up; |
|
1174 } else if (isSizedString) { |
|
1175 if (!XPCConvert::NativeStringWithSize2JS(&val, |
|
1176 (const void*)&pv->val, |
|
1177 datum_type, |
|
1178 array_count, nullptr)) |
|
1179 goto pre_call_clean_up; |
|
1180 } else { |
|
1181 if (!XPCConvert::NativeData2JS(&val, &pv->val, type, |
|
1182 ¶m_iid, nullptr)) |
|
1183 goto pre_call_clean_up; |
|
1184 } |
|
1185 } |
|
1186 |
|
1187 if (param.IsOut() || param.IsDipper()) { |
|
1188 // create an 'out' object |
|
1189 RootedObject out_obj(cx, NewOutObject(cx, obj)); |
|
1190 if (!out_obj) { |
|
1191 retval = NS_ERROR_OUT_OF_MEMORY; |
|
1192 goto pre_call_clean_up; |
|
1193 } |
|
1194 |
|
1195 if (param.IsIn()) { |
|
1196 if (!JS_SetPropertyById(cx, out_obj, |
|
1197 mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE), |
|
1198 val)) { |
|
1199 goto pre_call_clean_up; |
|
1200 } |
|
1201 } |
|
1202 *sp++ = OBJECT_TO_JSVAL(out_obj); |
|
1203 } else |
|
1204 *sp++ = val; |
|
1205 } |
|
1206 |
|
1207 readyToDoTheCall = true; |
|
1208 |
|
1209 pre_call_clean_up: |
|
1210 // clean up any 'out' params handed in |
|
1211 for (i = 0; i < paramCount; i++) { |
|
1212 const nsXPTParamInfo& param = info->params[i]; |
|
1213 if (!param.IsOut()) |
|
1214 continue; |
|
1215 |
|
1216 const nsXPTType& type = param.GetType(); |
|
1217 if (!type.deprecated_IsPointer()) |
|
1218 continue; |
|
1219 void* p; |
|
1220 if (!(p = nativeParams[i].val.p)) |
|
1221 continue; |
|
1222 |
|
1223 if (param.IsIn()) { |
|
1224 if (type.IsArray()) { |
|
1225 void** pp; |
|
1226 if (nullptr != (pp = *((void***)p))) { |
|
1227 |
|
1228 // we need to get the array length and iterate the items |
|
1229 uint32_t array_count; |
|
1230 nsXPTType datum_type; |
|
1231 |
|
1232 if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, |
|
1233 1, &datum_type)) && |
|
1234 datum_type.deprecated_IsPointer() && |
|
1235 GetArraySizeFromParam(cx, info, param, methodIndex, |
|
1236 i, nativeParams, &array_count) && |
|
1237 array_count) { |
|
1238 |
|
1239 CleanupPointerArray(datum_type, array_count, pp); |
|
1240 } |
|
1241 |
|
1242 // always release the array if it is inout |
|
1243 nsMemory::Free(pp); |
|
1244 } |
|
1245 } else |
|
1246 CleanupPointerTypeObject(type, (void**)p); |
|
1247 } |
|
1248 *((void**)p) = nullptr; |
|
1249 } |
|
1250 |
|
1251 // Make sure "this" doesn't get deleted during this call. |
|
1252 nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this); |
|
1253 |
|
1254 if (!readyToDoTheCall) |
|
1255 return retval; |
|
1256 |
|
1257 // do the deed - note exceptions |
|
1258 |
|
1259 JS_ClearPendingException(cx); |
|
1260 |
|
1261 RootedValue rval(cx); |
|
1262 if (XPT_MD_IS_GETTER(info->flags)) { |
|
1263 success = JS_GetProperty(cx, obj, name, &rval); |
|
1264 } else if (XPT_MD_IS_SETTER(info->flags)) { |
|
1265 rval = *argv; |
|
1266 success = JS_SetProperty(cx, obj, name, rval); |
|
1267 } else { |
|
1268 if (!JSVAL_IS_PRIMITIVE(fval)) { |
|
1269 AutoSaveContextOptions asco(cx); |
|
1270 ContextOptionsRef(cx).setDontReportUncaught(true); |
|
1271 |
|
1272 success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval); |
|
1273 } else { |
|
1274 // The property was not an object so can't be a function. |
|
1275 // Let's build and 'throw' an exception. |
|
1276 |
|
1277 static const nsresult code = |
|
1278 NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED; |
|
1279 static const char format[] = "%s \"%s\""; |
|
1280 const char * msg; |
|
1281 char* sz = nullptr; |
|
1282 |
|
1283 if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg) |
|
1284 sz = JS_smprintf(format, msg, name); |
|
1285 |
|
1286 nsCOMPtr<nsIException> e; |
|
1287 |
|
1288 XPCConvert::ConstructException(code, sz, GetInterfaceName(), name, |
|
1289 nullptr, getter_AddRefs(e), nullptr, nullptr); |
|
1290 xpcc->SetException(e); |
|
1291 if (sz) |
|
1292 JS_smprintf_free(sz); |
|
1293 success = false; |
|
1294 } |
|
1295 } |
|
1296 |
|
1297 if (!success) { |
|
1298 bool forceReport; |
|
1299 if (NS_FAILED(mInfo->IsFunction(&forceReport))) |
|
1300 forceReport = false; |
|
1301 |
|
1302 // May also want to check if we're moving from content->chrome and force |
|
1303 // a report in that case. |
|
1304 |
|
1305 return CheckForException(ccx, name, GetInterfaceName(), forceReport); |
|
1306 } |
|
1307 |
|
1308 XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary? |
|
1309 |
|
1310 // convert out args and result |
|
1311 // NOTE: this is the total number of native params, not just the args |
|
1312 // Convert independent params only. |
|
1313 // When we later convert the dependent params (if any) we will know that |
|
1314 // the params upon which they depend will have already been converted - |
|
1315 // regardless of ordering. |
|
1316 |
|
1317 foundDependentParam = false; |
|
1318 for (i = 0; i < paramCount; i++) { |
|
1319 const nsXPTParamInfo& param = info->params[i]; |
|
1320 MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!"); |
|
1321 if (!param.IsOut() && !param.IsDipper()) |
|
1322 continue; |
|
1323 |
|
1324 const nsXPTType& type = param.GetType(); |
|
1325 if (type.IsDependent()) { |
|
1326 foundDependentParam = true; |
|
1327 continue; |
|
1328 } |
|
1329 |
|
1330 RootedValue val(cx); |
|
1331 uint8_t type_tag = type.TagPart(); |
|
1332 nsXPTCMiniVariant* pv; |
|
1333 |
|
1334 if (param.IsDipper()) |
|
1335 pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p; |
|
1336 else |
|
1337 pv = (nsXPTCMiniVariant*) nativeParams[i].val.p; |
|
1338 |
|
1339 if (param.IsRetval()) |
|
1340 val = rval; |
|
1341 else if (argv[i].isPrimitive()) |
|
1342 break; |
|
1343 else { |
|
1344 RootedObject obj(cx, &argv[i].toObject()); |
|
1345 if (!JS_GetPropertyById(cx, obj, |
|
1346 mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE), |
|
1347 &val)) |
|
1348 break; |
|
1349 } |
|
1350 |
|
1351 // setup allocator and/or iid |
|
1352 |
|
1353 if (type_tag == nsXPTType::T_INTERFACE) { |
|
1354 if (NS_FAILED(GetInterfaceInfo()-> |
|
1355 GetIIDForParamNoAlloc(methodIndex, ¶m, |
|
1356 ¶m_iid))) |
|
1357 break; |
|
1358 } |
|
1359 |
|
1360 // see bug #961488 |
|
1361 #if (defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(_AIX)) && \ |
|
1362 ((defined(__sparc) && !defined(__sparcv9) && !defined(__sparcv9__)) || \ |
|
1363 (defined(__powerpc__) && !defined (__powerpc64__))) |
|
1364 if (type_tag == nsXPTType::T_JSVAL) { |
|
1365 if (!XPCConvert::JSData2Native(*(void**)(&pv->val), val, type, |
|
1366 !param.IsDipper(), ¶m_iid, nullptr)) |
|
1367 break; |
|
1368 } else |
|
1369 #endif |
|
1370 { |
|
1371 if (!XPCConvert::JSData2Native(&pv->val, val, type, |
|
1372 !param.IsDipper(), ¶m_iid, nullptr)) |
|
1373 break; |
|
1374 } |
|
1375 } |
|
1376 |
|
1377 // if any params were dependent, then we must iterate again to convert them. |
|
1378 if (foundDependentParam && i == paramCount) { |
|
1379 for (i = 0; i < paramCount; i++) { |
|
1380 const nsXPTParamInfo& param = info->params[i]; |
|
1381 if (!param.IsOut()) |
|
1382 continue; |
|
1383 |
|
1384 const nsXPTType& type = param.GetType(); |
|
1385 if (!type.IsDependent()) |
|
1386 continue; |
|
1387 |
|
1388 RootedValue val(cx); |
|
1389 nsXPTCMiniVariant* pv; |
|
1390 nsXPTType datum_type; |
|
1391 uint32_t array_count; |
|
1392 bool isArray = type.IsArray(); |
|
1393 bool isSizedString = isArray ? |
|
1394 false : |
|
1395 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || |
|
1396 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS; |
|
1397 |
|
1398 pv = (nsXPTCMiniVariant*) nativeParams[i].val.p; |
|
1399 |
|
1400 if (param.IsRetval()) |
|
1401 val = rval; |
|
1402 else { |
|
1403 RootedObject obj(cx, &argv[i].toObject()); |
|
1404 if (!JS_GetPropertyById(cx, obj, |
|
1405 mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE), |
|
1406 &val)) |
|
1407 break; |
|
1408 } |
|
1409 |
|
1410 // setup allocator and/or iid |
|
1411 |
|
1412 if (isArray) { |
|
1413 if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, ¶m, 1, |
|
1414 &datum_type))) |
|
1415 break; |
|
1416 } else |
|
1417 datum_type = type; |
|
1418 |
|
1419 if (datum_type.IsInterfacePointer()) { |
|
1420 if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex, |
|
1421 datum_type, nativeParams, |
|
1422 ¶m_iid)) |
|
1423 break; |
|
1424 } |
|
1425 |
|
1426 if (isArray || isSizedString) { |
|
1427 if (!GetArraySizeFromParam(cx, info, param, methodIndex, |
|
1428 i, nativeParams, &array_count)) |
|
1429 break; |
|
1430 } |
|
1431 |
|
1432 if (isArray) { |
|
1433 if (array_count && |
|
1434 !XPCConvert::JSArray2Native((void**)&pv->val, val, |
|
1435 array_count, datum_type, |
|
1436 ¶m_iid, nullptr)) |
|
1437 break; |
|
1438 } else if (isSizedString) { |
|
1439 if (!XPCConvert::JSStringWithSize2Native((void*)&pv->val, val, |
|
1440 array_count, datum_type, |
|
1441 nullptr)) |
|
1442 break; |
|
1443 } else { |
|
1444 if (!XPCConvert::JSData2Native(&pv->val, val, type, |
|
1445 true, ¶m_iid, |
|
1446 nullptr)) |
|
1447 break; |
|
1448 } |
|
1449 } |
|
1450 } |
|
1451 |
|
1452 if (i != paramCount) { |
|
1453 // We didn't manage all the result conversions! |
|
1454 // We have to cleanup any junk that *did* get converted. |
|
1455 |
|
1456 for (uint8_t k = 0; k < i; k++) { |
|
1457 const nsXPTParamInfo& param = info->params[k]; |
|
1458 if (!param.IsOut()) |
|
1459 continue; |
|
1460 const nsXPTType& type = param.GetType(); |
|
1461 if (!type.deprecated_IsPointer()) |
|
1462 continue; |
|
1463 void* p; |
|
1464 if (!(p = nativeParams[k].val.p)) |
|
1465 continue; |
|
1466 |
|
1467 if (type.IsArray()) { |
|
1468 void** pp; |
|
1469 if (nullptr != (pp = *((void***)p))) { |
|
1470 // we need to get the array length and iterate the items |
|
1471 uint32_t array_count; |
|
1472 nsXPTType datum_type; |
|
1473 |
|
1474 if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, |
|
1475 1, &datum_type)) && |
|
1476 datum_type.deprecated_IsPointer() && |
|
1477 GetArraySizeFromParam(cx, info, param, methodIndex, |
|
1478 k, nativeParams, &array_count) && |
|
1479 array_count) { |
|
1480 |
|
1481 CleanupPointerArray(datum_type, array_count, pp); |
|
1482 } |
|
1483 nsMemory::Free(pp); |
|
1484 } |
|
1485 } else |
|
1486 CleanupPointerTypeObject(type, (void**)p); |
|
1487 *((void**)p) = nullptr; |
|
1488 } |
|
1489 } else { |
|
1490 // set to whatever the JS code might have set as the result |
|
1491 retval = pending_result; |
|
1492 } |
|
1493 |
|
1494 return retval; |
|
1495 } |
|
1496 |
|
1497 const char* |
|
1498 nsXPCWrappedJSClass::GetInterfaceName() |
|
1499 { |
|
1500 if (!mName) |
|
1501 mInfo->GetName(&mName); |
|
1502 return mName; |
|
1503 } |
|
1504 |
|
1505 static void |
|
1506 FinalizeStub(JSFreeOp *fop, JSObject *obj) |
|
1507 { |
|
1508 } |
|
1509 |
|
1510 static const JSClass XPCOutParamClass = { |
|
1511 "XPCOutParam", |
|
1512 0, |
|
1513 JS_PropertyStub, |
|
1514 JS_DeletePropertyStub, |
|
1515 JS_PropertyStub, |
|
1516 JS_StrictPropertyStub, |
|
1517 JS_EnumerateStub, |
|
1518 JS_ResolveStub, |
|
1519 JS_ConvertStub, |
|
1520 FinalizeStub, |
|
1521 nullptr, /* call */ |
|
1522 nullptr, /* hasInstance */ |
|
1523 nullptr, /* construct */ |
|
1524 nullptr /* trace */ |
|
1525 }; |
|
1526 |
|
1527 bool |
|
1528 xpc::IsOutObject(JSContext* cx, JSObject* obj) |
|
1529 { |
|
1530 return js::GetObjectJSClass(obj) == &XPCOutParamClass; |
|
1531 } |
|
1532 |
|
1533 JSObject* |
|
1534 xpc::NewOutObject(JSContext* cx, JSObject* scope) |
|
1535 { |
|
1536 RootedObject global(cx, JS_GetGlobalForObject(cx, scope)); |
|
1537 return JS_NewObject(cx, nullptr, NullPtr(), global); |
|
1538 } |
|
1539 |
|
1540 |
|
1541 NS_IMETHODIMP |
|
1542 nsXPCWrappedJSClass::DebugDump(int16_t depth) |
|
1543 { |
|
1544 #ifdef DEBUG |
|
1545 depth-- ; |
|
1546 XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x with mRefCnt = %d", this, mRefCnt.get())); |
|
1547 XPC_LOG_INDENT(); |
|
1548 char* name; |
|
1549 mInfo->GetName(&name); |
|
1550 XPC_LOG_ALWAYS(("interface name is %s", name)); |
|
1551 if (name) |
|
1552 nsMemory::Free(name); |
|
1553 char * iid = mIID.ToString(); |
|
1554 XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid")); |
|
1555 if (iid) |
|
1556 NS_Free(iid); |
|
1557 XPC_LOG_ALWAYS(("InterfaceInfo @ %x", mInfo.get())); |
|
1558 uint16_t methodCount = 0; |
|
1559 if (depth) { |
|
1560 uint16_t i; |
|
1561 nsCOMPtr<nsIInterfaceInfo> parent; |
|
1562 XPC_LOG_INDENT(); |
|
1563 mInfo->GetParent(getter_AddRefs(parent)); |
|
1564 XPC_LOG_ALWAYS(("parent @ %x", parent.get())); |
|
1565 mInfo->GetMethodCount(&methodCount); |
|
1566 XPC_LOG_ALWAYS(("MethodCount = %d", methodCount)); |
|
1567 mInfo->GetConstantCount(&i); |
|
1568 XPC_LOG_ALWAYS(("ConstantCount = %d", i)); |
|
1569 XPC_LOG_OUTDENT(); |
|
1570 } |
|
1571 XPC_LOG_ALWAYS(("mRuntime @ %x", mRuntime)); |
|
1572 XPC_LOG_ALWAYS(("mDescriptors @ %x count = %d", mDescriptors, methodCount)); |
|
1573 if (depth && mDescriptors && methodCount) { |
|
1574 depth--; |
|
1575 XPC_LOG_INDENT(); |
|
1576 for (uint16_t i = 0; i < methodCount; i++) { |
|
1577 XPC_LOG_ALWAYS(("Method %d is %s%s", \ |
|
1578 i, IsReflectable(i) ? "":" NOT ","reflectable")); |
|
1579 } |
|
1580 XPC_LOG_OUTDENT(); |
|
1581 depth++; |
|
1582 } |
|
1583 XPC_LOG_OUTDENT(); |
|
1584 #endif |
|
1585 return NS_OK; |
|
1586 } |