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

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

mercurial