XPCWrappedNativeJSOps.cpp (40363B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */ 8 9 #include "xpcprivate.h" 10 #include "xpc_make_class.h" 11 #include "mozilla/dom/BindingUtils.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/Preferences.h" 14 #include "js/CharacterEncoding.h" 15 #include "js/Class.h" 16 #include "js/Object.h" // JS::GetClass 17 #include "js/Printf.h" 18 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_GetProperty, JS_GetPropertyById 19 #include "js/Symbol.h" 20 21 #include <string_view> 22 23 using namespace mozilla; 24 using namespace JS; 25 using namespace xpc; 26 27 /***************************************************************************/ 28 29 // All of the exceptions thrown into JS from this file go through here. 30 // That makes this a nice place to set a breakpoint. 31 32 static bool Throw(nsresult errNum, JSContext* cx) { 33 XPCThrower::Throw(errNum, cx); 34 return false; 35 } 36 37 // Handy macro used in many callback stub below. 38 39 #define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \ 40 PR_BEGIN_MACRO \ 41 if (!wrapper) return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \ 42 if (!wrapper->IsValid()) return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \ 43 PR_END_MACRO 44 45 /***************************************************************************/ 46 47 static bool ToStringGuts(XPCCallContext& ccx) { 48 UniqueChars sz; 49 XPCWrappedNative* wrapper = ccx.GetWrapper(); 50 51 if (wrapper) { 52 sz.reset(wrapper->ToString(ccx.GetTearOff())); 53 } else { 54 sz = JS_smprintf("[xpconnect wrapped native prototype]"); 55 } 56 57 if (!sz) { 58 JS_ReportOutOfMemory(ccx); 59 return false; 60 } 61 62 JSString* str = JS_NewStringCopyZ(ccx, sz.get()); 63 if (!str) { 64 return false; 65 } 66 67 ccx.SetRetVal(JS::StringValue(str)); 68 return true; 69 } 70 71 /***************************************************************************/ 72 73 static bool XPC_WN_Shared_ToString(JSContext* cx, unsigned argc, Value* vp) { 74 CallArgs args = CallArgsFromVp(argc, vp); 75 76 RootedObject obj(cx); 77 if (!args.computeThis(cx, &obj)) { 78 return false; 79 } 80 81 XPCCallContext ccx(cx, obj); 82 if (!ccx.IsValid()) { 83 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); 84 } 85 ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING)); 86 ccx.SetArgsAndResultPtr(args.length(), args.array(), vp); 87 return ToStringGuts(ccx); 88 } 89 90 static bool XPC_WN_Shared_ToSource(JSContext* cx, unsigned argc, Value* vp) { 91 CallArgs args = CallArgsFromVp(argc, vp); 92 static constexpr std::string_view empty = "({})"; 93 JSString* str = JS_NewStringCopyN(cx, empty.data(), empty.length()); 94 if (!str) { 95 return false; 96 } 97 args.rval().setString(str); 98 99 return true; 100 } 101 102 static bool XPC_WN_Shared_toPrimitive(JSContext* cx, unsigned argc, Value* vp) { 103 CallArgs args = CallArgsFromVp(argc, vp); 104 105 RootedObject obj(cx); 106 if (!JS_ValueToObject(cx, args.thisv(), &obj)) { 107 return false; 108 } 109 XPCCallContext ccx(cx, obj); 110 XPCWrappedNative* wrapper = ccx.GetWrapper(); 111 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 112 113 JSType hint; 114 if (!GetFirstArgumentAsTypeHint(cx, args, &hint)) { 115 return false; 116 } 117 118 if (hint == JSTYPE_NUMBER) { 119 args.rval().set(NaNValue()); 120 return true; 121 } 122 123 MOZ_ASSERT(hint == JSTYPE_STRING || hint == JSTYPE_UNDEFINED); 124 ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING)); 125 ccx.SetArgsAndResultPtr(0, nullptr, args.rval().address()); 126 127 XPCNativeMember* member = ccx.GetMember(); 128 if (member && member->IsMethod()) { 129 if (!XPCWrappedNative::CallMethod(ccx)) { 130 return false; 131 } 132 133 if (args.rval().isPrimitive()) { 134 return true; 135 } 136 } 137 138 // else... 139 return ToStringGuts(ccx); 140 } 141 142 /***************************************************************************/ 143 144 // A "double wrapped object" is a user JSObject that has been wrapped as a 145 // wrappedJS in order to be used by native code and then re-wrapped by a 146 // wrappedNative wrapper to be used by JS code. One might think of it as: 147 // wrappedNative(wrappedJS(underlying_JSObject)) 148 // This is done (as opposed to just unwrapping the wrapped JS and automatically 149 // returning the underlying JSObject) so that JS callers will see what looks 150 // Like any other xpcom object - and be limited to use its interfaces. 151 // 152 153 /** 154 * When JavaScript code uses a component that is itself implemented in 155 * JavaScript then XPConnect will build a wrapper rather than directly 156 * expose the JSObject of the component. This allows components implemented 157 * in JavaScript to 'look' just like any other xpcom component (from the 158 * perspective of the JavaScript caller). This insulates the component from 159 * the caller and hides any properties or methods that are not part of the 160 * interface as declared in xpidl. Usually this is a good thing. 161 * 162 * However, in some cases it is useful to allow the JS caller access to the 163 * JS component's underlying implementation. In order to facilitate this 164 * XPConnect supports the 'wrappedJSObject' property. This 'wrappedJSObject' 165 * property is different than the XrayWrapper meaning. (The naming collision 166 * avoids having more than one magic XPConnect property name, but is 167 * confusing.) 168 * 169 * The caller code can do: 170 * 171 * // 'foo' is some xpcom component (that might be implemented in JS). 172 * var bar = foo.wrappedJSObject; 173 * if(bar) { 174 * // bar is the underlying JSObject. Do stuff with it here. 175 * } 176 * 177 * Recall that 'foo' above is an XPConnect wrapper, not the underlying JS 178 * object. The property get "foo.wrappedJSObject" will only succeed if three 179 * conditions are met: 180 * 181 * 1) 'foo' really is an XPConnect wrapper around a JSObject. 182 * 3) The caller must be system JS and not content. Double-wrapped XPCWJS should 183 * not be exposed to content except with a remote-XUL domain. 184 * 185 * Notes: 186 * 187 * a) If 'foo' above were the underlying JSObject and not a wrapper at all, 188 * then this all just works and XPConnect is not part of the picture at all. 189 * b) One might ask why 'foo' should not just implement an interface through 190 * which callers might get at the underlying object. There are two reasons: 191 * i) XPConnect would still have to do magic since JSObject is not a 192 * scriptable type. 193 * ii) Avoiding the explicit interface makes it easier for both the caller 194 * and the component. 195 */ 196 197 static JSObject* GetDoubleWrappedJSObject(XPCCallContext& ccx, 198 XPCWrappedNative* wrapper) { 199 RootedObject obj(ccx); 200 { 201 nsCOMPtr<nsIXPConnectWrappedJS> underware = 202 do_QueryInterface(wrapper->GetIdentityObject()); 203 if (!underware) { 204 return nullptr; 205 } 206 RootedObject mainObj(ccx, underware->GetJSObject()); 207 if (mainObj) { 208 JSAutoRealm ar(ccx, underware->GetJSObjectGlobal()); 209 210 // We don't have to root this ID, as it's already rooted by our context. 211 HandleId id = 212 ccx.GetContext()->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT); 213 214 // If the `wrappedJSObject` property is defined, use the result of getting 215 // that property, otherwise fall back to the `mainObj` object which is 216 // directly being wrapped. 217 RootedValue val(ccx); 218 if (JS_GetPropertyById(ccx, mainObj, id, &val) && !val.isPrimitive()) { 219 obj = val.toObjectOrNull(); 220 } else { 221 obj = mainObj; 222 } 223 } 224 } 225 return obj; 226 } 227 228 // This is the getter native function we use to handle 'wrappedJSObject' for 229 // double wrapped JSObjects. 230 231 static bool XPC_WN_DoubleWrappedGetter(JSContext* cx, unsigned argc, 232 Value* vp) { 233 CallArgs args = CallArgsFromVp(argc, vp); 234 235 if (!args.thisv().isObject()) { 236 JS_ReportErrorASCII( 237 cx, 238 "xpconnect double wrapped getter called on incompatible non-object"); 239 return false; 240 } 241 RootedObject obj(cx, &args.thisv().toObject()); 242 243 XPCCallContext ccx(cx, obj); 244 XPCWrappedNative* wrapper = ccx.GetWrapper(); 245 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 246 247 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, 248 "bad function"); 249 250 RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper)); 251 if (!realObject) { 252 // This is pretty unexpected at this point. The object originally 253 // responded to this get property call and now gives no object. 254 // XXX Should this throw something at the caller? 255 args.rval().setNull(); 256 return true; 257 } 258 259 // It is a double wrapped object. This should really never appear in 260 // content these days, but addons still do it - see bug 965921. 261 if (MOZ_UNLIKELY(!nsContentUtils::IsSystemCaller(cx))) { 262 JS_ReportErrorASCII(cx, 263 "Attempt to use .wrappedJSObject in untrusted code"); 264 return false; 265 } 266 args.rval().setObject(*realObject); 267 return JS_WrapValue(cx, args.rval()); 268 } 269 270 /***************************************************************************/ 271 272 // This is our shared function to define properties on our JSObjects. 273 274 /* 275 * NOTE: 276 * We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE. 277 * We *never* set toString or toSource as JS_ENUMERATE. 278 */ 279 280 static bool DefinePropertyIfFound( 281 XPCCallContext& ccx, HandleObject obj, HandleId idArg, XPCNativeSet* set, 282 XPCNativeInterface* ifaceArg, XPCNativeMember* member, 283 XPCWrappedNativeScope* scope, bool reflectToStringAndToSource, 284 XPCWrappedNative* wrapperToReflectInterfaceNames, 285 XPCWrappedNative* wrapperToReflectDoubleWrap, nsIXPCScriptable* scr, 286 unsigned propFlags, bool* resolved) { 287 RootedId id(ccx, idArg); 288 RefPtr<XPCNativeInterface> iface = ifaceArg; 289 XPCJSContext* xpccx = ccx.GetContext(); 290 bool found; 291 const char* name; 292 293 propFlags |= JSPROP_RESOLVING; 294 295 if (set) { 296 if (iface) { 297 found = true; 298 } else { 299 found = set->FindMember(id, &member, &iface); 300 } 301 } else 302 found = (nullptr != (member = iface->FindMember(id))); 303 304 if (!found) { 305 if (reflectToStringAndToSource) { 306 JSNative call; 307 if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING)) { 308 call = XPC_WN_Shared_ToString; 309 name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING); 310 } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) { 311 call = XPC_WN_Shared_ToSource; 312 name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE); 313 } else if (id.isWellKnownSymbol(JS::SymbolCode::toPrimitive)) { 314 call = XPC_WN_Shared_toPrimitive; 315 name = "[Symbol.toPrimitive]"; 316 } else { 317 call = nullptr; 318 } 319 320 if (call) { 321 RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name)); 322 if (!fun) { 323 JS_ReportOutOfMemory(ccx); 324 return false; 325 } 326 327 AutoResolveName arn(ccx, id); 328 if (resolved) { 329 *resolved = true; 330 } 331 RootedObject value(ccx, JS_GetFunctionObject(fun)); 332 return JS_DefinePropertyById(ccx, obj, id, value, 333 propFlags & ~JSPROP_ENUMERATE); 334 } 335 } 336 // This *might* be a tearoff name that is not yet part of our 337 // set. Let's lookup the name and see if it is the name of an 338 // interface. Then we'll see if the object actually *does* this 339 // interface and add a tearoff as necessary. 340 341 if (wrapperToReflectInterfaceNames) { 342 JS::UniqueChars name; 343 RefPtr<XPCNativeInterface> iface2; 344 XPCWrappedNativeTearOff* to; 345 RootedObject jso(ccx); 346 nsresult rv = NS_OK; 347 348 bool defineProperty = false; 349 do { 350 if (!id.isString()) { 351 break; 352 } 353 354 name = JS_EncodeStringToLatin1(ccx, id.toString()); 355 if (!name) { 356 break; 357 } 358 359 iface2 = XPCNativeInterface::GetNewOrUsed(ccx, name.get()); 360 if (!iface2) { 361 break; 362 } 363 364 to = 365 wrapperToReflectInterfaceNames->FindTearOff(ccx, iface2, true, &rv); 366 if (!to) { 367 break; 368 } 369 370 jso = to->GetJSObject(); 371 if (!jso) { 372 break; 373 } 374 375 defineProperty = true; 376 } while (false); 377 378 if (defineProperty) { 379 AutoResolveName arn(ccx, id); 380 if (resolved) { 381 *resolved = true; 382 } 383 return JS_DefinePropertyById(ccx, obj, id, jso, 384 propFlags & ~JSPROP_ENUMERATE); 385 } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) { 386 return Throw(rv, ccx); 387 } 388 } 389 390 // This *might* be a double wrapped JSObject 391 if (wrapperToReflectDoubleWrap && 392 id == xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT) && 393 GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) { 394 // We build and add a getter function. 395 // A security check is done on a per-get basis. 396 397 JSFunction* fun; 398 399 id = xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT); 400 name = xpccx->GetStringName(XPCJSContext::IDX_WRAPPED_JSOBJECT); 401 402 fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter, 0, 0, name); 403 404 if (!fun) { 405 return false; 406 } 407 408 RootedObject funobj(ccx, JS_GetFunctionObject(fun)); 409 if (!funobj) { 410 return false; 411 } 412 413 propFlags &= ~JSPROP_ENUMERATE; 414 415 AutoResolveName arn(ccx, id); 416 if (resolved) { 417 *resolved = true; 418 } 419 return JS_DefinePropertyById(ccx, obj, id, funobj, nullptr, propFlags); 420 } 421 422 if (resolved) { 423 *resolved = false; 424 } 425 return true; 426 } 427 428 if (!member) { 429 if (wrapperToReflectInterfaceNames) { 430 XPCWrappedNativeTearOff* to = 431 wrapperToReflectInterfaceNames->FindTearOff(ccx, iface, true); 432 433 if (!to) { 434 return false; 435 } 436 RootedObject jso(ccx, to->GetJSObject()); 437 if (!jso) { 438 return false; 439 } 440 441 AutoResolveName arn(ccx, id); 442 if (resolved) { 443 *resolved = true; 444 } 445 return JS_DefinePropertyById(ccx, obj, id, jso, 446 propFlags & ~JSPROP_ENUMERATE); 447 } 448 if (resolved) { 449 *resolved = false; 450 } 451 return true; 452 } 453 454 if (member->IsConstant()) { 455 RootedValue val(ccx); 456 AutoResolveName arn(ccx, id); 457 if (resolved) { 458 *resolved = true; 459 } 460 return member->GetConstantValue(ccx, iface, val.address()) && 461 JS_DefinePropertyById(ccx, obj, id, val, propFlags); 462 } 463 464 if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) || 465 id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE) || 466 (scr && scr->DontEnumQueryInterface() && 467 id == xpccx->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE))) 468 propFlags &= ~JSPROP_ENUMERATE; 469 470 RootedValue funval(ccx); 471 if (!member->NewFunctionObject(ccx, iface, obj, funval.address())) { 472 return false; 473 } 474 475 if (member->IsMethod()) { 476 AutoResolveName arn(ccx, id); 477 if (resolved) { 478 *resolved = true; 479 } 480 return JS_DefinePropertyById(ccx, obj, id, funval, propFlags); 481 } 482 483 // else... 484 485 MOZ_ASSERT(member->IsAttribute(), "way broken!"); 486 487 propFlags &= ~JSPROP_READONLY; 488 RootedObject funobjGetter(ccx, funval.toObjectOrNull()); 489 RootedObject funobjSetter(ccx); 490 if (member->IsWritableAttribute()) { 491 funobjSetter = funobjGetter; 492 } 493 494 AutoResolveName arn(ccx, id); 495 if (resolved) { 496 *resolved = true; 497 } 498 499 return JS_DefinePropertyById(ccx, obj, id, funobjGetter, funobjSetter, 500 propFlags); 501 } 502 503 /***************************************************************************/ 504 /***************************************************************************/ 505 506 static bool XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, 507 HandleId id, HandleValue v) { 508 XPCCallContext ccx(cx, obj, nullptr, id); 509 XPCWrappedNative* wrapper = ccx.GetWrapper(); 510 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 511 512 // Allow only XPConnect to add/set the property 513 if (ccx.GetResolveName() == id) { 514 return true; 515 } 516 517 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); 518 } 519 520 bool XPC_WN_CannotModifyPropertyStub(JSContext* cx, HandleObject obj, 521 HandleId id, HandleValue v) { 522 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); 523 } 524 525 bool XPC_WN_CannotDeletePropertyStub(JSContext* cx, HandleObject obj, 526 HandleId id, ObjectOpResult& result) { 527 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); 528 } 529 530 bool XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj) { 531 XPCCallContext ccx(cx, obj); 532 XPCWrappedNative* wrapper = ccx.GetWrapper(); 533 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 534 535 // Since we aren't going to enumerate tearoff names and the prototype 536 // handles non-mutated members, we can do this potential short-circuit. 537 if (!wrapper->HasMutatedSet()) { 538 return true; 539 } 540 541 XPCNativeSet* set = wrapper->GetSet(); 542 XPCNativeSet* protoSet = 543 wrapper->HasProto() ? wrapper->GetProto()->GetSet() : nullptr; 544 545 uint16_t interface_count = set->GetInterfaceCount(); 546 XPCNativeInterface** interfaceArray = set->GetInterfaceArray(); 547 for (uint16_t i = 0; i < interface_count; i++) { 548 XPCNativeInterface* iface = interfaceArray[i]; 549 uint16_t member_count = iface->GetMemberCount(); 550 for (uint16_t k = 0; k < member_count; k++) { 551 XPCNativeMember* member = iface->GetMemberAt(k); 552 jsid name = member->GetName(); 553 554 // Skip if this member is going to come from the proto. 555 uint16_t index; 556 if (protoSet && protoSet->FindMember(name, nullptr, &index) && index == i) 557 continue; 558 559 JS_MarkCrossZoneId(cx, name); 560 if (!xpc_ForcePropertyResolve(cx, obj, name)) { 561 return false; 562 } 563 } 564 } 565 return true; 566 } 567 568 /***************************************************************************/ 569 570 enum WNHelperType { WN_NOHELPER, WN_HELPER }; 571 572 static void WrappedNativeFinalize(JS::GCContext* gcx, JSObject* obj, 573 WNHelperType helperType) { 574 const JSClass* clazz = JS::GetClass(obj); 575 if (clazz->flags & JSCLASS_DOM_GLOBAL) { 576 mozilla::dom::DestroyProtoAndIfaceCache(obj); 577 } 578 XPCWrappedNative* wrapper = JS::GetObjectISupports<XPCWrappedNative>(obj); 579 if (!wrapper) { 580 return; 581 } 582 583 if (helperType == WN_HELPER) { 584 wrapper->GetScriptable()->Finalize(wrapper, gcx, obj); 585 } 586 wrapper->FlatJSObjectFinalized(); 587 } 588 589 static size_t WrappedNativeObjectMoved(JSObject* obj, JSObject* old) { 590 XPCWrappedNative* wrapper = JS::GetObjectISupports<XPCWrappedNative>(obj); 591 if (!wrapper) { 592 return 0; 593 } 594 595 wrapper->FlatJSObjectMoved(obj, old); 596 return 0; 597 } 598 599 void XPC_WN_NoHelper_Finalize(JS::GCContext* gcx, JSObject* obj) { 600 WrappedNativeFinalize(gcx, obj, WN_NOHELPER); 601 } 602 603 /* 604 * General comment about XPConnect tracing: Given a C++ object |wrapper| and its 605 * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS 606 * engine to mark |obj|. Eventually, this will lead to the trace hook being 607 * called for |obj|. The trace hook should call |wrapper->TraceInside|, which 608 * should mark any JS objects held by |wrapper| as members. 609 */ 610 611 /* static */ 612 void XPCWrappedNative::Trace(JSTracer* trc, JSObject* obj) { 613 const JSClass* clazz = JS::GetClass(obj); 614 if (clazz->flags & JSCLASS_DOM_GLOBAL) { 615 mozilla::dom::TraceProtoAndIfaceCache(trc, obj); 616 } 617 MOZ_ASSERT(clazz->isWrappedNative()); 618 619 XPCWrappedNative* wrapper = XPCWrappedNative::Get(obj); 620 if (wrapper && wrapper->IsValid()) { 621 wrapper->TraceInside(trc); 622 } 623 } 624 625 void XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj) { 626 XPCWrappedNative::Trace(trc, obj); 627 } 628 629 static bool XPC_WN_NoHelper_Resolve(JSContext* cx, HandleObject obj, 630 HandleId id, bool* resolvedp) { 631 XPCCallContext ccx(cx, obj, nullptr, id); 632 XPCWrappedNative* wrapper = ccx.GetWrapper(); 633 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 634 635 XPCNativeSet* set = ccx.GetSet(); 636 if (!set) { 637 return true; 638 } 639 640 // Don't resolve properties that are on our prototype. 641 if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal()) { 642 return true; 643 } 644 645 return DefinePropertyIfFound( 646 ccx, obj, id, set, nullptr, nullptr, wrapper->GetScope(), true, wrapper, 647 wrapper, nullptr, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, 648 resolvedp); 649 } 650 651 static const JSClassOps XPC_WN_NoHelper_JSClassOps = { 652 XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty 653 XPC_WN_CannotDeletePropertyStub, // delProperty 654 XPC_WN_Shared_Enumerate, // enumerate 655 nullptr, // newEnumerate 656 XPC_WN_NoHelper_Resolve, // resolve 657 nullptr, // mayResolve 658 XPC_WN_NoHelper_Finalize, // finalize 659 nullptr, // call 660 nullptr, // construct 661 XPCWrappedNative::Trace, // trace 662 }; 663 664 const js::ClassExtension XPC_WN_JSClassExtension = { 665 WrappedNativeObjectMoved, // objectMovedOp 666 }; 667 668 const JSClass XPC_WN_NoHelper_JSClass = { 669 "XPCWrappedNative_NoHelper", 670 JSCLASS_IS_WRAPPED_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(1) | 671 JSCLASS_SLOT0_IS_NSISUPPORTS | JSCLASS_FOREGROUND_FINALIZE, 672 &XPC_WN_NoHelper_JSClassOps, 673 JS_NULL_CLASS_SPEC, 674 &XPC_WN_JSClassExtension, 675 JS_NULL_OBJECT_OPS}; 676 677 /***************************************************************************/ 678 679 bool XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj, 680 HandleId id, HandleValue v) { 681 XPCCallContext ccx(cx, obj); 682 XPCWrappedNative* wrapper = ccx.GetWrapper(); 683 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 684 685 if (ccx.GetResolvingWrapper() == wrapper) { 686 return true; 687 } 688 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); 689 } 690 691 bool XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, HandleObject obj, 692 HandleId id, 693 ObjectOpResult& result) { 694 XPCCallContext ccx(cx, obj); 695 XPCWrappedNative* wrapper = ccx.GetWrapper(); 696 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 697 698 if (ccx.GetResolvingWrapper() == wrapper) { 699 return result.succeed(); 700 } 701 return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); 702 } 703 704 // macro fun! 705 #define PRE_HELPER_STUB \ 706 /* It's very important for "unwrapped" to be rooted here. */ \ 707 RootedObject unwrapped(cx, js::CheckedUnwrapDynamic(obj, cx, false)); \ 708 if (!unwrapped) { \ 709 JS_ReportErrorASCII(cx, "Permission denied to operate on object."); \ 710 return false; \ 711 } \ 712 if (!IsWrappedNativeReflector(unwrapped)) { \ 713 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \ 714 } \ 715 XPCWrappedNative* wrapper = XPCWrappedNative::Get(unwrapped); \ 716 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); \ 717 bool retval = true; \ 718 nsresult rv = wrapper->GetScriptable()-> 719 720 #define POST_HELPER_STUB \ 721 if (NS_FAILED(rv)) return Throw(rv, cx); \ 722 return retval; 723 724 bool XPC_WN_Helper_Call(JSContext* cx, unsigned argc, Value* vp) { 725 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 726 // N.B. we want obj to be the callee, not JS_THIS(cx, vp) 727 RootedObject obj(cx, &args.callee()); 728 729 XPCCallContext ccx(cx, obj, nullptr, JS::VoidHandlePropertyKey, args.length(), 730 args.array(), args.rval().address()); 731 if (!ccx.IsValid()) { 732 return false; 733 } 734 735 PRE_HELPER_STUB 736 Call(wrapper, cx, obj, args, &retval); 737 POST_HELPER_STUB 738 } 739 740 bool XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, Value* vp) { 741 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 742 RootedObject obj(cx, &args.callee()); 743 if (!obj) { 744 return false; 745 } 746 747 XPCCallContext ccx(cx, obj, nullptr, JS::VoidHandlePropertyKey, args.length(), 748 args.array(), args.rval().address()); 749 if (!ccx.IsValid()) { 750 return false; 751 } 752 753 PRE_HELPER_STUB 754 Construct(wrapper, cx, obj, args, &retval); 755 POST_HELPER_STUB 756 } 757 758 static bool XPC_WN_Helper_HasInstance(JSContext* cx, unsigned argc, Value* vp) { 759 CallArgs args = CallArgsFromVp(argc, vp); 760 if (!args.requireAtLeast(cx, "WrappedNative[Symbol.hasInstance]", 1)) { 761 return false; 762 } 763 764 if (!args.thisv().isObject()) { 765 JS_ReportErrorASCII( 766 cx, "WrappedNative[Symbol.hasInstance]: unexpected this value"); 767 return false; 768 } 769 770 RootedObject obj(cx, &args.thisv().toObject()); 771 RootedValue val(cx, args.get(0)); 772 773 bool retval2; 774 PRE_HELPER_STUB 775 HasInstance(wrapper, cx, obj, val, &retval2, &retval); 776 args.rval().setBoolean(retval2); 777 POST_HELPER_STUB 778 } 779 780 void XPC_WN_Helper_Finalize(JS::GCContext* gcx, JSObject* obj) { 781 WrappedNativeFinalize(gcx, obj, WN_HELPER); 782 } 783 784 // RAII class used to store the wrapper in the context when resolving a lazy 785 // property on its JS reflector. This is used by XPC_WN_MaybeResolving to allow 786 // adding properties while resolving. 787 class MOZ_RAII AutoSetResolvingWrapper { 788 public: 789 AutoSetResolvingWrapper(XPCCallContext& ccx, XPCWrappedNative* wrapper) 790 : mCcx(ccx), mOldResolvingWrapper(ccx.SetResolvingWrapper(wrapper)) {} 791 792 ~AutoSetResolvingWrapper() { 793 (void)mCcx.SetResolvingWrapper(mOldResolvingWrapper); 794 } 795 796 private: 797 XPCCallContext& mCcx; 798 XPCWrappedNative* mOldResolvingWrapper; 799 }; 800 801 bool XPC_WN_Helper_Resolve(JSContext* cx, HandleObject obj, HandleId id, 802 bool* resolvedp) { 803 nsresult rv = NS_OK; 804 bool retval = true; 805 bool resolved = false; 806 XPCCallContext ccx(cx, obj); 807 XPCWrappedNative* wrapper = ccx.GetWrapper(); 808 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 809 810 RootedId old(cx, ccx.SetResolveName(id)); 811 812 nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable(); 813 814 // Resolve a Symbol.hasInstance property if we want custom `instanceof` 815 // behavior. 816 if (scr && scr->WantHasInstance() && 817 id.isWellKnownSymbol(SymbolCode::hasInstance)) { 818 mozilla::Maybe<AutoSetResolvingWrapper> asrw; 819 if (scr->AllowPropModsDuringResolve()) { 820 asrw.emplace(ccx, wrapper); 821 } 822 if (!JS_DefineFunctionById( 823 cx, obj, id, XPC_WN_Helper_HasInstance, 1, 824 JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING)) { 825 rv = NS_ERROR_FAILURE; 826 } else { 827 resolved = true; 828 } 829 } 830 831 if (scr && scr->WantResolve()) { 832 mozilla::Maybe<AutoSetResolvingWrapper> asrw; 833 if (scr->AllowPropModsDuringResolve()) { 834 asrw.emplace(ccx, wrapper); 835 } 836 rv = scr->Resolve(wrapper, cx, obj, id, &resolved, &retval); 837 } 838 839 old = ccx.SetResolveName(old); 840 MOZ_ASSERT(old == id, "bad nest"); 841 842 if (NS_FAILED(rv)) { 843 return Throw(rv, cx); 844 } 845 846 if (resolved) { 847 *resolvedp = true; 848 } else if (wrapper->HasMutatedSet()) { 849 // We are here if scriptable did not resolve this property and 850 // it *might* be in the instance set but not the proto set. 851 852 XPCNativeSet* set = wrapper->GetSet(); 853 XPCNativeSet* protoSet = 854 wrapper->HasProto() ? wrapper->GetProto()->GetSet() : nullptr; 855 XPCNativeMember* member = nullptr; 856 RefPtr<XPCNativeInterface> iface; 857 bool IsLocal = false; 858 859 if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) && IsLocal) { 860 XPCWrappedNative* wrapperForInterfaceNames = 861 (scr && scr->DontReflectInterfaceNames()) ? nullptr : wrapper; 862 863 AutoSetResolvingWrapper asrw(ccx, wrapper); 864 retval = DefinePropertyIfFound( 865 ccx, obj, id, set, iface, member, wrapper->GetScope(), false, 866 wrapperForInterfaceNames, nullptr, scr, JSPROP_ENUMERATE, resolvedp); 867 } 868 } 869 870 return retval; 871 } 872 873 /***************************************************************************/ 874 875 bool XPC_WN_NewEnumerate(JSContext* cx, HandleObject obj, 876 MutableHandleIdVector properties, 877 bool enumerableOnly) { 878 XPCCallContext ccx(cx, obj); 879 XPCWrappedNative* wrapper = ccx.GetWrapper(); 880 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 881 882 nsCOMPtr<nsIXPCScriptable> scr = wrapper->GetScriptable(); 883 if (!scr || !scr->WantNewEnumerate()) { 884 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); 885 } 886 887 if (!XPC_WN_Shared_Enumerate(cx, obj)) { 888 return false; 889 } 890 891 bool retval = true; 892 nsresult rv = 893 scr->NewEnumerate(wrapper, cx, obj, properties, enumerableOnly, &retval); 894 if (NS_FAILED(rv)) { 895 return Throw(rv, cx); 896 } 897 return retval; 898 } 899 900 /***************************************************************************/ 901 /***************************************************************************/ 902 903 // Compatibility hack. 904 // 905 // XPConnect used to do all sorts of funny tricks to find the "correct" 906 // |this| object for a given method (often to the detriment of proper 907 // call/apply). When these tricks were removed, a fair amount of chrome 908 // code broke, because it was relying on being able to grab methods off 909 // some XPCOM object (like the nsITelemetry service) and invoke them without 910 // a proper |this|. So, if it's quite clear that we're in this situation and 911 // about to use a |this| argument that just won't work, fix things up. 912 // 913 // This hack is only useful for getters/setters if someone sets an XPCOM object 914 // as the prototype for a vanilla JS object and expects the XPCOM attributes to 915 // work on the derived object, which we really don't want to support. But we 916 // handle it anyway, for now, to minimize regression risk on an already-risky 917 // landing. 918 // 919 // This hack is mainly useful for the NoHelper JSClass. We also fix up 920 // Components.utils because it implements nsIXPCScriptable (giving it a custom 921 // JSClass) but not nsIClassInfo (which would put the methods on a prototype). 922 923 #define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass) 924 #define IS_CU_CLASS(clasp) \ 925 (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils")) 926 927 MOZ_ALWAYS_INLINE JSObject* FixUpThisIfBroken(JSObject* obj, JSObject* funobj) { 928 if (funobj) { 929 JSObject* parentObj = 930 &js::GetFunctionNativeReserved(funobj, XPC_FUNCTION_PARENT_OBJECT_SLOT) 931 .toObject(); 932 const JSClass* parentClass = JS::GetClass(parentObj); 933 if (MOZ_UNLIKELY( 934 (IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) && 935 (JS::GetClass(obj) != parentClass))) { 936 return parentObj; 937 } 938 } 939 return obj; 940 } 941 942 bool XPC_WN_CallMethod(JSContext* cx, unsigned argc, Value* vp) { 943 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 944 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, 945 "bad function"); 946 RootedObject funobj(cx, &args.callee()); 947 948 RootedObject obj(cx); 949 if (!args.computeThis(cx, &obj)) { 950 return false; 951 } 952 953 obj = FixUpThisIfBroken(obj, funobj); 954 XPCCallContext ccx(cx, obj, funobj, JS::VoidHandlePropertyKey, args.length(), 955 args.array(), vp); 956 XPCWrappedNative* wrapper = ccx.GetWrapper(); 957 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 958 959 RefPtr<XPCNativeInterface> iface; 960 XPCNativeMember* member; 961 962 if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member)) { 963 return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx); 964 } 965 ccx.SetCallInfo(iface, member, false); 966 return XPCWrappedNative::CallMethod(ccx); 967 } 968 969 bool XPC_WN_GetterSetter(JSContext* cx, unsigned argc, Value* vp) { 970 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 971 MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, 972 "bad function"); 973 RootedObject funobj(cx, &args.callee()); 974 975 if (!args.thisv().isObject()) { 976 JS_ReportErrorASCII( 977 cx, "xpconnect getter/setter called on incompatible non-object"); 978 return false; 979 } 980 RootedObject obj(cx, &args.thisv().toObject()); 981 982 obj = FixUpThisIfBroken(obj, funobj); 983 XPCCallContext ccx(cx, obj, funobj, JS::VoidHandlePropertyKey, args.length(), 984 args.array(), vp); 985 XPCWrappedNative* wrapper = ccx.GetWrapper(); 986 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 987 988 RefPtr<XPCNativeInterface> iface; 989 XPCNativeMember* member; 990 991 if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member)) { 992 return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx); 993 } 994 995 if (args.length() != 0 && member->IsWritableAttribute()) { 996 ccx.SetCallInfo(iface, member, true); 997 bool retval = XPCWrappedNative::SetAttribute(ccx); 998 if (retval) { 999 args.rval().set(args[0]); 1000 } 1001 return retval; 1002 } 1003 // else... 1004 1005 ccx.SetCallInfo(iface, member, false); 1006 return XPCWrappedNative::GetAttribute(ccx); 1007 } 1008 1009 /***************************************************************************/ 1010 1011 /* static */ 1012 XPCWrappedNativeProto* XPCWrappedNativeProto::Get(JSObject* obj) { 1013 MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass); 1014 return JS::GetMaybePtrFromReservedSlot<XPCWrappedNativeProto>(obj, ProtoSlot); 1015 } 1016 1017 /* static */ 1018 XPCWrappedNativeTearOff* XPCWrappedNativeTearOff::Get(JSObject* obj) { 1019 MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Tearoff_JSClass); 1020 return JS::GetMaybePtrFromReservedSlot<XPCWrappedNativeTearOff>(obj, 1021 TearOffSlot); 1022 } 1023 1024 static bool XPC_WN_Proto_Enumerate(JSContext* cx, HandleObject obj) { 1025 MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass, "bad proto"); 1026 XPCWrappedNativeProto* self = XPCWrappedNativeProto::Get(obj); 1027 if (!self) { 1028 return false; 1029 } 1030 1031 XPCNativeSet* set = self->GetSet(); 1032 if (!set) { 1033 return false; 1034 } 1035 1036 XPCCallContext ccx(cx); 1037 if (!ccx.IsValid()) { 1038 return false; 1039 } 1040 1041 uint16_t interface_count = set->GetInterfaceCount(); 1042 XPCNativeInterface** interfaceArray = set->GetInterfaceArray(); 1043 for (uint16_t i = 0; i < interface_count; i++) { 1044 XPCNativeInterface* iface = interfaceArray[i]; 1045 uint16_t member_count = iface->GetMemberCount(); 1046 1047 for (uint16_t k = 0; k < member_count; k++) { 1048 jsid name = iface->GetMemberAt(k)->GetName(); 1049 JS_MarkCrossZoneId(cx, name); 1050 if (!xpc_ForcePropertyResolve(cx, obj, name)) { 1051 return false; 1052 } 1053 } 1054 } 1055 1056 return true; 1057 } 1058 1059 static void XPC_WN_Proto_Finalize(JS::GCContext* gcx, JSObject* obj) { 1060 // This can be null if xpc shutdown has already happened 1061 XPCWrappedNativeProto* p = XPCWrappedNativeProto::Get(obj); 1062 if (p) { 1063 p->JSProtoObjectFinalized(gcx, obj); 1064 } 1065 } 1066 1067 static size_t XPC_WN_Proto_ObjectMoved(JSObject* obj, JSObject* old) { 1068 // This can be null if xpc shutdown has already happened 1069 XPCWrappedNativeProto* p = XPCWrappedNativeProto::Get(obj); 1070 if (!p) { 1071 return 0; 1072 } 1073 1074 p->JSProtoObjectMoved(obj, old); 1075 return 0; 1076 } 1077 1078 /*****************************************************/ 1079 1080 static bool XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx, 1081 HandleObject obj, 1082 HandleId id, 1083 HandleValue v) { 1084 MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass, "bad proto"); 1085 1086 XPCWrappedNativeProto* self = XPCWrappedNativeProto::Get(obj); 1087 if (!self) { 1088 return false; 1089 } 1090 1091 XPCCallContext ccx(cx); 1092 if (!ccx.IsValid()) { 1093 return false; 1094 } 1095 1096 // Allow XPConnect to add the property only 1097 if (ccx.GetResolveName() == id) { 1098 return true; 1099 } 1100 1101 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); 1102 } 1103 1104 static bool XPC_WN_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, 1105 bool* resolvedp) { 1106 MOZ_ASSERT(JS::GetClass(obj) == &XPC_WN_Proto_JSClass, "bad proto"); 1107 1108 XPCWrappedNativeProto* self = XPCWrappedNativeProto::Get(obj); 1109 if (!self) { 1110 return false; 1111 } 1112 1113 XPCCallContext ccx(cx); 1114 if (!ccx.IsValid()) { 1115 return false; 1116 } 1117 1118 nsCOMPtr<nsIXPCScriptable> scr = self->GetScriptable(); 1119 1120 return DefinePropertyIfFound( 1121 ccx, obj, id, self->GetSet(), nullptr, nullptr, self->GetScope(), true, 1122 nullptr, nullptr, scr, 1123 JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE, resolvedp); 1124 } 1125 1126 static const JSClassOps XPC_WN_Proto_JSClassOps = { 1127 XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty 1128 XPC_WN_CannotDeletePropertyStub, // delProperty 1129 XPC_WN_Proto_Enumerate, // enumerate 1130 nullptr, // newEnumerate 1131 XPC_WN_Proto_Resolve, // resolve 1132 nullptr, // mayResolve 1133 XPC_WN_Proto_Finalize, // finalize 1134 nullptr, // call 1135 nullptr, // construct 1136 nullptr, // trace 1137 }; 1138 1139 static const js::ClassExtension XPC_WN_Proto_ClassExtension = { 1140 XPC_WN_Proto_ObjectMoved, // objectMovedOp 1141 }; 1142 1143 const JSClass XPC_WN_Proto_JSClass = { 1144 "XPC_WN_Proto_JSClass", 1145 JSCLASS_HAS_RESERVED_SLOTS(XPCWrappedNativeProto::SlotCount) | 1146 JSCLASS_FOREGROUND_FINALIZE, 1147 &XPC_WN_Proto_JSClassOps, 1148 JS_NULL_CLASS_SPEC, 1149 &XPC_WN_Proto_ClassExtension, 1150 JS_NULL_OBJECT_OPS}; 1151 1152 /***************************************************************************/ 1153 1154 static bool XPC_WN_TearOff_Enumerate(JSContext* cx, HandleObject obj) { 1155 XPCCallContext ccx(cx, obj); 1156 XPCWrappedNative* wrapper = ccx.GetWrapper(); 1157 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 1158 1159 XPCWrappedNativeTearOff* to = ccx.GetTearOff(); 1160 XPCNativeInterface* iface; 1161 1162 if (!to || nullptr == (iface = to->GetInterface())) { 1163 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); 1164 } 1165 1166 uint16_t member_count = iface->GetMemberCount(); 1167 for (uint16_t k = 0; k < member_count; k++) { 1168 jsid name = iface->GetMemberAt(k)->GetName(); 1169 JS_MarkCrossZoneId(cx, name); 1170 if (!xpc_ForcePropertyResolve(cx, obj, name)) { 1171 return false; 1172 } 1173 } 1174 1175 return true; 1176 } 1177 1178 static bool XPC_WN_TearOff_Resolve(JSContext* cx, HandleObject obj, HandleId id, 1179 bool* resolvedp) { 1180 XPCCallContext ccx(cx, obj); 1181 XPCWrappedNative* wrapper = ccx.GetWrapper(); 1182 THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); 1183 1184 XPCWrappedNativeTearOff* to = ccx.GetTearOff(); 1185 XPCNativeInterface* iface; 1186 1187 if (!to || nullptr == (iface = to->GetInterface())) { 1188 return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); 1189 } 1190 1191 return DefinePropertyIfFound( 1192 ccx, obj, id, nullptr, iface, nullptr, wrapper->GetScope(), true, nullptr, 1193 nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE, 1194 resolvedp); 1195 } 1196 1197 static void XPC_WN_TearOff_Finalize(JS::GCContext* gcx, JSObject* obj) { 1198 XPCWrappedNativeTearOff* p = XPCWrappedNativeTearOff::Get(obj); 1199 if (!p) { 1200 return; 1201 } 1202 p->JSObjectFinalized(); 1203 } 1204 1205 static size_t XPC_WN_TearOff_ObjectMoved(JSObject* obj, JSObject* old) { 1206 XPCWrappedNativeTearOff* p = XPCWrappedNativeTearOff::Get(obj); 1207 if (!p) { 1208 return 0; 1209 } 1210 p->JSObjectMoved(obj, old); 1211 return 0; 1212 } 1213 1214 static const JSClassOps XPC_WN_Tearoff_JSClassOps = { 1215 XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty 1216 XPC_WN_CannotDeletePropertyStub, // delProperty 1217 XPC_WN_TearOff_Enumerate, // enumerate 1218 nullptr, // newEnumerate 1219 XPC_WN_TearOff_Resolve, // resolve 1220 nullptr, // mayResolve 1221 XPC_WN_TearOff_Finalize, // finalize 1222 nullptr, // call 1223 nullptr, // construct 1224 nullptr, // trace 1225 }; 1226 1227 static const js::ClassExtension XPC_WN_Tearoff_JSClassExtension = { 1228 XPC_WN_TearOff_ObjectMoved, // objectMovedOp 1229 }; 1230 1231 const JSClass XPC_WN_Tearoff_JSClass = { 1232 "WrappedNative_TearOff", 1233 JSCLASS_HAS_RESERVED_SLOTS(XPCWrappedNativeTearOff::SlotCount) | 1234 JSCLASS_FOREGROUND_FINALIZE, 1235 &XPC_WN_Tearoff_JSClassOps, JS_NULL_CLASS_SPEC, 1236 &XPC_WN_Tearoff_JSClassExtension};