Back to index

lightning-sunbird  0.9+nobinonly
XPCNativeWrapper.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=80: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * The Mozilla Foundation.
00020  * Portions created by the Initial Developer are Copyright (C) 2005
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Johnny Stenback <jst@mozilla.org> (original author)
00025  *   Brendan Eich <brendan@mozilla.org>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "xpcprivate.h"
00042 #include "XPCNativeWrapper.h"
00043 #include "jsdbgapi.h"
00044 
00045 JS_STATIC_DLL_CALLBACK(JSBool)
00046 XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00047 
00048 JS_STATIC_DLL_CALLBACK(JSBool)
00049 XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00050 
00051 JS_STATIC_DLL_CALLBACK(JSBool)
00052 XPC_NW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00053 
00054 JS_STATIC_DLL_CALLBACK(JSBool)
00055 XPC_NW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00056 
00057 JS_STATIC_DLL_CALLBACK(JSBool)
00058 XPC_NW_Enumerate(JSContext *cx, JSObject *obj);
00059 
00060 JS_STATIC_DLL_CALLBACK(JSBool)
00061 XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
00062                   JSObject **objp);
00063 
00064 JS_STATIC_DLL_CALLBACK(JSBool)
00065 XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
00066 
00067 JS_STATIC_DLL_CALLBACK(void)
00068 XPC_NW_Finalize(JSContext *cx, JSObject *obj);
00069 
00070 JS_STATIC_DLL_CALLBACK(JSBool)
00071 XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsval id,
00072                    JSAccessMode mode, jsval *vp);
00073 
00074 JS_STATIC_DLL_CALLBACK(JSBool)
00075 XPC_NW_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00076             jsval *rval);
00077 
00078 JS_STATIC_DLL_CALLBACK(JSBool)
00079 XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00080                  jsval *rval);
00081 
00082 JS_STATIC_DLL_CALLBACK(JSBool)
00083 XPC_NW_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
00084 
00085 JS_STATIC_DLL_CALLBACK(uint32)
00086 XPC_NW_Mark(JSContext *cx, JSObject *obj, void *arg);
00087 
00088 JS_STATIC_DLL_CALLBACK(JSBool)
00089 XPC_NW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
00090 
00091 static JSBool
00092 RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval);
00093 
00094 JS_STATIC_DLL_CALLBACK(JSBool)
00095 XPC_NW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00096                        jsval *rval);
00097 
00098 // JS class for XPCNativeWrapper (and this doubles as the constructor
00099 // for XPCNativeWrapper for the moment too...)
00100 
00101 JSExtendedClass XPCNativeWrapper::sXPC_NW_JSClass = {
00102   // JSClass (JSExtendedClass.base) initialization
00103   { "XPCNativeWrapper",
00104     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
00105     // Our one reserved slot holds a jsint of flag bits
00106     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
00107     JSCLASS_IS_EXTENDED,
00108     XPC_NW_AddProperty, XPC_NW_DelProperty,
00109     XPC_NW_GetProperty, XPC_NW_SetProperty,
00110     XPC_NW_Enumerate,   (JSResolveOp)XPC_NW_NewResolve,
00111     XPC_NW_Convert,     XPC_NW_Finalize,
00112     nsnull,             XPC_NW_CheckAccess,
00113     XPC_NW_Call,        XPC_NW_Construct,
00114     nsnull,             XPC_NW_HasInstance,
00115     XPC_NW_Mark,        nsnull
00116   },
00117   // JSExtendedClass initialization
00118   XPC_NW_Equality
00119 };
00120 
00121 #define FLAG_DEEP     0x1
00122 #define FLAG_EXPLICIT 0x2
00123 // FLAG_RESOLVING is used to tag an XPCNativeWrapper when while it's calling
00124 // the newResolve hook on the XPCWrappedNative's scriptable info.
00125 #define FLAG_RESOLVING 0x4
00126 
00127 #define HAS_FLAGS(_val, _flags) \
00128   ((PRUint32(JSVAL_TO_INT(_val)) & (_flags)) != 0)
00129 
00130 #define NATIVE_HAS_FLAG(_wn, _flag)                \
00131   ((_wn)->GetScriptableInfo() &&                   \
00132    (_wn)->GetScriptableInfo()->GetFlags()._flag())
00133 
00134 // If one of our class hooks is ever called from a non-system script, bypass
00135 // the hook by calling the same hook on our wrapped native, with obj reset to
00136 // the wrapped native's flat JSObject, so the hook and args macro parameters
00137 // can be simply:
00138 //
00139 //      convert, (cx, obj, type, vp)
00140 //
00141 // in the call from XPC_NW_Convert, for example.
00142 
00143 #define XPC_NW_CALL_HOOK(cx, obj, hook, args)                                 \
00144   return JS_GET_CLASS(cx, obj)->hook args;
00145 
00146 #define XPC_NW_CAST_HOOK(cx, obj, type, hook, args)                           \
00147   return ((type) JS_GET_CLASS(cx, obj)->hook) args;
00148 
00149 static JSBool
00150 ShouldBypassNativeWrapper(JSContext *cx, JSObject *obj)
00151 {
00152   NS_ASSERTION(XPCNativeWrapper::IsNativeWrapper(cx, obj),
00153                "Unexpected object");
00154   jsval flags;
00155 
00156   ::JS_GetReservedSlot(cx, obj, 0, &flags);
00157   if (HAS_FLAGS(flags, FLAG_EXPLICIT))
00158     return JS_FALSE;
00159 
00160   // Check what the script calling us looks like
00161   JSScript *script = nsnull;
00162   JSStackFrame *fp = cx->fp;
00163   while(!script && fp) {
00164     script = fp->script;
00165     fp = fp->down;
00166   }
00167 
00168   // If there's no script, bypass for now because that's what the old code did.
00169   // XXX FIXME: bug 341477 covers figuring out what we _should_ do.
00170   return !script || !(::JS_GetScriptFilenameFlags(script) & JSFILENAME_SYSTEM);
00171 }
00172 
00173 #define XPC_NW_BYPASS_BASE(cx, obj, code)                                     \
00174   JS_BEGIN_MACRO                                                              \
00175     if (ShouldBypassNativeWrapper(cx, obj)) {                                 \
00176       XPCWrappedNative *wn_ = XPCNativeWrapper::GetWrappedNative(cx, obj);    \
00177       if (!wn_) {                                                             \
00178         return JS_TRUE;                                                       \
00179       }                                                                       \
00180       obj = wn_->GetFlatJSObject();                                           \
00181       code                                                                    \
00182     }                                                                         \
00183   JS_END_MACRO
00184 
00185 #define XPC_NW_BYPASS(cx, obj, hook, args)                                    \
00186   XPC_NW_BYPASS_BASE(cx, obj, XPC_NW_CALL_HOOK(cx, obj, hook, args))
00187 
00188 #define XPC_NW_BYPASS_CAST(cx, obj, type, hook, args)                         \
00189   XPC_NW_BYPASS_BASE(cx, obj, XPC_NW_CAST_HOOK(cx, obj, type, hook, args))
00190 
00191 #define XPC_NW_BYPASS_TEST(cx, obj, hook, args)                               \
00192   XPC_NW_BYPASS_BASE(cx, obj,                                                 \
00193     JSClass *clasp_ = JS_GET_CLASS(cx, obj);                                  \
00194     return !clasp_->hook || clasp_->hook args;                                \
00195   )
00196 
00197 JS_STATIC_DLL_CALLBACK(JSBool)
00198 XPC_NW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00199                 jsval *rval);
00200 
00201 JS_STATIC_DLL_CALLBACK(JSBool)
00202 XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00203                      jsval *rval);
00204 
00205 static inline
00206 JSBool
00207 ThrowException(nsresult ex, JSContext *cx)
00208 {
00209   XPCThrower::Throw(ex, cx);
00210 
00211   return JS_FALSE;
00212 }
00213 
00214 static inline
00215 jsval
00216 GetStringByIndex(JSContext *cx, uintN index)
00217 {
00218   XPCJSRuntime *rt = nsXPConnect::GetRuntime();
00219 
00220   if (!rt)
00221     return JSVAL_VOID;
00222 
00223   return ID_TO_VALUE(rt->GetStringID(index));
00224 }
00225 
00226 static inline
00227 already_AddRefed<nsIScriptSecurityManager>
00228 GetSecurityManager(JSContext *cx)
00229 {
00230   XPCCallContext ccx(JS_CALLER, cx);
00231   nsIXPCSecurityManager *sm = ccx.GetXPCContext()->
00232     GetAppropriateSecurityManager(nsIXPCSecurityManager::HOOK_CALL_METHOD);
00233   nsCOMPtr<nsIScriptSecurityManager> ssm(do_QueryInterface(sm));
00234 
00235   nsIScriptSecurityManager *rval = nsnull;
00236   ssm.swap(rval);
00237   return rval;
00238 }
00239 
00240 static inline
00241 JSBool
00242 EnsureLegalActivity(JSContext *cx, JSObject *obj)
00243 {
00244   jsval flags;
00245 
00246   ::JS_GetReservedSlot(cx, obj, 0, &flags);
00247   if (HAS_FLAGS(flags, FLAG_EXPLICIT)) {
00248     // Can't make any assertions about the owner of this wrapper.
00249     return JS_TRUE;
00250   }
00251 
00252   JSStackFrame *frame = nsnull;
00253   uint32 fileFlags = JS_GetTopScriptFilenameFlags(cx, NULL);
00254   if (!JS_FrameIterator(cx, &frame) ||
00255       fileFlags == JSFILENAME_NULL ||
00256       (fileFlags & JSFILENAME_SYSTEM)) {
00257     // We expect implicit native wrappers in system files.
00258     return JS_TRUE;
00259   }
00260 
00261   nsCOMPtr<nsIScriptSecurityManager> ssm(GetSecurityManager(cx));
00262   if (!ssm) {
00263     // If there's no security manager, then we're not running in a browser
00264     // context: allow access.
00265     return JS_TRUE;
00266   }
00267 
00268   // A last ditch effort to allow access: if the currently-running code
00269   // has UniversalXPConnect privileges, then allow access.
00270   PRBool isPrivileged;
00271   nsresult rv = ssm->IsCapabilityEnabled("UniversalXPConnect", &isPrivileged);
00272   if (NS_SUCCEEDED(rv) && isPrivileged) {
00273     return JS_TRUE;
00274   }
00275 
00276   // Otherwise, we're looking at a non-system file with a handle on an
00277   // implcit wrapper. This is a bug! Deny access.
00278   return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx);
00279 }
00280 
00281 static JSBool
00282 WrapFunction(JSContext* cx, JSObject* funobj, jsval *rval)
00283 {
00284   // If funobj is already a wrapped function, just return it.
00285   if (JS_GetFunctionNative(cx,
00286                            JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj))) ==
00287       XPC_NW_FunctionWrapper) {
00288     *rval = OBJECT_TO_JSVAL(funobj);
00289     return JS_TRUE;
00290   }
00291 
00292   // Ensure that we've been called from JS. Native code should extract
00293   // the wrapped native and deal with that directly.
00294   // XXX Can we simply trust |cx| here?
00295   JSStackFrame *iterator = nsnull;
00296   if (!::JS_FrameIterator(cx, &iterator)) {
00297     ::JS_ReportError(cx, "XPCNativeWrappers must be used from script");
00298     return nsnull;
00299   }
00300   
00301   // Create a new function that'll call our given function.  This new
00302   // function's parent will be the original function and that's how we
00303   // get the right thing to call when this function is called.
00304   // Note that we pass nsnull as the nominal parent so that we'll inherit
00305   // our caller's Function.prototype.
00306   JSFunction *funWrapper =
00307     ::JS_NewFunction(cx, XPC_NW_FunctionWrapper, 0, 0, nsnull,
00308                      "XPCNativeWrapper function wrapper");
00309   if (!funWrapper) {
00310     return JS_FALSE;
00311   }
00312 
00313   JSObject* funWrapperObj = ::JS_GetFunctionObject(funWrapper);
00314   ::JS_SetParent(cx, funWrapperObj, funobj);
00315   *rval = OBJECT_TO_JSVAL(funWrapperObj);
00316   return JS_TRUE;
00317 }
00318 
00319 JS_STATIC_DLL_CALLBACK(JSBool)
00320 XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00321 {
00322   JSProperty *prop;
00323   JSObject *objp;
00324   jsid idAsId;
00325 
00326   if (!::JS_ValueToId(cx, id, &idAsId) ||
00327       !OBJ_LOOKUP_PROPERTY(cx, obj, idAsId, &objp, &prop)) {
00328     return JS_FALSE;
00329   }
00330 
00331   // Do not allow scripted getters or setters on XPCNativeWrappers.
00332   NS_ASSERTION(prop && objp == obj, "Wasn't this property just added?");
00333   JSScopeProperty *sprop = (JSScopeProperty *) prop;
00334   if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
00335     OBJ_DROP_PROPERTY(cx, objp, prop);
00336     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
00337   }
00338 
00339   OBJ_DROP_PROPERTY(cx, objp, prop);
00340 
00341   jsval flags;
00342   ::JS_GetReservedSlot(cx, obj, 0, &flags);
00343   if (!HAS_FLAGS(flags, FLAG_RESOLVING)) {
00344     return JS_TRUE;
00345   }
00346 
00347   // Note: no need to protect *vp from GC here, since it's already in the slot
00348   // on |obj|.
00349   return EnsureLegalActivity(cx, obj) &&
00350          RewrapIfDeepWrapper(cx, obj, *vp, vp);
00351 }
00352 
00353 JS_STATIC_DLL_CALLBACK(JSBool)
00354 XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00355 {
00356   if (!EnsureLegalActivity(cx, obj)) {
00357     return JS_FALSE;
00358   }
00359 
00360   XPC_NW_BYPASS_BASE(cx, obj,
00361     // We're being notified of a delete operation on id in this
00362     // XPCNativeWrapper, so forward to the right high-level hook,
00363     // OBJ_DELETE_PROPERTY, on the XPCWrappedNative's object.
00364     {
00365       jsid interned_id;
00366 
00367       if (!::JS_ValueToId(cx, id, &interned_id)) {
00368         return JS_FALSE;
00369       }
00370 
00371       return OBJ_DELETE_PROPERTY(cx, obj, interned_id, vp);
00372     }
00373   );
00374 
00375   return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx);
00376 }
00377 
00378 static JSBool
00379 RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval)
00380 {
00381   NS_ASSERTION(XPCNativeWrapper::IsNativeWrapper(cx, obj),
00382                "Unexpected object");
00383 
00384   JSBool primitive = JSVAL_IS_PRIMITIVE(v);
00385   JSObject* nativeObj = primitive ? nsnull : JSVAL_TO_OBJECT(v);
00386   
00387   // We always want to wrap function objects, no matter whether we're deep.
00388   if (!primitive && JS_ObjectIsFunction(cx, nativeObj)) {
00389     return WrapFunction(cx, nativeObj, rval);
00390   }
00391 
00392   jsval flags;
00393   ::JS_GetReservedSlot(cx, obj, 0, &flags);
00394 
00395   // Re-wrap non-primitive values if this is a deep wrapper, i.e.
00396   // if (HAS_FLAGS(flags, FLAG_DEEP).
00397   if (HAS_FLAGS(flags, FLAG_DEEP) && !primitive) {
00398 
00399     XPCWrappedNative* wrappedNative =
00400       XPCWrappedNative::GetWrappedNativeOfJSObject(cx, nativeObj);
00401     if (!wrappedNative) {
00402       // Not something we can protect... just make it JSVAL_NULL
00403       *rval = JSVAL_NULL;
00404       return JS_TRUE;
00405     }
00406 
00407     if (HAS_FLAGS(flags, FLAG_EXPLICIT)) {
00408 #ifdef DEBUG_XPCNativeWrapper
00409       printf("Rewrapping for deep explicit wrapper\n");
00410 #endif
00411       if (wrappedNative == XPCNativeWrapper::GetWrappedNative(cx, obj)) {
00412         // Already wrapped, return the wrapper.
00413         *rval = OBJECT_TO_JSVAL(obj);
00414         return JS_TRUE;
00415       }
00416 
00417       // |obj| is an explicit deep wrapper.  We want to construct another
00418       // explicit deep wrapper for |v|.  Just call XPCNativeWrapperCtor by hand
00419       // (passing null as the pre-created object it doesn't use anyway) so we
00420       // don't have to create an object we never use.
00421 
00422       return XPCNativeWrapperCtor(cx, nsnull, 1, &v, rval);
00423     }
00424     
00425 #ifdef DEBUG_XPCNativeWrapper
00426     printf("Rewrapping for deep implicit wrapper\n");
00427 #endif
00428     // Just using GetNewOrUsed on the return value of
00429     // GetWrappedNativeOfJSObject will give the right thing -- the unique deep
00430     // implicit wrapper associated with wrappedNative.
00431     JSObject* wrapperObj = XPCNativeWrapper::GetNewOrUsed(cx, wrappedNative,
00432                                                           nsnull);
00433     if (!wrapperObj) {
00434       return JS_FALSE;
00435     }
00436 
00437     *rval = OBJECT_TO_JSVAL(wrapperObj);
00438   } else {
00439     *rval = v;
00440   }
00441 
00442   return JS_TRUE;
00443 }
00444 
00445 JS_STATIC_DLL_CALLBACK(JSBool)
00446 XPC_NW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00447                        jsval *rval)
00448 {
00449   JSObject *funObj = JSVAL_TO_OBJECT(argv[-2]);
00450   if (!::JS_ObjectIsFunction(cx, funObj)) {
00451     obj = nsnull;
00452   }
00453 
00454   while (obj && !XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
00455     obj = ::JS_GetPrototype(cx, obj);
00456   }
00457 
00458   if (!obj) {
00459     return ThrowException(NS_ERROR_UNEXPECTED, cx);
00460   }
00461 
00462   // The real method we're going to call is the parent of this
00463   // function's JSObject.
00464   JSObject *methodToCallObj = ::JS_GetParent(cx, funObj);
00465   XPCWrappedNative *wrappedNative =
00466     XPCNativeWrapper::GetWrappedNative(cx, obj);
00467 
00468   if (!::JS_ObjectIsFunction(cx, methodToCallObj) || !wrappedNative) {
00469     return ThrowException(NS_ERROR_UNEXPECTED, cx);
00470   }
00471 
00472   jsval v;
00473   if (!::JS_CallFunctionValue(cx, wrappedNative->GetFlatJSObject(),
00474                               OBJECT_TO_JSVAL(methodToCallObj), argc, argv,
00475                               &v)) {
00476     return JS_FALSE;
00477   }
00478 
00479   XPCCallContext ccx(JS_CALLER, cx, obj);
00480 
00481   // Make sure v doesn't get collected while we're re-wrapping it.
00482   AUTO_MARK_JSVAL(ccx, v);
00483 
00484   return RewrapIfDeepWrapper(cx, obj, v, rval);
00485 }
00486 
00487 static JSBool
00488 XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp,
00489                         JSBool aIsSet)
00490 {
00491   // We don't deal with the following properties here.
00492   if (id == GetStringByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE) ||
00493       id == GetStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) {
00494     return JS_TRUE;
00495   }
00496 
00497   while (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
00498     obj = ::JS_GetPrototype(cx, obj);
00499     if (!obj) {
00500       return ThrowException(NS_ERROR_UNEXPECTED, cx);
00501     }
00502   }
00503 
00504   if (!EnsureLegalActivity(cx, obj)) {
00505     return JS_FALSE;
00506   }
00507 
00508   XPCWrappedNative *wrappedNative =
00509     XPCNativeWrapper::GetWrappedNative(cx, obj);
00510 
00511   if (!wrappedNative) {
00512     return ThrowException(NS_ERROR_INVALID_ARG, cx);
00513   }
00514 
00515   JSObject *nativeObj = wrappedNative->GetFlatJSObject();
00516 
00517   // We can't use XPC_NW_BYPASS here, because we need to do a full
00518   // OBJ_SET_PROPERTY or OBJ_GET_PROPERTY on the wrapped native's
00519   // object, in order to trigger reflection done by the underlying
00520   // OBJ_LOOKUP_PROPERTY done by SET and GET.
00521 
00522   if (ShouldBypassNativeWrapper(cx, obj)) {
00523     jsid interned_id;
00524 
00525     if (!::JS_ValueToId(cx, id, &interned_id)) {
00526       return JS_FALSE;
00527     }
00528 
00529     return aIsSet
00530            ? OBJ_SET_PROPERTY(cx, nativeObj, interned_id, vp)
00531            : OBJ_GET_PROPERTY(cx, nativeObj, interned_id, vp);
00532   }
00533 
00534   if (!aIsSet &&
00535       id == GetStringByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) {
00536     // Return the underlying native object, the XPConnect wrapped
00537     // object that this additional wrapper layer wraps.
00538 
00539     *vp = OBJECT_TO_JSVAL(nativeObj);
00540 
00541     return JS_TRUE;
00542   }
00543 
00544   // This will do verification and the method lookup for us.
00545   XPCCallContext ccx(JS_CALLER, cx, nativeObj, nsnull, id);
00546 
00547   if (aIsSet ? NATIVE_HAS_FLAG(wrappedNative, WantSetProperty) :
00548                NATIVE_HAS_FLAG(wrappedNative, WantGetProperty)) {
00549 
00550     jsval v = *vp;
00551     // Note that some sets return random DOM objects (setting
00552     // document.location, say), so we want to rewrap for sets too if v != *vp.
00553     JSBool retval = JS_TRUE;
00554     nsresult rv;
00555     if (aIsSet) {
00556       rv = wrappedNative->GetScriptableCallback()->
00557         SetProperty(wrappedNative, cx, obj, id, &v, &retval);
00558     } else {
00559       rv = wrappedNative->GetScriptableCallback()->
00560         GetProperty(wrappedNative, cx, obj, id, &v, &retval);
00561     }
00562     
00563     if (NS_FAILED(rv)) {
00564       return ThrowException(rv, cx);
00565     }
00566     if (!retval) {
00567       return JS_FALSE;
00568     }
00569 
00570     if (rv == NS_SUCCESS_I_DID_SOMETHING) {
00571       // Make sure v doesn't get collected while we're re-wrapping it.
00572       AUTO_MARK_JSVAL(ccx, v);
00573 
00574 #ifdef DEBUG_XPCNativeWrapper
00575       JSString* strId = ::JS_ValueToString(cx, id);
00576       if (strId) {
00577         NS_ConvertUTF16toUTF8 propName((PRUnichar*)::JS_GetStringChars(strId),
00578                                        ::JS_GetStringLength(strId));
00579         printf("%s via scriptable hooks for '%s'\n",
00580                aIsSet ? "Set" : "Got", propName.get());
00581       }
00582 #endif
00583 
00584       return RewrapIfDeepWrapper(cx, obj, v, vp);
00585     }
00586   }
00587   
00588   if (!JSVAL_IS_STRING(id)) {
00589     // Not going to be found here
00590     return JS_TRUE;
00591   }
00592 
00593   // Verify that our jsobject really is a wrapped native.
00594   XPCWrappedNative* wrapper = ccx.GetWrapper();
00595   if (wrapper != wrappedNative || !wrapper->IsValid()) {
00596     NS_ASSERTION(wrapper == wrappedNative, "Uh, how did this happen!");
00597     return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
00598   }
00599 
00600   // it would be a big surprise if there is a member without an
00601   // interface :)
00602   XPCNativeInterface* iface = ccx.GetInterface();
00603   if (!iface) {
00604 
00605     return JS_TRUE;
00606   }
00607 
00608   // did we find a method/attribute by that name?
00609   XPCNativeMember* member = ccx.GetMember();
00610   NS_ASSERTION(member, "not doing IDispatch, how'd this happen?");
00611   if (!member) {
00612     // No member, no IDL property to expose.
00613 
00614     return JS_TRUE;
00615   }
00616 
00617   // Get (and perhaps lazily create) the member's value (commonly a
00618   // cloneable function).
00619   jsval memberval;
00620   if (!member->GetValue(ccx, iface, &memberval)) {
00621     return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
00622   }
00623 
00624   if (member->IsConstant()) {
00625     // Getting the value of constants is easy, just return the
00626     // value. Setting is not supported (obviously).
00627     if (aIsSet) {
00628       return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
00629     }
00630 
00631     *vp = memberval;
00632 
00633     return JS_TRUE;
00634   }
00635 
00636   if (!member->IsAttribute()) {
00637     // Getting the value of a method. Just return and let the value
00638     // from XPC_NW_NewResolve() be used.
00639 
00640     return JS_TRUE;
00641   }
00642 
00643   // Make sure the function we're cloning doesn't go away while
00644   // we're cloning it.
00645   AUTO_MARK_JSVAL(ccx, memberval);
00646 
00647   // clone a function we can use for this object
00648   JSObject* funobj = xpc_CloneJSFunction(ccx, JSVAL_TO_OBJECT(memberval),
00649                                          wrapper->GetFlatJSObject());
00650   if (!funobj) {
00651     return JS_FALSE;
00652   }
00653 
00654   jsval *argv = nsnull;
00655   uintN argc = 0;
00656 
00657   if (aIsSet) {
00658     if (member->IsReadOnlyAttribute()) {
00659       // Trying to set a property for which there is no setter!
00660       return ThrowException(NS_ERROR_NOT_AVAILABLE, cx);
00661     }
00662 
00663 #ifdef DEBUG_XPCNativeWrapper
00664     printf("Calling setter for %s\n",
00665            ::JS_GetStringBytes(JSVAL_TO_STRING(id)));
00666 #endif
00667 
00668     argv = vp;
00669     argc = 1;
00670   } else {
00671 #ifdef DEBUG_XPCNativeWrapper
00672     printf("Calling getter for %s\n",
00673            ::JS_GetStringBytes(JSVAL_TO_STRING(id)));
00674 #endif
00675   }
00676 
00677   // Call the getter
00678   jsval v;
00679   if (!::JS_CallFunctionValue(cx, wrapper->GetFlatJSObject(),
00680                               OBJECT_TO_JSVAL(funobj), argc, argv, &v)) {
00681     return JS_FALSE;
00682   }
00683 
00684   if (aIsSet) {
00685     return JS_TRUE;
00686   }
00687 
00688   {
00689     // Make sure v doesn't get collected while we're re-wrapping it.
00690     AUTO_MARK_JSVAL(ccx, v);
00691 
00692     return RewrapIfDeepWrapper(cx, obj, v, vp);
00693   }
00694 }
00695 
00696 JS_STATIC_DLL_CALLBACK(JSBool)
00697 XPC_NW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00698 {
00699   return XPC_NW_GetOrSetProperty(cx, obj, id, vp, PR_FALSE);
00700 }
00701 
00702 JS_STATIC_DLL_CALLBACK(JSBool)
00703 XPC_NW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00704 {
00705   return XPC_NW_GetOrSetProperty(cx, obj, id, vp, PR_TRUE);
00706 }
00707 
00708 JS_STATIC_DLL_CALLBACK(JSBool)
00709 XPC_NW_Enumerate(JSContext *cx, JSObject *obj)
00710 {
00711   // We are being notified of a for-in loop or similar operation on this
00712   // XPCNativeWrapper, so forward to the correct high-level object hook,
00713   // OBJ_ENUMERATE on the XPCWrappedNative's object, called via the
00714   // JS_Enumerate API.  Then reflect properties named by the enumerated
00715   // identifiers from the wrapped native to the native wrapper.
00716 
00717   if (!EnsureLegalActivity(cx, obj)) {
00718     return JS_FALSE;
00719   }
00720 
00721   XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
00722   if (!wn) {
00723     return JS_TRUE;
00724   }
00725 
00726   JSIdArray *ida = JS_Enumerate(cx, wn->GetFlatJSObject());
00727   if (!ida) {
00728     return JS_FALSE;
00729   }
00730 
00731   JSBool ok = JS_TRUE;
00732 
00733   for (jsint i = 0, n = ida->length; i < n; i++) {
00734     JSObject *pobj;
00735     JSProperty *prop;
00736 
00737     // Let OBJ_LOOKUP_PROPERTY, in particular XPC_NW_NewResolve, figure
00738     // out whether this id should be bypassed or reflected.
00739     ok = OBJ_LOOKUP_PROPERTY(cx, obj, ida->vector[i], &pobj, &prop);
00740     if (!ok) {
00741       break;
00742     }
00743     if (prop) {
00744       OBJ_DROP_PROPERTY(cx, pobj, prop);
00745     }
00746   }
00747 
00748   JS_DestroyIdArray(cx, ida);
00749   return ok;
00750 }
00751 
00752 static
00753 JSBool MaybePreserveWrapper(JSContext* cx, XPCWrappedNative *wn, uintN flags)
00754 {
00755   if ((flags & JSRESOLVE_ASSIGNING) &&
00756       (::JS_GetOptions(cx) & JSOPTION_PRIVATE_IS_NSISUPPORTS)) {
00757     nsCOMPtr<nsIXPCScriptNotify> scriptNotify = 
00758       do_QueryInterface(NS_STATIC_CAST(nsISupports*,
00759                                        JS_GetContextPrivate(cx)));
00760     if (scriptNotify) {
00761       return NS_SUCCEEDED(scriptNotify->PreserveWrapper(wn));
00762     }
00763   }
00764   return JS_TRUE;
00765 }
00766 
00767 JS_STATIC_DLL_CALLBACK(JSBool)
00768 XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
00769                   JSObject **objp)
00770 {
00771   // No need to preserve on sets of wrappedJSObject or toString, since callers
00772   // couldn't get at those values anyway.  Also, we always deal with
00773   // wrappedJSObject and toString before looking at our scriptable hooks, so no
00774   // need to mess with our flags yet.
00775   if (id == GetStringByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) {
00776     return JS_TRUE;
00777   }
00778 
00779   if (!EnsureLegalActivity(cx, obj)) {
00780     return JS_FALSE;
00781   }
00782 
00783   if (id == GetStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) {
00784     *objp = obj;
00785     return JS_DefineFunction(cx, obj, "toString",
00786                              XPC_NW_toString, 0, 0) != nsnull;
00787   }
00788 
00789   // We can't use XPC_NW_BYPASS here, because we need to do a full
00790   // OBJ_LOOKUP_PROPERTY on the wrapped native's object, in order to
00791   // trigger reflection along the wrapped native prototype chain.
00792   // All we need to do is define the property in obj if it exists in
00793   // the wrapped native's object.
00794 
00795   if (ShouldBypassNativeWrapper(cx, obj)) {
00796     XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
00797     if (!wn) {
00798       return JS_TRUE;
00799     }
00800 
00801     jsid interned_id;
00802     JSObject *pobj;
00803     JSProperty *prop;
00804 
00805     if (!::JS_ValueToId(cx, id, &interned_id) ||
00806         !OBJ_LOOKUP_PROPERTY(cx, wn->GetFlatJSObject(), interned_id,
00807                              &pobj, &prop)) {
00808       return JS_FALSE;
00809     }
00810 
00811     if (prop) {
00812       OBJ_DROP_PROPERTY(cx, pobj, prop);
00813 
00814       if (!OBJ_DEFINE_PROPERTY(cx, obj, interned_id, JSVAL_VOID,
00815                                nsnull, nsnull, 0, nsnull)) {
00816         return JS_FALSE;
00817       }
00818 
00819       *objp = obj;
00820     }
00821     return JS_TRUE;
00822   }
00823 
00824   while (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
00825     obj = ::JS_GetPrototype(cx, obj);
00826     if (!obj) {
00827       return ThrowException(NS_ERROR_UNEXPECTED, cx);
00828     }
00829   }
00830 
00831   XPCWrappedNative *wrappedNative =
00832     XPCNativeWrapper::GetWrappedNative(cx, obj);
00833 
00834   if (!wrappedNative) {
00835     // No wrapped native, no properties.
00836 
00837     return JS_TRUE;
00838   }
00839 
00840   JSObject *nativeObj = wrappedNative->GetFlatJSObject();
00841 
00842   // This will do verification and the method lookup for us.
00843   XPCCallContext ccx(JS_CALLER, cx, nativeObj, nsnull, id);
00844 
00845   // For "constructor" we don't want to call into the resolve hooks on the
00846   // wrapped native, since that would give the wrong constructor.
00847   if (NATIVE_HAS_FLAG(wrappedNative, WantNewResolve) &&
00848       id != GetStringByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) {
00849 
00850     // Mark ourselves as resolving so our AddProperty hook can do the
00851     // right thing here.
00852     jsval oldFlags;
00853     ::JS_GetReservedSlot(cx, obj, 0, &oldFlags);
00854     if (!::JS_SetReservedSlot(cx, obj, 0,
00855                               INT_TO_JSVAL(JSVAL_TO_INT(oldFlags) |
00856                                            FLAG_RESOLVING))) {
00857       return JS_FALSE;
00858     }        
00859     
00860     XPCWrappedNative* oldResolvingWrapper = nsnull;
00861     JSBool allowPropMods =
00862       NATIVE_HAS_FLAG(wrappedNative, AllowPropModsDuringResolve);
00863     if (allowPropMods) {
00864       oldResolvingWrapper = ccx.SetResolvingWrapper(wrappedNative);
00865     }
00866       
00867     JSBool retval = JS_TRUE;
00868     JSObject* newObj = nsnull;
00869     nsresult rv = wrappedNative->GetScriptableInfo()->
00870       GetCallback()->NewResolve(wrappedNative, cx, obj, id, flags,
00871                                 &newObj, &retval);
00872 
00873     if (allowPropMods) {
00874       ccx.SetResolvingWrapper(oldResolvingWrapper);
00875     }
00876 
00877     if (!::JS_SetReservedSlot(cx, obj, 0, oldFlags)) {
00878       return JS_FALSE;
00879     }
00880     
00881     if (NS_FAILED(rv)) {
00882       return ThrowException(rv, cx);
00883     }
00884 
00885     if (newObj) {
00886 #ifdef DEBUG_XPCNativeWrapper
00887       JSString* strId = ::JS_ValueToString(cx, id);
00888       if (strId) {
00889         NS_ConvertUTF16toUTF8 propName((PRUnichar*)::JS_GetStringChars(strId),
00890                                        ::JS_GetStringLength(strId));
00891         printf("Resolved via scriptable hooks for '%s'\n", propName.get());
00892       }
00893 #endif
00894       // Note that we don't need to preserve the wrapper here, since this is
00895       // not an "expando" property if the scriptable newResolve hook found it.
00896       *objp = newObj;
00897       return retval;
00898     }      
00899   }
00900 
00901   if (!JSVAL_IS_STRING(id)) {
00902     // A non-string id is being resolved. Won't be found here, return
00903     // early.
00904 
00905     return MaybePreserveWrapper(cx, wrappedNative, flags);
00906   }
00907 
00908   // Verify that our jsobject really is a wrapped native.
00909   XPCWrappedNative* wrapper = ccx.GetWrapper();
00910   if (wrapper != wrappedNative || !wrapper->IsValid()) {
00911     NS_ASSERTION(wrapper == wrappedNative, "Uh, how did this happen!");
00912     return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
00913   }
00914 
00915   // it would be a big surprise if there is a member without an
00916   // interface :)
00917   XPCNativeInterface* iface = ccx.GetInterface();
00918   if (!iface) {
00919     // No interface, nothing to resolve.
00920 
00921     return MaybePreserveWrapper(cx, wrappedNative, flags);
00922   }
00923 
00924   // did we find a method/attribute by that name?
00925   XPCNativeMember* member = ccx.GetMember();
00926   NS_ASSERTION(member, "not doing IDispatch, how'd this happen?");
00927   if (!member) {
00928     // No member, nothing to resolve.
00929 
00930     return MaybePreserveWrapper(cx, wrappedNative, flags);
00931   }
00932 
00933   // Get (and perhaps lazily create) the member's value (commonly a
00934   // cloneable function).
00935   jsval memberval;
00936   if (!member->GetValue(ccx, iface, &memberval)) {
00937     return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
00938   }
00939 
00940   // Make sure memberval doesn't go away while we mess with it.
00941   AUTO_MARK_JSVAL(ccx, memberval);
00942   
00943   JSString *str = JSVAL_TO_STRING(id);
00944   if (!str) {
00945     return ThrowException(NS_ERROR_UNEXPECTED, cx);
00946   }
00947 
00948   jsval v;
00949   uintN attrs = JSPROP_ENUMERATE;
00950 
00951   if (member->IsConstant()) {
00952     v = memberval;
00953   } else if (member->IsAttribute()) {
00954     // An attribute is being resolved. Define the property, the value
00955     // will be dealt with in the get/set hooks.  Use JSPROP_SHARED to
00956     // avoid entraining last-got or last-set garbage beyond the life
00957     // of the value in the getter or setter call site.
00958 
00959     v = JSVAL_VOID;
00960     attrs |= JSPROP_SHARED;
00961   } else {
00962     // We're dealing with a method member here. Clone a function we can
00963     // use for this object.  NB: cx's newborn roots will protect funobj
00964     // and funWrapper and its object from GC.
00965 
00966     JSObject* funobj = xpc_CloneJSFunction(ccx, JSVAL_TO_OBJECT(memberval),
00967                                            wrapper->GetFlatJSObject());
00968     if (!funobj) {
00969       return JS_FALSE;
00970     }
00971 
00972     AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(funobj));
00973 
00974 #ifdef DEBUG_XPCNativeWrapper
00975     printf("Wrapping function object for %s\n",
00976            ::JS_GetStringBytes(JSVAL_TO_STRING(id)));
00977 #endif
00978 
00979     if (!WrapFunction(cx, funobj, &v)) {
00980       return JS_FALSE;
00981     }
00982   }
00983 
00984   if (!::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str),
00985                              ::JS_GetStringLength(str), v, nsnull, nsnull,
00986                              attrs)) {
00987     return JS_FALSE;
00988   }
00989 
00990   *objp = obj;
00991 
00992   return JS_TRUE;
00993 }
00994 
00995 JS_STATIC_DLL_CALLBACK(JSBool)
00996 XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
00997 {
00998   if (!EnsureLegalActivity(cx, obj)) {
00999     return JS_FALSE;
01000   }
01001 
01002   XPC_NW_BYPASS(cx, obj, convert, (cx, obj, type, vp));
01003   return JS_TRUE;
01004 }
01005 
01006 JS_STATIC_DLL_CALLBACK(void)
01007 XPC_NW_Finalize(JSContext *cx, JSObject *obj)
01008 {
01009   // We must not use obj's private data here since it's likely that it
01010   // has already been finalized.
01011   XPCJSRuntime *rt = nsXPConnect::GetRuntime();
01012 
01013   {
01014     // scoped lock
01015     XPCAutoLock lock(rt->GetMapLock());
01016     rt->GetExplicitNativeWrapperMap()->Remove(obj);
01017   }
01018 }
01019 
01020 JS_STATIC_DLL_CALLBACK(JSBool)
01021 XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsval id,
01022                    JSAccessMode mode, jsval *vp)
01023 {
01024   // Prevent setting __proto__ on an XPCNativeWrapper
01025   if ((mode & JSACC_WATCH) == JSACC_PROTO && (mode & JSACC_WRITE)) {
01026     return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx);
01027   }
01028 
01029   // Forward to the checkObjectAccess hook in the JSContext, if any.
01030   if (cx->runtime->checkObjectAccess &&
01031       !cx->runtime->checkObjectAccess(cx, obj, id, mode, vp)) {
01032     return JS_FALSE;
01033   }
01034 
01035   XPCWrappedNative *wrappedNative =
01036     XPCNativeWrapper::GetWrappedNative(cx, obj);
01037   if (!wrappedNative) {
01038     return JS_TRUE;
01039   }
01040 
01041   JSObject *wrapperJSObject = wrappedNative->GetFlatJSObject();
01042 
01043   JSClass *clazz = JS_GET_CLASS(cx, wrapperJSObject);
01044   return !clazz->checkAccess ||
01045     clazz->checkAccess(cx, wrapperJSObject, id, mode, vp);
01046 }
01047 
01048 JS_STATIC_DLL_CALLBACK(JSBool)
01049 XPC_NW_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
01050 {
01051   XPC_NW_BYPASS_TEST(cx, obj, call, (cx, obj, argc, argv, rval));
01052 
01053   return JS_TRUE;
01054 }
01055 
01056 JS_STATIC_DLL_CALLBACK(JSBool)
01057 XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01058                  jsval *rval)
01059 {
01060   // The object given to us by the JS engine is actually a stub object (the
01061   // "new" object). This isn't any help to us, so instead use the function
01062   // object of the constructor that we're calling (which is the native
01063   // wrapper).
01064   obj = JSVAL_TO_OBJECT(argv[-2]);
01065 
01066   XPC_NW_BYPASS_TEST(cx, obj, construct, (cx, obj, argc, argv, rval));
01067 
01068   XPCWrappedNative *wrappedNative =
01069     XPCNativeWrapper::GetWrappedNative(cx, obj);
01070   if (!wrappedNative) {
01071     return JS_TRUE;
01072   }
01073 
01074   JSBool retval = JS_TRUE;
01075 
01076   if (!NATIVE_HAS_FLAG(wrappedNative, WantConstruct)) {
01077     return ThrowException(NS_ERROR_INVALID_ARG, cx);
01078   }
01079 
01080   nsresult rv = wrappedNative->GetScriptableInfo()->
01081     GetCallback()->Construct(wrappedNative, cx, obj, argc, argv, rval,
01082                              &retval);
01083   if (NS_FAILED(rv)) {
01084     return ThrowException(rv, cx);
01085   }
01086 
01087   if (!retval) {
01088     return JS_FALSE;
01089   }
01090 
01091   if (JSVAL_IS_PRIMITIVE(*rval)) {
01092     return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx);
01093   }
01094 
01095   return RewrapIfDeepWrapper(cx, obj, *rval, rval);
01096 }
01097 
01098 JS_STATIC_DLL_CALLBACK(JSBool)
01099 XPC_NW_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
01100 {
01101   XPC_NW_BYPASS_TEST(cx, obj, hasInstance, (cx, obj, v, bp));
01102 
01103   return JS_TRUE;
01104 }
01105 
01106 static JSBool
01107 MirrorWrappedNativeParent(JSContext *cx, XPCWrappedNative *wrapper,
01108                           JSObject **result)
01109 {
01110   JSObject *wn_parent = ::JS_GetParent(cx, wrapper->GetFlatJSObject());
01111   if (!wn_parent) {
01112     *result = nsnull;
01113   } else {
01114     XPCWrappedNative *parent_wrapper =
01115       XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wn_parent);
01116 
01117     *result = XPCNativeWrapper::GetNewOrUsed(cx, parent_wrapper, nsnull);
01118     if (!*result)
01119       return JS_FALSE;
01120   }
01121   return JS_TRUE;
01122 }
01123 
01124 JS_STATIC_DLL_CALLBACK(JSBool)
01125 XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01126                      jsval *rval)
01127 {
01128   if (argc < 1) {
01129     return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
01130   }
01131 
01132   // |obj| almost always has the wrong proto and parent so we have to create
01133   // our own object anyway.  Set |obj| to null so we don't use it by accident.
01134   obj = nsnull;
01135 
01136   jsval native = argv[0];
01137 
01138   if (JSVAL_IS_PRIMITIVE(native)) {
01139     return ThrowException(NS_ERROR_XPC_BAD_CONVERT_JS, cx);
01140   }
01141 
01142   JSObject *nativeObj = JSVAL_TO_OBJECT(native);
01143 
01144   XPCWrappedNative *wrappedNative;
01145 
01146   if (XPCNativeWrapper::IsNativeWrapper(cx, nativeObj)) {
01147     // We're asked to wrap an already wrapped object. Re-wrap the
01148     // object wrapped by the given wrapper.
01149 
01150 #ifdef DEBUG_XPCNativeWrapper
01151     printf("Wrapping already wrapped object\n");
01152 #endif
01153 
01154     wrappedNative = XPCNativeWrapper::GetWrappedNative(cx, nativeObj);
01155 
01156     if (!wrappedNative) {
01157       return ThrowException(NS_ERROR_INVALID_ARG, cx);
01158     }
01159 
01160     nativeObj = wrappedNative->GetFlatJSObject();
01161     native = OBJECT_TO_JSVAL(nativeObj);
01162   } else {
01163     wrappedNative
01164       = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, nativeObj);
01165 
01166     if (!wrappedNative) {
01167       return ThrowException(NS_ERROR_INVALID_ARG, cx);
01168     }
01169 
01170     // Prevent wrapping a double-wrapped JS object in an
01171     // XPCNativeWrapper!
01172     nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs =
01173       do_QueryWrappedNative(wrappedNative);
01174 
01175     if (xpcwrappedjs) {
01176       return ThrowException(NS_ERROR_INVALID_ARG, cx);
01177     }
01178   }
01179 
01180   JSObject *wrapperObj;
01181 
01182   // Don't use the object the JS engine created for us, it is in most
01183   // cases incorectly parented and has a proto from the wrong scope.
01184 #ifdef DEBUG_XPCNativeWrapper
01185   printf("Creating new JSObject\n");
01186 #endif
01187   wrapperObj = ::JS_NewObject(cx, XPCNativeWrapper::GetJSClass(), nsnull,
01188                               nsnull);
01189 
01190   if (!wrapperObj ||
01191       !::JS_SetParent(cx, wrapperObj,
01192                       wrappedNative->GetScope()->GetGlobalJSObject()) ||
01193       !::JS_SetPrototype(cx, wrapperObj, nsnull)) {
01194     // JS_NewObject already threw (or reported OOM).
01195     return JS_FALSE;
01196   }
01197 
01198   PRBool hasStringArgs = PR_FALSE;
01199   for (uintN i = 1; i < argc; ++i) {
01200     if (!JSVAL_IS_STRING(argv[i])) {
01201       hasStringArgs = PR_FALSE;
01202 
01203       break;
01204     }
01205 
01206     if (i == 1) {
01207 #ifdef DEBUG_XPCNativeWrapper
01208       printf("Constructing XPCNativeWrapper() with string args\n");
01209 #endif
01210     }
01211 
01212 #ifdef DEBUG_XPCNativeWrapper
01213     printf("  %s\n", ::JS_GetStringBytes(JSVAL_TO_STRING(argv[i])));
01214 #endif
01215 
01216     hasStringArgs = PR_TRUE;
01217   }
01218 
01219   JSBool isDeep = !hasStringArgs;
01220   jsuint flags = isDeep ? FLAG_DEEP | FLAG_EXPLICIT : FLAG_EXPLICIT;
01221   if (!::JS_SetReservedSlot(cx, wrapperObj, 0, INT_TO_JSVAL(flags))) {
01222     return JS_FALSE;
01223   }
01224 
01225   JSObject *parent = nsnull;
01226 
01227   if (isDeep) {
01228     // Make sure wrapperObj doesn't get collected while we're wrapping
01229     // parents for it.
01230     ::JS_LockGCThing(cx, wrapperObj);
01231 
01232     // A deep XPCNativeWrapper has a __parent__ chain that mirrors its
01233     // XPCWrappedNative's chain.
01234     if (!MirrorWrappedNativeParent(cx, wrappedNative, &parent))
01235       return JS_FALSE;
01236 
01237     ::JS_UnlockGCThing(cx, wrapperObj);
01238 
01239     if (argc == 2 && !JSVAL_IS_PRIMITIVE(argv[1])) {
01240       // An object was passed as the second argument to the
01241       // constructor. In this case we check that the object we're
01242       // wrapping is an instance of the assumed constructor that we
01243       // got. If not, throw an exception.
01244       JSBool hasInstance;
01245       if (!::JS_HasInstance(cx, JSVAL_TO_OBJECT(argv[1]), native,
01246                             &hasInstance)) {
01247         return ThrowException(NS_ERROR_UNEXPECTED, cx);
01248       }
01249 
01250       if (!hasInstance) {
01251         return ThrowException(NS_ERROR_INVALID_ARG, cx);
01252       }
01253     }
01254   }
01255 
01256   if (!parent) {
01257     parent = wrappedNative->GetScope()->GetGlobalJSObject();
01258   }
01259     
01260   if (!::JS_SetParent(cx, wrapperObj, parent))
01261     return JS_FALSE;
01262 
01263   // Set the XPCWrappedNative as private data in the native wrapper.
01264   if (!::JS_SetPrivate(cx, wrapperObj, wrappedNative)) {
01265     return JS_FALSE;
01266   }
01267 
01268 #ifdef DEBUG_XPCNativeWrapper
01269   {
01270     XPCCallContext ccx(JS_CALLER, cx);
01271 
01272     // Keep wrapperObj alive while we mess with strings
01273     AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(wrapperObj));
01274 
01275     char *s = wrappedNative->ToString(ccx);
01276     printf("Created new XPCNativeWrapper %p for wrapped native %s\n",
01277            (void*)wrapperObj, s);
01278     if (s)
01279       JS_smprintf_free(s);
01280   }
01281 #endif
01282   
01283   *rval = OBJECT_TO_JSVAL(wrapperObj);
01284 
01285   {
01286     XPCJSRuntime *rt = wrappedNative->GetRuntime();
01287 
01288     // scoped lock
01289     XPCAutoLock lock(rt->GetMapLock());
01290     rt->GetExplicitNativeWrapperMap()->Add(wrapperObj);
01291   }
01292 
01293   return JS_TRUE;
01294 }
01295 
01296 JS_STATIC_DLL_CALLBACK(uint32)
01297 XPC_NW_Mark(JSContext *cx, JSObject *obj, void *arg)
01298 {
01299   XPCWrappedNative *wrappedNative =
01300     XPCNativeWrapper::GetWrappedNative(cx, obj);
01301 
01302   if (wrappedNative && wrappedNative->IsValid()) {
01303     ::JS_MarkGCThing(cx, wrappedNative->GetFlatJSObject(),
01304                      "XPCNativeWrapper wrapped native", arg);
01305   }
01306 
01307   return 0;
01308 }
01309 
01310 extern nsISupports *
01311 GetIdentityObject(JSContext *cx, JSObject *obj);
01312 
01313 JS_STATIC_DLL_CALLBACK(JSBool)
01314 XPC_NW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
01315 {
01316   NS_ASSERTION(XPCNativeWrapper::IsNativeWrapper(cx, obj),
01317                "Uh, we should only ever be called for XPCNativeWrapper "
01318                "objects!");
01319 
01320   if (JSVAL_IS_PRIMITIVE(v)) {
01321     *bp = JS_FALSE;
01322 
01323     return JS_TRUE;
01324   }
01325 
01326   XPCWrappedNative *wrappedNative =
01327     XPCNativeWrapper::GetWrappedNative(cx, obj);
01328 
01329   if (wrappedNative && wrappedNative->IsValid() &&
01330       NATIVE_HAS_FLAG(wrappedNative, WantEquality)) {
01331     // Forward the call to the wrapped native's Equality() hook.
01332     nsresult rv = wrappedNative->GetScriptableCallback()->
01333       Equality(wrappedNative, cx, obj, v, bp);
01334 
01335     if (NS_FAILED(rv)) {
01336       return ThrowException(rv, cx);
01337     }
01338   } else {
01339     JSObject *other = JSVAL_TO_OBJECT(v);
01340 
01341     *bp = (obj == other ||
01342            GetIdentityObject(cx, obj) == GetIdentityObject(cx, other));
01343   }
01344 
01345   return JS_TRUE;
01346 }
01347 
01348 JS_STATIC_DLL_CALLBACK(JSBool)
01349 XPC_NW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01350                 jsval *rval)
01351 {
01352   while (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
01353     obj = ::JS_GetPrototype(cx, obj);
01354     if (!obj) {
01355       return ThrowException(NS_ERROR_UNEXPECTED, cx);
01356     }
01357   }
01358 
01359   if (!EnsureLegalActivity(cx, obj)) {
01360     return JS_FALSE;
01361   }
01362 
01363   // Check whether toString was overridden in any object along
01364   // the wrapped native's object's prototype chain.
01365   XPCJSRuntime *rt = nsXPConnect::GetRuntime();
01366   if (!rt)
01367     return JS_FALSE;
01368 
01369   jsid id = rt->GetStringID(XPCJSRuntime::IDX_TO_STRING);
01370   jsval idAsVal;
01371   if (!::JS_IdToValue(cx, id, &idAsVal)) {
01372     return JS_FALSE;
01373   }
01374 
01375   XPCWrappedNative *wrappedNative =
01376     XPCNativeWrapper::GetWrappedNative(cx, obj);
01377 
01378   if (!wrappedNative) {
01379     // toString() called on XPCNativeWrapper.prototype
01380     NS_NAMED_LITERAL_STRING(protoString, "[object XPCNativeWrapper]");
01381     JSString *str =
01382       ::JS_NewUCStringCopyN(cx, NS_REINTERPRET_CAST(const jschar*,
01383                                                     protoString.get()),
01384                             protoString.Length());
01385     NS_ENSURE_TRUE(str, JS_FALSE);
01386     *rval = STRING_TO_JSVAL(str);
01387     return JS_TRUE;
01388   }
01389 
01390   // Someone is trying to call toString on our wrapped object.
01391   JSObject *wn_obj = wrappedNative->GetFlatJSObject();
01392   XPCCallContext ccx(JS_CALLER, cx, wn_obj, nsnull, idAsVal);
01393   if (!ccx.IsValid()) {
01394     // Shouldn't really happen.
01395     return ThrowException(NS_ERROR_FAILURE, cx);
01396   }
01397 
01398   XPCNativeInterface *iface = ccx.GetInterface();
01399   XPCNativeMember *member = ccx.GetMember();
01400   JSBool overridden = JS_FALSE;
01401   jsval toStringVal;
01402 
01403   // First, try to see if the object declares a toString in its IDL. If it does,
01404   // then we need to defer to that.
01405   if (iface && member) {
01406     if (!member->GetValue(ccx, iface, &toStringVal)) {
01407       return JS_FALSE;
01408     }
01409 
01410     overridden = member->IsMethod();
01411   }
01412 
01413   JSString* str = nsnull;
01414   if (overridden) {
01415     // Defer to the IDL-declared toString.
01416 
01417     AUTO_MARK_JSVAL(ccx, toStringVal);
01418 
01419     JSObject *funobj = xpc_CloneJSFunction(ccx, JSVAL_TO_OBJECT(toStringVal),
01420                                            wn_obj);
01421     if (!funobj) {
01422       return JS_FALSE;
01423     }
01424 
01425     jsval v;
01426     if (!::JS_CallFunctionValue(cx, wn_obj, OBJECT_TO_JSVAL(funobj), argc, argv,
01427                                 &v)) {
01428       return JS_FALSE;
01429     }
01430 
01431     if (JSVAL_IS_STRING(v)) {
01432       str = JSVAL_TO_STRING(v);
01433     }
01434   }
01435 
01436   if (!str) {
01437     // Ok, we do no damage, and add value, by returning our own idea
01438     // of what toString() should be.
01439     // Note: We can't just call JS_ValueToString on the wrapped object. Instead,
01440     // we need to call the wrapper's ToString in order to safely convert our
01441     // object to a string.
01442 
01443     nsAutoString resultString;
01444     resultString.AppendLiteral("[object XPCNativeWrapper");
01445 
01446     char *wrapperStr = wrappedNative->ToString(ccx);
01447     if (!wrapperStr) {
01448       return JS_FALSE;
01449     }
01450 
01451     resultString.Append(' ');
01452     resultString.AppendASCII(wrapperStr);
01453     JS_smprintf_free(wrapperStr);
01454 
01455     resultString.Append(']');
01456 
01457     str = ::JS_NewUCStringCopyN(cx, NS_REINTERPRET_CAST(const jschar *,
01458                                                         resultString.get()),
01459                                 resultString.Length());
01460   }
01461 
01462   NS_ENSURE_TRUE(str, JS_FALSE);
01463 
01464   *rval = STRING_TO_JSVAL(str);
01465   return JS_TRUE;
01466 }
01467 
01468 // static
01469 PRBool
01470 XPCNativeWrapper::AttachNewConstructorObject(XPCCallContext &ccx,
01471                                              JSObject *aGlobalObject)
01472 {
01473   JSObject *class_obj =
01474     ::JS_InitClass(ccx, aGlobalObject, nsnull, &sXPC_NW_JSClass.base,
01475                    XPCNativeWrapperCtor, 0, nsnull, nsnull,
01476                    nsnull, nsnull);
01477   if (!class_obj) {
01478     NS_WARNING("can't initialize the XPCNativeWrapper class");
01479     return PR_FALSE;
01480   }
01481   
01482   // Make sure our prototype chain is empty and that people can't mess
01483   // with XPCNativeWrapper.prototype.
01484   ::JS_SetPrototype(ccx, class_obj, nsnull);
01485   if (!::JS_SealObject(ccx, class_obj, JS_FALSE)) {
01486     NS_WARNING("Failed to seal XPCNativeWrapper.prototype");
01487     return PR_FALSE;
01488   }
01489 
01490   JSBool found;
01491   return ::JS_SetPropertyAttributes(ccx, aGlobalObject,
01492                                     sXPC_NW_JSClass.base.name,
01493                                     JSPROP_READONLY | JSPROP_PERMANENT,
01494                                     &found);
01495 }
01496 
01497 // static
01498 JSObject *
01499 XPCNativeWrapper::GetNewOrUsed(JSContext *cx, XPCWrappedNative *wrapper,
01500                                JSObject *callee)
01501 {
01502   if (callee) {
01503     nsCOMPtr<nsIPrincipal> prin;
01504 
01505     nsCOMPtr<nsIScriptSecurityManager> ssm = GetSecurityManager(cx);
01506     nsresult rv = ssm
01507                   ? ssm->GetObjectPrincipal(cx, callee, getter_AddRefs(prin))
01508                   : NS_ERROR_FAILURE;
01509     if (NS_SUCCEEDED(rv) && prin) {
01510       nsCOMPtr<nsIPrincipal> sysprin;
01511       rv = ssm->GetSystemPrincipal(getter_AddRefs(sysprin));
01512       if (NS_SUCCEEDED(rv) && sysprin != prin) {
01513         jsval v = OBJECT_TO_JSVAL(wrapper->GetFlatJSObject());
01514         if (!XPCNativeWrapperCtor(cx, JSVAL_TO_OBJECT(v), 1, &v, &v))
01515           return nsnull;
01516         return JSVAL_TO_OBJECT(v);
01517       }
01518     }
01519   }
01520 
01521   // Prevent wrapping a double-wrapped JS object in an
01522   // XPCNativeWrapper!
01523   nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs(do_QueryWrappedNative(wrapper));
01524 
01525   if (xpcwrappedjs) {
01526     XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
01527 
01528     return nsnull;
01529   }
01530 
01531   JSObject *obj = wrapper->GetNativeWrapper();
01532   if (obj) {
01533     return obj;
01534   }
01535 
01536   JSObject *nw_parent;
01537   if (!MirrorWrappedNativeParent(cx, wrapper, &nw_parent)) {
01538     return nsnull;
01539   }
01540 
01541   PRBool lock;
01542 
01543   if (!nw_parent) {
01544     nw_parent = wrapper->GetScope()->GetGlobalJSObject();
01545 
01546     lock = PR_FALSE;
01547   } else {
01548     lock = PR_TRUE;
01549   }
01550 
01551   if (lock) {
01552     // Make sure nw_parent doesn't get collected while we're creating
01553     // the new wrapper.
01554     ::JS_LockGCThing(cx, nw_parent);
01555   }
01556 
01557   obj = ::JS_NewObject(cx, GetJSClass(), nsnull, nsnull);
01558 
01559   if (lock) {
01560     ::JS_UnlockGCThing(cx, nw_parent);
01561   }
01562 
01563   if (!obj ||
01564       !::JS_SetParent(cx, obj, nw_parent) ||
01565       !::JS_SetPrivate(cx, obj, wrapper) ||
01566       !::JS_SetPrototype(cx, obj, nsnull) ||
01567       !::JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(FLAG_DEEP))) {
01568     return nsnull;
01569   }
01570 
01571   wrapper->SetNativeWrapper(obj);
01572 
01573 #ifdef DEBUG_XPCNativeWrapper
01574   {
01575     XPCCallContext ccx(NATIVE_CALLER, cx);
01576 
01577     // Keep obj alive while we mess with strings
01578     AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(obj));
01579 
01580     char *s = wrapper->ToString(ccx);
01581     printf("Created new XPCNativeWrapper %p for wrapped native %s\n",
01582            (void*)obj, s);
01583     if (s)
01584       JS_smprintf_free(s);
01585   }
01586 #endif
01587   
01588   return obj;
01589 }
01590 
01591 struct WrapperAndCxHolder
01592 {
01593     XPCWrappedNative* wrapper;
01594     JSContext* cx;
01595 };
01596 
01597 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
01598 ClearNativeWrapperScope(JSDHashTable *table, JSDHashEntryHdr *hdr,
01599                         uint32 number, void *arg)
01600 {
01601     JSDHashEntryStub* entry = (JSDHashEntryStub*)hdr;
01602     WrapperAndCxHolder* d = (WrapperAndCxHolder*)arg;
01603 
01604     if (d->wrapper->GetNativeWrapper() == (JSObject*)entry->key)
01605     {
01606         ::JS_ClearScope(d->cx, (JSObject*)entry->key);
01607     }
01608 
01609     return JS_DHASH_NEXT;
01610 }
01611 
01612 // static
01613 void
01614 XPCNativeWrapper::ClearWrappedNativeScopes(JSContext* cx,
01615                                            XPCWrappedNative* wrapper)
01616 {
01617   JSObject *nativeWrapper = wrapper->GetNativeWrapper();
01618 
01619   if (nativeWrapper) {
01620     ::JS_ClearScope(cx, nativeWrapper);
01621   }
01622 
01623   WrapperAndCxHolder d =
01624     {
01625       wrapper,
01626       cx
01627     };
01628 
01629   wrapper->GetRuntime()->GetExplicitNativeWrapperMap()->
01630     Enumerate(ClearNativeWrapperScope, &d);
01631 }