bool JavaScriptShared::toDescriptor(JSContext* cx, const PPropertyDescriptor& in, MutableHandle<JSPropertyDescriptor> out) { out.setAttributes(in.attrs()); if (!fromVariant(cx, in.value(), out.value())) return false; out.object().set(fromObjectOrNullVariant(cx, in.obj())); if (in.getter().type() == GetterSetter::Tuint64_t && !in.getter().get_uint64_t()) { out.setGetter(nullptr); } else if (in.attrs() & JSPROP_GETTER) { Rooted<JSObject*> getter(cx); getter = fromObjectVariant(cx, in.getter().get_ObjectVariant()); if (!getter) return false; out.setGetter(JS_DATA_TO_FUNC_PTR(JSGetterOp, getter.get())); } else { out.setGetter(UnknownPropertyStub); } if (in.setter().type() == GetterSetter::Tuint64_t && !in.setter().get_uint64_t()) { out.setSetter(nullptr); } else if (in.attrs() & JSPROP_SETTER) { Rooted<JSObject*> setter(cx); setter = fromObjectVariant(cx, in.setter().get_ObjectVariant()); if (!setter) return false; out.setSetter(JS_DATA_TO_FUNC_PTR(JSSetterOp, setter.get())); } else { out.setSetter(UnknownStrictPropertyStub); } return true; }
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; }
bool JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in, MutableHandle<JSPropertyDescriptor> out) { out.setAttributes(in.attrs()); out.setShortId(in.shortid()); if (!toValue(cx, in.value(), out.value())) return false; Rooted<JSObject*> obj(cx); if (!unwrap(cx, in.objId(), &obj)) return false; out.object().set(obj); if (!in.getter()) { out.setGetter(nullptr); } else if (in.attrs() & JSPROP_GETTER) { Rooted<JSObject*> getter(cx); if (!unwrap(cx, in.getter(), &getter)) return false; out.setGetter(JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get())); } else { if (in.getter() == DefaultPropertyOp) out.setGetter(JS_PropertyStub); else out.setGetter(UnknownPropertyStub); } if (!in.setter()) { out.setSetter(nullptr); } else if (in.attrs() & JSPROP_SETTER) { Rooted<JSObject*> setter(cx); if (!unwrap(cx, in.setter(), &setter)) return false; out.setSetter(JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get())); } else { if (in.setter() == DefaultPropertyOp) out.setSetter(JS_StrictPropertyStub); else if (in.setter() == GetterOnlyPropertyStub) out.setSetter(js_GetterOnlyPropertyStub); else out.setSetter(UnknownStrictPropertyStub); } return true; }
bool js::SetPropertyIgnoringNamedGetter(JSContext* cx, const BaseProxyHandler* handler, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandle<PropertyDescriptor> desc, bool descIsOwn, bool strict, MutableHandleValue vp) { /* The control-flow here differs from ::get() because of the fall-through case below. */ MOZ_ASSERT_IF(descIsOwn, desc.object()); if (desc.object()) { MOZ_ASSERT(desc.getter() != JS_PropertyStub); MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub); // Check for read-only properties. if (desc.isReadonly()) { if (strict) return Throw(cx, id, descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP); return true; } if (desc.hasSetterObject() || desc.setter()) { if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) return false; if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler) return true; if (desc.isShared()) return true; } desc.value().set(vp.get()); if (descIsOwn) { MOZ_ASSERT(desc.object() == proxy); return handler->defineProperty(cx, proxy, id, desc); } return DefineProperty(cx, receiver, id, desc.value(), desc.getter(), desc.setter(), desc.attributes()); } desc.object().set(receiver); desc.value().set(vp.get()); desc.setAttributes(JSPROP_ENUMERATE); desc.setGetter(nullptr); desc.setSetter(nullptr); // Pick up the class getter/setter. return DefineProperty(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE); }
bool js::SetPropertyIgnoringNamedGetter(JSContext *cx, const BaseProxyHandler *handler, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandle<PropertyDescriptor> desc, bool descIsOwn, bool strict, MutableHandleValue vp) { /* The control-flow here differs from ::get() because of the fall-through case below. */ if (descIsOwn) { MOZ_ASSERT(desc.object()); // Check for read-only properties. if (desc.isReadonly()) return strict ? Throw(cx, id, JSMSG_READ_ONLY) : true; if (!desc.setter()) { // Be wary of the odd explicit undefined setter case possible through // Object.defineProperty. if (!desc.hasSetterObject()) desc.setSetter(JS_StrictPropertyStub); } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) { if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) return false; if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler) return true; if (desc.isShared()) return true; } if (!desc.getter()) { // Same as above for the null setter case. if (!desc.hasGetterObject()) desc.setGetter(JS_PropertyStub); } desc.value().set(vp.get()); return handler->defineProperty(cx, receiver, id, desc); } if (desc.object()) { // Check for read-only properties. if (desc.isReadonly()) return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true; if (!desc.setter()) { // Be wary of the odd explicit undefined setter case possible through // Object.defineProperty. if (!desc.hasSetterObject()) desc.setSetter(JS_StrictPropertyStub); } else if (desc.hasSetterObject() || desc.setter() != JS_StrictPropertyStub) { if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) return false; if (!proxy->is<ProxyObject>() || proxy->as<ProxyObject>().handler() != handler) return true; if (desc.isShared()) return true; } if (!desc.getter()) { // Same as above for the null setter case. if (!desc.hasGetterObject()) desc.setGetter(JS_PropertyStub); } desc.value().set(vp.get()); return JSObject::defineGeneric(cx, receiver, id, desc.value(), desc.getter(), desc.setter(), desc.attributes()); } desc.object().set(receiver); desc.value().set(vp.get()); desc.setAttributes(JSPROP_ENUMERATE); desc.setGetter(nullptr); desc.setSetter(nullptr); // Pick up the class getter/setter. return JSObject::defineGeneric(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE); }