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