bool InterposeProperty(JSContext* cx, HandleObject target, const nsIID* iid, HandleId id, MutableHandle<JSPropertyDescriptor> descriptor) { // We only want to do interpostion on DOM instances and // wrapped natives. RootedObject unwrapped(cx, UncheckedUnwrap(target)); const js::Class* clasp = js::GetObjectClass(unwrapped); if (!mozilla::dom::IsDOMClass(clasp) && !IS_WN_CLASS(clasp) && !IS_PROTO_CLASS(clasp) && clasp != &OuterWindowProxyClass) { return true; } XPCWrappedNativeScope* scope = ObjectScope(CurrentGlobalOrNull(cx)); MOZ_ASSERT(scope->HasInterposition()); nsCOMPtr<nsIAddonInterposition> interp = scope->GetInterposition(); JSAddonId* addonId = AddonIdOfObject(target); RootedValue addonIdValue(cx, StringValue(StringOfAddonId(addonId))); RootedValue prop(cx, IdToValue(id)); RootedValue targetValue(cx, ObjectValue(*target)); RootedValue descriptorVal(cx); nsresult rv = interp->InterposeProperty(addonIdValue, targetValue, iid, prop, &descriptorVal); if (NS_FAILED(rv)) { xpc::Throw(cx, rv); return false; } if (!descriptorVal.isObject()) return true; // We need to be careful parsing descriptorVal. |cx| is in the compartment // of the add-on and the descriptor is in the compartment of the // interposition. We could wrap the descriptor in the add-on's compartment // and then parse it. However, parsing the descriptor fetches properties // from it, and we would try to interpose on those property accesses. So // instead we parse in the interposition's compartment and then wrap the // descriptor. { JSAutoCompartment ac(cx, &descriptorVal.toObject()); if (!JS::ObjectToCompletePropertyDescriptor(cx, target, descriptorVal, descriptor)) return false; } // Always make the property non-configurable regardless of what the // interposition wants. descriptor.setAttributes(descriptor.attributes() | JSPROP_PERMANENT); if (!JS_WrapPropertyDescriptor(cx, descriptor)) return false; return true; }
static JSBool DefineGetterOrSetter(JSContext *cx, uintN argc, JSBool wantGetter, jsval *vp) { uintN attrs; JSBool found; JSPropertyOp getter, setter; JSObject *obj2; jsval v; jsid interned_id; XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; JSFastNative forward = wantGetter ? js_obj_defineGetter : js_obj_defineSetter; jsval id = (argc >= 1) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID; if(!JSVAL_IS_STRING(id)) return forward(cx, argc, vp); JSString *str = JSVAL_TO_STRING(id); const char *name = JS_GetStringBytes(str); if(!JS_ValueToId(cx, id, &interned_id) || !JS_LookupPropertyWithFlagsById(cx, obj, interned_id, JSRESOLVE_QUALIFIED, &obj2, &v) || (obj2 && !JS_GetPropertyAttrsGetterAndSetterById(cx, obj2, interned_id, &attrs, &found, &getter, &setter))) return JS_FALSE; // The property didn't exist, already has a getter or setter, or is not // our property, then just forward now. if(!obj2 || (attrs & (JSPROP_GETTER | JSPROP_SETTER)) || !(getter || setter) || !IS_PROTO_CLASS(STOBJ_GET_CLASS(obj2))) return forward(cx, argc, vp); // Reify the getter and setter... if(!ReifyPropertyOps(cx, obj, id, interned_id, name, getter, setter, nsnull, nsnull)) return JS_FALSE; return forward(cx, argc, vp); }
static JSBool LookupGetterOrSetter(JSContext *cx, JSBool wantGetter, uintN argc, jsval *vp) { uintN attrs; JSBool found; JSPropertyOp getter, setter; JSObject *obj2; jsid interned_id; jsval v; XPC_QS_ASSERT_CONTEXT_OK(cx); if(argc == 0) { JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; jsval idval = JS_ARGV(cx, vp)[0]; const char *name = JSVAL_IS_STRING(idval) ? JS_GetStringBytes(JSVAL_TO_STRING(idval)) : nsnull; if(!JS_ValueToId(cx, idval, &interned_id) || !JS_LookupPropertyWithFlagsById(cx, obj, interned_id, JSRESOLVE_QUALIFIED, &obj2, &v) || (obj2 && !JS_GetPropertyAttrsGetterAndSetterById(cx, obj2, interned_id, &attrs, &found, &getter, &setter))) return JS_FALSE; // No property at all means no getters or setters possible. if(!obj2 || !found) { JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } // Inline obj_lookup[GS]etter here. if(wantGetter) { if(attrs & JSPROP_GETTER) { JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, getter))); return JS_TRUE; } } else { if(attrs & JSPROP_SETTER) { JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, setter))); return JS_TRUE; } } // Since XPConnect doesn't use JSPropertyOps in any other contexts, // ensuring that we have an XPConnect prototype object ensures that // we are only going to expose quickstubbed properties to script. // Also be careful not to overwrite existing properties! if(!name || !IS_PROTO_CLASS(STOBJ_GET_CLASS(obj2)) || (attrs & (JSPROP_GETTER | JSPROP_SETTER)) || !(getter || setter)) { JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } JSObject *getterobj, *setterobj; if(!ReifyPropertyOps(cx, obj, idval, interned_id, name, getter, setter, &getterobj, &setterobj)) return JS_FALSE; JSObject *wantedobj = wantGetter ? getterobj : setterobj; v = wantedobj ? OBJECT_TO_JSVAL(wantedobj) : JSVAL_VOID; JS_SET_RVAL(cx, vp, v); return JS_TRUE; }