js/xpconnect/src/XPCWrappedJSClass.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f245ebec36b0
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, &param, 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, &param, 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 &param, &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, &param, 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 &param_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, &param_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 &param_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, &param,
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, &param,
1356 &param_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(), &param_iid, nullptr))
1367 break;
1368 } else
1369 #endif
1370 {
1371 if (!XPCConvert::JSData2Native(&pv->val, val, type,
1372 !param.IsDipper(), &param_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, &param, 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 &param_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 &param_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, &param_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, &param,
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 }

mercurial