js/xpconnect/src/XPCWrappedNativeJSOps.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
michael@0 8
michael@0 9 #include "xpcprivate.h"
michael@0 10 #include "jsprf.h"
michael@0 11 #include "mozilla/dom/BindingUtils.h"
michael@0 12 #include "mozilla/Preferences.h"
michael@0 13
michael@0 14 using namespace mozilla;
michael@0 15 using namespace JS;
michael@0 16
michael@0 17 /***************************************************************************/
michael@0 18
michael@0 19 // All of the exceptions thrown into JS from this file go through here.
michael@0 20 // That makes this a nice place to set a breakpoint.
michael@0 21
michael@0 22 static bool Throw(nsresult errNum, JSContext* cx)
michael@0 23 {
michael@0 24 XPCThrower::Throw(errNum, cx);
michael@0 25 return false;
michael@0 26 }
michael@0 27
michael@0 28 // Handy macro used in many callback stub below.
michael@0 29
michael@0 30 #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \
michael@0 31 PR_BEGIN_MACRO \
michael@0 32 if (!wrapper) \
michael@0 33 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
michael@0 34 if (!wrapper->IsValid()) \
michael@0 35 return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \
michael@0 36 PR_END_MACRO
michael@0 37
michael@0 38 /***************************************************************************/
michael@0 39
michael@0 40 static bool
michael@0 41 ToStringGuts(XPCCallContext& ccx)
michael@0 42 {
michael@0 43 char* sz;
michael@0 44 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 45
michael@0 46 if (wrapper)
michael@0 47 sz = wrapper->ToString(ccx.GetTearOff());
michael@0 48 else
michael@0 49 sz = JS_smprintf("[xpconnect wrapped native prototype]");
michael@0 50
michael@0 51 if (!sz) {
michael@0 52 JS_ReportOutOfMemory(ccx);
michael@0 53 return false;
michael@0 54 }
michael@0 55
michael@0 56 JSString* str = JS_NewStringCopyZ(ccx, sz);
michael@0 57 JS_smprintf_free(sz);
michael@0 58 if (!str)
michael@0 59 return false;
michael@0 60
michael@0 61 ccx.SetRetVal(STRING_TO_JSVAL(str));
michael@0 62 return true;
michael@0 63 }
michael@0 64
michael@0 65 /***************************************************************************/
michael@0 66
michael@0 67 static bool
michael@0 68 XPC_WN_Shared_ToString(JSContext *cx, unsigned argc, jsval *vp)
michael@0 69 {
michael@0 70 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 71 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
michael@0 72 if (!obj)
michael@0 73 return false;
michael@0 74
michael@0 75 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 76 if (!ccx.IsValid())
michael@0 77 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
michael@0 78 ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
michael@0 79 ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
michael@0 80 return ToStringGuts(ccx);
michael@0 81 }
michael@0 82
michael@0 83 static bool
michael@0 84 XPC_WN_Shared_ToSource(JSContext *cx, unsigned argc, jsval *vp)
michael@0 85 {
michael@0 86 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 87 static const char empty[] = "({})";
michael@0 88 JSString *str = JS_NewStringCopyN(cx, empty, sizeof(empty)-1);
michael@0 89 if (!str)
michael@0 90 return false;
michael@0 91 args.rval().setString(str);
michael@0 92
michael@0 93 return true;
michael@0 94 }
michael@0 95
michael@0 96 /***************************************************************************/
michael@0 97
michael@0 98 // A "double wrapped object" is a user JSObject that has been wrapped as a
michael@0 99 // wrappedJS in order to be used by native code and then re-wrapped by a
michael@0 100 // wrappedNative wrapper to be used by JS code. One might think of it as:
michael@0 101 // wrappedNative(wrappedJS(underlying_JSObject))
michael@0 102 // This is done (as opposed to just unwrapping the wrapped JS and automatically
michael@0 103 // returning the underlying JSObject) so that JS callers will see what looks
michael@0 104 // Like any other xpcom object - and be limited to use its interfaces.
michael@0 105 //
michael@0 106 // See the comment preceding nsIXPCWrappedJSObjectGetter in nsIXPConnect.idl.
michael@0 107
michael@0 108 static JSObject*
michael@0 109 GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper)
michael@0 110 {
michael@0 111 RootedObject obj(ccx);
michael@0 112 nsCOMPtr<nsIXPConnectWrappedJS>
michael@0 113 underware = do_QueryInterface(wrapper->GetIdentityObject());
michael@0 114 if (underware) {
michael@0 115 RootedObject mainObj(ccx, underware->GetJSObject());
michael@0 116 if (mainObj) {
michael@0 117 RootedId id(ccx, ccx.GetRuntime()->
michael@0 118 GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT));
michael@0 119
michael@0 120 JSAutoCompartment ac(ccx, mainObj);
michael@0 121
michael@0 122 RootedValue val(ccx);
michael@0 123 if (JS_GetPropertyById(ccx, mainObj, id, &val) &&
michael@0 124 !JSVAL_IS_PRIMITIVE(val)) {
michael@0 125 obj = JSVAL_TO_OBJECT(val);
michael@0 126 }
michael@0 127 }
michael@0 128 }
michael@0 129 return obj;
michael@0 130 }
michael@0 131
michael@0 132 // This is the getter native function we use to handle 'wrappedJSObject' for
michael@0 133 // double wrapped JSObjects.
michael@0 134
michael@0 135 static bool
michael@0 136 XPC_WN_DoubleWrappedGetter(JSContext *cx, unsigned argc, jsval *vp)
michael@0 137 {
michael@0 138 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 139
michael@0 140 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
michael@0 141 if (!obj)
michael@0 142 return false;
michael@0 143
michael@0 144 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 145 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 146 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 147
michael@0 148 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
michael@0 149
michael@0 150 RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
michael@0 151 if (!realObject) {
michael@0 152 // This is pretty unexpected at this point. The object originally
michael@0 153 // responded to this get property call and now gives no object.
michael@0 154 // XXX Should this throw something at the caller?
michael@0 155 args.rval().setNull();
michael@0 156 return true;
michael@0 157 }
michael@0 158
michael@0 159 // It is a double wrapped object. This should really never appear in
michael@0 160 // content these days, but addons still do it - see bug 965921.
michael@0 161 if (MOZ_UNLIKELY(!nsContentUtils::IsCallerChrome())) {
michael@0 162 JS_ReportError(cx, "Attempt to use .wrappedJSObject in untrusted code");
michael@0 163 return false;
michael@0 164 }
michael@0 165 args.rval().setObject(*realObject);
michael@0 166 return JS_WrapValue(cx, args.rval());
michael@0 167 }
michael@0 168
michael@0 169 /***************************************************************************/
michael@0 170
michael@0 171 // This is our shared function to define properties on our JSObjects.
michael@0 172
michael@0 173 /*
michael@0 174 * NOTE:
michael@0 175 * We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE.
michael@0 176 * We *never* set toString or toSource as JS_ENUMERATE.
michael@0 177 */
michael@0 178
michael@0 179 static bool
michael@0 180 DefinePropertyIfFound(XPCCallContext& ccx,
michael@0 181 HandleObject obj,
michael@0 182 HandleId idArg,
michael@0 183 XPCNativeSet* set,
michael@0 184 XPCNativeInterface* iface,
michael@0 185 XPCNativeMember* member,
michael@0 186 XPCWrappedNativeScope* scope,
michael@0 187 bool reflectToStringAndToSource,
michael@0 188 XPCWrappedNative* wrapperToReflectInterfaceNames,
michael@0 189 XPCWrappedNative* wrapperToReflectDoubleWrap,
michael@0 190 XPCNativeScriptableInfo* scriptableInfo,
michael@0 191 unsigned propFlags,
michael@0 192 bool* resolved)
michael@0 193 {
michael@0 194 RootedId id(ccx, idArg);
michael@0 195 XPCJSRuntime* rt = ccx.GetRuntime();
michael@0 196 bool found;
michael@0 197 const char* name;
michael@0 198
michael@0 199 if (set) {
michael@0 200 if (iface)
michael@0 201 found = true;
michael@0 202 else
michael@0 203 found = set->FindMember(id, &member, &iface);
michael@0 204 } else
michael@0 205 found = (nullptr != (member = iface->FindMember(id)));
michael@0 206
michael@0 207 if (!found) {
michael@0 208 if (reflectToStringAndToSource) {
michael@0 209 JSNative call;
michael@0 210 uint32_t flags = 0;
michael@0 211
michael@0 212 if (scriptableInfo) {
michael@0 213 nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(
michael@0 214 scriptableInfo->GetCallback());
michael@0 215
michael@0 216 if (classInfo) {
michael@0 217 nsresult rv = classInfo->GetFlags(&flags);
michael@0 218 if (NS_FAILED(rv))
michael@0 219 return Throw(rv, ccx);
michael@0 220 }
michael@0 221 }
michael@0 222
michael@0 223 bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT)
michael@0 224 || Preferences::GetBool("dom.XPCToStringForDOMClasses", false);
michael@0 225
michael@0 226 if(id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING)
michael@0 227 && overwriteToString)
michael@0 228 {
michael@0 229 call = XPC_WN_Shared_ToString;
michael@0 230 name = rt->GetStringName(XPCJSRuntime::IDX_TO_STRING);
michael@0 231 id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING);
michael@0 232 } else if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE)) {
michael@0 233 call = XPC_WN_Shared_ToSource;
michael@0 234 name = rt->GetStringName(XPCJSRuntime::IDX_TO_SOURCE);
michael@0 235 id = rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE);
michael@0 236 }
michael@0 237
michael@0 238 else
michael@0 239 call = nullptr;
michael@0 240
michael@0 241 if (call) {
michael@0 242 RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, obj, name));
michael@0 243 if (!fun) {
michael@0 244 JS_ReportOutOfMemory(ccx);
michael@0 245 return false;
michael@0 246 }
michael@0 247
michael@0 248 AutoResolveName arn(ccx, id);
michael@0 249 if (resolved)
michael@0 250 *resolved = true;
michael@0 251 return JS_DefinePropertyById(ccx, obj, id,
michael@0 252 OBJECT_TO_JSVAL(JS_GetFunctionObject(fun)),
michael@0 253 nullptr, nullptr,
michael@0 254 propFlags & ~JSPROP_ENUMERATE);
michael@0 255 }
michael@0 256 }
michael@0 257 // This *might* be a tearoff name that is not yet part of our
michael@0 258 // set. Let's lookup the name and see if it is the name of an
michael@0 259 // interface. Then we'll see if the object actually *does* this
michael@0 260 // interface and add a tearoff as necessary.
michael@0 261
michael@0 262 if (wrapperToReflectInterfaceNames) {
michael@0 263 JSAutoByteString name;
michael@0 264 AutoMarkingNativeInterfacePtr iface2(ccx);
michael@0 265 XPCWrappedNativeTearOff* to;
michael@0 266 RootedObject jso(ccx);
michael@0 267 nsresult rv = NS_OK;
michael@0 268
michael@0 269 if (JSID_IS_STRING(id) &&
michael@0 270 name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
michael@0 271 (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) &&
michael@0 272 nullptr != (to = wrapperToReflectInterfaceNames->
michael@0 273 FindTearOff(iface2, true, &rv)) &&
michael@0 274 nullptr != (jso = to->GetJSObject()))
michael@0 275
michael@0 276 {
michael@0 277 AutoResolveName arn(ccx, id);
michael@0 278 if (resolved)
michael@0 279 *resolved = true;
michael@0 280 return JS_DefinePropertyById(ccx, obj, id, OBJECT_TO_JSVAL(jso),
michael@0 281 nullptr, nullptr,
michael@0 282 propFlags & ~JSPROP_ENUMERATE);
michael@0 283 } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
michael@0 284 return Throw(rv, ccx);
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 // This *might* be a double wrapped JSObject
michael@0 289 if (wrapperToReflectDoubleWrap &&
michael@0 290 id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT) &&
michael@0 291 GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) {
michael@0 292 // We build and add a getter function.
michael@0 293 // A security check is done on a per-get basis.
michael@0 294
michael@0 295 JSFunction* fun;
michael@0 296
michael@0 297 id = rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
michael@0 298 name = rt->GetStringName(XPCJSRuntime::IDX_WRAPPED_JSOBJECT);
michael@0 299
michael@0 300 fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter,
michael@0 301 0, 0, obj, name);
michael@0 302
michael@0 303 if (!fun)
michael@0 304 return false;
michael@0 305
michael@0 306 RootedObject funobj(ccx, JS_GetFunctionObject(fun));
michael@0 307 if (!funobj)
michael@0 308 return false;
michael@0 309
michael@0 310 propFlags |= JSPROP_GETTER | JSPROP_SHARED;
michael@0 311 propFlags &= ~JSPROP_ENUMERATE;
michael@0 312
michael@0 313 AutoResolveName arn(ccx, id);
michael@0 314 if (resolved)
michael@0 315 *resolved = true;
michael@0 316 return JS_DefinePropertyById(ccx, obj, id, JSVAL_VOID,
michael@0 317 JS_DATA_TO_FUNC_PTR(JSPropertyOp,
michael@0 318 funobj.get()),
michael@0 319 nullptr, propFlags);
michael@0 320 }
michael@0 321
michael@0 322 if (resolved)
michael@0 323 *resolved = false;
michael@0 324 return true;
michael@0 325 }
michael@0 326
michael@0 327 if (!member) {
michael@0 328 if (wrapperToReflectInterfaceNames) {
michael@0 329 XPCWrappedNativeTearOff* to =
michael@0 330 wrapperToReflectInterfaceNames->FindTearOff(iface, true);
michael@0 331
michael@0 332 if (!to)
michael@0 333 return false;
michael@0 334 RootedObject jso(ccx, to->GetJSObject());
michael@0 335 if (!jso)
michael@0 336 return false;
michael@0 337
michael@0 338 AutoResolveName arn(ccx, id);
michael@0 339 if (resolved)
michael@0 340 *resolved = true;
michael@0 341 return JS_DefinePropertyById(ccx, obj, id, OBJECT_TO_JSVAL(jso),
michael@0 342 nullptr, nullptr,
michael@0 343 propFlags & ~JSPROP_ENUMERATE);
michael@0 344 }
michael@0 345 if (resolved)
michael@0 346 *resolved = false;
michael@0 347 return true;
michael@0 348 }
michael@0 349
michael@0 350 if (member->IsConstant()) {
michael@0 351 RootedValue val(ccx);
michael@0 352 AutoResolveName arn(ccx, id);
michael@0 353 if (resolved)
michael@0 354 *resolved = true;
michael@0 355 return member->GetConstantValue(ccx, iface, val.address()) &&
michael@0 356 JS_DefinePropertyById(ccx, obj, id, val, nullptr, nullptr,
michael@0 357 propFlags);
michael@0 358 }
michael@0 359
michael@0 360 if (id == rt->GetStringID(XPCJSRuntime::IDX_TO_STRING) ||
michael@0 361 id == rt->GetStringID(XPCJSRuntime::IDX_TO_SOURCE) ||
michael@0 362 (scriptableInfo &&
michael@0 363 scriptableInfo->GetFlags().DontEnumQueryInterface() &&
michael@0 364 id == rt->GetStringID(XPCJSRuntime::IDX_QUERY_INTERFACE)))
michael@0 365 propFlags &= ~JSPROP_ENUMERATE;
michael@0 366
michael@0 367 RootedValue funval(ccx);
michael@0 368 if (!member->NewFunctionObject(ccx, iface, obj, funval.address()))
michael@0 369 return false;
michael@0 370
michael@0 371 if (member->IsMethod()) {
michael@0 372 AutoResolveName arn(ccx, id);
michael@0 373 if (resolved)
michael@0 374 *resolved = true;
michael@0 375 return JS_DefinePropertyById(ccx, obj, id, funval, nullptr, nullptr,
michael@0 376 propFlags);
michael@0 377 }
michael@0 378
michael@0 379 // else...
michael@0 380
michael@0 381 MOZ_ASSERT(member->IsAttribute(), "way broken!");
michael@0 382
michael@0 383 propFlags |= JSPROP_GETTER | JSPROP_SHARED;
michael@0 384 JSObject* funobj = JSVAL_TO_OBJECT(funval);
michael@0 385 JSPropertyOp getter = JS_DATA_TO_FUNC_PTR(JSPropertyOp, funobj);
michael@0 386 JSStrictPropertyOp setter;
michael@0 387 if (member->IsWritableAttribute()) {
michael@0 388 propFlags |= JSPROP_SETTER;
michael@0 389 propFlags &= ~JSPROP_READONLY;
michael@0 390 setter = JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, funobj);
michael@0 391 } else {
michael@0 392 setter = js_GetterOnlyPropertyStub;
michael@0 393 }
michael@0 394
michael@0 395 AutoResolveName arn(ccx, id);
michael@0 396 if (resolved)
michael@0 397 *resolved = true;
michael@0 398
michael@0 399 return JS_DefinePropertyById(ccx, obj, id, JSVAL_VOID, getter, setter,
michael@0 400 propFlags);
michael@0 401 }
michael@0 402
michael@0 403 /***************************************************************************/
michael@0 404 /***************************************************************************/
michael@0 405
michael@0 406 static bool
michael@0 407 XPC_WN_OnlyIWrite_AddPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
michael@0 408 {
michael@0 409 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id);
michael@0 410 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 411 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 412
michael@0 413 // Allow only XPConnect to add/set the property
michael@0 414 if (ccx.GetResolveName() == id)
michael@0 415 return true;
michael@0 416
michael@0 417 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
michael@0 418 }
michael@0 419
michael@0 420 static bool
michael@0 421 XPC_WN_OnlyIWrite_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
michael@0 422 MutableHandleValue vp)
michael@0 423 {
michael@0 424 return XPC_WN_OnlyIWrite_AddPropertyStub(cx, obj, id, vp);
michael@0 425 }
michael@0 426
michael@0 427 static bool
michael@0 428 XPC_WN_CannotModifyPropertyStub(JSContext *cx, HandleObject obj, HandleId id,
michael@0 429 MutableHandleValue vp)
michael@0 430 {
michael@0 431 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
michael@0 432 }
michael@0 433
michael@0 434 static bool
michael@0 435 XPC_WN_CantDeletePropertyStub(JSContext *cx, HandleObject obj, HandleId id,
michael@0 436 bool *succeeded)
michael@0 437 {
michael@0 438 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
michael@0 439 }
michael@0 440
michael@0 441 static bool
michael@0 442 XPC_WN_CannotModifyStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
michael@0 443 MutableHandleValue vp)
michael@0 444 {
michael@0 445 return XPC_WN_CannotModifyPropertyStub(cx, obj, id, vp);
michael@0 446 }
michael@0 447
michael@0 448 static bool
michael@0 449 XPC_WN_Shared_Convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
michael@0 450 {
michael@0 451 if (type == JSTYPE_OBJECT) {
michael@0 452 vp.set(OBJECT_TO_JSVAL(obj));
michael@0 453 return true;
michael@0 454 }
michael@0 455
michael@0 456 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 457 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 458 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 459
michael@0 460 switch (type) {
michael@0 461 case JSTYPE_FUNCTION:
michael@0 462 {
michael@0 463 if (!ccx.GetTearOff()) {
michael@0 464 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
michael@0 465 if (si && (si->GetFlags().WantCall() ||
michael@0 466 si->GetFlags().WantConstruct())) {
michael@0 467 vp.set(OBJECT_TO_JSVAL(obj));
michael@0 468 return true;
michael@0 469 }
michael@0 470 }
michael@0 471 }
michael@0 472 return Throw(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN, cx);
michael@0 473 case JSTYPE_NUMBER:
michael@0 474 vp.set(JS_GetNaNValue(cx));
michael@0 475 return true;
michael@0 476 case JSTYPE_BOOLEAN:
michael@0 477 vp.set(JSVAL_TRUE);
michael@0 478 return true;
michael@0 479 case JSTYPE_VOID:
michael@0 480 case JSTYPE_STRING:
michael@0 481 {
michael@0 482 ccx.SetName(ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
michael@0 483 ccx.SetArgsAndResultPtr(0, nullptr, vp.address());
michael@0 484
michael@0 485 XPCNativeMember* member = ccx.GetMember();
michael@0 486 if (member && member->IsMethod()) {
michael@0 487 if (!XPCWrappedNative::CallMethod(ccx))
michael@0 488 return false;
michael@0 489
michael@0 490 if (JSVAL_IS_PRIMITIVE(vp))
michael@0 491 return true;
michael@0 492 }
michael@0 493
michael@0 494 // else...
michael@0 495 return ToStringGuts(ccx);
michael@0 496 }
michael@0 497 default:
michael@0 498 NS_ERROR("bad type in conversion");
michael@0 499 return false;
michael@0 500 }
michael@0 501 NS_NOTREACHED("huh?");
michael@0 502 return false;
michael@0 503 }
michael@0 504
michael@0 505 static bool
michael@0 506 XPC_WN_Shared_Enumerate(JSContext *cx, HandleObject obj)
michael@0 507 {
michael@0 508 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 509 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 510 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 511
michael@0 512 // Since we aren't going to enumerate tearoff names and the prototype
michael@0 513 // handles non-mutated members, we can do this potential short-circuit.
michael@0 514 if (!wrapper->HasMutatedSet())
michael@0 515 return true;
michael@0 516
michael@0 517 XPCNativeSet* set = wrapper->GetSet();
michael@0 518 XPCNativeSet* protoSet = wrapper->HasProto() ?
michael@0 519 wrapper->GetProto()->GetSet() : nullptr;
michael@0 520
michael@0 521 uint16_t interface_count = set->GetInterfaceCount();
michael@0 522 XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
michael@0 523 for (uint16_t i = 0; i < interface_count; i++) {
michael@0 524 XPCNativeInterface* iface = interfaceArray[i];
michael@0 525 uint16_t member_count = iface->GetMemberCount();
michael@0 526 for (uint16_t k = 0; k < member_count; k++) {
michael@0 527 XPCNativeMember* member = iface->GetMemberAt(k);
michael@0 528 jsid name = member->GetName();
michael@0 529
michael@0 530 // Skip if this member is going to come from the proto.
michael@0 531 uint16_t index;
michael@0 532 if (protoSet &&
michael@0 533 protoSet->FindMember(name, nullptr, &index) && index == i)
michael@0 534 continue;
michael@0 535 if (!xpc_ForcePropertyResolve(cx, obj, name))
michael@0 536 return false;
michael@0 537 }
michael@0 538 }
michael@0 539 return true;
michael@0 540 }
michael@0 541
michael@0 542 /***************************************************************************/
michael@0 543
michael@0 544 enum WNHelperType {
michael@0 545 WN_NOHELPER,
michael@0 546 WN_HELPER
michael@0 547 };
michael@0 548
michael@0 549 static void
michael@0 550 WrappedNativeFinalize(js::FreeOp *fop, JSObject *obj, WNHelperType helperType)
michael@0 551 {
michael@0 552 const js::Class* clazz = js::GetObjectClass(obj);
michael@0 553 if (clazz->flags & JSCLASS_DOM_GLOBAL) {
michael@0 554 mozilla::dom::DestroyProtoAndIfaceCache(obj);
michael@0 555 }
michael@0 556 nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
michael@0 557 if (!p)
michael@0 558 return;
michael@0 559
michael@0 560 XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
michael@0 561 if (helperType == WN_HELPER)
michael@0 562 wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
michael@0 563 wrapper->FlatJSObjectFinalized();
michael@0 564 }
michael@0 565
michael@0 566 static void
michael@0 567 XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
michael@0 568 {
michael@0 569 WrappedNativeFinalize(fop, obj, WN_NOHELPER);
michael@0 570 }
michael@0 571
michael@0 572 /*
michael@0 573 * General comment about XPConnect tracing: Given a C++ object |wrapper| and its
michael@0 574 * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS
michael@0 575 * engine to mark |obj|. Eventually, this will lead to the trace hook being
michael@0 576 * called for |obj|. The trace hook should call |wrapper->TraceInside|, which
michael@0 577 * should mark any JS objects held by |wrapper| as members.
michael@0 578 */
michael@0 579
michael@0 580 static void
michael@0 581 MarkWrappedNative(JSTracer *trc, JSObject *obj)
michael@0 582 {
michael@0 583 const js::Class* clazz = js::GetObjectClass(obj);
michael@0 584 if (clazz->flags & JSCLASS_DOM_GLOBAL) {
michael@0 585 mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
michael@0 586 }
michael@0 587 MOZ_ASSERT(IS_WN_CLASS(clazz));
michael@0 588
michael@0 589 XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
michael@0 590 if (wrapper && wrapper->IsValid())
michael@0 591 wrapper->TraceInside(trc);
michael@0 592 }
michael@0 593
michael@0 594 /* static */ void
michael@0 595 XPCWrappedNative::Trace(JSTracer *trc, JSObject *obj)
michael@0 596 {
michael@0 597 MarkWrappedNative(trc, obj);
michael@0 598 }
michael@0 599
michael@0 600 static bool
michael@0 601 XPC_WN_NoHelper_Resolve(JSContext *cx, HandleObject obj, HandleId id)
michael@0 602 {
michael@0 603 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), id);
michael@0 604 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 605 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 606
michael@0 607 XPCNativeSet* set = ccx.GetSet();
michael@0 608 if (!set)
michael@0 609 return true;
michael@0 610
michael@0 611 // Don't resolve properties that are on our prototype.
michael@0 612 if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal())
michael@0 613 return true;
michael@0 614
michael@0 615 return DefinePropertyIfFound(ccx, obj, id,
michael@0 616 set, nullptr, nullptr, wrapper->GetScope(),
michael@0 617 true, wrapper, wrapper, nullptr,
michael@0 618 JSPROP_ENUMERATE |
michael@0 619 JSPROP_READONLY |
michael@0 620 JSPROP_PERMANENT, nullptr);
michael@0 621 }
michael@0 622
michael@0 623 static JSObject *
michael@0 624 XPC_WN_OuterObject(JSContext *cx, HandleObject objArg)
michael@0 625 {
michael@0 626 JSObject *obj = objArg;
michael@0 627
michael@0 628 XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj);
michael@0 629 if (!wrapper) {
michael@0 630 Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
michael@0 631
michael@0 632 return nullptr;
michael@0 633 }
michael@0 634
michael@0 635 if (!wrapper->IsValid()) {
michael@0 636 Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx);
michael@0 637
michael@0 638 return nullptr;
michael@0 639 }
michael@0 640
michael@0 641 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
michael@0 642 if (si && si->GetFlags().WantOuterObject()) {
michael@0 643 RootedObject newThis(cx);
michael@0 644 nsresult rv =
michael@0 645 si->GetCallback()->OuterObject(wrapper, cx, obj, newThis.address());
michael@0 646
michael@0 647 if (NS_FAILED(rv)) {
michael@0 648 Throw(rv, cx);
michael@0 649
michael@0 650 return nullptr;
michael@0 651 }
michael@0 652
michael@0 653 obj = newThis;
michael@0 654 }
michael@0 655
michael@0 656 return obj;
michael@0 657 }
michael@0 658
michael@0 659 const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
michael@0 660 { // base
michael@0 661 "XPCWrappedNative_NoHelper", // name;
michael@0 662 WRAPPER_SLOTS |
michael@0 663 JSCLASS_PRIVATE_IS_NSISUPPORTS, // flags
michael@0 664
michael@0 665 /* Mandatory non-null function pointer members. */
michael@0 666 XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
michael@0 667 XPC_WN_CantDeletePropertyStub, // delProperty
michael@0 668 JS_PropertyStub, // getProperty
michael@0 669 XPC_WN_OnlyIWrite_SetPropertyStub, // setProperty
michael@0 670
michael@0 671 XPC_WN_Shared_Enumerate, // enumerate
michael@0 672 XPC_WN_NoHelper_Resolve, // resolve
michael@0 673 XPC_WN_Shared_Convert, // convert
michael@0 674 XPC_WN_NoHelper_Finalize, // finalize
michael@0 675
michael@0 676 /* Optionally non-null members start here. */
michael@0 677 nullptr, // call
michael@0 678 nullptr, // construct
michael@0 679 nullptr, // hasInstance
michael@0 680 XPCWrappedNative::Trace, // trace
michael@0 681 JS_NULL_CLASS_SPEC,
michael@0 682
michael@0 683 // ClassExtension
michael@0 684 {
michael@0 685 nullptr, // outerObject
michael@0 686 nullptr, // innerObject
michael@0 687 nullptr, // iteratorObject
michael@0 688 true, // isWrappedNative
michael@0 689 },
michael@0 690
michael@0 691 // ObjectOps
michael@0 692 {
michael@0 693 nullptr, // lookupGeneric
michael@0 694 nullptr, // lookupProperty
michael@0 695 nullptr, // lookupElement
michael@0 696 nullptr, // defineGeneric
michael@0 697 nullptr, // defineProperty
michael@0 698 nullptr, // defineElement
michael@0 699 nullptr, // getGeneric
michael@0 700 nullptr, // getProperty
michael@0 701 nullptr, // getElement
michael@0 702 nullptr, // setGeneric
michael@0 703 nullptr, // setProperty
michael@0 704 nullptr, // setElement
michael@0 705 nullptr, // getGenericAttributes
michael@0 706 nullptr, // setGenericAttributes
michael@0 707 nullptr, // deleteProperty
michael@0 708 nullptr, // deleteElement
michael@0 709 nullptr, nullptr, // watch/unwatch
michael@0 710 nullptr, // slice
michael@0 711 XPC_WN_JSOp_Enumerate,
michael@0 712 XPC_WN_JSOp_ThisObject,
michael@0 713 }
michael@0 714 },
michael@0 715 0 // interfacesBitmap
michael@0 716 };
michael@0 717
michael@0 718
michael@0 719 /***************************************************************************/
michael@0 720
michael@0 721 static bool
michael@0 722 XPC_WN_MaybeResolvingPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
michael@0 723 {
michael@0 724 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 725 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 726 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 727
michael@0 728 if (ccx.GetResolvingWrapper() == wrapper)
michael@0 729 return true;
michael@0 730 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
michael@0 731 }
michael@0 732
michael@0 733 static bool
michael@0 734 XPC_WN_MaybeResolvingStrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
michael@0 735 MutableHandleValue vp)
michael@0 736 {
michael@0 737 return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp);
michael@0 738 }
michael@0 739
michael@0 740 static bool
michael@0 741 XPC_WN_MaybeResolvingDeletePropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
michael@0 742 {
michael@0 743 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 744 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 745 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 746
michael@0 747 if (ccx.GetResolvingWrapper() == wrapper) {
michael@0 748 *succeeded = true;
michael@0 749 return true;
michael@0 750 }
michael@0 751 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
michael@0 752 }
michael@0 753
michael@0 754 // macro fun!
michael@0 755 #define PRE_HELPER_STUB \
michael@0 756 JSObject *unwrapped = js::CheckedUnwrap(obj, false); \
michael@0 757 if (!unwrapped) { \
michael@0 758 JS_ReportError(cx, "Permission denied to operate on object."); \
michael@0 759 return false; \
michael@0 760 } \
michael@0 761 if (!IS_WN_REFLECTOR(unwrapped)) { \
michael@0 762 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
michael@0 763 } \
michael@0 764 XPCWrappedNative *wrapper = XPCWrappedNative::Get(unwrapped); \
michael@0 765 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); \
michael@0 766 bool retval = true; \
michael@0 767 nsresult rv = wrapper->GetScriptableCallback()->
michael@0 768
michael@0 769 #define POST_HELPER_STUB \
michael@0 770 if (NS_FAILED(rv)) \
michael@0 771 return Throw(rv, cx); \
michael@0 772 return retval;
michael@0 773
michael@0 774 static bool
michael@0 775 XPC_WN_Helper_AddProperty(JSContext *cx, HandleObject obj, HandleId id,
michael@0 776 MutableHandleValue vp)
michael@0 777 {
michael@0 778 PRE_HELPER_STUB
michael@0 779 AddProperty(wrapper, cx, obj, id, vp.address(), &retval);
michael@0 780 POST_HELPER_STUB
michael@0 781 }
michael@0 782
michael@0 783 static bool
michael@0 784 XPC_WN_Helper_DelProperty(JSContext *cx, HandleObject obj, HandleId id,
michael@0 785 bool *succeeded)
michael@0 786 {
michael@0 787 *succeeded = true;
michael@0 788 PRE_HELPER_STUB
michael@0 789 DelProperty(wrapper, cx, obj, id, &retval);
michael@0 790 POST_HELPER_STUB
michael@0 791 }
michael@0 792
michael@0 793 bool
michael@0 794 XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id,
michael@0 795 MutableHandleValue vp)
michael@0 796 {
michael@0 797 PRE_HELPER_STUB
michael@0 798 GetProperty(wrapper, cx, obj, id, vp.address(), &retval);
michael@0 799 POST_HELPER_STUB
michael@0 800 }
michael@0 801
michael@0 802 bool
michael@0 803 XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict,
michael@0 804 MutableHandleValue vp)
michael@0 805 {
michael@0 806 PRE_HELPER_STUB
michael@0 807 SetProperty(wrapper, cx, obj, id, vp.address(), &retval);
michael@0 808 POST_HELPER_STUB
michael@0 809 }
michael@0 810
michael@0 811 static bool
michael@0 812 XPC_WN_Helper_Convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
michael@0 813 {
michael@0 814 PRE_HELPER_STUB
michael@0 815 Convert(wrapper, cx, obj, type, vp.address(), &retval);
michael@0 816 POST_HELPER_STUB
michael@0 817 }
michael@0 818
michael@0 819 static bool
michael@0 820 XPC_WN_Helper_Call(JSContext *cx, unsigned argc, jsval *vp)
michael@0 821 {
michael@0 822 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 823 // N.B. we want obj to be the callee, not JS_THIS(cx, vp)
michael@0 824 RootedObject obj(cx, &args.callee());
michael@0 825
michael@0 826 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), JSID_VOIDHANDLE, args.length(),
michael@0 827 args.array(), args.rval().address());
michael@0 828 if (!ccx.IsValid())
michael@0 829 return false;
michael@0 830
michael@0 831 MOZ_ASSERT(obj == ccx.GetFlattenedJSObject());
michael@0 832
michael@0 833 PRE_HELPER_STUB
michael@0 834 Call(wrapper, cx, obj, args, &retval);
michael@0 835 POST_HELPER_STUB
michael@0 836 }
michael@0 837
michael@0 838 static bool
michael@0 839 XPC_WN_Helper_Construct(JSContext *cx, unsigned argc, jsval *vp)
michael@0 840 {
michael@0 841 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 842 RootedObject obj(cx, &args.callee());
michael@0 843 if (!obj)
michael@0 844 return false;
michael@0 845
michael@0 846 XPCCallContext ccx(JS_CALLER, cx, obj, NullPtr(), JSID_VOIDHANDLE, args.length(),
michael@0 847 args.array(), args.rval().address());
michael@0 848 if (!ccx.IsValid())
michael@0 849 return false;
michael@0 850
michael@0 851 MOZ_ASSERT(obj == ccx.GetFlattenedJSObject());
michael@0 852
michael@0 853 PRE_HELPER_STUB
michael@0 854 Construct(wrapper, cx, obj, args, &retval);
michael@0 855 POST_HELPER_STUB
michael@0 856 }
michael@0 857
michael@0 858 static bool
michael@0 859 XPC_WN_Helper_HasInstance(JSContext *cx, HandleObject obj, MutableHandleValue valp, bool *bp)
michael@0 860 {
michael@0 861 bool retval2;
michael@0 862 PRE_HELPER_STUB
michael@0 863 HasInstance(wrapper, cx, obj, valp, &retval2, &retval);
michael@0 864 *bp = retval2;
michael@0 865 POST_HELPER_STUB
michael@0 866 }
michael@0 867
michael@0 868 static void
michael@0 869 XPC_WN_Helper_Finalize(js::FreeOp *fop, JSObject *obj)
michael@0 870 {
michael@0 871 WrappedNativeFinalize(fop, obj, WN_HELPER);
michael@0 872 }
michael@0 873
michael@0 874 static bool
michael@0 875 XPC_WN_Helper_NewResolve(JSContext *cx, HandleObject obj, HandleId id,
michael@0 876 MutableHandleObject objp)
michael@0 877 {
michael@0 878 nsresult rv = NS_OK;
michael@0 879 bool retval = true;
michael@0 880 RootedObject obj2FromScriptable(cx);
michael@0 881 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 882 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 883 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 884
michael@0 885 RootedId old(cx, ccx.SetResolveName(id));
michael@0 886
michael@0 887 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
michael@0 888 if (si && si->GetFlags().WantNewResolve()) {
michael@0 889 XPCWrappedNative* oldResolvingWrapper;
michael@0 890 bool allowPropMods = si->GetFlags().AllowPropModsDuringResolve();
michael@0 891
michael@0 892 if (allowPropMods)
michael@0 893 oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
michael@0 894
michael@0 895 rv = si->GetCallback()->NewResolve(wrapper, cx, obj, id,
michael@0 896 obj2FromScriptable.address(), &retval);
michael@0 897
michael@0 898 if (allowPropMods)
michael@0 899 (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
michael@0 900 }
michael@0 901
michael@0 902 old = ccx.SetResolveName(old);
michael@0 903 MOZ_ASSERT(old == id, "bad nest");
michael@0 904
michael@0 905 if (NS_FAILED(rv)) {
michael@0 906 return Throw(rv, cx);
michael@0 907 }
michael@0 908
michael@0 909 if (obj2FromScriptable) {
michael@0 910 objp.set(obj2FromScriptable);
michael@0 911 } else if (wrapper->HasMutatedSet()) {
michael@0 912 // We are here if scriptable did not resolve this property and
michael@0 913 // it *might* be in the instance set but not the proto set.
michael@0 914
michael@0 915 XPCNativeSet* set = wrapper->GetSet();
michael@0 916 XPCNativeSet* protoSet = wrapper->HasProto() ?
michael@0 917 wrapper->GetProto()->GetSet() : nullptr;
michael@0 918 XPCNativeMember* member;
michael@0 919 XPCNativeInterface* iface;
michael@0 920 bool IsLocal;
michael@0 921
michael@0 922 if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) &&
michael@0 923 IsLocal) {
michael@0 924 XPCWrappedNative* oldResolvingWrapper;
michael@0 925
michael@0 926 XPCNativeScriptableFlags siFlags(0);
michael@0 927 if (si)
michael@0 928 siFlags = si->GetFlags();
michael@0 929
michael@0 930 unsigned enumFlag =
michael@0 931 siFlags.DontEnumStaticProps() ? 0 : JSPROP_ENUMERATE;
michael@0 932
michael@0 933 XPCWrappedNative* wrapperForInterfaceNames =
michael@0 934 siFlags.DontReflectInterfaceNames() ? nullptr : wrapper;
michael@0 935
michael@0 936 bool resolved;
michael@0 937 oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
michael@0 938 retval = DefinePropertyIfFound(ccx, obj, id,
michael@0 939 set, iface, member,
michael@0 940 wrapper->GetScope(),
michael@0 941 false,
michael@0 942 wrapperForInterfaceNames,
michael@0 943 nullptr, si,
michael@0 944 enumFlag, &resolved);
michael@0 945 (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
michael@0 946 if (retval && resolved)
michael@0 947 objp.set(obj);
michael@0 948 }
michael@0 949 }
michael@0 950
michael@0 951 return retval;
michael@0 952 }
michael@0 953
michael@0 954 /***************************************************************************/
michael@0 955
michael@0 956 /*
michael@0 957 Here are the enumerator cases:
michael@0 958
michael@0 959 set jsclass enumerate to stub (unless noted otherwise)
michael@0 960
michael@0 961 if ( helper wants new enumerate )
michael@0 962 if ( DONT_ENUM_STATICS )
michael@0 963 forward to scriptable enumerate
michael@0 964 else
michael@0 965 if ( set not mutated )
michael@0 966 forward to scriptable enumerate
michael@0 967 else
michael@0 968 call shared enumerate
michael@0 969 forward to scriptable enumerate
michael@0 970 else if ( helper wants old enumerate )
michael@0 971 use this JSOp
michael@0 972 if ( DONT_ENUM_STATICS )
michael@0 973 call scriptable enumerate
michael@0 974 call stub
michael@0 975 else
michael@0 976 if ( set not mutated )
michael@0 977 call scriptable enumerate
michael@0 978 call stub
michael@0 979 else
michael@0 980 call shared enumerate
michael@0 981 call scriptable enumerate
michael@0 982 call stub
michael@0 983
michael@0 984 else //... if ( helper wants NO enumerate )
michael@0 985 if ( DONT_ENUM_STATICS )
michael@0 986 use enumerate stub - don't use this JSOp thing at all
michael@0 987 else
michael@0 988 do shared enumerate - don't use this JSOp thing at all
michael@0 989 */
michael@0 990
michael@0 991 bool
michael@0 992 XPC_WN_JSOp_Enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
michael@0 993 MutableHandleValue statep, MutableHandleId idp)
michael@0 994 {
michael@0 995 const js::Class *clazz = js::GetObjectClass(obj);
michael@0 996 if (!IS_WN_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass.base) {
michael@0 997 // obj must be a prototype object or a wrapper w/o a
michael@0 998 // helper. Short circuit this call to the default
michael@0 999 // implementation.
michael@0 1000
michael@0 1001 return JS_EnumerateState(cx, obj, enum_op, statep, idp);
michael@0 1002 }
michael@0 1003
michael@0 1004 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 1005 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 1006 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 1007
michael@0 1008 XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
michael@0 1009 if (!si)
michael@0 1010 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
michael@0 1011
michael@0 1012 bool retval = true;
michael@0 1013 nsresult rv;
michael@0 1014
michael@0 1015 if (si->GetFlags().WantNewEnumerate()) {
michael@0 1016 if (((enum_op == JSENUMERATE_INIT &&
michael@0 1017 !si->GetFlags().DontEnumStaticProps()) ||
michael@0 1018 enum_op == JSENUMERATE_INIT_ALL) &&
michael@0 1019 wrapper->HasMutatedSet() &&
michael@0 1020 !XPC_WN_Shared_Enumerate(cx, obj)) {
michael@0 1021 statep.set(JSVAL_NULL);
michael@0 1022 return false;
michael@0 1023 }
michael@0 1024
michael@0 1025 // XXX Might we really need to wrap this call and *also* call
michael@0 1026 // js_ObjectOps.enumerate ???
michael@0 1027
michael@0 1028 rv = si->GetCallback()->
michael@0 1029 NewEnumerate(wrapper, cx, obj, enum_op, statep.address(), idp.address(), &retval);
michael@0 1030
michael@0 1031 if ((enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) &&
michael@0 1032 (NS_FAILED(rv) || !retval)) {
michael@0 1033 statep.set(JSVAL_NULL);
michael@0 1034 }
michael@0 1035
michael@0 1036 if (NS_FAILED(rv))
michael@0 1037 return Throw(rv, cx);
michael@0 1038 return retval;
michael@0 1039 }
michael@0 1040
michael@0 1041 if (si->GetFlags().WantEnumerate()) {
michael@0 1042 if (enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL) {
michael@0 1043 if ((enum_op == JSENUMERATE_INIT_ALL ||
michael@0 1044 !si->GetFlags().DontEnumStaticProps()) &&
michael@0 1045 wrapper->HasMutatedSet() &&
michael@0 1046 !XPC_WN_Shared_Enumerate(cx, obj)) {
michael@0 1047 statep.set(JSVAL_NULL);
michael@0 1048 return false;
michael@0 1049 }
michael@0 1050 rv = si->GetCallback()->
michael@0 1051 Enumerate(wrapper, cx, obj, &retval);
michael@0 1052
michael@0 1053 if (NS_FAILED(rv) || !retval)
michael@0 1054 statep.set(JSVAL_NULL);
michael@0 1055
michael@0 1056 if (NS_FAILED(rv))
michael@0 1057 return Throw(rv, cx);
michael@0 1058 if (!retval)
michael@0 1059 return false;
michael@0 1060 // Then fall through and call the default implementation...
michael@0 1061 }
michael@0 1062 }
michael@0 1063
michael@0 1064 // else call js_ObjectOps.enumerate...
michael@0 1065
michael@0 1066 return JS_EnumerateState(cx, obj, enum_op, statep, idp);
michael@0 1067 }
michael@0 1068
michael@0 1069 JSObject*
michael@0 1070 XPC_WN_JSOp_ThisObject(JSContext *cx, HandleObject obj)
michael@0 1071 {
michael@0 1072 return JS_ObjectToOuterObject(cx, obj);
michael@0 1073 }
michael@0 1074
michael@0 1075 /***************************************************************************/
michael@0 1076
michael@0 1077 // static
michael@0 1078 XPCNativeScriptableInfo*
michael@0 1079 XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci)
michael@0 1080 {
michael@0 1081 MOZ_ASSERT(sci, "bad param");
michael@0 1082 MOZ_ASSERT(sci->GetCallback(), "bad param");
michael@0 1083
michael@0 1084 XPCNativeScriptableInfo* newObj =
michael@0 1085 new XPCNativeScriptableInfo(sci->GetCallback());
michael@0 1086 if (!newObj)
michael@0 1087 return nullptr;
michael@0 1088
michael@0 1089 char* name = nullptr;
michael@0 1090 if (NS_FAILED(sci->GetCallback()->GetClassName(&name)) || !name) {
michael@0 1091 delete newObj;
michael@0 1092 return nullptr;
michael@0 1093 }
michael@0 1094
michael@0 1095 bool success;
michael@0 1096
michael@0 1097 XPCJSRuntime* rt = XPCJSRuntime::Get();
michael@0 1098 XPCNativeScriptableSharedMap* map = rt->GetNativeScriptableSharedMap();
michael@0 1099 success = map->GetNewOrUsed(sci->GetFlags(), name,
michael@0 1100 sci->GetInterfacesBitmap(), newObj);
michael@0 1101
michael@0 1102 if (!success) {
michael@0 1103 delete newObj;
michael@0 1104 return nullptr;
michael@0 1105 }
michael@0 1106
michael@0 1107 return newObj;
michael@0 1108 }
michael@0 1109
michael@0 1110 void
michael@0 1111 XPCNativeScriptableShared::PopulateJSClass()
michael@0 1112 {
michael@0 1113 MOZ_ASSERT(mJSClass.base.name, "bad state!");
michael@0 1114
michael@0 1115 mJSClass.base.flags = WRAPPER_SLOTS |
michael@0 1116 JSCLASS_PRIVATE_IS_NSISUPPORTS |
michael@0 1117 JSCLASS_NEW_RESOLVE;
michael@0 1118
michael@0 1119 if (mFlags.IsGlobalObject())
michael@0 1120 mJSClass.base.flags |= XPCONNECT_GLOBAL_FLAGS;
michael@0 1121
michael@0 1122 JSPropertyOp addProperty;
michael@0 1123 if (mFlags.WantAddProperty())
michael@0 1124 addProperty = XPC_WN_Helper_AddProperty;
michael@0 1125 else if (mFlags.UseJSStubForAddProperty())
michael@0 1126 addProperty = JS_PropertyStub;
michael@0 1127 else if (mFlags.AllowPropModsDuringResolve())
michael@0 1128 addProperty = XPC_WN_MaybeResolvingPropertyStub;
michael@0 1129 else
michael@0 1130 addProperty = XPC_WN_CannotModifyPropertyStub;
michael@0 1131 mJSClass.base.addProperty = addProperty;
michael@0 1132
michael@0 1133 JSDeletePropertyOp delProperty;
michael@0 1134 if (mFlags.WantDelProperty())
michael@0 1135 delProperty = XPC_WN_Helper_DelProperty;
michael@0 1136 else if (mFlags.UseJSStubForDelProperty())
michael@0 1137 delProperty = JS_DeletePropertyStub;
michael@0 1138 else if (mFlags.AllowPropModsDuringResolve())
michael@0 1139 delProperty = XPC_WN_MaybeResolvingDeletePropertyStub;
michael@0 1140 else
michael@0 1141 delProperty = XPC_WN_CantDeletePropertyStub;
michael@0 1142 mJSClass.base.delProperty = delProperty;
michael@0 1143
michael@0 1144 if (mFlags.WantGetProperty())
michael@0 1145 mJSClass.base.getProperty = XPC_WN_Helper_GetProperty;
michael@0 1146 else
michael@0 1147 mJSClass.base.getProperty = JS_PropertyStub;
michael@0 1148
michael@0 1149 JSStrictPropertyOp setProperty;
michael@0 1150 if (mFlags.WantSetProperty())
michael@0 1151 setProperty = XPC_WN_Helper_SetProperty;
michael@0 1152 else if (mFlags.UseJSStubForSetProperty())
michael@0 1153 setProperty = JS_StrictPropertyStub;
michael@0 1154 else if (mFlags.AllowPropModsDuringResolve())
michael@0 1155 setProperty = XPC_WN_MaybeResolvingStrictPropertyStub;
michael@0 1156 else
michael@0 1157 setProperty = XPC_WN_CannotModifyStrictPropertyStub;
michael@0 1158 mJSClass.base.setProperty = setProperty;
michael@0 1159
michael@0 1160 // We figure out most of the enumerate strategy at call time.
michael@0 1161
michael@0 1162 if (mFlags.WantNewEnumerate() || mFlags.WantEnumerate() ||
michael@0 1163 mFlags.DontEnumStaticProps())
michael@0 1164 mJSClass.base.enumerate = JS_EnumerateStub;
michael@0 1165 else
michael@0 1166 mJSClass.base.enumerate = XPC_WN_Shared_Enumerate;
michael@0 1167
michael@0 1168 // We have to figure out resolve strategy at call time
michael@0 1169 mJSClass.base.resolve = (JSResolveOp) XPC_WN_Helper_NewResolve;
michael@0 1170
michael@0 1171 // We need to respect content-defined toString() hooks on Window objects.
michael@0 1172 // In particular, js::DefaultValue checks for a convert stub, and the one
michael@0 1173 // we would install below ignores anything implemented in JS.
michael@0 1174 //
michael@0 1175 // We've always had this behavior for most XPCWrappedNative-implemented
michael@0 1176 // objects. However, Window was special, because the outer-window proxy
michael@0 1177 // had a null convert hook, which means that we'd end up with the default
michael@0 1178 // JS-engine behavior (which respects toString() overrides). We've fixed
michael@0 1179 // the convert hook on the outer-window proxy to invoke the defaultValue
michael@0 1180 // hook on the proxy, which in this case invokes js::DefaultValue on the
michael@0 1181 // target. So now we need to special-case this for Window to maintain
michael@0 1182 // consistent behavior. This can go away once Window is on WebIDL bindings.
michael@0 1183 //
michael@0 1184 // Note that WantOuterObject() is true if and only if this is a Window object.
michael@0 1185 if (mFlags.WantConvert())
michael@0 1186 mJSClass.base.convert = XPC_WN_Helper_Convert;
michael@0 1187 else if (mFlags.WantOuterObject())
michael@0 1188 mJSClass.base.convert = JS_ConvertStub;
michael@0 1189 else
michael@0 1190 mJSClass.base.convert = XPC_WN_Shared_Convert;
michael@0 1191
michael@0 1192 if (mFlags.WantFinalize())
michael@0 1193 mJSClass.base.finalize = XPC_WN_Helper_Finalize;
michael@0 1194 else
michael@0 1195 mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
michael@0 1196
michael@0 1197 js::ObjectOps *ops = &mJSClass.base.ops;
michael@0 1198 ops->enumerate = XPC_WN_JSOp_Enumerate;
michael@0 1199 ops->thisObject = XPC_WN_JSOp_ThisObject;
michael@0 1200
michael@0 1201
michael@0 1202 if (mFlags.WantCall())
michael@0 1203 mJSClass.base.call = XPC_WN_Helper_Call;
michael@0 1204 if (mFlags.WantConstruct())
michael@0 1205 mJSClass.base.construct = XPC_WN_Helper_Construct;
michael@0 1206
michael@0 1207 if (mFlags.WantHasInstance())
michael@0 1208 mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance;
michael@0 1209
michael@0 1210 if (mFlags.IsGlobalObject())
michael@0 1211 mJSClass.base.trace = JS_GlobalObjectTraceHook;
michael@0 1212 else
michael@0 1213 mJSClass.base.trace = XPCWrappedNative::Trace;
michael@0 1214
michael@0 1215 if (mFlags.WantOuterObject())
michael@0 1216 mJSClass.base.ext.outerObject = XPC_WN_OuterObject;
michael@0 1217
michael@0 1218 mJSClass.base.ext.isWrappedNative = true;
michael@0 1219 }
michael@0 1220
michael@0 1221 /***************************************************************************/
michael@0 1222 /***************************************************************************/
michael@0 1223
michael@0 1224 // Compatibility hack.
michael@0 1225 //
michael@0 1226 // XPConnect used to do all sorts of funny tricks to find the "correct"
michael@0 1227 // |this| object for a given method (often to the detriment of proper
michael@0 1228 // call/apply). When these tricks were removed, a fair amount of chrome
michael@0 1229 // code broke, because it was relying on being able to grab methods off
michael@0 1230 // some XPCOM object (like the nsITelemetry service) and invoke them without
michael@0 1231 // a proper |this|. So, if it's quite clear that we're in this situation and
michael@0 1232 // about to use a |this| argument that just won't work, fix things up.
michael@0 1233 //
michael@0 1234 // This hack is only useful for getters/setters if someone sets an XPCOM object
michael@0 1235 // as the prototype for a vanilla JS object and expects the XPCOM attributes to
michael@0 1236 // work on the derived object, which we really don't want to support. But we
michael@0 1237 // handle it anyway, for now, to minimize regression risk on an already-risky
michael@0 1238 // landing.
michael@0 1239 //
michael@0 1240 // This hack is mainly useful for the NoHelper JSClass. We also fix up
michael@0 1241 // Components.utils because it implements nsIXPCScriptable (giving it a custom
michael@0 1242 // JSClass) but not nsIClassInfo (which would put the methods on a prototype).
michael@0 1243
michael@0 1244 #define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass.base)
michael@0 1245 #define IS_CU_CLASS(clasp) (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils"))
michael@0 1246
michael@0 1247 MOZ_ALWAYS_INLINE JSObject*
michael@0 1248 FixUpThisIfBroken(JSObject *obj, JSObject *funobj)
michael@0 1249 {
michael@0 1250 if (funobj) {
michael@0 1251 const js::Class *parentClass = js::GetObjectClass(js::GetObjectParent(funobj));
michael@0 1252 if (MOZ_UNLIKELY((IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) &&
michael@0 1253 (js::GetObjectClass(obj) != parentClass)))
michael@0 1254 {
michael@0 1255 return js::GetObjectParent(funobj);
michael@0 1256 }
michael@0 1257 }
michael@0 1258 return obj;
michael@0 1259 }
michael@0 1260
michael@0 1261 bool
michael@0 1262 XPC_WN_CallMethod(JSContext *cx, unsigned argc, jsval *vp)
michael@0 1263 {
michael@0 1264 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 1265 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
michael@0 1266 RootedObject funobj(cx, &args.callee());
michael@0 1267
michael@0 1268 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
michael@0 1269 if (!obj)
michael@0 1270 return false;
michael@0 1271
michael@0 1272 obj = FixUpThisIfBroken(obj, funobj);
michael@0 1273 XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
michael@0 1274 args.array(), vp);
michael@0 1275 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 1276 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 1277
michael@0 1278 XPCNativeInterface* iface;
michael@0 1279 XPCNativeMember* member;
michael@0 1280
michael@0 1281 if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
michael@0 1282 return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
michael@0 1283 ccx.SetCallInfo(iface, member, false);
michael@0 1284 return XPCWrappedNative::CallMethod(ccx);
michael@0 1285 }
michael@0 1286
michael@0 1287 bool
michael@0 1288 XPC_WN_GetterSetter(JSContext *cx, unsigned argc, jsval *vp)
michael@0 1289 {
michael@0 1290 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 1291 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
michael@0 1292 RootedObject funobj(cx, &args.callee());
michael@0 1293
michael@0 1294 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
michael@0 1295 if (!obj)
michael@0 1296 return false;
michael@0 1297
michael@0 1298 obj = FixUpThisIfBroken(obj, funobj);
michael@0 1299 XPCCallContext ccx(JS_CALLER, cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
michael@0 1300 args.array(), vp);
michael@0 1301 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 1302 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 1303
michael@0 1304 XPCNativeInterface* iface;
michael@0 1305 XPCNativeMember* member;
michael@0 1306
michael@0 1307 if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
michael@0 1308 return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
michael@0 1309
michael@0 1310 if (args.length() != 0 && member->IsWritableAttribute()) {
michael@0 1311 ccx.SetCallInfo(iface, member, true);
michael@0 1312 bool retval = XPCWrappedNative::SetAttribute(ccx);
michael@0 1313 if (retval)
michael@0 1314 args.rval().set(args[0]);
michael@0 1315 return retval;
michael@0 1316 }
michael@0 1317 // else...
michael@0 1318
michael@0 1319 ccx.SetCallInfo(iface, member, false);
michael@0 1320 return XPCWrappedNative::GetAttribute(ccx);
michael@0 1321 }
michael@0 1322
michael@0 1323 /***************************************************************************/
michael@0 1324
michael@0 1325 static bool
michael@0 1326 XPC_WN_Shared_Proto_Enumerate(JSContext *cx, HandleObject obj)
michael@0 1327 {
michael@0 1328 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
michael@0 1329 js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass ||
michael@0 1330 js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
michael@0 1331 js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
michael@0 1332 "bad proto");
michael@0 1333 XPCWrappedNativeProto* self =
michael@0 1334 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
michael@0 1335 if (!self)
michael@0 1336 return false;
michael@0 1337
michael@0 1338 if (self->GetScriptableInfo() &&
michael@0 1339 self->GetScriptableInfo()->GetFlags().DontEnumStaticProps())
michael@0 1340 return true;
michael@0 1341
michael@0 1342 XPCNativeSet* set = self->GetSet();
michael@0 1343 if (!set)
michael@0 1344 return false;
michael@0 1345
michael@0 1346 XPCCallContext ccx(JS_CALLER, cx);
michael@0 1347 if (!ccx.IsValid())
michael@0 1348 return false;
michael@0 1349
michael@0 1350 uint16_t interface_count = set->GetInterfaceCount();
michael@0 1351 XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
michael@0 1352 for (uint16_t i = 0; i < interface_count; i++) {
michael@0 1353 XPCNativeInterface* iface = interfaceArray[i];
michael@0 1354 uint16_t member_count = iface->GetMemberCount();
michael@0 1355
michael@0 1356 for (uint16_t k = 0; k < member_count; k++) {
michael@0 1357 if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
michael@0 1358 return false;
michael@0 1359 }
michael@0 1360 }
michael@0 1361
michael@0 1362 return true;
michael@0 1363 }
michael@0 1364
michael@0 1365 static void
michael@0 1366 XPC_WN_Shared_Proto_Finalize(js::FreeOp *fop, JSObject *obj)
michael@0 1367 {
michael@0 1368 // This can be null if xpc shutdown has already happened
michael@0 1369 XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
michael@0 1370 if (p)
michael@0 1371 p->JSProtoObjectFinalized(fop, obj);
michael@0 1372 }
michael@0 1373
michael@0 1374 static void
michael@0 1375 XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj)
michael@0 1376 {
michael@0 1377 // This can be null if xpc shutdown has already happened
michael@0 1378 XPCWrappedNativeProto* p =
michael@0 1379 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
michael@0 1380 if (p)
michael@0 1381 p->TraceInside(trc);
michael@0 1382 }
michael@0 1383
michael@0 1384 /*****************************************************/
michael@0 1385
michael@0 1386 static bool
michael@0 1387 XPC_WN_ModsAllowed_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id)
michael@0 1388 {
michael@0 1389 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
michael@0 1390 js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass,
michael@0 1391 "bad proto");
michael@0 1392
michael@0 1393 XPCWrappedNativeProto* self =
michael@0 1394 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
michael@0 1395 if (!self)
michael@0 1396 return false;
michael@0 1397
michael@0 1398 XPCCallContext ccx(JS_CALLER, cx);
michael@0 1399 if (!ccx.IsValid())
michael@0 1400 return false;
michael@0 1401
michael@0 1402 XPCNativeScriptableInfo* si = self->GetScriptableInfo();
michael@0 1403 unsigned enumFlag = (si && si->GetFlags().DontEnumStaticProps()) ?
michael@0 1404 0 : JSPROP_ENUMERATE;
michael@0 1405
michael@0 1406 return DefinePropertyIfFound(ccx, obj, id,
michael@0 1407 self->GetSet(), nullptr, nullptr,
michael@0 1408 self->GetScope(),
michael@0 1409 true, nullptr, nullptr, si,
michael@0 1410 enumFlag, nullptr);
michael@0 1411 }
michael@0 1412
michael@0 1413 const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
michael@0 1414 "XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name;
michael@0 1415 WRAPPER_SLOTS, // flags;
michael@0 1416
michael@0 1417 /* Mandatory non-null function pointer members. */
michael@0 1418 JS_PropertyStub, // addProperty;
michael@0 1419 JS_DeletePropertyStub, // delProperty;
michael@0 1420 JS_PropertyStub, // getProperty;
michael@0 1421 JS_StrictPropertyStub, // setProperty;
michael@0 1422 XPC_WN_Shared_Proto_Enumerate, // enumerate;
michael@0 1423 XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
michael@0 1424 JS_ConvertStub, // convert;
michael@0 1425 XPC_WN_Shared_Proto_Finalize, // finalize;
michael@0 1426
michael@0 1427 /* Optionally non-null members start here. */
michael@0 1428 nullptr, // call;
michael@0 1429 nullptr, // construct;
michael@0 1430 nullptr, // hasInstance;
michael@0 1431 XPC_WN_Shared_Proto_Trace, // trace;
michael@0 1432
michael@0 1433 JS_NULL_CLASS_SPEC,
michael@0 1434 JS_NULL_CLASS_EXT,
michael@0 1435 XPC_WN_WithCall_ObjectOps
michael@0 1436 };
michael@0 1437
michael@0 1438 const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
michael@0 1439 "XPC_WN_ModsAllowed_NoCall_Proto_JSClass", // name;
michael@0 1440 WRAPPER_SLOTS, // flags;
michael@0 1441
michael@0 1442 /* Mandatory non-null function pointer members. */
michael@0 1443 JS_PropertyStub, // addProperty;
michael@0 1444 JS_DeletePropertyStub, // delProperty;
michael@0 1445 JS_PropertyStub, // getProperty;
michael@0 1446 JS_StrictPropertyStub, // setProperty;
michael@0 1447 XPC_WN_Shared_Proto_Enumerate, // enumerate;
michael@0 1448 XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
michael@0 1449 JS_ConvertStub, // convert;
michael@0 1450 XPC_WN_Shared_Proto_Finalize, // finalize;
michael@0 1451
michael@0 1452 /* Optionally non-null members start here. */
michael@0 1453 nullptr, // call;
michael@0 1454 nullptr, // construct;
michael@0 1455 nullptr, // hasInstance;
michael@0 1456 XPC_WN_Shared_Proto_Trace, // trace;
michael@0 1457
michael@0 1458 JS_NULL_CLASS_SPEC,
michael@0 1459 JS_NULL_CLASS_EXT,
michael@0 1460 XPC_WN_NoCall_ObjectOps
michael@0 1461 };
michael@0 1462
michael@0 1463 /***************************************************************************/
michael@0 1464
michael@0 1465 static bool
michael@0 1466 XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext *cx, HandleObject obj, HandleId id,
michael@0 1467 MutableHandleValue vp)
michael@0 1468 {
michael@0 1469 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
michael@0 1470 js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
michael@0 1471 "bad proto");
michael@0 1472
michael@0 1473 XPCWrappedNativeProto* self =
michael@0 1474 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
michael@0 1475 if (!self)
michael@0 1476 return false;
michael@0 1477
michael@0 1478 XPCCallContext ccx(JS_CALLER, cx);
michael@0 1479 if (!ccx.IsValid())
michael@0 1480 return false;
michael@0 1481
michael@0 1482 // Allow XPConnect to add the property only
michael@0 1483 if (ccx.GetResolveName() == id)
michael@0 1484 return true;
michael@0 1485
michael@0 1486 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
michael@0 1487 }
michael@0 1488
michael@0 1489 static bool
michael@0 1490 XPC_WN_OnlyIWrite_Proto_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
michael@0 1491 MutableHandleValue vp)
michael@0 1492 {
michael@0 1493 return XPC_WN_OnlyIWrite_Proto_AddPropertyStub(cx, obj, id, vp);
michael@0 1494 }
michael@0 1495
michael@0 1496 static bool
michael@0 1497 XPC_WN_NoMods_Proto_Resolve(JSContext *cx, HandleObject obj, HandleId id)
michael@0 1498 {
michael@0 1499 MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
michael@0 1500 js::GetObjectClass(obj) == &XPC_WN_NoMods_NoCall_Proto_JSClass,
michael@0 1501 "bad proto");
michael@0 1502
michael@0 1503 XPCWrappedNativeProto* self =
michael@0 1504 (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
michael@0 1505 if (!self)
michael@0 1506 return false;
michael@0 1507
michael@0 1508 XPCCallContext ccx(JS_CALLER, cx);
michael@0 1509 if (!ccx.IsValid())
michael@0 1510 return false;
michael@0 1511
michael@0 1512 XPCNativeScriptableInfo* si = self->GetScriptableInfo();
michael@0 1513 unsigned enumFlag = (si && si->GetFlags().DontEnumStaticProps()) ?
michael@0 1514 0 : JSPROP_ENUMERATE;
michael@0 1515
michael@0 1516 return DefinePropertyIfFound(ccx, obj, id,
michael@0 1517 self->GetSet(), nullptr, nullptr,
michael@0 1518 self->GetScope(),
michael@0 1519 true, nullptr, nullptr, si,
michael@0 1520 JSPROP_READONLY |
michael@0 1521 JSPROP_PERMANENT |
michael@0 1522 enumFlag, nullptr);
michael@0 1523 }
michael@0 1524
michael@0 1525 const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = {
michael@0 1526 "XPC_WN_NoMods_WithCall_Proto_JSClass", // name;
michael@0 1527 WRAPPER_SLOTS, // flags;
michael@0 1528
michael@0 1529 /* Mandatory non-null function pointer members. */
michael@0 1530 XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty;
michael@0 1531 XPC_WN_CantDeletePropertyStub, // delProperty;
michael@0 1532 JS_PropertyStub, // getProperty;
michael@0 1533 XPC_WN_OnlyIWrite_Proto_SetPropertyStub, // setProperty;
michael@0 1534 XPC_WN_Shared_Proto_Enumerate, // enumerate;
michael@0 1535 XPC_WN_NoMods_Proto_Resolve, // resolve;
michael@0 1536 JS_ConvertStub, // convert;
michael@0 1537 XPC_WN_Shared_Proto_Finalize, // finalize;
michael@0 1538
michael@0 1539 /* Optionally non-null members start here. */
michael@0 1540 nullptr, // call;
michael@0 1541 nullptr, // construct;
michael@0 1542 nullptr, // hasInstance;
michael@0 1543 XPC_WN_Shared_Proto_Trace, // trace;
michael@0 1544
michael@0 1545 JS_NULL_CLASS_SPEC,
michael@0 1546 JS_NULL_CLASS_EXT,
michael@0 1547 XPC_WN_WithCall_ObjectOps
michael@0 1548 };
michael@0 1549
michael@0 1550 const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = {
michael@0 1551 "XPC_WN_NoMods_NoCall_Proto_JSClass", // name;
michael@0 1552 WRAPPER_SLOTS, // flags;
michael@0 1553
michael@0 1554 /* Mandatory non-null function pointer members. */
michael@0 1555 XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty;
michael@0 1556 XPC_WN_CantDeletePropertyStub, // delProperty;
michael@0 1557 JS_PropertyStub, // getProperty;
michael@0 1558 XPC_WN_OnlyIWrite_Proto_SetPropertyStub, // setProperty;
michael@0 1559 XPC_WN_Shared_Proto_Enumerate, // enumerate;
michael@0 1560 XPC_WN_NoMods_Proto_Resolve, // resolve;
michael@0 1561 JS_ConvertStub, // convert;
michael@0 1562 XPC_WN_Shared_Proto_Finalize, // finalize;
michael@0 1563
michael@0 1564 /* Optionally non-null members start here. */
michael@0 1565 nullptr, // call;
michael@0 1566 nullptr, // construct;
michael@0 1567 nullptr, // hasInstance;
michael@0 1568 XPC_WN_Shared_Proto_Trace, // trace;
michael@0 1569
michael@0 1570 JS_NULL_CLASS_SPEC,
michael@0 1571 JS_NULL_CLASS_EXT,
michael@0 1572 XPC_WN_NoCall_ObjectOps
michael@0 1573 };
michael@0 1574
michael@0 1575 /***************************************************************************/
michael@0 1576
michael@0 1577 static bool
michael@0 1578 XPC_WN_TearOff_Enumerate(JSContext *cx, HandleObject obj)
michael@0 1579 {
michael@0 1580 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 1581 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 1582 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 1583
michael@0 1584 XPCWrappedNativeTearOff* to = ccx.GetTearOff();
michael@0 1585 XPCNativeInterface* iface;
michael@0 1586
michael@0 1587 if (!to || nullptr == (iface = to->GetInterface()))
michael@0 1588 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
michael@0 1589
michael@0 1590 uint16_t member_count = iface->GetMemberCount();
michael@0 1591 for (uint16_t k = 0; k < member_count; k++) {
michael@0 1592 if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
michael@0 1593 return false;
michael@0 1594 }
michael@0 1595
michael@0 1596 return true;
michael@0 1597 }
michael@0 1598
michael@0 1599 static bool
michael@0 1600 XPC_WN_TearOff_Resolve(JSContext *cx, HandleObject obj, HandleId id)
michael@0 1601 {
michael@0 1602 XPCCallContext ccx(JS_CALLER, cx, obj);
michael@0 1603 XPCWrappedNative* wrapper = ccx.GetWrapper();
michael@0 1604 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
michael@0 1605
michael@0 1606 XPCWrappedNativeTearOff* to = ccx.GetTearOff();
michael@0 1607 XPCNativeInterface* iface;
michael@0 1608
michael@0 1609 if (!to || nullptr == (iface = to->GetInterface()))
michael@0 1610 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
michael@0 1611
michael@0 1612 return DefinePropertyIfFound(ccx, obj, id, nullptr, iface, nullptr,
michael@0 1613 wrapper->GetScope(),
michael@0 1614 true, nullptr, nullptr, nullptr,
michael@0 1615 JSPROP_READONLY |
michael@0 1616 JSPROP_PERMANENT |
michael@0 1617 JSPROP_ENUMERATE, nullptr);
michael@0 1618 }
michael@0 1619
michael@0 1620 static void
michael@0 1621 XPC_WN_TearOff_Finalize(js::FreeOp *fop, JSObject *obj)
michael@0 1622 {
michael@0 1623 XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
michael@0 1624 xpc_GetJSPrivate(obj);
michael@0 1625 if (!p)
michael@0 1626 return;
michael@0 1627 p->JSObjectFinalized();
michael@0 1628 }
michael@0 1629
michael@0 1630 const js::Class XPC_WN_Tearoff_JSClass = {
michael@0 1631 "WrappedNative_TearOff", // name;
michael@0 1632 WRAPPER_SLOTS, // flags;
michael@0 1633
michael@0 1634 XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty;
michael@0 1635 XPC_WN_CantDeletePropertyStub, // delProperty;
michael@0 1636 JS_PropertyStub, // getProperty;
michael@0 1637 XPC_WN_OnlyIWrite_SetPropertyStub, // setProperty;
michael@0 1638 XPC_WN_TearOff_Enumerate, // enumerate;
michael@0 1639 XPC_WN_TearOff_Resolve, // resolve;
michael@0 1640 XPC_WN_Shared_Convert, // convert;
michael@0 1641 XPC_WN_TearOff_Finalize // finalize;
michael@0 1642 };

mercurial