tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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};