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 "js/Printf.h"
11 #include "nsArrayEnumerator.h"
12 #include "nsINamed.h"
13 #include "nsIScriptError.h"
14 #include "nsWrapperCache.h"
15 #include "AccessCheck.h"
16 #include "nsJSUtils.h"
17 #include "nsPrintfCString.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/dom/BindingUtils.h"
20 #include "mozilla/dom/DOMException.h"
21 #include "mozilla/dom/DOMExceptionBinding.h"
22 #include "mozilla/dom/DOMPrefs.h"
23 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
24
25 #include "jsapi.h"
26 #include "jsfriendapi.h"
27
28 using namespace xpc;
29 using namespace JS;
30 using namespace mozilla;
31 using namespace mozilla::dom;
32
33 NS_IMPL_ISUPPORTS(nsXPCWrappedJSClass, nsIXPCWrappedJSClass)
34
35 // the value of this variable is never used - we use its address as a sentinel
36 static uint32_t zero_methods_descriptor;
37
StartEvaluating(HandleObject scope)38 bool AutoScriptEvaluate::StartEvaluating(HandleObject scope) {
39 NS_PRECONDITION(!mEvaluated,
40 "AutoScriptEvaluate::Evaluate should only be called once");
41
42 if (!mJSContext) return true;
43
44 mEvaluated = true;
45
46 JS_BeginRequest(mJSContext);
47 mAutoCompartment.emplace(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.emplace(mJSContext);
56
57 return true;
58 }
59
~AutoScriptEvaluate()60 AutoScriptEvaluate::~AutoScriptEvaluate() {
61 if (!mJSContext || !mEvaluated) return;
62 mState->restore();
63
64 JS_EndRequest(mJSContext);
65 }
66
67 // It turns out that some errors may be not worth reporting. So, this
68 // function is factored out to manage that.
xpc_IsReportableErrorCode(nsresult code)69 bool xpc_IsReportableErrorCode(nsresult code) {
70 if (NS_SUCCEEDED(code)) return false;
71
72 switch (code) {
73 // Error codes that we don't want to report as errors...
74 // These generally indicate bad interface design AFAIC.
75 case NS_ERROR_FACTORY_REGISTER_AGAIN:
76 case NS_BASE_STREAM_WOULD_BLOCK:
77 return false;
78 default:
79 return true;
80 }
81 }
82
83 // A little stack-based RAII class to help management of the XPCJSContext
84 // PendingResult.
85 class MOZ_STACK_CLASS AutoSavePendingResult {
86 public:
AutoSavePendingResult(XPCJSContext * xpccx)87 explicit AutoSavePendingResult(XPCJSContext* xpccx) : mXPCContext(xpccx) {
88 // Save any existing pending result and reset to NS_OK for this invocation.
89 mSavedResult = xpccx->GetPendingResult();
90 xpccx->SetPendingResult(NS_OK);
91 }
~AutoSavePendingResult()92 ~AutoSavePendingResult() { mXPCContext->SetPendingResult(mSavedResult); }
93
94 private:
95 XPCJSContext* mXPCContext;
96 nsresult mSavedResult;
97 };
98
99 // static
GetNewOrUsed(JSContext * cx,REFNSIID aIID,bool allowNonScriptable)100 already_AddRefed<nsXPCWrappedJSClass> nsXPCWrappedJSClass::GetNewOrUsed(
101 JSContext* cx, REFNSIID aIID, bool allowNonScriptable) {
102 XPCJSRuntime* xpcrt = nsXPConnect::GetRuntimeInstance();
103 IID2WrappedJSClassMap* map = xpcrt->GetWrappedJSClassMap();
104 RefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID);
105
106 if (!clasp) {
107 nsCOMPtr<nsIInterfaceInfo> info;
108 nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
109 if (info) {
110 bool canScript, isBuiltin;
111 if (NS_SUCCEEDED(info->IsScriptable(&canScript)) &&
112 (canScript || allowNonScriptable) &&
113 NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin &&
114 nsXPConnect::IsISupportsDescendant(info)) {
115 clasp = new nsXPCWrappedJSClass(cx, aIID, info);
116 if (!clasp->mDescriptors) clasp = nullptr;
117 }
118 }
119 }
120 return clasp.forget();
121 }
122
nsXPCWrappedJSClass(JSContext * cx,REFNSIID aIID,nsIInterfaceInfo * aInfo)123 nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
124 nsIInterfaceInfo* aInfo)
125 : mRuntime(nsXPConnect::GetRuntimeInstance()),
126 mInfo(aInfo),
127 mName(nullptr),
128 mIID(aIID),
129 mDescriptors(nullptr) {
130 mRuntime->GetWrappedJSClassMap()->Add(this);
131
132 uint16_t methodCount;
133 if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) {
134 if (methodCount) {
135 int wordCount = (methodCount / 32) + 1;
136 if (nullptr != (mDescriptors = new uint32_t[wordCount])) {
137 int i;
138 // init flags to 0;
139 for (i = wordCount - 1; i >= 0; i--) mDescriptors[i] = 0;
140
141 for (i = 0; i < methodCount; i++) {
142 const nsXPTMethodInfo* info;
143 if (NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info)))
144 SetReflectable(i, XPCConvert::IsMethodReflectable(*info));
145 else {
146 delete[] mDescriptors;
147 mDescriptors = nullptr;
148 break;
149 }
150 }
151 }
152 } else {
153 mDescriptors = &zero_methods_descriptor;
154 }
155 }
156 }
157
~nsXPCWrappedJSClass()158 nsXPCWrappedJSClass::~nsXPCWrappedJSClass() {
159 if (mDescriptors && mDescriptors != &zero_methods_descriptor)
160 delete[] mDescriptors;
161 if (mRuntime) mRuntime->GetWrappedJSClassMap()->Remove(this);
162
163 if (mName) free(mName);
164 }
165
CallQueryInterfaceOnJSObject(JSContext * cx,JSObject * jsobjArg,REFNSIID aIID)166 JSObject* nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
167 JSObject* jsobjArg,
168 REFNSIID aIID) {
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 // In bug 503926, we added a security check to make sure that we don't
177 // invoke content QI functions. In the modern world, this is probably
178 // unnecessary, because invoking QI involves passing an IID object to
179 // content, which will fail. But we do a belt-and-suspenders check to
180 // make sure that content can never trigger the rat's nest of code below.
181 // Once we completely turn off XPConnect for the web, this can definitely
182 // go away.
183 if (!AccessCheck::isChrome(jsobj) ||
184 !AccessCheck::isChrome(js::UncheckedUnwrap(jsobj))) {
185 return nullptr;
186 }
187
188 // OK, it looks like we'll be calling into JS code.
189 AutoScriptEvaluate scriptEval(cx);
190
191 // XXX we should install an error reporter that will send reports to
192 // the JS error console service.
193 if (!scriptEval.StartEvaluating(jsobj)) return nullptr;
194
195 // check upfront for the existence of the function property
196 HandleId funid = mRuntime->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE);
197 if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || fun.isPrimitive())
198 return nullptr;
199
200 // Ensure that we are asking for a scriptable interface.
201 // NB: It's important for security that this check is here rather
202 // than later, since it prevents untrusted objects from implementing
203 // some interfaces in JS and aggregating a trusted object to
204 // implement intentionally (for security) unscriptable interfaces.
205 // We so often ask for nsISupports that we can short-circuit the test...
206 if (!aIID.Equals(NS_GET_IID(nsISupports))) {
207 bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsobj);
208
209 nsCOMPtr<nsIInterfaceInfo> info;
210 nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
211 if (!info) return nullptr;
212 bool canScript, isBuiltin;
213 if (NS_FAILED(info->IsScriptable(&canScript)) ||
214 (!canScript && !allowNonScriptable) ||
215 NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin)
216 return nullptr;
217 }
218
219 id = xpc_NewIDObject(cx, jsobj, aIID);
220 if (id) {
221 // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It
222 // is not an exception that is ever worth reporting, but we don't want
223 // to eat all exceptions either.
224
225 {
226 RootedValue arg(cx, JS::ObjectValue(*id));
227 success =
228 JS_CallFunctionValue(cx, jsobj, fun, HandleValueArray(arg), &retval);
229 }
230
231 if (!success && JS_IsExceptionPending(cx)) {
232 RootedValue jsexception(cx, NullValue());
233
234 if (JS_GetPendingException(cx, &jsexception)) {
235 if (jsexception.isObject()) {
236 // XPConnect may have constructed an object to represent a
237 // C++ QI failure. See if that is the case.
238 JS::Rooted<JSObject*> exceptionObj(cx, &jsexception.toObject());
239 Exception* e = nullptr;
240 UNWRAP_OBJECT(Exception, &exceptionObj, e);
241
242 if (e && e->GetResult() == NS_NOINTERFACE) {
243 JS_ClearPendingException(cx);
244 }
245 } else if (jsexception.isNumber()) {
246 nsresult rv;
247 // JS often throws an nsresult.
248 if (jsexception.isDouble())
249 // Visual Studio 9 doesn't allow casting directly from
250 // a double to an enumeration type, contrary to
251 // 5.2.9(10) of C++11, so add an intermediate cast.
252 rv = (nsresult)(uint32_t)(jsexception.toDouble());
253 else
254 rv = (nsresult)(jsexception.toInt32());
255
256 if (rv == NS_NOINTERFACE) JS_ClearPendingException(cx);
257 }
258 }
259 } else if (!success) {
260 NS_WARNING("QI hook ran OOMed - this is probably a bug!");
261 }
262 }
263
264 if (success) success = JS_ValueToObject(cx, retval, &retObj);
265
266 return success ? retObj.get() : nullptr;
267 }
268
269 /***************************************************************************/
270
GetNamedPropertyAsVariantRaw(XPCCallContext & ccx,HandleObject aJSObj,HandleId aName,nsIVariant ** aResult,nsresult * pErr)271 static bool GetNamedPropertyAsVariantRaw(XPCCallContext& ccx,
272 HandleObject aJSObj, HandleId aName,
273 nsIVariant** aResult, nsresult* pErr) {
274 nsXPTType type = nsXPTType((uint8_t)TD_INTERFACE_TYPE);
275 RootedValue val(ccx);
276
277 return JS_GetPropertyById(ccx, aJSObj, aName, &val) &&
278 XPCConvert::JSData2Native(aResult, val, type, &NS_GET_IID(nsIVariant),
279 pErr);
280 }
281
282 // static
GetNamedPropertyAsVariant(XPCCallContext & ccx,JSObject * aJSObjArg,const nsAString & aName,nsIVariant ** aResult)283 nsresult nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx,
284 JSObject* aJSObjArg,
285 const nsAString& aName,
286 nsIVariant** aResult) {
287 JSContext* cx = ccx.GetJSContext();
288 RootedObject aJSObj(cx, aJSObjArg);
289
290 AutoScriptEvaluate scriptEval(cx);
291 if (!scriptEval.StartEvaluating(aJSObj)) return NS_ERROR_FAILURE;
292
293 // Wrap the string in a Value after the AutoScriptEvaluate, so that the
294 // resulting value ends up in the correct compartment.
295 nsStringBuffer* buf;
296 RootedValue value(cx);
297 if (!XPCStringConvert::ReadableToJSVal(ccx, aName, &buf, &value))
298 return NS_ERROR_OUT_OF_MEMORY;
299 if (buf) buf->AddRef();
300
301 RootedId id(cx);
302 nsresult rv = NS_OK;
303 if (!JS_ValueToId(cx, value, &id) ||
304 !GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv)) {
305 if (NS_FAILED(rv)) return rv;
306 return NS_ERROR_FAILURE;
307 }
308 return NS_OK;
309 }
310
311 /***************************************************************************/
312
313 // static
BuildPropertyEnumerator(XPCCallContext & ccx,JSObject * aJSObjArg,nsISimpleEnumerator ** aEnumerate)314 nsresult nsXPCWrappedJSClass::BuildPropertyEnumerator(
315 XPCCallContext& ccx, JSObject* aJSObjArg,
316 nsISimpleEnumerator** aEnumerate) {
317 JSContext* cx = ccx.GetJSContext();
318 RootedObject aJSObj(cx, aJSObjArg);
319
320 AutoScriptEvaluate scriptEval(cx);
321 if (!scriptEval.StartEvaluating(aJSObj)) return NS_ERROR_FAILURE;
322
323 Rooted<IdVector> idArray(cx, IdVector(cx));
324 if (!JS_Enumerate(cx, aJSObj, &idArray)) return NS_ERROR_FAILURE;
325
326 nsCOMArray<nsIProperty> propertyArray(idArray.length());
327 RootedId idName(cx);
328 for (size_t i = 0; i < idArray.length(); i++) {
329 idName = idArray[i];
330
331 nsCOMPtr<nsIVariant> value;
332 nsresult rv;
333 if (!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName,
334 getter_AddRefs(value), &rv)) {
335 if (NS_FAILED(rv)) return rv;
336 return NS_ERROR_FAILURE;
337 }
338
339 RootedValue jsvalName(cx);
340 if (!JS_IdToValue(cx, idName, &jsvalName)) return NS_ERROR_FAILURE;
341
342 JSString* name = ToString(cx, jsvalName);
343 if (!name) return NS_ERROR_FAILURE;
344
345 nsAutoJSString autoStr;
346 if (!autoStr.init(cx, name)) return NS_ERROR_FAILURE;
347
348 nsCOMPtr<nsIProperty> property =
349 new xpcProperty(autoStr.get(), (uint32_t)autoStr.Length(), value);
350
351 if (!propertyArray.AppendObject(property)) return NS_ERROR_FAILURE;
352 }
353
354 return NS_NewArrayEnumerator(aEnumerate, propertyArray);
355 }
356
357 /***************************************************************************/
358
NS_IMPL_ISUPPORTS(xpcProperty,nsIProperty)359 NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty)
360
361 xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen,
362 nsIVariant* aValue)
363 : mName(aName, aNameLen), mValue(aValue) {}
364
GetName(nsAString & aName)365 NS_IMETHODIMP xpcProperty::GetName(nsAString& aName) {
366 aName.Assign(mName);
367 return NS_OK;
368 }
369
GetValue(nsIVariant ** aValue)370 NS_IMETHODIMP xpcProperty::GetValue(nsIVariant** aValue) {
371 nsCOMPtr<nsIVariant> rval = mValue;
372 rval.forget(aValue);
373 return NS_OK;
374 }
375
376 /***************************************************************************/
377
378 namespace {
379
380 class WrappedJSNamed final : public nsINamed {
381 nsCString mName;
382
~WrappedJSNamed()383 ~WrappedJSNamed() {}
384
385 public:
386 NS_DECL_ISUPPORTS
387
WrappedJSNamed(const nsACString & aName)388 explicit WrappedJSNamed(const nsACString& aName) : mName(aName) {}
389
GetName(nsACString & aName)390 NS_IMETHOD GetName(nsACString& aName) override {
391 aName = mName;
392 aName.AppendLiteral(":JS");
393 return NS_OK;
394 }
395 };
396
NS_IMPL_ISUPPORTS(WrappedJSNamed,nsINamed)397 NS_IMPL_ISUPPORTS(WrappedJSNamed, nsINamed)
398
399 nsCString GetFunctionName(JSContext* cx, HandleObject obj) {
400 RootedObject inner(cx, js::UncheckedUnwrap(obj));
401 JSAutoCompartment ac(cx, inner);
402
403 RootedFunction fun(cx, JS_GetObjectFunction(inner));
404 if (!fun) {
405 // If the object isn't a function, it's likely that it has a single
406 // function property (for things like nsITimerCallback). In this case,
407 // return the name of that function property.
408
409 Rooted<IdVector> idArray(cx, IdVector(cx));
410 if (!JS_Enumerate(cx, inner, &idArray)) {
411 JS_ClearPendingException(cx);
412 return nsCString("error");
413 }
414
415 if (idArray.length() != 1) return nsCString("nonfunction");
416
417 RootedId id(cx, idArray[0]);
418 RootedValue v(cx);
419 if (!JS_GetPropertyById(cx, inner, id, &v)) {
420 JS_ClearPendingException(cx);
421 return nsCString("nonfunction");
422 }
423
424 if (!v.isObject()) return nsCString("nonfunction");
425
426 RootedObject vobj(cx, &v.toObject());
427 return GetFunctionName(cx, vobj);
428 }
429
430 RootedString funName(cx, JS_GetFunctionDisplayId(fun));
431 RootedScript script(cx, JS_GetFunctionScript(cx, fun));
432 const char* filename = script ? JS_GetScriptFilename(script) : "anonymous";
433 const char* filenameSuffix = strrchr(filename, '/');
434
435 if (filenameSuffix) {
436 filenameSuffix++;
437 } else {
438 filenameSuffix = filename;
439 }
440
441 nsCString displayName("anonymous");
442 if (funName) {
443 nsCString* displayNamePtr = &displayName;
444 RootedValue funNameVal(cx, StringValue(funName));
445 if (!XPCConvert::JSData2Native(&displayNamePtr, funNameVal,
446 nsXPTType::T_UTF8STRING, nullptr, nullptr)) {
447 JS_ClearPendingException(cx);
448 return nsCString("anonymous");
449 }
450 }
451
452 displayName.Append('[');
453 displayName.Append(filenameSuffix, strlen(filenameSuffix));
454 displayName.Append(']');
455 return displayName;
456 }
457
458 } // anonymous namespace
459
460 /***************************************************************************/
461
462 NS_IMETHODIMP
DelegatedQueryInterface(nsXPCWrappedJS * self,REFNSIID aIID,void ** aInstancePtr)463 nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
464 REFNSIID aIID,
465 void** aInstancePtr) {
466 if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) {
467 NS_ADDREF(self);
468 *aInstancePtr = (void*)static_cast<nsIXPConnectJSObjectHolder*>(self);
469 return NS_OK;
470 }
471
472 if (aIID.Equals(NS_GET_IID(nsIPropertyBag))) {
473 // We only want to expose one implementation from our aggregate.
474 nsXPCWrappedJS* root = self->GetRootWrapper();
475
476 if (!root->IsValid()) {
477 *aInstancePtr = nullptr;
478 return NS_NOINTERFACE;
479 }
480
481 NS_ADDREF(root);
482 *aInstancePtr = (void*)static_cast<nsIPropertyBag*>(root);
483 return NS_OK;
484 }
485
486 // We can't have a cached wrapper.
487 if (aIID.Equals(NS_GET_IID(nsWrapperCache))) {
488 *aInstancePtr = nullptr;
489 return NS_NOINTERFACE;
490 }
491
492 // QI on an XPCWrappedJS can run script, so we need an AutoEntryScript.
493 // This is inherently Gecko-specific.
494 // We check both nativeGlobal and nativeGlobal->GetGlobalJSObject() even
495 // though we have derived nativeGlobal from the JS global, because we know
496 // there are cases where this can happen. See bug 1094953.
497 nsIGlobalObject* nativeGlobal =
498 NativeGlobal(js::GetGlobalForObjectCrossCompartment(self->GetJSObject()));
499 NS_ENSURE_TRUE(nativeGlobal, NS_ERROR_FAILURE);
500 NS_ENSURE_TRUE(nativeGlobal->GetGlobalJSObject(), NS_ERROR_FAILURE);
501 AutoEntryScript aes(nativeGlobal, "XPCWrappedJS QueryInterface",
502 /* aIsMainThread = */ true);
503 XPCCallContext ccx(aes.cx());
504 if (!ccx.IsValid()) {
505 *aInstancePtr = nullptr;
506 return NS_NOINTERFACE;
507 }
508
509 // We support nsISupportsWeakReference iff the root wrapped JSObject
510 // claims to support it in its QueryInterface implementation.
511 if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
512 // We only want to expose one implementation from our aggregate.
513 nsXPCWrappedJS* root = self->GetRootWrapper();
514
515 // Fail if JSObject doesn't claim support for nsISupportsWeakReference
516 if (!root->IsValid() ||
517 !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID)) {
518 *aInstancePtr = nullptr;
519 return NS_NOINTERFACE;
520 }
521
522 NS_ADDREF(root);
523 *aInstancePtr = (void*)static_cast<nsISupportsWeakReference*>(root);
524 return NS_OK;
525 }
526
527 // Checks for any existing wrapper explicitly constructed for this iid.
528 // This includes the current 'self' wrapper. This also deals with the
529 // nsISupports case (for which it returns mRoot).
530 // Also check if asking for an interface from which one of our wrappers
531 // inherits.
532 if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) {
533 NS_ADDREF(sibling);
534 *aInstancePtr = sibling->GetXPTCStub();
535 return NS_OK;
536 }
537
538 // Check if the desired interface is a function interface. If so, we don't
539 // want to QI, because the function almost certainly doesn't have a
540 // QueryInterface property, and doesn't need one.
541 bool isFunc = false;
542 nsCOMPtr<nsIInterfaceInfo> info;
543 nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
544 if (info && NS_SUCCEEDED(info->IsFunction(&isFunc)) && isFunc) {
545 RefPtr<nsXPCWrappedJS> wrapper;
546 RootedObject obj(RootingCx(), self->GetJSObject());
547 nsresult rv =
548 nsXPCWrappedJS::GetNewOrUsed(obj, aIID, getter_AddRefs(wrapper));
549
550 // Do the same thing we do for the "check for any existing wrapper" case
551 // above.
552 if (NS_SUCCEEDED(rv) && wrapper) {
553 *aInstancePtr = wrapper.forget().take()->GetXPTCStub();
554 }
555 return rv;
556 }
557
558 // else we do the more expensive stuff...
559
560 // check if the JSObject claims to implement this interface
561 RootedObject jsobj(
562 ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(), aIID));
563 if (jsobj) {
564 // We can't use XPConvert::JSObject2NativeInterface() here
565 // since that can find a XPCWrappedNative directly on the
566 // proto chain, and we don't want that here. We need to find
567 // the actual JS object that claimed it supports the interface
568 // we're looking for or we'll potentially bypass security
569 // checks etc by calling directly through to a native found on
570 // the prototype chain.
571 //
572 // Instead, simply do the nsXPCWrappedJS part of
573 // XPConvert::JSObject2NativeInterface() here to make sure we
574 // get a new (or used) nsXPCWrappedJS.
575 RefPtr<nsXPCWrappedJS> wrapper;
576 nsresult rv =
577 nsXPCWrappedJS::GetNewOrUsed(jsobj, aIID, getter_AddRefs(wrapper));
578 if (NS_SUCCEEDED(rv) && wrapper) {
579 // We need to go through the QueryInterface logic to make
580 // this return the right thing for the various 'special'
581 // interfaces; e.g. nsIPropertyBag.
582 rv = wrapper->QueryInterface(aIID, aInstancePtr);
583 return rv;
584 }
585 }
586
587 // If we're asked to QI to nsINamed, we pretend that this is possible. We'll
588 // try to return a name that makes sense for the wrapped JS value.
589 if (aIID.Equals(NS_GET_IID(nsINamed))) {
590 RootedObject obj(RootingCx(), self->GetJSObject());
591 nsCString name = GetFunctionName(ccx, obj);
592 RefPtr<WrappedJSNamed> named = new WrappedJSNamed(name);
593 *aInstancePtr = named.forget().take();
594 return NS_OK;
595 }
596
597 // else...
598 // no can do
599 *aInstancePtr = nullptr;
600 return NS_NOINTERFACE;
601 }
602
GetRootJSObject(JSContext * cx,JSObject * aJSObjArg)603 JSObject* nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx,
604 JSObject* aJSObjArg) {
605 RootedObject aJSObj(cx, aJSObjArg);
606 JSObject* result =
607 CallQueryInterfaceOnJSObject(cx, aJSObj, NS_GET_IID(nsISupports));
608 if (!result) result = aJSObj;
609 JSObject* inner = js::UncheckedUnwrap(result);
610 if (inner) return inner;
611 return result;
612 }
613
GetArraySizeFromParam(JSContext * cx,const nsXPTMethodInfo * method,const nsXPTParamInfo & param,uint16_t methodIndex,uint8_t paramIndex,nsXPTCMiniVariant * nativeParams,uint32_t * result) const614 bool nsXPCWrappedJSClass::GetArraySizeFromParam(
615 JSContext* cx, const nsXPTMethodInfo* method, const nsXPTParamInfo& param,
616 uint16_t methodIndex, uint8_t paramIndex, nsXPTCMiniVariant* nativeParams,
617 uint32_t* result) const {
618 uint8_t argnum;
619 nsresult rv;
620
621 rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, ¶m, 0, &argnum);
622 if (NS_FAILED(rv)) return false;
623
624 const nsXPTParamInfo& arg_param = method->GetParam(argnum);
625
626 // This should be enforced by the xpidl compiler, but it's not.
627 // See bug 695235.
628 MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32,
629 "size_is references parameter of invalid type.");
630
631 if (arg_param.IsIndirect())
632 *result = *(uint32_t*)nativeParams[argnum].val.p;
633 else
634 *result = nativeParams[argnum].val.u32;
635
636 return true;
637 }
638
GetInterfaceTypeFromParam(JSContext * cx,const nsXPTMethodInfo * method,const nsXPTParamInfo & param,uint16_t methodIndex,const nsXPTType & type,nsXPTCMiniVariant * nativeParams,nsID * result) const639 bool nsXPCWrappedJSClass::GetInterfaceTypeFromParam(
640 JSContext* cx, const nsXPTMethodInfo* method, const nsXPTParamInfo& param,
641 uint16_t methodIndex, const nsXPTType& type,
642 nsXPTCMiniVariant* nativeParams, nsID* result) const {
643 uint8_t type_tag = type.TagPart();
644
645 if (type_tag == nsXPTType::T_INTERFACE) {
646 if (NS_SUCCEEDED(GetInterfaceInfo()->GetIIDForParamNoAlloc(
647 methodIndex, ¶m, result))) {
648 return true;
649 }
650 } else if (type_tag == nsXPTType::T_INTERFACE_IS) {
651 uint8_t argnum;
652 nsresult rv;
653 rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex, ¶m, &argnum);
654 if (NS_FAILED(rv)) return false;
655
656 const nsXPTParamInfo& arg_param = method->GetParam(argnum);
657 const nsXPTType& arg_type = arg_param.GetType();
658
659 if (arg_type.TagPart() == nsXPTType::T_IID) {
660 if (arg_param.IsIndirect()) {
661 nsID** p = (nsID**)nativeParams[argnum].val.p;
662 if (!p || !*p) return false;
663 *result = **p;
664 } else {
665 nsID* p = (nsID*)nativeParams[argnum].val.p;
666 if (!p) return false;
667 *result = *p;
668 }
669 return true;
670 }
671 }
672 return false;
673 }
674
CleanupPointerArray(const nsXPTType & datum_type,uint32_t array_count,void ** arrayp)675 /* static */ void nsXPCWrappedJSClass::CleanupPointerArray(
676 const nsXPTType& datum_type, uint32_t array_count, void** arrayp) {
677 if (datum_type.IsInterfacePointer()) {
678 nsISupports** pp = (nsISupports**)arrayp;
679 for (uint32_t k = 0; k < array_count; k++) {
680 nsISupports* p = pp[k];
681 NS_IF_RELEASE(p);
682 }
683 } else {
684 void** pp = (void**)arrayp;
685 for (uint32_t k = 0; k < array_count; k++) {
686 void* p = pp[k];
687 if (p) free(p);
688 }
689 }
690 }
691
CleanupPointerTypeObject(const nsXPTType & type,void ** pp)692 /* static */ void nsXPCWrappedJSClass::CleanupPointerTypeObject(
693 const nsXPTType& type, void** pp) {
694 MOZ_ASSERT(pp, "null pointer");
695 if (type.IsInterfacePointer()) {
696 nsISupports* p = *((nsISupports**)pp);
697 if (p) p->Release();
698 } else {
699 void* p = *((void**)pp);
700 if (p) free(p);
701 }
702 }
703
CleanupOutparams(JSContext * cx,uint16_t methodIndex,const nsXPTMethodInfo * info,nsXPTCMiniVariant * nativeParams,bool inOutOnly,uint8_t n) const704 void nsXPCWrappedJSClass::CleanupOutparams(JSContext* cx, uint16_t methodIndex,
705 const nsXPTMethodInfo* info,
706 nsXPTCMiniVariant* nativeParams,
707 bool inOutOnly, uint8_t n) const {
708 // clean up any 'out' params handed in
709 for (uint8_t i = 0; i < n; i++) {
710 const nsXPTParamInfo& param = info->GetParam(i);
711 if (!param.IsOut()) continue;
712
713 const nsXPTType& type = param.GetType();
714 if (!type.deprecated_IsPointer()) continue;
715 void* p = nativeParams[i].val.p;
716 if (!p) continue;
717
718 // The inOutOnly flag was introduced when consolidating two very
719 // similar code paths in CallMethod in bug 1175513. I don't know
720 // if and why the difference is necessary.
721 if (!inOutOnly || param.IsIn()) {
722 if (type.IsArray()) {
723 void** pp = *static_cast<void***>(p);
724 if (pp) {
725 // we need to get the array length and iterate the items
726 uint32_t array_count;
727 nsXPTType datum_type;
728
729 if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, 1,
730 &datum_type)) &&
731 datum_type.deprecated_IsPointer() &&
732 GetArraySizeFromParam(cx, info, param, methodIndex, i,
733 nativeParams, &array_count) &&
734 array_count) {
735 CleanupPointerArray(datum_type, array_count, pp);
736 }
737
738 // always release the array if it is inout
739 free(pp);
740 }
741 } else {
742 CleanupPointerTypeObject(type, static_cast<void**>(p));
743 }
744 }
745 *static_cast<void**>(p) = nullptr;
746 }
747 }
748
CheckForException(XPCCallContext & ccx,AutoEntryScript & aes,const char * aPropertyName,const char * anInterfaceName,Exception * aSyntheticException)749 nsresult nsXPCWrappedJSClass::CheckForException(
750 XPCCallContext& ccx, AutoEntryScript& aes, const char* aPropertyName,
751 const char* anInterfaceName, Exception* aSyntheticException) {
752 JSContext* cx = ccx.GetJSContext();
753 MOZ_ASSERT(cx == aes.cx());
754 RefPtr<Exception> xpc_exception = aSyntheticException;
755 /* this one would be set by our error reporter */
756
757 XPCJSContext* xpccx = ccx.GetContext();
758
759 // Get this right away in case we do something below to cause JS code
760 // to run.
761 nsresult pending_result = xpccx->GetPendingResult();
762
763 RootedValue js_exception(cx);
764 bool is_js_exception = JS_GetPendingException(cx, &js_exception);
765
766 /* JS might throw an expection whether the reporter was called or not */
767 if (is_js_exception) {
768 if (!xpc_exception)
769 XPCConvert::JSValToXPCException(&js_exception, anInterfaceName,
770 aPropertyName,
771 getter_AddRefs(xpc_exception));
772
773 /* cleanup and set failed even if we can't build an exception */
774 if (!xpc_exception) {
775 xpccx->SetPendingException(nullptr); // XXX necessary?
776 }
777 }
778
779 // Clear the pending exception now, because xpc_exception might be JS-
780 // implemented, so invoking methods on it might re-enter JS, which we can't
781 // do with an exception on the stack.
782 aes.ClearException();
783
784 if (xpc_exception) {
785 nsresult e_result = xpc_exception->GetResult();
786 // Figure out whether or not we should report this exception.
787 bool reportable = xpc_IsReportableErrorCode(e_result);
788 if (reportable) {
789 // Ugly special case for GetInterface. It's "special" in the
790 // same way as QueryInterface in that a failure is not
791 // exceptional and shouldn't be reported. We have to do this
792 // check here instead of in xpcwrappedjs (like we do for QI) to
793 // avoid adding extra code to all xpcwrappedjs objects.
794 if (e_result == NS_ERROR_NO_INTERFACE &&
795 !strcmp(anInterfaceName, "nsIInterfaceRequestor") &&
796 !strcmp(aPropertyName, "getInterface")) {
797 reportable = false;
798 }
799
800 // More special case, see bug 877760.
801 if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) {
802 reportable = false;
803 }
804 }
805
806 // Try to use the error reporter set on the context to handle this
807 // error if it came from a JS exception.
808 if (reportable && is_js_exception) {
809 // Note that we cleared the exception above, so we need to set it again,
810 // just so that we can tell the JS engine to pass it back to us via the
811 // error reporting callback. This is all very dumb.
812 JS_SetPendingException(cx, js_exception);
813 aes.ReportException();
814 reportable = false;
815 }
816
817 if (reportable) {
818 if (DOMPrefs::DumpEnabled()) {
819 static const char line[] =
820 "************************************************************\n";
821 static const char preamble[] =
822 "* Call to xpconnect wrapped JSObject produced this error: *\n";
823 static const char cant_get_text[] =
824 "FAILED TO GET TEXT FROM EXCEPTION\n";
825
826 fputs(line, stdout);
827 fputs(preamble, stdout);
828 nsCString text;
829 xpc_exception->ToString(cx, text);
830 if (!text.IsEmpty()) {
831 fputs(text.get(), stdout);
832 fputs("\n", stdout);
833 } else
834 fputs(cant_get_text, stdout);
835 fputs(line, stdout);
836 }
837
838 // Log the exception to the JS Console, so that users can do
839 // something with it.
840 nsCOMPtr<nsIConsoleService> consoleService(
841 do_GetService(XPC_CONSOLE_CONTRACTID));
842 if (nullptr != consoleService) {
843 nsCOMPtr<nsIScriptError> scriptError =
844 do_QueryInterface(xpc_exception->GetData());
845
846 if (nullptr == scriptError) {
847 // No luck getting one from the exception, so
848 // try to cook one up.
849 scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID);
850 if (nullptr != scriptError) {
851 nsCString newMessage;
852 xpc_exception->ToString(cx, newMessage);
853 // try to get filename, lineno from the first
854 // stack frame location.
855 int32_t lineNumber = 0;
856 nsString sourceName;
857
858 nsCOMPtr<nsIStackFrame> location = xpc_exception->GetLocation();
859 if (location) {
860 // Get line number.
861 lineNumber = location->GetLineNumber(cx);
862
863 // get a filename.
864 location->GetFilename(cx, sourceName);
865 }
866
867 nsresult rv = scriptError->InitWithWindowID(
868 NS_ConvertUTF8toUTF16(newMessage), sourceName, EmptyString(),
869 lineNumber, 0, 0, "XPConnect JavaScript",
870 nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
871 if (NS_FAILED(rv)) scriptError = nullptr;
872 }
873 }
874 if (nullptr != scriptError) consoleService->LogMessage(scriptError);
875 }
876 }
877 // Whether or not it passes the 'reportable' test, it might
878 // still be an error and we have to do the right thing here...
879 if (NS_FAILED(e_result)) {
880 xpccx->SetPendingException(xpc_exception);
881 return e_result;
882 }
883 } else {
884 // see if JS code signaled failure result without throwing exception
885 if (NS_FAILED(pending_result)) {
886 return pending_result;
887 }
888 }
889 return NS_ERROR_FAILURE;
890 }
891
892 NS_IMETHODIMP
CallMethod(nsXPCWrappedJS * wrapper,uint16_t methodIndex,const nsXPTMethodInfo * info,nsXPTCMiniVariant * nativeParams)893 nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
894 const nsXPTMethodInfo* info,
895 nsXPTCMiniVariant* nativeParams) {
896 Value* sp = nullptr;
897 Value* argv = nullptr;
898 uint8_t i;
899 nsresult retval = NS_ERROR_FAILURE;
900 bool success;
901 bool readyToDoTheCall = false;
902 nsID param_iid;
903 const char* name = info->GetName();
904 bool foundDependentParam;
905
906 // Make sure not to set the callee on ccx until after we've gone through
907 // the whole nsIXPCFunctionThisTranslator bit. That code uses ccx to
908 // convert natives to JSObjects, but we do NOT plan to pass those JSObjects
909 // to our real callee.
910 //
911 // We're about to call into script via an XPCWrappedJS, so we need an
912 // AutoEntryScript. This is probably Gecko-specific at this point, and
913 // definitely will be when we turn off XPConnect for the web.
914 nsIGlobalObject* nativeGlobal = NativeGlobal(
915 js::GetGlobalForObjectCrossCompartment(wrapper->GetJSObject()));
916 AutoEntryScript aes(nativeGlobal, "XPCWrappedJS method call",
917 /* aIsMainThread = */ true);
918 XPCCallContext ccx(aes.cx());
919 if (!ccx.IsValid()) return retval;
920
921 JSContext* cx = ccx.GetJSContext();
922
923 if (!cx || !IsReflectable(methodIndex)) return NS_ERROR_FAILURE;
924
925 // [implicit_jscontext] and [optional_argc] have a different calling
926 // convention, which we don't support for JS-implemented components.
927 if (info->WantsOptArgc() || info->WantsContext()) {
928 const char* str =
929 "IDL methods marked with [implicit_jscontext] "
930 "or [optional_argc] may not be implemented in JS";
931 // Throw and warn for good measure.
932 JS_ReportErrorASCII(cx, "%s", str);
933 NS_WARNING(str);
934 return CheckForException(ccx, aes, name, GetInterfaceName());
935 }
936
937 RootedValue fval(cx);
938 RootedObject obj(cx, wrapper->GetJSObject());
939 RootedObject thisObj(cx, obj);
940
941 JSAutoCompartment ac(cx, obj);
942
943 AutoValueVector args(cx);
944 AutoScriptEvaluate scriptEval(cx);
945
946 XPCJSContext* xpccx = ccx.GetContext();
947 AutoSavePendingResult apr(xpccx);
948
949 // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
950 uint8_t paramCount = info->GetParamCount();
951 uint8_t argc = paramCount;
952 if (paramCount > 0 && info->GetParam(paramCount - 1).IsRetval()) {
953 argc -= 1;
954 }
955
956 if (!scriptEval.StartEvaluating(obj)) goto pre_call_clean_up;
957
958 xpccx->SetPendingException(nullptr);
959
960 // We use js_Invoke so that the gcthings we use as args will be rooted by
961 // the engine as we do conversions and prepare to do the function call.
962
963 // setup stack
964
965 // if this isn't a function call then we don't need to push extra stuff
966 if (!(info->IsSetter() || info->IsGetter())) {
967 // We get fval before allocating the stack to avoid gc badness that can
968 // happen if the GetProperty call leaves our request and the gc runs
969 // while the stack we allocate contains garbage.
970
971 // If the interface is marked as a [function] then we will assume that
972 // our JSObject is a function and not an object with a named method.
973
974 bool isFunction;
975 if (NS_FAILED(mInfo->IsFunction(&isFunction))) goto pre_call_clean_up;
976
977 // In the xpidl [function] case we are making sure now that the
978 // JSObject is callable. If it is *not* callable then we silently
979 // fallback to looking up the named property...
980 // (because jst says he thinks this fallback is 'The Right Thing'.)
981 //
982 // In the normal (non-function) case we just lookup the property by
983 // name and as long as the object has such a named property we go ahead
984 // and try to make the call. If it turns out the named property is not
985 // a callable object then the JS engine will throw an error and we'll
986 // pass this along to the caller as an exception/result code.
987
988 fval = ObjectValue(*obj);
989 if (isFunction && JS_TypeOfValue(ccx, fval) == JSTYPE_FUNCTION) {
990 // We may need to translate the 'this' for the function object.
991
992 if (paramCount) {
993 const nsXPTParamInfo& firstParam = info->GetParam(0);
994 if (firstParam.IsIn()) {
995 const nsXPTType& firstType = firstParam.GetType();
996
997 if (firstType.IsInterfacePointer()) {
998 nsIXPCFunctionThisTranslator* translator;
999
1000 IID2ThisTranslatorMap* map = mRuntime->GetThisTranslatorMap();
1001
1002 translator = map->Find(mIID);
1003
1004 if (translator) {
1005 nsCOMPtr<nsISupports> newThis;
1006 if (NS_FAILED(translator->TranslateThis(
1007 (nsISupports*)nativeParams[0].val.p,
1008 getter_AddRefs(newThis)))) {
1009 goto pre_call_clean_up;
1010 }
1011 if (newThis) {
1012 RootedValue v(cx);
1013 xpcObjectHelper helper(newThis);
1014 bool ok = XPCConvert::NativeInterface2JSObject(
1015 &v, helper, nullptr, false, nullptr);
1016 if (!ok) {
1017 goto pre_call_clean_up;
1018 }
1019 thisObj = v.toObjectOrNull();
1020 if (!JS_WrapObject(cx, &thisObj)) goto pre_call_clean_up;
1021 }
1022 }
1023 }
1024 }
1025 }
1026 } else {
1027 if (!JS_GetProperty(cx, obj, name, &fval)) goto pre_call_clean_up;
1028 // XXX We really want to factor out the error reporting better and
1029 // specifically report the failure to find a function with this name.
1030 // This is what we do below if the property is found but is not a
1031 // function. We just need to factor better so we can get to that
1032 // reporting path from here.
1033
1034 thisObj = obj;
1035 }
1036 }
1037
1038 if (!args.resize(argc)) {
1039 retval = NS_ERROR_OUT_OF_MEMORY;
1040 goto pre_call_clean_up;
1041 }
1042
1043 argv = args.begin();
1044 sp = argv;
1045
1046 // build the args
1047 // NB: This assignment *looks* wrong because we haven't yet called our
1048 // function. However, we *have* already entered the compartmen that we're
1049 // about to call, and that's the global that we want here. In other words:
1050 // we're trusting the JS engine to come up with a good global to use for
1051 // our object (whatever it was).
1052 for (i = 0; i < argc; i++) {
1053 const nsXPTParamInfo& param = info->GetParam(i);
1054 const nsXPTType& type = param.GetType();
1055 nsXPTType datum_type;
1056 uint32_t array_count;
1057 bool isArray = type.IsArray();
1058 RootedValue val(cx, NullValue());
1059 bool isSizedString =
1060 isArray ? false
1061 : type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
1062 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
1063
1064 // verify that null was not passed for 'out' param
1065 if (param.IsOut() && !nativeParams[i].val.p) {
1066 retval = NS_ERROR_INVALID_ARG;
1067 goto pre_call_clean_up;
1068 }
1069
1070 if (isArray) {
1071 if (NS_FAILED(
1072 mInfo->GetTypeForParam(methodIndex, ¶m, 1, &datum_type)))
1073 goto pre_call_clean_up;
1074 } else
1075 datum_type = type;
1076
1077 if (param.IsIn()) {
1078 nsXPTCMiniVariant* pv;
1079
1080 if (param.IsIndirect())
1081 pv = (nsXPTCMiniVariant*)nativeParams[i].val.p;
1082 else
1083 pv = &nativeParams[i];
1084
1085 if (datum_type.IsInterfacePointer() &&
1086 !GetInterfaceTypeFromParam(cx, info, param, methodIndex, datum_type,
1087 nativeParams, ¶m_iid))
1088 goto pre_call_clean_up;
1089
1090 if (isArray || isSizedString) {
1091 if (!GetArraySizeFromParam(cx, info, param, methodIndex, i,
1092 nativeParams, &array_count))
1093 goto pre_call_clean_up;
1094 }
1095
1096 if (isArray) {
1097 if (!XPCConvert::NativeArray2JS(&val, (const void**)&pv->val,
1098 datum_type, ¶m_iid, array_count,
1099 nullptr))
1100 goto pre_call_clean_up;
1101 } else if (isSizedString) {
1102 if (!XPCConvert::NativeStringWithSize2JS(
1103 &val, (const void*)&pv->val, datum_type, array_count, nullptr))
1104 goto pre_call_clean_up;
1105 } else {
1106 if (!XPCConvert::NativeData2JS(&val, &pv->val, type, ¶m_iid,
1107 nullptr))
1108 goto pre_call_clean_up;
1109 }
1110 }
1111
1112 if (param.IsOut() || param.IsDipper()) {
1113 // create an 'out' object
1114 RootedObject out_obj(cx, NewOutObject(cx));
1115 if (!out_obj) {
1116 retval = NS_ERROR_OUT_OF_MEMORY;
1117 goto pre_call_clean_up;
1118 }
1119
1120 if (param.IsIn()) {
1121 if (!JS_SetPropertyById(cx, out_obj,
1122 mRuntime->GetStringID(XPCJSContext::IDX_VALUE),
1123 val)) {
1124 goto pre_call_clean_up;
1125 }
1126 }
1127 *sp++ = JS::ObjectValue(*out_obj);
1128 } else
1129 *sp++ = val;
1130 }
1131
1132 readyToDoTheCall = true;
1133
1134 pre_call_clean_up:
1135 // clean up any 'out' params handed in
1136 CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ true,
1137 paramCount);
1138
1139 // Make sure "this" doesn't get deleted during this call.
1140 nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this);
1141
1142 if (!readyToDoTheCall) return retval;
1143
1144 // do the deed - note exceptions
1145
1146 MOZ_ASSERT(!aes.HasException());
1147
1148 RefPtr<Exception> syntheticException;
1149 RootedValue rval(cx);
1150 if (info->IsGetter()) {
1151 success = JS_GetProperty(cx, obj, name, &rval);
1152 } else if (info->IsSetter()) {
1153 rval = *argv;
1154 success = JS_SetProperty(cx, obj, name, rval);
1155 } else {
1156 if (!fval.isPrimitive()) {
1157 success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval);
1158 } else {
1159 // The property was not an object so can't be a function.
1160 // Let's build and 'throw' an exception.
1161
1162 static const nsresult code = NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
1163 static const char format[] = "%s \"%s\"";
1164 const char* msg;
1165 UniqueChars sz;
1166
1167 if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg)
1168 sz = JS_smprintf(format, msg, name);
1169
1170 XPCConvert::ConstructException(
1171 code, sz.get(), GetInterfaceName(), name, nullptr,
1172 getter_AddRefs(syntheticException), nullptr, nullptr);
1173 success = false;
1174 }
1175 }
1176
1177 if (!success)
1178 return CheckForException(ccx, aes, name, GetInterfaceName(),
1179 syntheticException);
1180
1181 xpccx->SetPendingException(nullptr); // XXX necessary?
1182
1183 // convert out args and result
1184 // NOTE: this is the total number of native params, not just the args
1185 // Convert independent params only.
1186 // When we later convert the dependent params (if any) we will know that
1187 // the params upon which they depend will have already been converted -
1188 // regardless of ordering.
1189
1190 foundDependentParam = false;
1191 for (i = 0; i < paramCount; i++) {
1192 const nsXPTParamInfo& param = info->GetParam(i);
1193 MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!");
1194 if (!param.IsOut() && !param.IsDipper()) continue;
1195
1196 const nsXPTType& type = param.GetType();
1197 if (type.IsDependent()) {
1198 foundDependentParam = true;
1199 continue;
1200 }
1201
1202 RootedValue val(cx);
1203 uint8_t type_tag = type.TagPart();
1204 nsXPTCMiniVariant* pv;
1205
1206 if (param.IsDipper())
1207 pv = (nsXPTCMiniVariant*)&nativeParams[i].val.p;
1208 else
1209 pv = (nsXPTCMiniVariant*)nativeParams[i].val.p;
1210
1211 if (param.IsRetval())
1212 val = rval;
1213 else if (argv[i].isPrimitive())
1214 break;
1215 else {
1216 RootedObject obj(cx, &argv[i].toObject());
1217 if (!JS_GetPropertyById(
1218 cx, obj, mRuntime->GetStringID(XPCJSContext::IDX_VALUE), &val))
1219 break;
1220 }
1221
1222 // setup allocator and/or iid
1223
1224 if (type_tag == nsXPTType::T_INTERFACE) {
1225 if (NS_FAILED(GetInterfaceInfo()->GetIIDForParamNoAlloc(
1226 methodIndex, ¶m, ¶m_iid)))
1227 break;
1228 }
1229
1230 if (!XPCConvert::JSData2Native(&pv->val, val, type, ¶m_iid, nullptr))
1231 break;
1232 }
1233
1234 // if any params were dependent, then we must iterate again to convert them.
1235 if (foundDependentParam && i == paramCount) {
1236 for (i = 0; i < paramCount; i++) {
1237 const nsXPTParamInfo& param = info->GetParam(i);
1238 if (!param.IsOut()) continue;
1239
1240 const nsXPTType& type = param.GetType();
1241 if (!type.IsDependent()) continue;
1242
1243 RootedValue val(cx);
1244 nsXPTCMiniVariant* pv;
1245 nsXPTType datum_type;
1246 uint32_t array_count;
1247 bool isArray = type.IsArray();
1248 bool isSizedString =
1249 isArray ? false
1250 : type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
1251 type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
1252
1253 pv = (nsXPTCMiniVariant*)nativeParams[i].val.p;
1254
1255 if (param.IsRetval())
1256 val = rval;
1257 else {
1258 RootedObject obj(cx, &argv[i].toObject());
1259 if (!JS_GetPropertyById(
1260 cx, obj, mRuntime->GetStringID(XPCJSContext::IDX_VALUE), &val))
1261 break;
1262 }
1263
1264 // setup allocator and/or iid
1265
1266 if (isArray) {
1267 if (NS_FAILED(
1268 mInfo->GetTypeForParam(methodIndex, ¶m, 1, &datum_type)))
1269 break;
1270 } else
1271 datum_type = type;
1272
1273 if (datum_type.IsInterfacePointer()) {
1274 if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex, datum_type,
1275 nativeParams, ¶m_iid))
1276 break;
1277 }
1278
1279 if (isArray || isSizedString) {
1280 if (!GetArraySizeFromParam(cx, info, param, methodIndex, i,
1281 nativeParams, &array_count))
1282 break;
1283 }
1284
1285 if (isArray) {
1286 if (array_count &&
1287 !XPCConvert::JSArray2Native((void**)&pv->val, val, array_count,
1288 datum_type, ¶m_iid, nullptr))
1289 break;
1290 } else if (isSizedString) {
1291 if (!XPCConvert::JSStringWithSize2Native(
1292 (void*)&pv->val, val, array_count, datum_type, nullptr))
1293 break;
1294 } else {
1295 if (!XPCConvert::JSData2Native(&pv->val, val, type, ¶m_iid,
1296 nullptr))
1297 break;
1298 }
1299 }
1300 }
1301
1302 if (i != paramCount) {
1303 // We didn't manage all the result conversions!
1304 // We have to cleanup any junk that *did* get converted.
1305 CleanupOutparams(cx, methodIndex, info, nativeParams,
1306 /* inOutOnly = */ false, i);
1307 } else {
1308 // set to whatever the JS code might have set as the result
1309 retval = xpccx->GetPendingResult();
1310 }
1311
1312 return retval;
1313 }
1314
GetInterfaceName()1315 const char* nsXPCWrappedJSClass::GetInterfaceName() {
1316 if (!mName) mInfo->GetName(&mName);
1317 return mName;
1318 }
1319
1320 static const JSClass XPCOutParamClass = {"XPCOutParam", 0, JS_NULL_CLASS_OPS};
1321
IsOutObject(JSContext * cx,JSObject * obj)1322 bool xpc::IsOutObject(JSContext* cx, JSObject* obj) {
1323 return js::GetObjectJSClass(obj) == &XPCOutParamClass;
1324 }
1325
NewOutObject(JSContext * cx)1326 JSObject* xpc::NewOutObject(JSContext* cx) {
1327 return JS_NewObject(cx, &XPCOutParamClass);
1328 }
1329
1330 NS_IMETHODIMP
DebugDump(int16_t depth)1331 nsXPCWrappedJSClass::DebugDump(int16_t depth) {
1332 #ifdef DEBUG
1333 depth--;
1334 XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %p with mRefCnt = %" PRIuPTR, this,
1335 mRefCnt.get()));
1336 XPC_LOG_INDENT();
1337 char* name;
1338 mInfo->GetName(&name);
1339 XPC_LOG_ALWAYS(("interface name is %s", name));
1340 if (name) free(name);
1341 char* iid = mIID.ToString();
1342 XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
1343 if (iid) free(iid);
1344 XPC_LOG_ALWAYS(("InterfaceInfo @ %p", mInfo.get()));
1345 uint16_t methodCount = 0;
1346 if (depth) {
1347 uint16_t i;
1348 nsCOMPtr<nsIInterfaceInfo> parent;
1349 XPC_LOG_INDENT();
1350 mInfo->GetParent(getter_AddRefs(parent));
1351 XPC_LOG_ALWAYS(("parent @ %p", parent.get()));
1352 mInfo->GetMethodCount(&methodCount);
1353 XPC_LOG_ALWAYS(("MethodCount = %d", methodCount));
1354 mInfo->GetConstantCount(&i);
1355 XPC_LOG_ALWAYS(("ConstantCount = %d", i));
1356 XPC_LOG_OUTDENT();
1357 }
1358 XPC_LOG_ALWAYS(("mRuntime @ %p", mRuntime));
1359 XPC_LOG_ALWAYS(("mDescriptors @ %p count = %d", mDescriptors, methodCount));
1360 if (depth && mDescriptors && methodCount) {
1361 depth--;
1362 XPC_LOG_INDENT();
1363 for (uint16_t i = 0; i < methodCount; i++) {
1364 XPC_LOG_ALWAYS(("Method %d is %s%s", i, IsReflectable(i) ? "" : " NOT ",
1365 "reflectable"));
1366 }
1367 XPC_LOG_OUTDENT();
1368 depth++;
1369 }
1370 XPC_LOG_OUTDENT();
1371 #endif
1372 return NS_OK;
1373 }
1374