1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1586 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Sharable code and data for wrapper around JSObjects. */ 1.11 + 1.12 +#include "xpcprivate.h" 1.13 +#include "jsprf.h" 1.14 +#include "nsArrayEnumerator.h" 1.15 +#include "nsContentUtils.h" 1.16 +#include "nsWrapperCache.h" 1.17 +#include "AccessCheck.h" 1.18 +#include "nsJSUtils.h" 1.19 +#include "mozilla/Attributes.h" 1.20 +#include "mozilla/dom/BindingUtils.h" 1.21 +#include "mozilla/dom/DOMException.h" 1.22 +#include "mozilla/dom/DOMExceptionBinding.h" 1.23 + 1.24 +#include "jsapi.h" 1.25 +#include "jsfriendapi.h" 1.26 + 1.27 +using namespace xpc; 1.28 +using namespace JS; 1.29 +using namespace mozilla; 1.30 + 1.31 +NS_IMPL_ISUPPORTS(nsXPCWrappedJSClass, nsIXPCWrappedJSClass) 1.32 + 1.33 +// the value of this variable is never used - we use its address as a sentinel 1.34 +static uint32_t zero_methods_descriptor; 1.35 + 1.36 +bool AutoScriptEvaluate::StartEvaluating(HandleObject scope, JSErrorReporter errorReporter) 1.37 +{ 1.38 + NS_PRECONDITION(!mEvaluated, "AutoScriptEvaluate::Evaluate should only be called once"); 1.39 + 1.40 + if (!mJSContext) 1.41 + return true; 1.42 + 1.43 + mEvaluated = true; 1.44 + if (!JS_GetErrorReporter(mJSContext)) { 1.45 + JS_SetErrorReporter(mJSContext, errorReporter); 1.46 + mErrorReporterSet = true; 1.47 + } 1.48 + 1.49 + JS_BeginRequest(mJSContext); 1.50 + mAutoCompartment.construct(mJSContext, scope); 1.51 + 1.52 + // Saving the exception state keeps us from interfering with another script 1.53 + // that may also be running on this context. This occurred first with the 1.54 + // js debugger, as described in 1.55 + // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could 1.56 + // show up in any situation where a script calls into a wrapped js component 1.57 + // on the same context, while the context has a nonzero exception state. 1.58 + mState.construct(mJSContext); 1.59 + 1.60 + return true; 1.61 +} 1.62 + 1.63 +AutoScriptEvaluate::~AutoScriptEvaluate() 1.64 +{ 1.65 + if (!mJSContext || !mEvaluated) 1.66 + return; 1.67 + mState.ref().restore(); 1.68 + 1.69 + JS_EndRequest(mJSContext); 1.70 + 1.71 + if (mErrorReporterSet) 1.72 + JS_SetErrorReporter(mJSContext, nullptr); 1.73 +} 1.74 + 1.75 +// It turns out that some errors may be not worth reporting. So, this 1.76 +// function is factored out to manage that. 1.77 +bool xpc_IsReportableErrorCode(nsresult code) 1.78 +{ 1.79 + if (NS_SUCCEEDED(code)) 1.80 + return false; 1.81 + 1.82 + switch (code) { 1.83 + // Error codes that we don't want to report as errors... 1.84 + // These generally indicate bad interface design AFAIC. 1.85 + case NS_ERROR_FACTORY_REGISTER_AGAIN: 1.86 + case NS_BASE_STREAM_WOULD_BLOCK: 1.87 + return false; 1.88 + default: 1.89 + return true; 1.90 + } 1.91 +} 1.92 + 1.93 +// static 1.94 +already_AddRefed<nsXPCWrappedJSClass> 1.95 +nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID) 1.96 +{ 1.97 + XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); 1.98 + IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap(); 1.99 + nsRefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID); 1.100 + 1.101 + if (!clasp) { 1.102 + nsCOMPtr<nsIInterfaceInfo> info; 1.103 + nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info)); 1.104 + if (info) { 1.105 + bool canScript, isBuiltin; 1.106 + if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && canScript && 1.107 + NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin && 1.108 + nsXPConnect::IsISupportsDescendant(info)) 1.109 + { 1.110 + clasp = new nsXPCWrappedJSClass(cx, aIID, info); 1.111 + if (!clasp->mDescriptors) 1.112 + clasp = nullptr; 1.113 + } 1.114 + } 1.115 + } 1.116 + return clasp.forget(); 1.117 +} 1.118 + 1.119 +nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID, 1.120 + nsIInterfaceInfo* aInfo) 1.121 + : mRuntime(nsXPConnect::GetRuntimeInstance()), 1.122 + mInfo(aInfo), 1.123 + mName(nullptr), 1.124 + mIID(aIID), 1.125 + mDescriptors(nullptr) 1.126 +{ 1.127 + mRuntime->GetWrappedJSClassMap()->Add(this); 1.128 + 1.129 + uint16_t methodCount; 1.130 + if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) { 1.131 + if (methodCount) { 1.132 + int wordCount = (methodCount/32)+1; 1.133 + if (nullptr != (mDescriptors = new uint32_t[wordCount])) { 1.134 + int i; 1.135 + // init flags to 0; 1.136 + for (i = wordCount-1; i >= 0; i--) 1.137 + mDescriptors[i] = 0; 1.138 + 1.139 + for (i = 0; i < methodCount; i++) { 1.140 + const nsXPTMethodInfo* info; 1.141 + if (NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info))) 1.142 + SetReflectable(i, XPCConvert::IsMethodReflectable(*info)); 1.143 + else { 1.144 + delete [] mDescriptors; 1.145 + mDescriptors = nullptr; 1.146 + break; 1.147 + } 1.148 + } 1.149 + } 1.150 + } else { 1.151 + mDescriptors = &zero_methods_descriptor; 1.152 + } 1.153 + } 1.154 +} 1.155 + 1.156 +nsXPCWrappedJSClass::~nsXPCWrappedJSClass() 1.157 +{ 1.158 + if (mDescriptors && mDescriptors != &zero_methods_descriptor) 1.159 + delete [] mDescriptors; 1.160 + if (mRuntime) 1.161 + mRuntime->GetWrappedJSClassMap()->Remove(this); 1.162 + 1.163 + if (mName) 1.164 + nsMemory::Free(mName); 1.165 +} 1.166 + 1.167 +JSObject* 1.168 +nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx, 1.169 + JSObject* jsobjArg, 1.170 + REFNSIID aIID) 1.171 +{ 1.172 + RootedObject jsobj(cx, jsobjArg); 1.173 + JSObject* id; 1.174 + RootedValue retval(cx); 1.175 + RootedObject retObj(cx); 1.176 + bool success = false; 1.177 + RootedValue fun(cx); 1.178 + 1.179 + // Don't call the actual function on a content object. We'll determine 1.180 + // whether or not a content object is capable of implementing the 1.181 + // interface (i.e. whether the interface is scriptable) and most content 1.182 + // objects don't have QI implementations anyway. Also see bug 503926. 1.183 + if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(jsobj))) { 1.184 + return nullptr; 1.185 + } 1.186 + 1.187 + // OK, it looks like we'll be calling into JS code. 1.188 + AutoScriptEvaluate scriptEval(cx); 1.189 + 1.190 + // XXX we should install an error reporter that will send reports to 1.191 + // the JS error console service. 1.192 + if (!scriptEval.StartEvaluating(jsobj)) 1.193 + return nullptr; 1.194 + 1.195 + // check upfront for the existence of the function property 1.196 + HandleId funid = mRuntime->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE); 1.197 + if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || JSVAL_IS_PRIMITIVE(fun)) 1.198 + return nullptr; 1.199 + 1.200 + // Ensure that we are asking for a scriptable interface. 1.201 + // NB: It's important for security that this check is here rather 1.202 + // than later, since it prevents untrusted objects from implementing 1.203 + // some interfaces in JS and aggregating a trusted object to 1.204 + // implement intentionally (for security) unscriptable interfaces. 1.205 + // We so often ask for nsISupports that we can short-circuit the test... 1.206 + if (!aIID.Equals(NS_GET_IID(nsISupports))) { 1.207 + nsCOMPtr<nsIInterfaceInfo> info; 1.208 + nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info)); 1.209 + if (!info) 1.210 + return nullptr; 1.211 + bool canScript, isBuiltin; 1.212 + if (NS_FAILED(info->IsScriptable(&canScript)) || !canScript || 1.213 + NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin) 1.214 + return nullptr; 1.215 + } 1.216 + 1.217 + id = xpc_NewIDObject(cx, jsobj, aIID); 1.218 + if (id) { 1.219 + // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It 1.220 + // is not an exception that is ever worth reporting, but we don't want 1.221 + // to eat all exceptions either. 1.222 + 1.223 + { 1.224 + AutoSaveContextOptions asco(cx); 1.225 + ContextOptionsRef(cx).setDontReportUncaught(true); 1.226 + RootedValue arg(cx, JS::ObjectValue(*id)); 1.227 + success = JS_CallFunctionValue(cx, jsobj, fun, arg, &retval); 1.228 + } 1.229 + 1.230 + if (!success && JS_IsExceptionPending(cx)) { 1.231 + RootedValue jsexception(cx, NullValue()); 1.232 + 1.233 + if (JS_GetPendingException(cx, &jsexception)) { 1.234 + nsresult rv; 1.235 + if (jsexception.isObject()) { 1.236 + // XPConnect may have constructed an object to represent a 1.237 + // C++ QI failure. See if that is the case. 1.238 + using namespace mozilla::dom; 1.239 + Exception *e = nullptr; 1.240 + UNWRAP_OBJECT(Exception, &jsexception.toObject(), e); 1.241 + 1.242 + if (e && 1.243 + NS_SUCCEEDED(e->GetResult(&rv)) && 1.244 + rv == NS_NOINTERFACE) { 1.245 + JS_ClearPendingException(cx); 1.246 + } 1.247 + } else if (JSVAL_IS_NUMBER(jsexception)) { 1.248 + // JS often throws an nsresult. 1.249 + if (JSVAL_IS_DOUBLE(jsexception)) 1.250 + // Visual Studio 9 doesn't allow casting directly from 1.251 + // a double to an enumeration type, contrary to 1.252 + // 5.2.9(10) of C++11, so add an intermediate cast. 1.253 + rv = (nsresult)(uint32_t)(JSVAL_TO_DOUBLE(jsexception)); 1.254 + else 1.255 + rv = (nsresult)(JSVAL_TO_INT(jsexception)); 1.256 + 1.257 + if (rv == NS_NOINTERFACE) 1.258 + JS_ClearPendingException(cx); 1.259 + } 1.260 + } 1.261 + 1.262 + // Don't report if reporting was disabled by someone else. 1.263 + if (!ContextOptionsRef(cx).dontReportUncaught()) 1.264 + JS_ReportPendingException(cx); 1.265 + } else if (!success) { 1.266 + NS_WARNING("QI hook ran OOMed - this is probably a bug!"); 1.267 + } 1.268 + } 1.269 + 1.270 + if (success) 1.271 + success = JS_ValueToObject(cx, retval, &retObj); 1.272 + 1.273 + return success ? retObj.get() : nullptr; 1.274 +} 1.275 + 1.276 +/***************************************************************************/ 1.277 + 1.278 +static bool 1.279 +GetNamedPropertyAsVariantRaw(XPCCallContext& ccx, 1.280 + HandleObject aJSObj, 1.281 + HandleId aName, 1.282 + nsIVariant** aResult, 1.283 + nsresult* pErr) 1.284 +{ 1.285 + nsXPTType type = nsXPTType((uint8_t)TD_INTERFACE_TYPE); 1.286 + RootedValue val(ccx); 1.287 + 1.288 + return JS_GetPropertyById(ccx, aJSObj, aName, &val) && 1.289 + // Note that this always takes the T_INTERFACE path through 1.290 + // JSData2Native, so the value passed for useAllocator 1.291 + // doesn't really matter. We pass true for consistency. 1.292 + XPCConvert::JSData2Native(aResult, val, type, true, 1.293 + &NS_GET_IID(nsIVariant), pErr); 1.294 +} 1.295 + 1.296 +// static 1.297 +nsresult 1.298 +nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx, 1.299 + JSObject* aJSObjArg, 1.300 + const nsAString& aName, 1.301 + nsIVariant** aResult) 1.302 +{ 1.303 + JSContext* cx = ccx.GetJSContext(); 1.304 + RootedObject aJSObj(cx, aJSObjArg); 1.305 + 1.306 + AutoScriptEvaluate scriptEval(cx); 1.307 + if (!scriptEval.StartEvaluating(aJSObj)) 1.308 + return NS_ERROR_FAILURE; 1.309 + 1.310 + // Wrap the string in a jsval after the AutoScriptEvaluate, so that the 1.311 + // resulting value ends up in the correct compartment. 1.312 + nsStringBuffer* buf; 1.313 + RootedValue value(cx); 1.314 + if (!XPCStringConvert::ReadableToJSVal(ccx, aName, &buf, &value)) 1.315 + return NS_ERROR_OUT_OF_MEMORY; 1.316 + if (buf) 1.317 + buf->AddRef(); 1.318 + 1.319 + RootedId id(cx); 1.320 + nsresult rv = NS_OK; 1.321 + if (!JS_ValueToId(cx, value, &id) || 1.322 + !GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv)) { 1.323 + if (NS_FAILED(rv)) 1.324 + return rv; 1.325 + return NS_ERROR_FAILURE; 1.326 + } 1.327 + return NS_OK; 1.328 +} 1.329 + 1.330 +/***************************************************************************/ 1.331 + 1.332 +// static 1.333 +nsresult 1.334 +nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx, 1.335 + JSObject* aJSObjArg, 1.336 + nsISimpleEnumerator** aEnumerate) 1.337 +{ 1.338 + JSContext* cx = ccx.GetJSContext(); 1.339 + RootedObject aJSObj(cx, aJSObjArg); 1.340 + 1.341 + AutoScriptEvaluate scriptEval(cx); 1.342 + if (!scriptEval.StartEvaluating(aJSObj)) 1.343 + return NS_ERROR_FAILURE; 1.344 + 1.345 + AutoIdArray idArray(cx, JS_Enumerate(cx, aJSObj)); 1.346 + if (!idArray) 1.347 + return NS_ERROR_FAILURE; 1.348 + 1.349 + nsCOMArray<nsIProperty> propertyArray(idArray.length()); 1.350 + RootedId idName(cx); 1.351 + for (size_t i = 0; i < idArray.length(); i++) { 1.352 + idName = idArray[i]; 1.353 + 1.354 + nsCOMPtr<nsIVariant> value; 1.355 + nsresult rv; 1.356 + if (!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName, 1.357 + getter_AddRefs(value), &rv)) { 1.358 + if (NS_FAILED(rv)) 1.359 + return rv; 1.360 + return NS_ERROR_FAILURE; 1.361 + } 1.362 + 1.363 + RootedValue jsvalName(cx); 1.364 + if (!JS_IdToValue(cx, idName, &jsvalName)) 1.365 + return NS_ERROR_FAILURE; 1.366 + 1.367 + JSString* name = ToString(cx, jsvalName); 1.368 + if (!name) 1.369 + return NS_ERROR_FAILURE; 1.370 + 1.371 + size_t length; 1.372 + const jschar *chars = JS_GetStringCharsAndLength(cx, name, &length); 1.373 + if (!chars) 1.374 + return NS_ERROR_FAILURE; 1.375 + 1.376 + nsCOMPtr<nsIProperty> property = 1.377 + new xpcProperty(chars, (uint32_t) length, value); 1.378 + 1.379 + if (!propertyArray.AppendObject(property)) 1.380 + return NS_ERROR_FAILURE; 1.381 + } 1.382 + 1.383 + return NS_NewArrayEnumerator(aEnumerate, propertyArray); 1.384 +} 1.385 + 1.386 +/***************************************************************************/ 1.387 + 1.388 +NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty) 1.389 + 1.390 +xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen, 1.391 + nsIVariant* aValue) 1.392 + : mName(aName, aNameLen), mValue(aValue) 1.393 +{ 1.394 +} 1.395 + 1.396 +/* readonly attribute AString name; */ 1.397 +NS_IMETHODIMP xpcProperty::GetName(nsAString & aName) 1.398 +{ 1.399 + aName.Assign(mName); 1.400 + return NS_OK; 1.401 +} 1.402 + 1.403 +/* readonly attribute nsIVariant value; */ 1.404 +NS_IMETHODIMP xpcProperty::GetValue(nsIVariant * *aValue) 1.405 +{ 1.406 + nsCOMPtr<nsIVariant> rval = mValue; 1.407 + rval.forget(aValue); 1.408 + return NS_OK; 1.409 +} 1.410 + 1.411 +/***************************************************************************/ 1.412 +// This 'WrappedJSIdentity' class and singleton allow us to figure out if 1.413 +// any given nsISupports* is implemented by a WrappedJS object. This is done 1.414 +// using a QueryInterface call on the interface pointer with our ID. If 1.415 +// that call returns NS_OK and the pointer is to our singleton, then the 1.416 +// interface must be implemented by a WrappedJS object. NOTE: the 1.417 +// 'WrappedJSIdentity' object is not a real XPCOM object and should not be 1.418 +// used for anything else (hence it is declared in this implementation file). 1.419 + 1.420 +// {5C5C3BB0-A9BA-11d2-BA64-00805F8A5DD7} 1.421 +#define NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID \ 1.422 +{ 0x5c5c3bb0, 0xa9ba, 0x11d2, \ 1.423 + { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } 1.424 + 1.425 +class WrappedJSIdentity 1.426 +{ 1.427 + // no instance methods... 1.428 +public: 1.429 + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID) 1.430 + 1.431 + static void* GetSingleton() 1.432 + { 1.433 + static WrappedJSIdentity* singleton = nullptr; 1.434 + if (!singleton) 1.435 + singleton = new WrappedJSIdentity(); 1.436 + return (void*) singleton; 1.437 + } 1.438 +}; 1.439 + 1.440 +NS_DEFINE_STATIC_IID_ACCESSOR(WrappedJSIdentity, 1.441 + NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID) 1.442 + 1.443 +/***************************************************************************/ 1.444 + 1.445 +// static 1.446 +bool 1.447 +nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr) 1.448 +{ 1.449 + void* result; 1.450 + NS_PRECONDITION(aPtr, "null pointer"); 1.451 + return aPtr && 1.452 + NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) && 1.453 + result == WrappedJSIdentity::GetSingleton(); 1.454 +} 1.455 + 1.456 +// NB: This will return the top JSContext on the JSContext stack if there is one, 1.457 +// before attempting to get the context from the wrapped JS object. 1.458 +static JSContext * 1.459 +GetContextFromObjectOrDefault(nsXPCWrappedJS* wrapper) 1.460 +{ 1.461 + // First, try the cx stack. 1.462 + XPCJSContextStack* stack = XPCJSRuntime::Get()->GetJSContextStack(); 1.463 + if (stack->Peek()) 1.464 + return stack->Peek(); 1.465 + 1.466 + // If the cx stack is empty, try the wrapper's JSObject. 1.467 + JSCompartment *c = js::GetObjectCompartment(wrapper->GetJSObject()); 1.468 + XPCContext *xpcc = EnsureCompartmentPrivate(c)->scope->GetContext(); 1.469 + if (xpcc) { 1.470 + JSContext *cx = xpcc->GetJSContext(); 1.471 + JS_AbortIfWrongThread(JS_GetRuntime(cx)); 1.472 + return cx; 1.473 + } 1.474 + 1.475 + // Fall back to the safe JSContext. 1.476 + return stack->GetSafeJSContext(); 1.477 +} 1.478 + 1.479 +NS_IMETHODIMP 1.480 +nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self, 1.481 + REFNSIID aIID, 1.482 + void** aInstancePtr) 1.483 +{ 1.484 + if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) { 1.485 + NS_ADDREF(self); 1.486 + *aInstancePtr = (void*) static_cast<nsIXPConnectJSObjectHolder*>(self); 1.487 + return NS_OK; 1.488 + } 1.489 + 1.490 + // Objects internal to xpconnect are the only objects that even know *how* 1.491 + // to ask for this iid. And none of them bother refcounting the thing. 1.492 + if (aIID.Equals(NS_GET_IID(WrappedJSIdentity))) { 1.493 + // asking to find out if this is a wrapper object 1.494 + *aInstancePtr = WrappedJSIdentity::GetSingleton(); 1.495 + return NS_OK; 1.496 + } 1.497 + 1.498 + if (aIID.Equals(NS_GET_IID(nsIPropertyBag))) { 1.499 + // We only want to expose one implementation from our aggregate. 1.500 + nsXPCWrappedJS* root = self->GetRootWrapper(); 1.501 + 1.502 + if (!root->IsValid()) { 1.503 + *aInstancePtr = nullptr; 1.504 + return NS_NOINTERFACE; 1.505 + } 1.506 + 1.507 + NS_ADDREF(root); 1.508 + *aInstancePtr = (void*) static_cast<nsIPropertyBag*>(root); 1.509 + return NS_OK; 1.510 + } 1.511 + 1.512 + // We can't have a cached wrapper. 1.513 + if (aIID.Equals(NS_GET_IID(nsWrapperCache))) { 1.514 + *aInstancePtr = nullptr; 1.515 + return NS_NOINTERFACE; 1.516 + } 1.517 + 1.518 + AutoPushJSContext context(GetContextFromObjectOrDefault(self)); 1.519 + XPCCallContext ccx(NATIVE_CALLER, context); 1.520 + if (!ccx.IsValid()) { 1.521 + *aInstancePtr = nullptr; 1.522 + return NS_NOINTERFACE; 1.523 + } 1.524 + 1.525 + // We support nsISupportsWeakReference iff the root wrapped JSObject 1.526 + // claims to support it in its QueryInterface implementation. 1.527 + if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) { 1.528 + // We only want to expose one implementation from our aggregate. 1.529 + nsXPCWrappedJS* root = self->GetRootWrapper(); 1.530 + 1.531 + // Fail if JSObject doesn't claim support for nsISupportsWeakReference 1.532 + if (!root->IsValid() || 1.533 + !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID)) { 1.534 + *aInstancePtr = nullptr; 1.535 + return NS_NOINTERFACE; 1.536 + } 1.537 + 1.538 + NS_ADDREF(root); 1.539 + *aInstancePtr = (void*) static_cast<nsISupportsWeakReference*>(root); 1.540 + return NS_OK; 1.541 + } 1.542 + 1.543 + // Checks for any existing wrapper explicitly constructed for this iid. 1.544 + // This includes the current 'self' wrapper. This also deals with the 1.545 + // nsISupports case (for which it returns mRoot). 1.546 + // Also check if asking for an interface from which one of our wrappers inherits. 1.547 + if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) { 1.548 + NS_ADDREF(sibling); 1.549 + *aInstancePtr = sibling->GetXPTCStub(); 1.550 + return NS_OK; 1.551 + } 1.552 + 1.553 + // else we do the more expensive stuff... 1.554 + 1.555 + // check if the JSObject claims to implement this interface 1.556 + RootedObject jsobj(ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(), 1.557 + aIID)); 1.558 + if (jsobj) { 1.559 + // We can't use XPConvert::JSObject2NativeInterface() here 1.560 + // since that can find a XPCWrappedNative directly on the 1.561 + // proto chain, and we don't want that here. We need to find 1.562 + // the actual JS object that claimed it supports the interface 1.563 + // we're looking for or we'll potentially bypass security 1.564 + // checks etc by calling directly through to a native found on 1.565 + // the prototype chain. 1.566 + // 1.567 + // Instead, simply do the nsXPCWrappedJS part of 1.568 + // XPConvert::JSObject2NativeInterface() here to make sure we 1.569 + // get a new (or used) nsXPCWrappedJS. 1.570 + nsXPCWrappedJS* wrapper; 1.571 + nsresult rv = nsXPCWrappedJS::GetNewOrUsed(jsobj, aIID, &wrapper); 1.572 + if (NS_SUCCEEDED(rv) && wrapper) { 1.573 + // We need to go through the QueryInterface logic to make 1.574 + // this return the right thing for the various 'special' 1.575 + // interfaces; e.g. nsIPropertyBag. 1.576 + rv = wrapper->QueryInterface(aIID, aInstancePtr); 1.577 + NS_RELEASE(wrapper); 1.578 + return rv; 1.579 + } 1.580 + } 1.581 + 1.582 + // else... 1.583 + // no can do 1.584 + *aInstancePtr = nullptr; 1.585 + return NS_NOINTERFACE; 1.586 +} 1.587 + 1.588 +JSObject* 1.589 +nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg) 1.590 +{ 1.591 + RootedObject aJSObj(cx, aJSObjArg); 1.592 + JSObject* result = CallQueryInterfaceOnJSObject(cx, aJSObj, 1.593 + NS_GET_IID(nsISupports)); 1.594 + if (!result) 1.595 + return aJSObj; 1.596 + JSObject* inner = js::UncheckedUnwrap(result); 1.597 + if (inner) 1.598 + return inner; 1.599 + return result; 1.600 +} 1.601 + 1.602 +void 1.603 +xpcWrappedJSErrorReporter(JSContext *cx, const char *message, 1.604 + JSErrorReport *report) 1.605 +{ 1.606 + if (report) { 1.607 + // If it is an exception report, then we can just deal with the 1.608 + // exception later (if not caught in the JS code). 1.609 + if (JSREPORT_IS_EXCEPTION(report->flags)) { 1.610 + // XXX We have a problem with error reports from uncaught exceptions. 1.611 + // 1.612 + // http://bugzilla.mozilla.org/show_bug.cgi?id=66453 1.613 + // 1.614 + // The issue is... 1.615 + // 1.616 + // We can't assume that the exception will *stay* uncaught. So, if 1.617 + // we build an nsIXPCException here and the underlying exception 1.618 + // really is caught before our script is done running then we blow 1.619 + // it by returning failure to our caller when the script didn't 1.620 + // really fail. However, This report contains error location info 1.621 + // that is no longer available after the script is done. So, if the 1.622 + // exception really is not caught (and is a non-engine exception) 1.623 + // then we've lost the oportunity to capture the script location 1.624 + // info that we *could* have captured here. 1.625 + // 1.626 + // This is expecially an issue with nested evaluations. 1.627 + // 1.628 + // Perhaps we could capture an expception here and store it as 1.629 + // 'provisional' and then later if there is a pending exception 1.630 + // when the script is done then we could maybe compare that in some 1.631 + // way with the 'provisional' one in which we captured location info. 1.632 + // We would not want to assume that the one discovered here is the 1.633 + // same one that is later detected. This could cause us to lie. 1.634 + // 1.635 + // The thing is. we do not currently store the right stuff to compare 1.636 + // these two nsIXPCExceptions (triggered by the same exception jsval 1.637 + // in the engine). Maybe we should store the jsval and compare that? 1.638 + // Maybe without even rooting it since we will not dereference it. 1.639 + // This is inexact, but maybe the right thing to do? 1.640 + // 1.641 + // if (report->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)) ... 1.642 + // 1.643 + 1.644 + return; 1.645 + } 1.646 + 1.647 + if (JSREPORT_IS_WARNING(report->flags)) { 1.648 + // XXX printf the warning (#ifdef DEBUG only!). 1.649 + // XXX send the warning to the console service. 1.650 + return; 1.651 + } 1.652 + } 1.653 + 1.654 + XPCCallContext ccx(NATIVE_CALLER, cx); 1.655 + if (!ccx.IsValid()) 1.656 + return; 1.657 + 1.658 + nsCOMPtr<nsIException> e; 1.659 + XPCConvert::JSErrorToXPCException(message, nullptr, nullptr, report, 1.660 + getter_AddRefs(e)); 1.661 + if (e) 1.662 + ccx.GetXPCContext()->SetException(e); 1.663 +} 1.664 + 1.665 +bool 1.666 +nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx, 1.667 + const XPTMethodDescriptor* method, 1.668 + const nsXPTParamInfo& param, 1.669 + uint16_t methodIndex, 1.670 + uint8_t paramIndex, 1.671 + nsXPTCMiniVariant* nativeParams, 1.672 + uint32_t* result) 1.673 +{ 1.674 + uint8_t argnum; 1.675 + nsresult rv; 1.676 + 1.677 + rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, ¶m, 0, &argnum); 1.678 + if (NS_FAILED(rv)) 1.679 + return false; 1.680 + 1.681 + const nsXPTParamInfo& arg_param = method->params[argnum]; 1.682 + 1.683 + // This should be enforced by the xpidl compiler, but it's not. 1.684 + // See bug 695235. 1.685 + MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32, 1.686 + "size_is references parameter of invalid type."); 1.687 + 1.688 + if (arg_param.IsIndirect()) 1.689 + *result = *(uint32_t*)nativeParams[argnum].val.p; 1.690 + else 1.691 + *result = nativeParams[argnum].val.u32; 1.692 + 1.693 + return true; 1.694 +} 1.695 + 1.696 +bool 1.697 +nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx, 1.698 + const XPTMethodDescriptor* method, 1.699 + const nsXPTParamInfo& param, 1.700 + uint16_t methodIndex, 1.701 + const nsXPTType& type, 1.702 + nsXPTCMiniVariant* nativeParams, 1.703 + nsID* result) 1.704 +{ 1.705 + uint8_t type_tag = type.TagPart(); 1.706 + 1.707 + if (type_tag == nsXPTType::T_INTERFACE) { 1.708 + if (NS_SUCCEEDED(GetInterfaceInfo()-> 1.709 + GetIIDForParamNoAlloc(methodIndex, ¶m, result))) { 1.710 + return true; 1.711 + } 1.712 + } else if (type_tag == nsXPTType::T_INTERFACE_IS) { 1.713 + uint8_t argnum; 1.714 + nsresult rv; 1.715 + rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex, 1.716 + ¶m, &argnum); 1.717 + if (NS_FAILED(rv)) 1.718 + return false; 1.719 + 1.720 + const nsXPTParamInfo& arg_param = method->params[argnum]; 1.721 + const nsXPTType& arg_type = arg_param.GetType(); 1.722 + 1.723 + if (arg_type.TagPart() == nsXPTType::T_IID) { 1.724 + if (arg_param.IsIndirect()) { 1.725 + nsID** p = (nsID**) nativeParams[argnum].val.p; 1.726 + if (!p || !*p) 1.727 + return false; 1.728 + *result = **p; 1.729 + } else { 1.730 + nsID* p = (nsID*) nativeParams[argnum].val.p; 1.731 + if (!p) 1.732 + return false; 1.733 + *result = *p; 1.734 + } 1.735 + return true; 1.736 + } 1.737 + } 1.738 + return false; 1.739 +} 1.740 + 1.741 +void 1.742 +nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type, 1.743 + uint32_t array_count, 1.744 + void** arrayp) 1.745 +{ 1.746 + if (datum_type.IsInterfacePointer()) { 1.747 + nsISupports** pp = (nsISupports**) arrayp; 1.748 + for (uint32_t k = 0; k < array_count; k++) { 1.749 + nsISupports* p = pp[k]; 1.750 + NS_IF_RELEASE(p); 1.751 + } 1.752 + } else { 1.753 + void** pp = (void**) arrayp; 1.754 + for (uint32_t k = 0; k < array_count; k++) { 1.755 + void* p = pp[k]; 1.756 + if (p) nsMemory::Free(p); 1.757 + } 1.758 + } 1.759 +} 1.760 + 1.761 +void 1.762 +nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type, 1.763 + void** pp) 1.764 +{ 1.765 + MOZ_ASSERT(pp,"null pointer"); 1.766 + if (type.IsInterfacePointer()) { 1.767 + nsISupports* p = *((nsISupports**)pp); 1.768 + if (p) p->Release(); 1.769 + } else { 1.770 + void* p = *((void**)pp); 1.771 + if (p) nsMemory::Free(p); 1.772 + } 1.773 +} 1.774 + 1.775 +class AutoClearPendingException 1.776 +{ 1.777 +public: 1.778 + AutoClearPendingException(JSContext *cx) : mCx(cx) { } 1.779 + ~AutoClearPendingException() { JS_ClearPendingException(mCx); } 1.780 +private: 1.781 + JSContext* mCx; 1.782 +}; 1.783 + 1.784 +nsresult 1.785 +nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx, 1.786 + const char * aPropertyName, 1.787 + const char * anInterfaceName, 1.788 + bool aForceReport) 1.789 +{ 1.790 + XPCContext * xpcc = ccx.GetXPCContext(); 1.791 + JSContext * cx = ccx.GetJSContext(); 1.792 + nsCOMPtr<nsIException> xpc_exception; 1.793 + /* this one would be set by our error reporter */ 1.794 + 1.795 + xpcc->GetException(getter_AddRefs(xpc_exception)); 1.796 + if (xpc_exception) 1.797 + xpcc->SetException(nullptr); 1.798 + 1.799 + // get this right away in case we do something below to cause JS code 1.800 + // to run on this JSContext 1.801 + nsresult pending_result = xpcc->GetPendingResult(); 1.802 + 1.803 + RootedValue js_exception(cx); 1.804 + bool is_js_exception = JS_GetPendingException(cx, &js_exception); 1.805 + 1.806 + /* JS might throw an expection whether the reporter was called or not */ 1.807 + if (is_js_exception) { 1.808 + if (!xpc_exception) 1.809 + XPCConvert::JSValToXPCException(&js_exception, anInterfaceName, 1.810 + aPropertyName, 1.811 + getter_AddRefs(xpc_exception)); 1.812 + 1.813 + /* cleanup and set failed even if we can't build an exception */ 1.814 + if (!xpc_exception) { 1.815 + XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary? 1.816 + } 1.817 + } 1.818 + 1.819 + AutoClearPendingException acpe(cx); 1.820 + 1.821 + if (xpc_exception) { 1.822 + nsresult e_result; 1.823 + if (NS_SUCCEEDED(xpc_exception->GetResult(&e_result))) { 1.824 + // Figure out whether or not we should report this exception. 1.825 + bool reportable = xpc_IsReportableErrorCode(e_result); 1.826 + if (reportable) { 1.827 + // Always want to report forced exceptions and XPConnect's own 1.828 + // errors. 1.829 + reportable = aForceReport || 1.830 + NS_ERROR_GET_MODULE(e_result) == NS_ERROR_MODULE_XPCONNECT; 1.831 + 1.832 + // See if an environment variable was set or someone has told us 1.833 + // that a user pref was set indicating that we should report all 1.834 + // exceptions. 1.835 + if (!reportable) 1.836 + reportable = nsXPConnect::ReportAllJSExceptions(); 1.837 + 1.838 + // Finally, check to see if this is the last JS frame on the 1.839 + // stack. If so then we always want to report it. 1.840 + if (!reportable) 1.841 + reportable = !JS::DescribeScriptedCaller(cx); 1.842 + 1.843 + // Ugly special case for GetInterface. It's "special" in the 1.844 + // same way as QueryInterface in that a failure is not 1.845 + // exceptional and shouldn't be reported. We have to do this 1.846 + // check here instead of in xpcwrappedjs (like we do for QI) to 1.847 + // avoid adding extra code to all xpcwrappedjs objects. 1.848 + if (reportable && e_result == NS_ERROR_NO_INTERFACE && 1.849 + !strcmp(anInterfaceName, "nsIInterfaceRequestor") && 1.850 + !strcmp(aPropertyName, "getInterface")) { 1.851 + reportable = false; 1.852 + } 1.853 + 1.854 + // More special case, see bug 877760. 1.855 + if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) { 1.856 + reportable = false; 1.857 + } 1.858 + } 1.859 + 1.860 + // Try to use the error reporter set on the context to handle this 1.861 + // error if it came from a JS exception. 1.862 + if (reportable && is_js_exception && 1.863 + JS_GetErrorReporter(cx) != xpcWrappedJSErrorReporter) 1.864 + { 1.865 + // If the error reporter ignores the error, it will call 1.866 + // xpc->MarkErrorUnreported(). 1.867 + xpcc->ClearUnreportedError(); 1.868 + reportable = !JS_ReportPendingException(cx); 1.869 + if (!xpcc->WasErrorReported()) 1.870 + reportable = true; 1.871 + } 1.872 + 1.873 + if (reportable) { 1.874 + if (nsContentUtils::DOMWindowDumpEnabled()) { 1.875 + static const char line[] = 1.876 + "************************************************************\n"; 1.877 + static const char preamble[] = 1.878 + "* Call to xpconnect wrapped JSObject produced this error: *\n"; 1.879 + static const char cant_get_text[] = 1.880 + "FAILED TO GET TEXT FROM EXCEPTION\n"; 1.881 + 1.882 + fputs(line, stdout); 1.883 + fputs(preamble, stdout); 1.884 + nsCString text; 1.885 + if (NS_SUCCEEDED(xpc_exception->ToString(text)) && 1.886 + !text.IsEmpty()) { 1.887 + fputs(text.get(), stdout); 1.888 + fputs("\n", stdout); 1.889 + } else 1.890 + fputs(cant_get_text, stdout); 1.891 + fputs(line, stdout); 1.892 + } 1.893 + 1.894 + // Log the exception to the JS Console, so that users can do 1.895 + // something with it. 1.896 + nsCOMPtr<nsIConsoleService> consoleService 1.897 + (do_GetService(XPC_CONSOLE_CONTRACTID)); 1.898 + if (nullptr != consoleService) { 1.899 + nsresult rv; 1.900 + nsCOMPtr<nsIScriptError> scriptError; 1.901 + nsCOMPtr<nsISupports> errorData; 1.902 + rv = xpc_exception->GetData(getter_AddRefs(errorData)); 1.903 + if (NS_SUCCEEDED(rv)) 1.904 + scriptError = do_QueryInterface(errorData); 1.905 + 1.906 + if (nullptr == scriptError) { 1.907 + // No luck getting one from the exception, so 1.908 + // try to cook one up. 1.909 + scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID); 1.910 + if (nullptr != scriptError) { 1.911 + nsCString newMessage; 1.912 + rv = xpc_exception->ToString(newMessage); 1.913 + if (NS_SUCCEEDED(rv)) { 1.914 + // try to get filename, lineno from the first 1.915 + // stack frame location. 1.916 + int32_t lineNumber = 0; 1.917 + nsString sourceName; 1.918 + 1.919 + nsCOMPtr<nsIStackFrame> location; 1.920 + xpc_exception-> 1.921 + GetLocation(getter_AddRefs(location)); 1.922 + if (location) { 1.923 + // Get line number w/o checking; 0 is ok. 1.924 + location->GetLineNumber(&lineNumber); 1.925 + 1.926 + // get a filename. 1.927 + rv = location->GetFilename(sourceName); 1.928 + } 1.929 + 1.930 + rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(newMessage), 1.931 + sourceName, 1.932 + EmptyString(), 1.933 + lineNumber, 0, 0, 1.934 + "XPConnect JavaScript", 1.935 + nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx)); 1.936 + if (NS_FAILED(rv)) 1.937 + scriptError = nullptr; 1.938 + } 1.939 + } 1.940 + } 1.941 + if (nullptr != scriptError) 1.942 + consoleService->LogMessage(scriptError); 1.943 + } 1.944 + } 1.945 + // Whether or not it passes the 'reportable' test, it might 1.946 + // still be an error and we have to do the right thing here... 1.947 + if (NS_FAILED(e_result)) { 1.948 + XPCJSRuntime::Get()->SetPendingException(xpc_exception); 1.949 + return e_result; 1.950 + } 1.951 + } 1.952 + } else { 1.953 + // see if JS code signaled failure result without throwing exception 1.954 + if (NS_FAILED(pending_result)) { 1.955 + return pending_result; 1.956 + } 1.957 + } 1.958 + return NS_ERROR_FAILURE; 1.959 +} 1.960 + 1.961 +NS_IMETHODIMP 1.962 +nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, 1.963 + const XPTMethodDescriptor* info_, 1.964 + nsXPTCMiniVariant* nativeParams) 1.965 +{ 1.966 + jsval* sp = nullptr; 1.967 + jsval* argv = nullptr; 1.968 + uint8_t i; 1.969 + nsresult retval = NS_ERROR_FAILURE; 1.970 + nsresult pending_result = NS_OK; 1.971 + bool success; 1.972 + bool readyToDoTheCall = false; 1.973 + nsID param_iid; 1.974 + const nsXPTMethodInfo* info = static_cast<const nsXPTMethodInfo*>(info_); 1.975 + const char* name = info->name; 1.976 + bool foundDependentParam; 1.977 + 1.978 + // Make sure not to set the callee on ccx until after we've gone through 1.979 + // the whole nsIXPCFunctionThisTranslator bit. That code uses ccx to 1.980 + // convert natives to JSObjects, but we do NOT plan to pass those JSObjects 1.981 + // to our real callee. 1.982 + AutoPushJSContext context(GetContextFromObjectOrDefault(wrapper)); 1.983 + XPCCallContext ccx(NATIVE_CALLER, context); 1.984 + if (!ccx.IsValid()) 1.985 + return retval; 1.986 + 1.987 + XPCContext *xpcc = ccx.GetXPCContext(); 1.988 + JSContext *cx = ccx.GetJSContext(); 1.989 + 1.990 + if (!cx || !xpcc || !IsReflectable(methodIndex)) 1.991 + return NS_ERROR_FAILURE; 1.992 + 1.993 + // [implicit_jscontext] and [optional_argc] have a different calling 1.994 + // convention, which we don't support for JS-implemented components. 1.995 + if (info->WantsOptArgc() || info->WantsContext()) { 1.996 + const char *str = "IDL methods marked with [implicit_jscontext] " 1.997 + "or [optional_argc] may not be implemented in JS"; 1.998 + // Throw and warn for good measure. 1.999 + JS_ReportError(cx, str); 1.1000 + NS_WARNING(str); 1.1001 + return NS_ERROR_FAILURE; 1.1002 + } 1.1003 + 1.1004 + RootedValue fval(cx); 1.1005 + RootedObject obj(cx, wrapper->GetJSObject()); 1.1006 + RootedObject thisObj(cx, obj); 1.1007 + 1.1008 + JSAutoCompartment ac(cx, obj); 1.1009 + 1.1010 + AutoValueVector args(cx); 1.1011 + AutoScriptEvaluate scriptEval(cx); 1.1012 + 1.1013 + // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this. 1.1014 + uint8_t paramCount = info->num_args; 1.1015 + uint8_t argc = paramCount - 1.1016 + (paramCount && XPT_PD_IS_RETVAL(info->params[paramCount-1].flags) ? 1 : 0); 1.1017 + 1.1018 + if (!scriptEval.StartEvaluating(obj, xpcWrappedJSErrorReporter)) 1.1019 + goto pre_call_clean_up; 1.1020 + 1.1021 + xpcc->SetPendingResult(pending_result); 1.1022 + xpcc->SetException(nullptr); 1.1023 + XPCJSRuntime::Get()->SetPendingException(nullptr); 1.1024 + 1.1025 + // We use js_Invoke so that the gcthings we use as args will be rooted by 1.1026 + // the engine as we do conversions and prepare to do the function call. 1.1027 + 1.1028 + // setup stack 1.1029 + 1.1030 + // if this isn't a function call then we don't need to push extra stuff 1.1031 + if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags))) { 1.1032 + // We get fval before allocating the stack to avoid gc badness that can 1.1033 + // happen if the GetProperty call leaves our request and the gc runs 1.1034 + // while the stack we allocate contains garbage. 1.1035 + 1.1036 + // If the interface is marked as a [function] then we will assume that 1.1037 + // our JSObject is a function and not an object with a named method. 1.1038 + 1.1039 + bool isFunction; 1.1040 + if (NS_FAILED(mInfo->IsFunction(&isFunction))) 1.1041 + goto pre_call_clean_up; 1.1042 + 1.1043 + // In the xpidl [function] case we are making sure now that the 1.1044 + // JSObject is callable. If it is *not* callable then we silently 1.1045 + // fallback to looking up the named property... 1.1046 + // (because jst says he thinks this fallback is 'The Right Thing'.) 1.1047 + // 1.1048 + // In the normal (non-function) case we just lookup the property by 1.1049 + // name and as long as the object has such a named property we go ahead 1.1050 + // and try to make the call. If it turns out the named property is not 1.1051 + // a callable object then the JS engine will throw an error and we'll 1.1052 + // pass this along to the caller as an exception/result code. 1.1053 + 1.1054 + fval = ObjectValue(*obj); 1.1055 + if (isFunction && 1.1056 + JS_TypeOfValue(ccx, fval) == JSTYPE_FUNCTION) { 1.1057 + 1.1058 + // We may need to translate the 'this' for the function object. 1.1059 + 1.1060 + if (paramCount) { 1.1061 + const nsXPTParamInfo& firstParam = info->params[0]; 1.1062 + if (firstParam.IsIn()) { 1.1063 + const nsXPTType& firstType = firstParam.GetType(); 1.1064 + 1.1065 + if (firstType.IsInterfacePointer()) { 1.1066 + nsIXPCFunctionThisTranslator* translator; 1.1067 + 1.1068 + IID2ThisTranslatorMap* map = 1.1069 + mRuntime->GetThisTranslatorMap(); 1.1070 + 1.1071 + translator = map->Find(mIID); 1.1072 + 1.1073 + if (translator) { 1.1074 + nsCOMPtr<nsISupports> newThis; 1.1075 + if (NS_FAILED(translator-> 1.1076 + TranslateThis((nsISupports*)nativeParams[0].val.p, 1.1077 + getter_AddRefs(newThis)))) { 1.1078 + goto pre_call_clean_up; 1.1079 + } 1.1080 + if (newThis) { 1.1081 + RootedValue v(cx); 1.1082 + xpcObjectHelper helper(newThis); 1.1083 + bool ok = 1.1084 + XPCConvert::NativeInterface2JSObject( 1.1085 + &v, nullptr, helper, nullptr, 1.1086 + nullptr, false, nullptr); 1.1087 + if (!ok) { 1.1088 + goto pre_call_clean_up; 1.1089 + } 1.1090 + thisObj = JSVAL_TO_OBJECT(v); 1.1091 + if (!JS_WrapObject(cx, &thisObj)) 1.1092 + goto pre_call_clean_up; 1.1093 + } 1.1094 + } 1.1095 + } 1.1096 + } 1.1097 + } 1.1098 + } else { 1.1099 + if (!JS_GetProperty(cx, obj, name, &fval)) 1.1100 + goto pre_call_clean_up; 1.1101 + // XXX We really want to factor out the error reporting better and 1.1102 + // specifically report the failure to find a function with this name. 1.1103 + // This is what we do below if the property is found but is not a 1.1104 + // function. We just need to factor better so we can get to that 1.1105 + // reporting path from here. 1.1106 + 1.1107 + thisObj = obj; 1.1108 + } 1.1109 + } 1.1110 + 1.1111 + if (!args.resize(argc)) { 1.1112 + retval = NS_ERROR_OUT_OF_MEMORY; 1.1113 + goto pre_call_clean_up; 1.1114 + } 1.1115 + 1.1116 + argv = args.begin(); 1.1117 + sp = argv; 1.1118 + 1.1119 + // build the args 1.1120 + // NB: This assignment *looks* wrong because we haven't yet called our 1.1121 + // function. However, we *have* already entered the compartmen that we're 1.1122 + // about to call, and that's the global that we want here. In other words: 1.1123 + // we're trusting the JS engine to come up with a good global to use for 1.1124 + // our object (whatever it was). 1.1125 + for (i = 0; i < argc; i++) { 1.1126 + const nsXPTParamInfo& param = info->params[i]; 1.1127 + const nsXPTType& type = param.GetType(); 1.1128 + nsXPTType datum_type; 1.1129 + uint32_t array_count; 1.1130 + bool isArray = type.IsArray(); 1.1131 + RootedValue val(cx, NullValue()); 1.1132 + bool isSizedString = isArray ? 1.1133 + false : 1.1134 + type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || 1.1135 + type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS; 1.1136 + 1.1137 + 1.1138 + // verify that null was not passed for 'out' param 1.1139 + if (param.IsOut() && !nativeParams[i].val.p) { 1.1140 + retval = NS_ERROR_INVALID_ARG; 1.1141 + goto pre_call_clean_up; 1.1142 + } 1.1143 + 1.1144 + if (isArray) { 1.1145 + if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, ¶m, 1, 1.1146 + &datum_type))) 1.1147 + goto pre_call_clean_up; 1.1148 + } else 1.1149 + datum_type = type; 1.1150 + 1.1151 + if (param.IsIn()) { 1.1152 + nsXPTCMiniVariant* pv; 1.1153 + 1.1154 + if (param.IsIndirect()) 1.1155 + pv = (nsXPTCMiniVariant*) nativeParams[i].val.p; 1.1156 + else 1.1157 + pv = &nativeParams[i]; 1.1158 + 1.1159 + if (datum_type.IsInterfacePointer() && 1.1160 + !GetInterfaceTypeFromParam(cx, info, param, methodIndex, 1.1161 + datum_type, nativeParams, 1.1162 + ¶m_iid)) 1.1163 + goto pre_call_clean_up; 1.1164 + 1.1165 + if (isArray || isSizedString) { 1.1166 + if (!GetArraySizeFromParam(cx, info, param, methodIndex, 1.1167 + i, nativeParams, &array_count)) 1.1168 + goto pre_call_clean_up; 1.1169 + } 1.1170 + 1.1171 + if (isArray) { 1.1172 + if (!XPCConvert::NativeArray2JS(&val, 1.1173 + (const void**)&pv->val, 1.1174 + datum_type, ¶m_iid, 1.1175 + array_count, nullptr)) 1.1176 + goto pre_call_clean_up; 1.1177 + } else if (isSizedString) { 1.1178 + if (!XPCConvert::NativeStringWithSize2JS(&val, 1.1179 + (const void*)&pv->val, 1.1180 + datum_type, 1.1181 + array_count, nullptr)) 1.1182 + goto pre_call_clean_up; 1.1183 + } else { 1.1184 + if (!XPCConvert::NativeData2JS(&val, &pv->val, type, 1.1185 + ¶m_iid, nullptr)) 1.1186 + goto pre_call_clean_up; 1.1187 + } 1.1188 + } 1.1189 + 1.1190 + if (param.IsOut() || param.IsDipper()) { 1.1191 + // create an 'out' object 1.1192 + RootedObject out_obj(cx, NewOutObject(cx, obj)); 1.1193 + if (!out_obj) { 1.1194 + retval = NS_ERROR_OUT_OF_MEMORY; 1.1195 + goto pre_call_clean_up; 1.1196 + } 1.1197 + 1.1198 + if (param.IsIn()) { 1.1199 + if (!JS_SetPropertyById(cx, out_obj, 1.1200 + mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE), 1.1201 + val)) { 1.1202 + goto pre_call_clean_up; 1.1203 + } 1.1204 + } 1.1205 + *sp++ = OBJECT_TO_JSVAL(out_obj); 1.1206 + } else 1.1207 + *sp++ = val; 1.1208 + } 1.1209 + 1.1210 + readyToDoTheCall = true; 1.1211 + 1.1212 +pre_call_clean_up: 1.1213 + // clean up any 'out' params handed in 1.1214 + for (i = 0; i < paramCount; i++) { 1.1215 + const nsXPTParamInfo& param = info->params[i]; 1.1216 + if (!param.IsOut()) 1.1217 + continue; 1.1218 + 1.1219 + const nsXPTType& type = param.GetType(); 1.1220 + if (!type.deprecated_IsPointer()) 1.1221 + continue; 1.1222 + void* p; 1.1223 + if (!(p = nativeParams[i].val.p)) 1.1224 + continue; 1.1225 + 1.1226 + if (param.IsIn()) { 1.1227 + if (type.IsArray()) { 1.1228 + void** pp; 1.1229 + if (nullptr != (pp = *((void***)p))) { 1.1230 + 1.1231 + // we need to get the array length and iterate the items 1.1232 + uint32_t array_count; 1.1233 + nsXPTType datum_type; 1.1234 + 1.1235 + if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, 1.1236 + 1, &datum_type)) && 1.1237 + datum_type.deprecated_IsPointer() && 1.1238 + GetArraySizeFromParam(cx, info, param, methodIndex, 1.1239 + i, nativeParams, &array_count) && 1.1240 + array_count) { 1.1241 + 1.1242 + CleanupPointerArray(datum_type, array_count, pp); 1.1243 + } 1.1244 + 1.1245 + // always release the array if it is inout 1.1246 + nsMemory::Free(pp); 1.1247 + } 1.1248 + } else 1.1249 + CleanupPointerTypeObject(type, (void**)p); 1.1250 + } 1.1251 + *((void**)p) = nullptr; 1.1252 + } 1.1253 + 1.1254 + // Make sure "this" doesn't get deleted during this call. 1.1255 + nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this); 1.1256 + 1.1257 + if (!readyToDoTheCall) 1.1258 + return retval; 1.1259 + 1.1260 + // do the deed - note exceptions 1.1261 + 1.1262 + JS_ClearPendingException(cx); 1.1263 + 1.1264 + RootedValue rval(cx); 1.1265 + if (XPT_MD_IS_GETTER(info->flags)) { 1.1266 + success = JS_GetProperty(cx, obj, name, &rval); 1.1267 + } else if (XPT_MD_IS_SETTER(info->flags)) { 1.1268 + rval = *argv; 1.1269 + success = JS_SetProperty(cx, obj, name, rval); 1.1270 + } else { 1.1271 + if (!JSVAL_IS_PRIMITIVE(fval)) { 1.1272 + AutoSaveContextOptions asco(cx); 1.1273 + ContextOptionsRef(cx).setDontReportUncaught(true); 1.1274 + 1.1275 + success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval); 1.1276 + } else { 1.1277 + // The property was not an object so can't be a function. 1.1278 + // Let's build and 'throw' an exception. 1.1279 + 1.1280 + static const nsresult code = 1.1281 + NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED; 1.1282 + static const char format[] = "%s \"%s\""; 1.1283 + const char * msg; 1.1284 + char* sz = nullptr; 1.1285 + 1.1286 + if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg) 1.1287 + sz = JS_smprintf(format, msg, name); 1.1288 + 1.1289 + nsCOMPtr<nsIException> e; 1.1290 + 1.1291 + XPCConvert::ConstructException(code, sz, GetInterfaceName(), name, 1.1292 + nullptr, getter_AddRefs(e), nullptr, nullptr); 1.1293 + xpcc->SetException(e); 1.1294 + if (sz) 1.1295 + JS_smprintf_free(sz); 1.1296 + success = false; 1.1297 + } 1.1298 + } 1.1299 + 1.1300 + if (!success) { 1.1301 + bool forceReport; 1.1302 + if (NS_FAILED(mInfo->IsFunction(&forceReport))) 1.1303 + forceReport = false; 1.1304 + 1.1305 + // May also want to check if we're moving from content->chrome and force 1.1306 + // a report in that case. 1.1307 + 1.1308 + return CheckForException(ccx, name, GetInterfaceName(), forceReport); 1.1309 + } 1.1310 + 1.1311 + XPCJSRuntime::Get()->SetPendingException(nullptr); // XXX necessary? 1.1312 + 1.1313 + // convert out args and result 1.1314 + // NOTE: this is the total number of native params, not just the args 1.1315 + // Convert independent params only. 1.1316 + // When we later convert the dependent params (if any) we will know that 1.1317 + // the params upon which they depend will have already been converted - 1.1318 + // regardless of ordering. 1.1319 + 1.1320 + foundDependentParam = false; 1.1321 + for (i = 0; i < paramCount; i++) { 1.1322 + const nsXPTParamInfo& param = info->params[i]; 1.1323 + MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!"); 1.1324 + if (!param.IsOut() && !param.IsDipper()) 1.1325 + continue; 1.1326 + 1.1327 + const nsXPTType& type = param.GetType(); 1.1328 + if (type.IsDependent()) { 1.1329 + foundDependentParam = true; 1.1330 + continue; 1.1331 + } 1.1332 + 1.1333 + RootedValue val(cx); 1.1334 + uint8_t type_tag = type.TagPart(); 1.1335 + nsXPTCMiniVariant* pv; 1.1336 + 1.1337 + if (param.IsDipper()) 1.1338 + pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p; 1.1339 + else 1.1340 + pv = (nsXPTCMiniVariant*) nativeParams[i].val.p; 1.1341 + 1.1342 + if (param.IsRetval()) 1.1343 + val = rval; 1.1344 + else if (argv[i].isPrimitive()) 1.1345 + break; 1.1346 + else { 1.1347 + RootedObject obj(cx, &argv[i].toObject()); 1.1348 + if (!JS_GetPropertyById(cx, obj, 1.1349 + mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE), 1.1350 + &val)) 1.1351 + break; 1.1352 + } 1.1353 + 1.1354 + // setup allocator and/or iid 1.1355 + 1.1356 + if (type_tag == nsXPTType::T_INTERFACE) { 1.1357 + if (NS_FAILED(GetInterfaceInfo()-> 1.1358 + GetIIDForParamNoAlloc(methodIndex, ¶m, 1.1359 + ¶m_iid))) 1.1360 + break; 1.1361 + } 1.1362 + 1.1363 +// see bug #961488 1.1364 +#if (defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(_AIX)) && \ 1.1365 + ((defined(__sparc) && !defined(__sparcv9) && !defined(__sparcv9__)) || \ 1.1366 + (defined(__powerpc__) && !defined (__powerpc64__))) 1.1367 + if (type_tag == nsXPTType::T_JSVAL) { 1.1368 + if (!XPCConvert::JSData2Native(*(void**)(&pv->val), val, type, 1.1369 + !param.IsDipper(), ¶m_iid, nullptr)) 1.1370 + break; 1.1371 + } else 1.1372 +#endif 1.1373 + { 1.1374 + if (!XPCConvert::JSData2Native(&pv->val, val, type, 1.1375 + !param.IsDipper(), ¶m_iid, nullptr)) 1.1376 + break; 1.1377 + } 1.1378 + } 1.1379 + 1.1380 + // if any params were dependent, then we must iterate again to convert them. 1.1381 + if (foundDependentParam && i == paramCount) { 1.1382 + for (i = 0; i < paramCount; i++) { 1.1383 + const nsXPTParamInfo& param = info->params[i]; 1.1384 + if (!param.IsOut()) 1.1385 + continue; 1.1386 + 1.1387 + const nsXPTType& type = param.GetType(); 1.1388 + if (!type.IsDependent()) 1.1389 + continue; 1.1390 + 1.1391 + RootedValue val(cx); 1.1392 + nsXPTCMiniVariant* pv; 1.1393 + nsXPTType datum_type; 1.1394 + uint32_t array_count; 1.1395 + bool isArray = type.IsArray(); 1.1396 + bool isSizedString = isArray ? 1.1397 + false : 1.1398 + type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || 1.1399 + type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS; 1.1400 + 1.1401 + pv = (nsXPTCMiniVariant*) nativeParams[i].val.p; 1.1402 + 1.1403 + if (param.IsRetval()) 1.1404 + val = rval; 1.1405 + else { 1.1406 + RootedObject obj(cx, &argv[i].toObject()); 1.1407 + if (!JS_GetPropertyById(cx, obj, 1.1408 + mRuntime->GetStringID(XPCJSRuntime::IDX_VALUE), 1.1409 + &val)) 1.1410 + break; 1.1411 + } 1.1412 + 1.1413 + // setup allocator and/or iid 1.1414 + 1.1415 + if (isArray) { 1.1416 + if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, ¶m, 1, 1.1417 + &datum_type))) 1.1418 + break; 1.1419 + } else 1.1420 + datum_type = type; 1.1421 + 1.1422 + if (datum_type.IsInterfacePointer()) { 1.1423 + if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex, 1.1424 + datum_type, nativeParams, 1.1425 + ¶m_iid)) 1.1426 + break; 1.1427 + } 1.1428 + 1.1429 + if (isArray || isSizedString) { 1.1430 + if (!GetArraySizeFromParam(cx, info, param, methodIndex, 1.1431 + i, nativeParams, &array_count)) 1.1432 + break; 1.1433 + } 1.1434 + 1.1435 + if (isArray) { 1.1436 + if (array_count && 1.1437 + !XPCConvert::JSArray2Native((void**)&pv->val, val, 1.1438 + array_count, datum_type, 1.1439 + ¶m_iid, nullptr)) 1.1440 + break; 1.1441 + } else if (isSizedString) { 1.1442 + if (!XPCConvert::JSStringWithSize2Native((void*)&pv->val, val, 1.1443 + array_count, datum_type, 1.1444 + nullptr)) 1.1445 + break; 1.1446 + } else { 1.1447 + if (!XPCConvert::JSData2Native(&pv->val, val, type, 1.1448 + true, ¶m_iid, 1.1449 + nullptr)) 1.1450 + break; 1.1451 + } 1.1452 + } 1.1453 + } 1.1454 + 1.1455 + if (i != paramCount) { 1.1456 + // We didn't manage all the result conversions! 1.1457 + // We have to cleanup any junk that *did* get converted. 1.1458 + 1.1459 + for (uint8_t k = 0; k < i; k++) { 1.1460 + const nsXPTParamInfo& param = info->params[k]; 1.1461 + if (!param.IsOut()) 1.1462 + continue; 1.1463 + const nsXPTType& type = param.GetType(); 1.1464 + if (!type.deprecated_IsPointer()) 1.1465 + continue; 1.1466 + void* p; 1.1467 + if (!(p = nativeParams[k].val.p)) 1.1468 + continue; 1.1469 + 1.1470 + if (type.IsArray()) { 1.1471 + void** pp; 1.1472 + if (nullptr != (pp = *((void***)p))) { 1.1473 + // we need to get the array length and iterate the items 1.1474 + uint32_t array_count; 1.1475 + nsXPTType datum_type; 1.1476 + 1.1477 + if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, ¶m, 1.1478 + 1, &datum_type)) && 1.1479 + datum_type.deprecated_IsPointer() && 1.1480 + GetArraySizeFromParam(cx, info, param, methodIndex, 1.1481 + k, nativeParams, &array_count) && 1.1482 + array_count) { 1.1483 + 1.1484 + CleanupPointerArray(datum_type, array_count, pp); 1.1485 + } 1.1486 + nsMemory::Free(pp); 1.1487 + } 1.1488 + } else 1.1489 + CleanupPointerTypeObject(type, (void**)p); 1.1490 + *((void**)p) = nullptr; 1.1491 + } 1.1492 + } else { 1.1493 + // set to whatever the JS code might have set as the result 1.1494 + retval = pending_result; 1.1495 + } 1.1496 + 1.1497 + return retval; 1.1498 +} 1.1499 + 1.1500 +const char* 1.1501 +nsXPCWrappedJSClass::GetInterfaceName() 1.1502 +{ 1.1503 + if (!mName) 1.1504 + mInfo->GetName(&mName); 1.1505 + return mName; 1.1506 +} 1.1507 + 1.1508 +static void 1.1509 +FinalizeStub(JSFreeOp *fop, JSObject *obj) 1.1510 +{ 1.1511 +} 1.1512 + 1.1513 +static const JSClass XPCOutParamClass = { 1.1514 + "XPCOutParam", 1.1515 + 0, 1.1516 + JS_PropertyStub, 1.1517 + JS_DeletePropertyStub, 1.1518 + JS_PropertyStub, 1.1519 + JS_StrictPropertyStub, 1.1520 + JS_EnumerateStub, 1.1521 + JS_ResolveStub, 1.1522 + JS_ConvertStub, 1.1523 + FinalizeStub, 1.1524 + nullptr, /* call */ 1.1525 + nullptr, /* hasInstance */ 1.1526 + nullptr, /* construct */ 1.1527 + nullptr /* trace */ 1.1528 +}; 1.1529 + 1.1530 +bool 1.1531 +xpc::IsOutObject(JSContext* cx, JSObject* obj) 1.1532 +{ 1.1533 + return js::GetObjectJSClass(obj) == &XPCOutParamClass; 1.1534 +} 1.1535 + 1.1536 +JSObject* 1.1537 +xpc::NewOutObject(JSContext* cx, JSObject* scope) 1.1538 +{ 1.1539 + RootedObject global(cx, JS_GetGlobalForObject(cx, scope)); 1.1540 + return JS_NewObject(cx, nullptr, NullPtr(), global); 1.1541 +} 1.1542 + 1.1543 + 1.1544 +NS_IMETHODIMP 1.1545 +nsXPCWrappedJSClass::DebugDump(int16_t depth) 1.1546 +{ 1.1547 +#ifdef DEBUG 1.1548 + depth-- ; 1.1549 + XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x with mRefCnt = %d", this, mRefCnt.get())); 1.1550 + XPC_LOG_INDENT(); 1.1551 + char* name; 1.1552 + mInfo->GetName(&name); 1.1553 + XPC_LOG_ALWAYS(("interface name is %s", name)); 1.1554 + if (name) 1.1555 + nsMemory::Free(name); 1.1556 + char * iid = mIID.ToString(); 1.1557 + XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid")); 1.1558 + if (iid) 1.1559 + NS_Free(iid); 1.1560 + XPC_LOG_ALWAYS(("InterfaceInfo @ %x", mInfo.get())); 1.1561 + uint16_t methodCount = 0; 1.1562 + if (depth) { 1.1563 + uint16_t i; 1.1564 + nsCOMPtr<nsIInterfaceInfo> parent; 1.1565 + XPC_LOG_INDENT(); 1.1566 + mInfo->GetParent(getter_AddRefs(parent)); 1.1567 + XPC_LOG_ALWAYS(("parent @ %x", parent.get())); 1.1568 + mInfo->GetMethodCount(&methodCount); 1.1569 + XPC_LOG_ALWAYS(("MethodCount = %d", methodCount)); 1.1570 + mInfo->GetConstantCount(&i); 1.1571 + XPC_LOG_ALWAYS(("ConstantCount = %d", i)); 1.1572 + XPC_LOG_OUTDENT(); 1.1573 + } 1.1574 + XPC_LOG_ALWAYS(("mRuntime @ %x", mRuntime)); 1.1575 + XPC_LOG_ALWAYS(("mDescriptors @ %x count = %d", mDescriptors, methodCount)); 1.1576 + if (depth && mDescriptors && methodCount) { 1.1577 + depth--; 1.1578 + XPC_LOG_INDENT(); 1.1579 + for (uint16_t i = 0; i < methodCount; i++) { 1.1580 + XPC_LOG_ALWAYS(("Method %d is %s%s", \ 1.1581 + i, IsReflectable(i) ? "":" NOT ","reflectable")); 1.1582 + } 1.1583 + XPC_LOG_OUTDENT(); 1.1584 + depth++; 1.1585 + } 1.1586 + XPC_LOG_OUTDENT(); 1.1587 +#endif 1.1588 + return NS_OK; 1.1589 +}