Fix for bug 778152 (Content window does not have an XMLHttpRequest property when accessed via an Xray wrapper in a subscript). r=bz.

Switch from using the interface objects from the Xrays compartment to wrapping
interface objects and interface prototype objects in Xrays. Make dom binding
Xrays deal with both instance objects and interface and interface prototype
objects.
This commit is contained in:
Peter Van der Beken 2012-10-09 20:50:27 +02:00
parent 36ab4d3065
commit 1ef60a85a4
22 changed files with 1182 additions and 476 deletions

View File

@ -6683,12 +6683,40 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
return NS_OK;
}
Maybe<JSAutoCompartment> ac;
JSObject* global;
bool defineOnXray = ObjectIsNativeWrapper(cx, obj);
if (defineOnXray) {
global = xpc::Unwrap(cx, obj, false);
if (!global) {
return NS_ERROR_DOM_SECURITY_ERR;
}
ac.construct(cx, global);
} else {
global = obj;
}
bool enabled;
bool defined = define(cx, obj, &enabled);
MOZ_ASSERT_IF(defined, enabled);
JSObject* interfaceObject = define(cx, global, &enabled);
if (enabled) {
*did_resolve = defined;
return defined ? NS_OK : NS_ERROR_FAILURE;
if (!interfaceObject) {
return NS_ERROR_FAILURE;
}
if (defineOnXray) {
// This really should be handled by the Xray for the window.
ac.destroy();
if (!JS_WrapObject(cx, &interfaceObject) ||
!JS_DefinePropertyById(cx, obj, id,
JS::ObjectValue(*interfaceObject), nullptr,
nullptr, 0)) {
return NS_ERROR_FAILURE;
}
}
*did_resolve = true;
return NS_OK;
}
}
}

View File

@ -146,10 +146,23 @@ InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
return xpc::NonVoidStringToJsval(cx, str, vp);
}
JSBool
Constructor(JSContext* cx, unsigned argc, JS::Value* vp)
{
JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
const JS::Value& v =
js::GetFunctionNativeReserved(callee,
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
JSNativeHolder* nativeHolder = static_cast<JSNativeHolder*>(v.toPrivate());
return (nativeHolder->mNative)(cx, argc, vp);
}
static JSObject*
CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver,
JSClass* constructorClass, JSNative constructorNative,
unsigned ctorNargs, JSObject* proto,
CreateInterfaceObject(JSContext* cx, JSObject* global,
JSClass* constructorClass,
JSNativeHolder* constructorNative,
unsigned ctorNargs,
JSObject* proto,
const NativeProperties* properties,
const NativeProperties* chromeOnlyProperties,
const char* name)
@ -163,12 +176,16 @@ CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver,
constructor = JS_NewObject(cx, constructorClass, functionProto, global);
} else {
MOZ_ASSERT(constructorNative);
JSFunction* fun = JS_NewFunction(cx, constructorNative, ctorNargs,
JSFUN_CONSTRUCTOR, global, name);
JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs,
JSFUN_CONSTRUCTOR, global,
name);
if (!fun) {
return NULL;
}
constructor = JS_GetFunctionObject(fun);
js::SetFunctionNativeReserved(constructor,
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT,
js::PrivateValue(constructorNative));
}
if (!constructor) {
return NULL;
@ -224,13 +241,13 @@ CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver,
}
JSBool alreadyDefined;
if (!JS_AlreadyHasOwnProperty(cx, receiver, name, &alreadyDefined)) {
if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) {
return NULL;
}
// This is Enumerable: False per spec.
if (!alreadyDefined &&
!JS_DefineProperty(cx, receiver, name, OBJECT_TO_JSVAL(constructor), NULL,
!JS_DefineProperty(cx, global, name, OBJECT_TO_JSVAL(constructor), NULL,
NULL, 0)) {
return NULL;
}
@ -287,11 +304,12 @@ CreateInterfacePrototypeObject(JSContext* cx, JSObject* global,
return ourProto;
}
JSObject*
CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver,
JSObject* protoProto, JSClass* protoClass,
JSClass* constructorClass, JSNative constructor,
unsigned ctorNargs, const DOMClass* domClass,
void
CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* protoProto,
JSClass* protoClass, JSObject** protoCache,
JSClass* constructorClass, JSNativeHolder* constructor,
unsigned ctorNargs, JSObject** constructorCache,
const DOMClass* domClass,
const NativeProperties* properties,
const NativeProperties* chromeOnlyProperties,
const char* name)
@ -311,17 +329,25 @@ CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver,
MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
"Must have name precisely when we have an interface object");
MOZ_ASSERT(!constructorClass || !constructor);
MOZ_ASSERT(!protoClass == !protoCache,
"If, and only if, there is an interface prototype object we need "
"to cache it");
MOZ_ASSERT(!(constructorClass || constructor) == !constructorCache,
"If, and only if, there is an interface object we need to cache "
"it");
JSObject* proto;
if (protoClass) {
proto = CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
properties, chromeOnlyProperties);
if (!proto) {
return NULL;
return;
}
js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
JS::PrivateValue(const_cast<DOMClass*>(domClass)));
*protoCache = proto;
}
else {
proto = NULL;
@ -329,15 +355,19 @@ CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver,
JSObject* interface;
if (constructorClass || constructor) {
interface = CreateInterfaceObject(cx, global, receiver, constructorClass,
constructor, ctorNargs, proto,
properties, chromeOnlyProperties, name);
interface = CreateInterfaceObject(cx, global, constructorClass, constructor,
ctorNargs, proto, properties,
chromeOnlyProperties, name);
if (!interface) {
return NULL;
if (protoCache) {
// If we fail we need to make sure to clear the value of protoCache we
// set above.
*protoCache = nullptr;
}
return;
}
*constructorCache = interface;
}
return protoClass ? proto : interface;
}
static bool
@ -472,21 +502,121 @@ ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
}
inline const NativePropertyHooks*
GetNativePropertyHooks(JSContext *cx, JSObject *obj, DOMObjectType& type)
{
const DOMClass* domClass;
if (GetDOMClass(obj, domClass) != eNonDOMObject) {
type = eInstance;
return domClass->mNativeHooks;
}
if (JS_ObjectIsFunction(cx, obj)) {
MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
type = eInterface;
const JS::Value& v =
js::GetFunctionNativeReserved(obj,
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
JSNativeHolder* nativeHolder = static_cast<JSNativeHolder*>(v.toPrivate());
return nativeHolder->mPropertyHooks;
}
MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
type = ifaceAndProtoJSClass->mType;
return ifaceAndProtoJSClass->mNativeHooks;
}
bool
XrayResolveOwnProperty(JSContext* cx, JSObject* wrapper, JSObject* obj, jsid id,
bool set, JSPropertyDescriptor* desc)
{
DOMObjectType type;
const NativePropertyHooks *nativePropertyHooks =
GetNativePropertyHooks(cx, obj, type);
return type != eInstance || !nativePropertyHooks->mResolveOwnProperty ||
nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, set,
desc);
}
static bool
XrayResolveAttribute(JSContext* cx, JSObject* wrapper, jsid id,
Prefable<JSPropertySpec>* attributes, jsid* attributeIds,
JSPropertySpec* attributeSpecs, JSPropertyDescriptor* desc)
{
for (; attributes->specs; ++attributes) {
if (attributes->enabled) {
// Set i to be the index into our full list of ids/specs that we're
// looking at now.
size_t i = attributes->specs - attributeSpecs;
for ( ; attributeIds[i] != JSID_VOID; ++i) {
if (id == attributeIds[i]) {
JSPropertySpec& attrSpec = attributeSpecs[i];
// Because of centralization, we need to make sure we fault in the
// JitInfos as well. At present, until the JSAPI changes, the easiest
// way to do this is wrap them up as functions ourselves.
desc->attrs = attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS;
// They all have getters, so we can just make it.
JSObject *global = JS_GetGlobalForObject(cx, wrapper);
JSFunction *fun = JS_NewFunction(cx, (JSNative)attrSpec.getter.op,
0, 0, global, nullptr);
if (!fun)
return false;
SET_JITINFO(fun, attrSpec.getter.info);
JSObject *funobj = JS_GetFunctionObject(fun);
desc->getter = js::CastAsJSPropertyOp(funobj);
desc->attrs |= JSPROP_GETTER;
if (attrSpec.setter.op) {
// We have a setter! Make it.
fun = JS_NewFunction(cx, (JSNative)attrSpec.setter.op, 1, 0,
global, nullptr);
if (!fun)
return false;
SET_JITINFO(fun, attrSpec.setter.info);
funobj = JS_GetFunctionObject(fun);
desc->setter = js::CastAsJSStrictPropertyOp(funobj);
desc->attrs |= JSPROP_SETTER;
} else {
desc->setter = nullptr;
}
desc->obj = wrapper;
return true;
}
}
}
}
return true;
}
static bool
XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
JSPropertyDescriptor* desc,
JSPropertyDescriptor* desc, DOMObjectType type,
const NativeProperties* nativeProperties)
{
if (nativeProperties->methods) {
Prefable<JSFunctionSpec>* methods;
jsid* methodIds;
JSFunctionSpec* methodsSpecs;
if (type == eInterface) {
methods = nativeProperties->staticMethods;
methodIds = nativeProperties->staticMethodIds;
methodsSpecs = nativeProperties->staticMethodsSpecs;
} else {
methods = nativeProperties->methods;
methodIds = nativeProperties->methodIds;
methodsSpecs = nativeProperties->methodsSpecs;
}
if (methods) {
Prefable<JSFunctionSpec>* method;
for (method = nativeProperties->methods; method->specs; ++method) {
for (method = methods; method->specs; ++method) {
if (method->enabled) {
// Set i to be the index into our full list of ids/specs that we're
// looking at now.
size_t i = method->specs - nativeProperties->methodsSpecs;
for ( ; nativeProperties->methodIds[i] != JSID_VOID; ++i) {
if (id == nativeProperties->methodIds[i]) {
JSFunctionSpec& methodSpec = nativeProperties->methodsSpecs[i];
size_t i = method->specs - methodsSpecs;
for ( ; methodIds[i] != JSID_VOID; ++i) {
if (id == methodIds[i]) {
JSFunctionSpec& methodSpec = methodsSpecs[i];
JSFunction *fun = JS_NewFunctionById(cx, methodSpec.call.op,
methodSpec.nargs, 0,
wrapper, id);
@ -507,58 +637,30 @@ XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
}
}
JSPropertySpec* attributeSpecs = nativeProperties->attributeSpecs;
Prefable<JSPropertySpec>* attr = nativeProperties->attributes;
jsid* attributeIds = nativeProperties->attributeIds;
// Do the attribute stuff for attributes, then for unforgeable attributes
for (int attrIteration = 0; attrIteration < 2; ++attrIteration) {
if (attr) {
for (; attr->specs; ++attr) {
if (attr->enabled) {
// Set i to be the index into our full list of ids/specs that we're
// looking at now.
size_t i = attr->specs - attributeSpecs;
for ( ; attributeIds[i] != JSID_VOID; ++i) {
if (id == attributeIds[i]) {
JSPropertySpec& attrSpec = attributeSpecs[i];
// Because of centralization, we need to make sure we fault in the
// JitInfos as well. At present, until the JSAPI changes, the easiest
// way to do this is wrap them up as functions ourselves.
desc->attrs = attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS;
// They all have getters, so we can just make it.
JSObject *global = JS_GetGlobalForObject(cx, wrapper);
JSFunction *fun = JS_NewFunction(cx, (JSNative)attrSpec.getter.op,
0, 0, global, nullptr);
if (!fun)
return false;
SET_JITINFO(fun, attrSpec.getter.info);
JSObject *funobj = JS_GetFunctionObject(fun);
desc->getter = js::CastAsJSPropertyOp(funobj);
desc->attrs |= JSPROP_GETTER;
if (attrSpec.setter.op) {
// We have a setter! Make it.
fun = JS_NewFunction(cx, (JSNative)attrSpec.setter.op, 1, 0,
global, nullptr);
if (!fun)
return false;
SET_JITINFO(fun, attrSpec.setter.info);
funobj = JS_GetFunctionObject(fun);
desc->setter = js::CastAsJSStrictPropertyOp(funobj);
desc->attrs |= JSPROP_SETTER;
} else {
desc->setter = nullptr;
}
desc->obj = wrapper;
return true;
}
}
}
if (type != eInterface) {
if (nativeProperties->attributes) {
if (!XrayResolveAttribute(cx, wrapper, id,
nativeProperties->attributes,
nativeProperties->attributeIds,
nativeProperties->attributeSpecs, desc)) {
return false;
}
if (desc->obj) {
return true;
}
}
if (nativeProperties->unforgeableAttributes) {
if (!XrayResolveAttribute(cx, wrapper, id,
nativeProperties->unforgeableAttributes,
nativeProperties->unforgeableAttributeIds,
nativeProperties->unforgeableAttributeSpecs,
desc)) {
return false;
}
if (desc->obj) {
return true;
}
}
attributeSpecs = nativeProperties->unforgeableAttributeSpecs;
attr = nativeProperties->unforgeableAttributes;
attributeIds = nativeProperties->unforgeableAttributeIds;
}
if (nativeProperties->constants) {
@ -583,21 +685,63 @@ XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
return true;
}
bool
XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
JSPropertyDescriptor* desc,
const NativeProperties* nativeProperties,
const NativeProperties* chromeOnlyNativeProperties)
static bool
ResolvePrototypeOrConstructor(JSContext* cx, JSObject* wrapper, JSObject* obj,
size_t protoAndIfaceArrayIndex,
JSPropertyDescriptor* desc)
{
if (nativeProperties &&
!XrayResolveProperty(cx, wrapper, id, desc, nativeProperties)) {
JSObject* global = js::GetGlobalForObjectCrossCompartment(obj);
{
JSAutoCompartment ac(cx, global);
JSObject** protoAndIfaceArray = GetProtoAndIfaceArray(global);
JSObject* protoOrIface = protoAndIfaceArray[protoAndIfaceArrayIndex];
if (!protoOrIface) {
return false;
}
desc->obj = wrapper;
desc->shortid = 0;
desc->attrs = JSPROP_PERMANENT | JSPROP_READONLY;
desc->getter = JS_PropertyStub;
desc->setter = JS_StrictPropertyStub;
desc->value = JS::ObjectValue(*protoOrIface);
}
return JS_WrapPropertyDescriptor(cx, desc);
}
bool
XrayResolveNativeProperty(JSContext* cx, JSObject* wrapper,
const NativePropertyHooks* nativePropertyHooks,
DOMObjectType type, JSObject* obj, jsid id,
JSPropertyDescriptor* desc)
{
if (type == eInterface && IdEquals(id, "prototype")) {
return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
ResolvePrototypeOrConstructor(cx, wrapper, obj,
nativePropertyHooks->mPrototypeID,
desc);
}
if (type == eInterfacePrototype && IdEquals(id, "constructor")) {
return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
ResolvePrototypeOrConstructor(cx, wrapper, obj,
nativePropertyHooks->mConstructorID,
desc);
}
const NativePropertiesHolder& nativeProperties =
nativePropertyHooks->mNativeProperties;
if (nativeProperties.regular &&
!XrayResolveProperty(cx, wrapper, id, desc, type,
nativeProperties.regular)) {
return false;
}
if (!desc->obj &&
chromeOnlyNativeProperties &&
nativeProperties.chromeOnly &&
xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
!XrayResolveProperty(cx, wrapper, id, desc, chromeOnlyNativeProperties)) {
!XrayResolveProperty(cx, wrapper, id, desc, type,
nativeProperties.chromeOnly)) {
return false;
}
@ -605,19 +749,85 @@ XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
}
bool
XrayEnumerateProperties(JS::AutoIdVector& props,
XrayResolveNativeProperty(JSContext* cx, JSObject* wrapper, JSObject* obj,
jsid id, JSPropertyDescriptor* desc)
{
DOMObjectType type;
const NativePropertyHooks* nativePropertyHooks =
GetNativePropertyHooks(cx, obj, type);
if (type == eInstance) {
// Force the type to be eInterfacePrototype, since we need to walk the
// prototype chain.
type = eInterfacePrototype;
}
if (type == eInterfacePrototype) {
do {
if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
obj, id, desc)) {
return false;
}
if (desc->obj) {
return true;
}
} while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
return true;
}
return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, obj,
id, desc);
}
bool
XrayEnumerateAttributes(Prefable<JSPropertySpec>* attributes,
jsid* attributeIds, JSPropertySpec* attributeSpecs,
JS::AutoIdVector& props)
{
for (; attributes->specs; ++attributes) {
if (attributes->enabled) {
// Set i to be the index into our full list of ids/specs that we're
// looking at now.
size_t i = attributes->specs - attributeSpecs;
for ( ; attributeIds[i] != JSID_VOID; ++i) {
if ((attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
!props.append(attributeIds[i])) {
return false;
}
}
}
}
return true;
}
bool
XrayEnumerateProperties(JS::AutoIdVector& props, DOMObjectType type,
const NativeProperties* nativeProperties)
{
if (nativeProperties->methods) {
Prefable<JSFunctionSpec>* methods;
jsid* methodIds;
JSFunctionSpec* methodsSpecs;
if (type == eInterface) {
methods = nativeProperties->staticMethods;
methodIds = nativeProperties->staticMethodIds;
methodsSpecs = nativeProperties->staticMethodsSpecs;
} else {
methods = nativeProperties->methods;
methodIds = nativeProperties->methodIds;
methodsSpecs = nativeProperties->methodsSpecs;
}
if (methods) {
Prefable<JSFunctionSpec>* method;
for (method = nativeProperties->methods; method->specs; ++method) {
for (method = methods; method->specs; ++method) {
if (method->enabled) {
// Set i to be the index into our full list of ids/specs that we're
// looking at now.
size_t i = method->specs - nativeProperties->methodsSpecs;
for ( ; nativeProperties->methodIds[i] != JSID_VOID; ++i) {
if ((nativeProperties->methodsSpecs[i].flags & JSPROP_ENUMERATE) &&
!props.append(nativeProperties->methodIds[i])) {
size_t i = method->specs - methodsSpecs;
for ( ; methodIds[i] != JSID_VOID; ++i) {
if ((methodsSpecs[i].flags & JSPROP_ENUMERATE) &&
!props.append(methodIds[i])) {
return false;
}
}
@ -625,30 +835,20 @@ XrayEnumerateProperties(JS::AutoIdVector& props,
}
}
JSPropertySpec* attributeSpecs = nativeProperties->attributeSpecs;
Prefable<JSPropertySpec>* attr = nativeProperties->attributes;
jsid* attributeIds = nativeProperties->attributeIds;
// Do the attribute stuff for attributes, then for unforgeable attributes
for (int attrIteration = 0; attrIteration < 2; ++attrIteration) {
if (attr) {
for (; attr->specs; ++attr) {
if (attr->enabled) {
// Set i to be the index into our full list of ids/specs that we're
// looking at now.
size_t i = attr->specs - attributeSpecs;
for ( ; attributeIds[i] != JSID_VOID; ++i) {
if ((attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
!props.append(attributeIds[i])) {
return false;
}
}
}
}
if (type != eInterface) {
if (nativeProperties->attributes &&
!XrayEnumerateAttributes(nativeProperties->attributes,
nativeProperties->attributeIds,
nativeProperties->attributeSpecs, props)) {
return false;
}
if (nativeProperties->unforgeableAttributes &&
!XrayEnumerateAttributes(nativeProperties->unforgeableAttributes,
nativeProperties->unforgeableAttributeIds,
nativeProperties->unforgeableAttributeSpecs,
props)) {
return false;
}
attributeSpecs = nativeProperties->unforgeableAttributeSpecs;
attr = nativeProperties->unforgeableAttributes;
attributeIds = nativeProperties->unforgeableAttributeIds;
}
if (nativeProperties->constants) {
@ -671,25 +871,91 @@ XrayEnumerateProperties(JS::AutoIdVector& props,
}
bool
XrayEnumerateProperties(JSObject* wrapper,
JS::AutoIdVector& props,
const NativeProperties* nativeProperties,
const NativeProperties* chromeOnlyNativeProperties)
XrayEnumerateNativeProperties(JSContext* cx, JSObject* wrapper,
const NativePropertyHooks* nativePropertyHooks,
DOMObjectType type, JSObject* obj,
JS::AutoIdVector& props)
{
if (nativeProperties &&
!XrayEnumerateProperties(props, nativeProperties)) {
if (type == eInterface &&
nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
!AddStringToIDVector(cx, props, "prototype")) {
return false;
}
if (chromeOnlyNativeProperties &&
if (type == eInterfacePrototype &&
nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
!AddStringToIDVector(cx, props, "constructor")) {
return false;
}
const NativePropertiesHolder& nativeProperties =
nativePropertyHooks->mNativeProperties;
if (nativeProperties.regular &&
!XrayEnumerateProperties(props, type, nativeProperties.regular)) {
return false;
}
if (nativeProperties.chromeOnly &&
xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
!XrayEnumerateProperties(props, chromeOnlyNativeProperties)) {
!XrayEnumerateProperties(props, type, nativeProperties.chromeOnly)) {
return false;
}
return true;
}
bool
XrayEnumerateProperties(JSContext* cx, JSObject* wrapper, JSObject* obj,
bool ownOnly, JS::AutoIdVector& props)
{
DOMObjectType type;
const NativePropertyHooks* nativePropertyHooks =
GetNativePropertyHooks(cx, obj, type);
if (type == eInstance) {
if (nativePropertyHooks->mEnumerateOwnProperties &&
!nativePropertyHooks->mEnumerateOwnProperties(cx, wrapper, obj,
props)) {
return false;
}
if (ownOnly) {
return true;
}
// Force the type to be eInterfacePrototype, since we need to walk the
// prototype chain.
type = eInterfacePrototype;
}
if (type == eInterfacePrototype) {
do {
if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
obj, props)) {
return false;
}
} while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
return true;
}
return XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
obj, props);
}
NativePropertyHooks sWorkerNativePropertyHooks = {
nullptr,
nullptr,
{
nullptr,
nullptr
},
prototypes::id::_ID_Count,
constructors::id::_ID_Count,
nullptr
};
bool
GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found,
JS::Value* vp)
@ -748,21 +1014,33 @@ WrapCallbackInterface(JSContext *cx, JSObject *scope, nsISupports* callback,
JSObject*
GetXrayExpandoChain(JSObject* obj)
{
MOZ_ASSERT(IsDOMObject(obj));
JS::Value v = IsDOMProxy(obj) ? js::GetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO)
: js::GetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT);
js::Class* clasp = js::GetObjectClass(obj);
JS::Value v;
if (IsDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
v = js::GetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT);
} else if (js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) {
MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
v = js::GetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO);
} else {
MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
v = js::GetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT);
}
return v.isUndefined() ? nullptr : &v.toObject();
}
void
SetXrayExpandoChain(JSObject* obj, JSObject* chain)
{
MOZ_ASSERT(IsDOMObject(obj));
JS::Value v = chain ? JS::ObjectValue(*chain) : JSVAL_VOID;
if (IsDOMProxy(obj)) {
js::Class* clasp = js::GetObjectClass(obj);
if (IsDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
js::SetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT, v);
} else if (js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) {
MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
js::SetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO, v);
} else {
js::SetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT, v);
MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
js::SetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT, v);
}
}
@ -788,5 +1066,82 @@ MainThreadDictionaryBase::ParseJSON(const nsAString& aJSON,
return cx;
}
static JSString*
ConcatJSString(JSContext* cx, const char* pre, JSString* str, const char* post)
{
if (!str) {
return nullptr;
}
JSString* preString = JS_NewStringCopyN(cx, pre, strlen(pre));
JSString* postString = JS_NewStringCopyN(cx, post, strlen(post));
if (!preString || !postString) {
return nullptr;
}
str = JS_ConcatStrings(cx, preString, str);
if (!str) {
return nullptr;
}
return JS_ConcatStrings(cx, str, postString);
}
bool
NativeToString(JSContext* cx, JSObject* wrapper, JSObject* obj, const char* pre,
const char* post, JS::Value* v)
{
JSPropertyDescriptor toStringDesc;
toStringDesc.obj = nullptr;
toStringDesc.attrs = 0;
toStringDesc.shortid = 0;
toStringDesc.getter = nullptr;
toStringDesc.setter = nullptr;
toStringDesc.value = JS::UndefinedValue();
if (!XrayResolveNativeProperty(cx, wrapper, obj,
nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING),
&toStringDesc)) {
return false;
}
JSString* str;
{
JSAutoCompartment ac(cx, obj);
if (toStringDesc.obj) {
JS::Value toString = toStringDesc.value;
if (!JS_WrapValue(cx, &toString)) {
return false;
}
MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject()));
JS::Value toStringResult;
if (JS_CallFunctionValue(cx, obj, toString, 0, nullptr,
&toStringResult)) {
str = toStringResult.toString();
} else {
str = nullptr;
}
} else {
if (IsDOMProxy(obj)) {
str = js::GetProxyHandler(obj)->obj_toString(cx, obj);
} else if (IsDOMClass(JS_GetClass(obj)) ||
IsDOMIfaceAndProtoClass(JS_GetClass(obj))) {
str = ConcatJSString(cx, "[object ",
JS_NewStringCopyZ(cx, JS_GetClass(obj)->name),
"]");
} else if (JS_IsNativeFunction(obj, Constructor)) {
str = JS_DecompileFunction(cx, JS_GetObjectFunction(obj), 0);
}
str = ConcatJSString(cx, pre, str, post);
}
}
if (!str) {
return false;
}
v->setString(str);
return JS_WrapValue(cx, v);
}
} // namespace dom
} // namespace mozilla

View File

@ -68,6 +68,7 @@ ThrowMethodFailedWithDetails(JSContext* cx, const ErrorResult& rv,
return Throw<mainThread>(cx, rv.ErrorCode());
}
// Returns true if the JSClass is used for DOM objects.
inline bool
IsDOMClass(const JSClass* clasp)
{
@ -80,6 +81,20 @@ IsDOMClass(const js::Class* clasp)
return IsDOMClass(Jsvalify(clasp));
}
// Returns true if the JSClass is used for DOM interface and interface
// prototype objects.
inline bool
IsDOMIfaceAndProtoClass(const JSClass* clasp)
{
return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
}
inline bool
IsDOMIfaceAndProtoClass(const js::Class* clasp)
{
return IsDOMIfaceAndProtoClass(Jsvalify(clasp));
}
// It's ok for eRegularDOMObject and eProxyDOMObject to be the same, but
// eNonDOMObject should always be different from the other two. This enum
// shouldn't be used to differentiate between non-proxy and proxy bindings.
@ -168,9 +183,7 @@ inline bool
IsDOMObject(JSObject* obj)
{
js::Class* clasp = js::GetObjectClass(obj);
return IsDOMClass(clasp) ||
((js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) &&
js::GetProxyHandler(obj)->family() == ProxyFamily());
return IsDOMClass(clasp) || IsDOMProxy(obj, clasp);
}
// Some callers don't want to set an exception when unwrapping fails
@ -275,8 +288,13 @@ UnwrapObject(JSContext* cx, JSObject* obj, U& value)
PrototypeIDMap<T>::PrototypeID), T>(cx, obj, value);
}
const size_t kProtoAndIfaceCacheCount =
prototypes::id::_ID_Count + constructors::id::_ID_Count;
// The items in the protoAndIfaceArray are indexed by the prototypes::id::ID and
// constructors::id::ID enums, in that order. The end of the prototype objects
// should be the start of the interface objects.
MOZ_STATIC_ASSERT((size_t)constructors::id::_ID_Start ==
(size_t)prototypes::id::_ID_Count,
"Overlapping or discontiguous indexes.");
const size_t kProtoAndIfaceCacheCount = constructors::id::_ID_Count;
inline void
AllocateProtoAndIfaceCache(JSObject* obj)
@ -323,27 +341,36 @@ DestroyProtoAndIfaceCache(JSObject* obj)
bool
DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs);
struct JSNativeHolder
{
JSNative mNative;
const NativePropertyHooks* mPropertyHooks;
};
/*
* Create a DOM interface object (if constructorClass is non-null) and/or a
* DOM interface prototype object (if protoClass is non-null).
*
* global is used as the parent of the interface object and the interface
* prototype object
* receiver is the object on which we need to define the interface object as a
* property
* protoProto is the prototype to use for the interface prototype object.
* protoClass is the JSClass to use for the interface prototype object.
* This is null if we should not create an interface prototype
* object.
* protoCache a pointer to a JSObject pointer where we should cache the
* interface prototype object. This must be null if protoClass is and
* vice versa.
* constructorClass is the JSClass to use for the interface object.
* This is null if we should not create an interface object or
* if it should be a function object.
* constructor is the JSNative to use as a constructor. If this is non-null, it
* should be used as a JSNative to back the interface object, which
* should be a Function. If this is null, then we should create an
* object of constructorClass, unless that's also null, in which
* case we should not create an interface object at all.
* constructor holds the JSNative to back the interface object which should be a
* Function, unless constructorClass is non-null in which case it is
* ignored. If this is null and constructorClass is also null then
* we should not create an interface object at all.
* ctorNargs is the length of the constructor function; 0 if no constructor
* constructorCache a pointer to a JSObject pointer where we should cache the
* interface object. This must be null if both constructorClass
* and constructor are null, and non-null otherwise.
* domClass is the DOMClass of instance objects for this class. This can be
* null if this is not a concrete proto.
* properties contains the methods, attributes and constants to be defined on
@ -353,21 +380,19 @@ DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs);
* interface doesn't have any ChromeOnly properties or if the
* object is being created in non-chrome compartment.
*
* At least one of protoClass and constructorClass should be non-null.
* If constructorClass is non-null, the resulting interface object will be
* defined on the given global with property name |name|, which must also be
* non-null.
*
* returns the interface prototype object if protoClass is non-null, else it
* returns the interface object.
* At least one of protoClass, constructorClass or constructor should be
* non-null. If constructorClass or constructor are non-null, the resulting
* interface object will be defined on the given global with property name
* |name|, which must also be non-null.
*/
JSObject*
CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver,
JSObject* protoProto, JSClass* protoClass,
JSClass* constructorClass, JSNative constructor,
unsigned ctorNargs, const DOMClass* domClass,
const NativeProperties* properties,
const NativeProperties* chromeProperties,
void
CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* protoProto,
JSClass* protoClass, JSObject** protoCache,
JSClass* constructorClass, JSNativeHolder* constructor,
unsigned ctorNargs, JSObject** constructorCache,
const DOMClass* domClass,
const NativeProperties* regularProperties,
const NativeProperties* chromeOnlyProperties,
const char* name);
/*
@ -1244,19 +1269,95 @@ public:
}
};
inline bool
IdEquals(jsid id, const char* string)
{
return JSID_IS_STRING(id) &&
JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), string);
}
inline bool
AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
{
return vector.growBy(1) &&
InternJSString(cx, vector[vector.length() - 1], name);
}
// Implementation of the bits that XrayWrapper needs
bool
XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
JSPropertyDescriptor* desc,
const NativeProperties* nativeProperties,
const NativeProperties* chromeOnlyNativeProperties);
/**
* This resolves indexed or named properties of obj.
*
* wrapper is the Xray JS object.
* obj is the target object of the Xray, a binding's instance object or a
* interface or interface prototype object.
*/
bool
XrayEnumerateProperties(JSObject* wrapper,
JS::AutoIdVector& props,
const NativeProperties* nativeProperties,
const NativeProperties* chromeOnlyNativeProperties);
XrayResolveOwnProperty(JSContext* cx, JSObject* wrapper, JSObject* obj,
jsid id, bool set, JSPropertyDescriptor* desc);
/**
* This resolves operations, attributes and constants of the interfaces for obj.
*
* wrapper is the Xray JS object.
* obj is the target object of the Xray, a binding's instance object or a
* interface or interface prototype object.
*/
bool
XrayResolveNativeProperty(JSContext* cx, JSObject* wrapper, JSObject* obj,
jsid id, JSPropertyDescriptor* desc);
/**
* This enumerates indexed or named properties of obj and operations, attributes
* and constants of the interfaces for obj.
*
* wrapper is the Xray JS object.
* obj is the target object of the Xray, a binding's instance object or a
* interface or interface prototype object.
*/
bool
XrayEnumerateProperties(JSContext* cx, JSObject* wrapper, JSObject* obj,
bool ownOnly, JS::AutoIdVector& props);
extern NativePropertyHooks sWorkerNativePropertyHooks;
// We use one constructor JSNative to represent all DOM interface objects (so
// we can easily detect when we need to wrap them in an Xray wrapper). We store
// the real JSNative in the mNative member of a JSNativeHolder in the
// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
// specific interface object. We also store the NativeProperties in the
// JSNativeHolder. The CONSTRUCTOR_XRAY_EXPANDO_SLOT is used to store the
// expando chain of the Xray for the interface object.
// Note that some interface objects are not yet a JSFunction but a normal
// JSObject with a DOMJSClass, those do not use these slots.
enum {
CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0,
CONSTRUCTOR_XRAY_EXPANDO_SLOT
};
JSBool
Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
inline bool
UseDOMXray(JSObject* obj)
{
const js::Class* clasp = js::GetObjectClass(obj);
return IsDOMClass(clasp) ||
IsDOMProxy(obj, clasp) ||
JS_IsNativeFunction(obj, Constructor) ||
IsDOMIfaceAndProtoClass(clasp);
}
#ifdef DEBUG
inline bool
HasConstructor(JSObject* obj)
{
return JS_IsNativeFunction(obj, Constructor) ||
js::GetObjectClass(obj)->construct;
}
#endif
// Transfer reference in ptr to smartPtr.
template<class T>
inline void
@ -1292,6 +1393,24 @@ protected:
JS::Value& aVal);
};
/**
* This creates a JSString containing the value that the toString function for
* obj should create according to the WebIDL specification, ignoring any
* modifications by script. The value is prefixed with pre and postfixed with
* post, unless this is called for an object that has a stringifier. It is
* specifically for use by Xray code.
*
* wrapper is the Xray JS object.
* obj is the target object of the Xray, a binding's instance object or a
* interface or interface prototype object.
* pre is a string that should be prefixed to the value.
* post is a string that should be prefixed to the value.
* v contains the JSString for the value if the function returns true.
*/
bool
NativeToString(JSContext* cx, JSObject* wrapper, JSObject* obj, const char* pre,
const char* post, JS::Value* v);
} // namespace dom
} // namespace mozilla

View File

@ -62,13 +62,14 @@ class CGNativePropertyHooks(CGThing):
"""
Generate a NativePropertyHooks for a given descriptor
"""
def __init__(self, descriptor):
def __init__(self, descriptor, properties):
CGThing.__init__(self)
self.descriptor = descriptor
self.properties = properties
def declare(self):
if self.descriptor.workers:
return ""
return "extern const NativePropertyHooks NativeHooks;\n"
return "extern const NativePropertyHooks sNativePropertyHooks;\n"
def define(self):
if self.descriptor.workers:
return ""
@ -76,13 +77,45 @@ class CGNativePropertyHooks(CGThing):
resolveOwnProperty = "ResolveOwnProperty"
enumerateOwnProperties = "EnumerateOwnProperties"
else:
enumerateOwnProperties = resolveOwnProperty = "NULL"
resolveOwnProperty = "nullptr"
enumerateOwnProperties = "nullptr"
if self.properties.hasNonChromeOnly():
regular = "&sNativeProperties"
else:
regular = "nullptr"
if self.properties.hasChromeOnly():
chrome = "&sChromeOnlyNativeProperties"
else:
chrome = "nullptr"
constructorID = "constructors::id::"
if self.descriptor.interface.hasInterfaceObject():
constructorID += self.descriptor.name
else:
constructorID += "_ID_Count"
prototypeID = "prototypes::id::"
if self.descriptor.interface.hasInterfacePrototypeObject():
prototypeID += self.descriptor.name
else:
prototypeID += "_ID_Count"
parent = self.descriptor.interface.parent
parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::sNativePropertyHooks"
if parent else 'NULL')
return """
const NativePropertyHooks NativeHooks = { %s, ResolveProperty, %s, EnumerateProperties, %s };
""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
return CGWrapper(CGIndenter(CGList([CGGeneric(resolveOwnProperty),
CGGeneric(enumerateOwnProperties),
CGWrapper(CGList([CGGeneric(regular),
CGGeneric(chrome)],
", "),
pre="{ ", post=" }"),
CGGeneric(prototypeID),
CGGeneric(constructorID),
CGGeneric(parentHooks)],
",\n")),
pre="const NativePropertyHooks sNativePropertyHooks = {\n",
post="\n};\n").define()
def NativePropertyHooks(descriptor):
return "&sWorkerNativePropertyHooks" if descriptor.workers else "&sNativePropertyHooks"
def DOMClass(descriptor):
protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
@ -92,16 +125,17 @@ def DOMClass(descriptor):
# padding.
protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
prototypeChainString = ', '.join(protoList)
nativeHooks = "NULL" if descriptor.workers else "&NativeHooks"
if descriptor.workers or descriptor.nativeOwnership != 'refcounted':
participant = "nullptr"
else:
participant = "NS_CYCLE_COLLECTION_PARTICIPANT(%s)" % descriptor.nativeType
return """{
{ %s },
%s, %s, %s
%s,
%s,
%s
}""" % (prototypeChainString, toStringBool(descriptor.nativeOwnership == 'nsisupports'),
nativeHooks,
NativePropertyHooks(descriptor),
participant)
class CGDOMJSClass(CGThing):
@ -144,64 +178,79 @@ DOMJSClass Class = {
CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
class CGPrototypeJSClass(CGThing):
def __init__(self, descriptor):
def __init__(self, descriptor, properties):
CGThing.__init__(self)
self.descriptor = descriptor
self.properties = properties
def declare(self):
# We're purely for internal consumption
return ""
def define(self):
return """static JSClass PrototypeClass = {
"%sPrototype",
JSCLASS_HAS_RESERVED_SLOTS(1),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* hasInstance */
NULL, /* construct */
NULL, /* trace */
JSCLASS_NO_INTERNAL_MEMBERS
return """static DOMIfaceAndProtoJSClass PrototypeClass = {
{
"%sPrototype",
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
nullptr, /* finalize */
nullptr, /* checkAccess */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
JSCLASS_NO_INTERNAL_MEMBERS
},
eInterfacePrototype,
%s
};
""" % (self.descriptor.interface.identifier.name)
""" % (self.descriptor.interface.identifier.name,
NativePropertyHooks(self.descriptor))
class CGInterfaceObjectJSClass(CGThing):
def __init__(self, descriptor):
def __init__(self, descriptor, properties):
CGThing.__init__(self)
self.descriptor = descriptor
self.properties = properties
def declare(self):
# We're purely for internal consumption
return ""
def define(self):
if not self.descriptor.hasInstanceInterface:
return ""
ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
if self.descriptor.interface.ctor():
ctorname = CONSTRUCT_HOOK_NAME
else:
ctorname = "ThrowingConstructor"
hasinstance = HASINSTANCE_HOOK_NAME
return """
static JSClass InterfaceObjectClass = {
"Function", 0,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* checkAccess */
%s, /* call */
%s, /* hasInstance */
%s, /* construct */
NULL, /* trace */
JSCLASS_NO_INTERNAL_MEMBERS
static DOMIfaceAndProtoJSClass InterfaceObjectClass = {
{
"Function",
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
nullptr, /* finalize */
nullptr, /* checkAccess */
%s, /* call */
%s, /* hasInstance */
%s, /* construct */
nullptr, /* trace */
JSCLASS_NO_INTERNAL_MEMBERS
},
eInterface,
%s
};
""" % (ctorname, hasinstance, ctorname)
""" % (ctorname, hasinstance, ctorname, NativePropertyHooks(self.descriptor))
class CGList(CGThing):
"""
@ -805,6 +854,18 @@ class CGClassConstructHook(CGAbstractStaticMethod):
self.descriptor, self._ctor)
return preamble + callGenerator.define();
class CGClassConstructHookHolder(CGGeneric):
def __init__(self, descriptor):
if descriptor.interface.ctor():
constructHook = CONSTRUCT_HOOK_NAME
else:
constructHook = "ThrowingConstructor"
CGGeneric.__init__(self,
"JSNativeHolder " + CONSTRUCT_HOOK_NAME + "_holder = {\n" +
" " + constructHook + ",\n" +
" " + NativePropertyHooks(descriptor) + "\n" +
"};\n")
class CGClassHasInstanceHook(CGAbstractStaticMethod):
def __init__(self, descriptor):
args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
@ -1166,6 +1227,7 @@ class ConstDefiner(PropertyDefiner):
class PropertyArrays():
def __init__(self, descriptor):
self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
self.staticAttrs = None
self.methods = MethodDefiner(descriptor, "Methods", False)
self.attrs = AttrDefiner(descriptor, "Attributes", unforgeable=False)
self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
@ -1233,8 +1295,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
"""
def __init__(self, descriptor, properties):
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
Argument('JSObject*', 'aReceiver')]
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'JSObject*', args)
Argument('JSObject**', 'protoAndIfaceArray')]
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
self.properties = properties
def definition_body(self):
protoChain = self.descriptor.prototypeChain
@ -1242,7 +1304,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
else:
parentProtoName = self.descriptor.prototypeChain[-2]
getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
toBindingNamespace(parentProtoName))
needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
@ -1275,7 +1337,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
initIds = CGList(
[initIds,
CGGeneric((" %s_ids[0] = JSID_VOID;\n"
" return NULL;") % idsToInit[0]),
" return;") % idsToInit[0]),
CGGeneric("}")],
"\n")
else:
@ -1299,20 +1361,33 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
getParentProto = ("JSObject* parentProto = %s;\n" +
"if (!parentProto) {\n" +
" return NULL;\n" +
" return;\n" +
"}\n") % getParentProto
needInterfaceObjectClass = (needInterfaceObject and
self.descriptor.hasInstanceInterface)
needConstructor = (needInterfaceObject and
not self.descriptor.hasInstanceInterface)
constructHook = "&" + CONSTRUCT_HOOK_NAME + "_holder"
if self.descriptor.interface.ctor():
constructHook = CONSTRUCT_HOOK_NAME
constructArgs = methodLength(self.descriptor.interface.ctor())
else:
constructHook = "ThrowingConstructor"
constructArgs = 0
if needInterfacePrototypeObject:
protoClass = "&PrototypeClass.mBase"
protoCache = "&protoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name
else:
protoClass = "nullptr"
protoCache = "nullptr"
if needInterfaceObject:
if self.descriptor.hasInstanceInterface:
interfaceClass = "&InterfaceObjectClass.mBase"
else:
interfaceClass = "nullptr"
interfaceCache = "&protoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
else:
interfaceClass = "nullptr"
interfaceCache = "nullptr"
if self.descriptor.concrete:
if self.descriptor.proxy:
domClass = "&Class"
@ -1330,20 +1405,20 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr"
else:
chromeProperties = "nullptr"
call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
%s, %s, %s, %d,
%s,
%s,
%s,
%s);""" % (
"&PrototypeClass" if needInterfacePrototypeObject else "NULL",
"&InterfaceObjectClass" if needInterfaceObjectClass else "NULL",
constructHook if needConstructor else "NULL",
constructArgs,
call = ("dom::CreateInterfaceObjects(aCx, aGlobal, parentProto,\n"
" %s, %s,\n"
" %s, %s, %d, %s,\n"
" %s,\n"
" %s,\n"
" %s,\n"
" %s);" % (
protoClass, protoCache,
interfaceClass, constructHook if needConstructor else "nullptr",
constructArgs, interfaceCache,
domClass,
properties,
chromeProperties,
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL"))
functionBody = CGList(
[CGGeneric(getParentProto), initIds, prefCache, CGGeneric(call)],
"\n\n")
@ -1355,20 +1430,13 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
constructor object).
"""
def __init__(self, descriptor, name, idPrefix=""):
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
Argument('JSObject*', 'aReceiver')]
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
CGAbstractMethod.__init__(self, descriptor, name,
'JSObject*', args, inline=True)
self.id = idPrefix + "id::" + self.descriptor.name
def definition_body(self):
return """
/* aGlobal and aReceiver are usually the same, but they can be different
too. For example a sandbox often has an xray wrapper for a window as the
prototype of the sandbox's global. In that case aReceiver is the xray
wrapper and aGlobal is the sandbox's global.
*/
/* Make sure our global is sane. Hopefully we can remove this sometime */
if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
return NULL;
@ -1377,7 +1445,8 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
JSObject** protoAndIfaceArray = GetProtoAndIfaceArray(aGlobal);
JSObject* cachedObject = protoAndIfaceArray[%s];
if (!cachedObject) {
protoAndIfaceArray[%s] = cachedObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray);
cachedObject = protoAndIfaceArray[%s];
}
/* cachedObject might _still_ be null, but that's OK */
@ -1441,9 +1510,9 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
a given interface.
"""
def __init__(self, descriptor):
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aReceiver'),
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
Argument('bool*', 'aEnabled')]
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args)
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
def declare(self):
if self.descriptor.workers:
@ -1456,18 +1525,10 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
return CGAbstractMethod.define(self)
def definition_body(self):
if self.descriptor.interface.hasInterfacePrototypeObject():
# We depend on GetProtoObject defining an interface constructor
# object as needed.
getter = "GetProtoObject"
else:
getter = "GetConstructorObject"
return (" JSObject* global = JS_GetGlobalForObject(aCx, aReceiver);\n" +
CheckPref(self.descriptor, "global", "*aEnabled", "false") +
return (CheckPref(self.descriptor, "aGlobal", "*aEnabled", "nullptr") +
"""
*aEnabled = true;
return !!%s(aCx, global, aReceiver);""" % (getter))
return GetConstructorObject(aCx, aGlobal);""")
class CGPrefEnabled(CGAbstractMethod):
"""
@ -1594,7 +1655,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
JSAutoCompartment ac(aCx, parent);
JSObject* global = JS_GetGlobalForObject(aCx, parent);
%s
JSObject* proto = GetProtoObject(aCx, global, global);
JSObject* proto = GetProtoObject(aCx, global);
if (!proto) {
return NULL;
}
@ -1636,7 +1697,7 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
def definition_body(self):
return """
JSObject* global = JS_GetGlobalForObject(aCx, aScope);
JSObject* proto = GetProtoObject(aCx, global, global);
JSObject* proto = GetProtoObject(aCx, global);
if (!proto) {
return NULL;
}
@ -4676,15 +4737,12 @@ class CGClass(CGThing):
class CGResolveOwnProperty(CGAbstractMethod):
def __init__(self, descriptor):
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
Argument('jsid', 'id'), Argument('bool', 'set'),
Argument('JSObject*', 'obj'), Argument('jsid', 'id'),
Argument('bool', 'set'),
Argument('JSPropertyDescriptor*', 'desc')]
CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
def definition_body(self):
return """ JSObject* obj = wrapper;
if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
obj = js::UnwrapObject(obj);
}
// We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
return """ // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
// properties. If that changes we'll need to filter here.
return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, set, desc);
"""
@ -4692,64 +4750,15 @@ class CGResolveOwnProperty(CGAbstractMethod):
class CGEnumerateOwnProperties(CGAbstractMethod):
def __init__(self, descriptor):
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
Argument('JSObject*', 'obj'),
Argument('JS::AutoIdVector&', 'props')]
CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
def definition_body(self):
return """ JSObject* obj = wrapper;
if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
obj = js::UnwrapObject(obj);
}
// We rely on getOwnPropertyNames not shadowing prototype properties by named
return """ // We rely on getOwnPropertyNames not shadowing prototype properties by named
// properties. If that changes we'll need to filter here.
return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
"""
class CGXrayHelper(CGAbstractMethod):
def __init__(self, descriptor, name, args, properties):
CGAbstractMethod.__init__(self, descriptor, name, "bool", args)
self.properties = properties
def definition_body(self):
prefixArgs = CGGeneric(self.getPrefixArgs())
if self.properties.hasNonChromeOnly():
regular = "&sNativeProperties"
else:
regular = "nullptr"
regular = CGGeneric(regular)
if self.properties.hasChromeOnly():
chrome = "&sChromeOnlyNativeProperties"
else:
chrome = "nullptr"
chrome = CGGeneric(chrome)
return CGIndenter(
CGWrapper(CGList([prefixArgs, regular, chrome], ",\n"),
pre=("return Xray%s(" % self.name),
post=");",
reindent=True)).define()
class CGResolveProperty(CGXrayHelper):
def __init__(self, descriptor, properties):
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
Argument('jsid', 'id'), Argument('bool', 'set'),
Argument('JSPropertyDescriptor*', 'desc')]
CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args,
properties)
def getPrefixArgs(self):
return "cx, wrapper, id, desc"
class CGEnumerateProperties(CGXrayHelper):
def __init__(self, descriptor, properties):
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
Argument('JS::AutoIdVector&', 'props')]
CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
properties)
def getPrefixArgs(self):
return "wrapper, props"
class CGPrototypeTraitsClass(CGClass):
def __init__(self, descriptor, indent=''):
templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
@ -5375,32 +5384,32 @@ class CGDescriptor(CGThing):
if (descriptor.customTrace):
cgThings.append(CGClassTraceHook(descriptor))
if descriptor.interface.hasInterfaceObject():
cgThings.append(CGClassConstructHook(descriptor))
cgThings.append(CGClassHasInstanceHook(descriptor))
cgThings.append(CGInterfaceObjectJSClass(descriptor))
if descriptor.interface.hasInterfacePrototypeObject():
cgThings.append(CGPrototypeJSClass(descriptor))
properties = PropertyArrays(descriptor)
cgThings.append(CGGeneric(define=str(properties)))
cgThings.append(CGNativeProperties(descriptor, properties))
cgThings.append(CGNativePropertyHooks(descriptor, properties))
if descriptor.interface.hasInterfaceObject():
cgThings.append(CGClassConstructHook(descriptor))
cgThings.append(CGClassHasInstanceHook(descriptor))
cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
cgThings.append(CGClassConstructHookHolder(descriptor))
if descriptor.interface.hasInterfacePrototypeObject():
cgThings.append(CGPrototypeJSClass(descriptor, properties))
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
if descriptor.interface.hasInterfacePrototypeObject():
cgThings.append(CGGetProtoObjectMethod(descriptor))
else:
if descriptor.interface.hasInterfaceObject():
cgThings.append(CGGetConstructorObjectMethod(descriptor))
# Set up our Xray callbacks as needed. Note that we don't need to do
# it in workers.
if (descriptor.interface.hasInterfacePrototypeObject() and
not descriptor.workers):
if descriptor.concrete and descriptor.proxy:
cgThings.append(CGResolveOwnProperty(descriptor))
cgThings.append(CGEnumerateOwnProperties(descriptor))
cgThings.append(CGResolveProperty(descriptor, properties))
cgThings.append(CGEnumerateProperties(descriptor, properties))
if not descriptor.workers and descriptor.concrete and descriptor.proxy:
cgThings.append(CGResolveOwnProperty(descriptor))
cgThings.append(CGEnumerateOwnProperties(descriptor))
if descriptor.interface.hasInterfaceObject():
cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
@ -5410,9 +5419,6 @@ class CGDescriptor(CGThing):
descriptor.interface.getExtendedAttribute("PrefControlled") is not None):
cgThings.append(CGPrefEnabled(descriptor))
if descriptor.interface.hasInterfacePrototypeObject():
cgThings.append(CGNativePropertyHooks(descriptor))
if descriptor.concrete:
if descriptor.proxy:
cgThings.append(CGProxyIsProxy(descriptor))
@ -6422,7 +6428,8 @@ class GlobalGenRoots():
# Prototype ID enum.
protos = [d.name for d in config.getDescriptors(hasInterfacePrototypeObject=True)]
idEnum = CGNamespacedEnum('id', 'ID', protos, [0])
idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos,
[0, '_ID_Start'])
idEnum = CGList([idEnum])
idEnum.append(CGGeneric(declare="const unsigned MaxProtoChainLength = " +
str(config.maxProtoChainLength) + ";\n\n"))
@ -6435,9 +6442,9 @@ class GlobalGenRoots():
curr = CGList([idEnum])
# Constructor ID enum.
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True,
hasInterfacePrototypeObject=False)]
idEnum = CGNamespacedEnum('id', 'ID', constructors, [0])
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
['prototypes::id::_ID_Count', '_ID_Start'])
# Wrap all of that in our namespaces.
idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],

View File

@ -8,6 +8,7 @@
#include "jsapi.h"
#include "jsfriendapi.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/PrototypeList.h" // auto-generated
@ -27,21 +28,27 @@ class nsCycleCollectionParticipant;
// We use these flag bits for the new bindings.
#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
#define JSCLASS_IS_DOMIFACEANDPROTOJSCLASS JSCLASS_USERBIT2
// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
// LSetDOMProperty. Those constants need to be changed accordingly if this value
// changes.
#define DOM_PROTO_INSTANCE_CLASS_SLOT 0
MOZ_STATIC_ASSERT(DOM_PROTO_INSTANCE_CLASS_SLOT != DOM_XRAY_EXPANDO_SLOT,
"Interface prototype object use both of these, so they must "
"not be the same slot.");
namespace mozilla {
namespace dom {
typedef bool
(* ResolveProperty)(JSContext* cx, JSObject* wrapper, jsid id, bool set,
JSPropertyDescriptor* desc);
(* ResolveOwnProperty)(JSContext* cx, JSObject* wrapper, JSObject* obj, jsid id,
bool set, JSPropertyDescriptor* desc);
typedef bool
(* EnumerateProperties)(JSContext* cx, JSObject* wrapper,
JS::AutoIdVector& props);
(* EnumerateOwnProperties)(JSContext* cx, JSObject* wrapper, JSObject* obj,
JS::AutoIdVector& props);
struct ConstantSpec
{
@ -78,14 +85,45 @@ struct NativeProperties
ConstantSpec* constantSpecs;
};
struct NativePropertiesHolder
{
const NativeProperties* regular;
const NativeProperties* chromeOnly;
};
// Helper structure for Xrays for DOM binding objects. The same instance is used
// for instances, interface objects and interface prototype objects of a
// specific interface.
struct NativePropertyHooks
{
ResolveProperty mResolveOwnProperty;
ResolveProperty mResolveProperty;
EnumerateProperties mEnumerateOwnProperties;
EnumerateProperties mEnumerateProperties;
// The hook to call for resolving indexed or named properties. May be null if
// there can't be any.
ResolveOwnProperty mResolveOwnProperty;
// The hook to call for enumerating indexed or named properties. May be null
// if there can't be any.
EnumerateOwnProperties mEnumerateOwnProperties;
const NativePropertyHooks *mProtoHooks;
// The property arrays for this interface.
NativePropertiesHolder mNativeProperties;
// This will be set to the ID of the interface prototype object for the
// interface, if it has one. If it doesn't have one it will be set to
// prototypes::id::_ID_Count.
prototypes::ID mPrototypeID;
// This will be set to the ID of the interface object for the interface, if it
// has one. If it doesn't have one it will be set to
// constructors::id::_ID_Count.
constructors::ID mConstructorID;
// The NativePropertyHooks instance for the parent interface.
const NativePropertyHooks* mProtoHooks;
};
enum DOMObjectType {
eInstance,
eInterface,
eInterfacePrototype
};
struct DOMClass
@ -137,6 +175,31 @@ struct DOMJSClass
JSClass* ToJSClass() { return &mBase; }
};
// Special JSClass for DOM interface and interface prototype objects.
struct DOMIfaceAndProtoJSClass
{
// It would be nice to just inherit from JSClass, but that precludes pure
// compile-time initialization of the form
// |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
// initialization for aggregate/POD types.
JSClass mBase;
// Either eInterface or eInterfacePrototype
DOMObjectType mType;
const NativePropertyHooks* mNativeHooks;
static const DOMIfaceAndProtoJSClass* FromJSClass(const JSClass* base) {
MOZ_ASSERT(base->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS);
return reinterpret_cast<const DOMIfaceAndProtoJSClass*>(base);
}
static const DOMIfaceAndProtoJSClass* FromJSClass(const js::Class* base) {
return FromJSClass(Jsvalify(base));
}
JSClass* ToJSClass() { return &mBase; }
};
inline bool
HasProtoAndIfaceArray(JSObject* global)
{

View File

@ -20,6 +20,7 @@ MOCHITEST_BROWSER_FILES := \
browser_ConsoleStoragePBTest.js \
browser_autofocus_preference.js \
browser_bug396843.js \
browser_xhr_sandbox.js \
$(NULL)
ifeq (Linux,$(OS_ARCH))

View File

@ -0,0 +1,49 @@
// This code is evaluated in a sandbox courtesy of toSource();
let sandboxCode = (function() {
let req = new XMLHttpRequest();
req.open("GET", "http://mochi.test:8888/browser/dom/tests/browser/", true);
req.onreadystatechange = function() {
if (req.readyState === 4) {
// If we get past the problem above, we end up with a req.status of zero
// (ie, blocked due to CORS) even though we are fetching from the same
// origin as the window itself.
let result;
if (req.status != 200) {
result = "ERROR: got request status of " + req.status;
} else if (req.responseText.length == 0) {
result = "ERROR: got zero byte response text";
} else {
result = "ok";
}
postMessage({result: result}, "*");
}
};
req.send(null);
}).toSource() + "();";
function test() {
waitForExplicitFinish();
let appShell = Cc["@mozilla.org/appshell/appShellService;1"]
.getService(Ci.nsIAppShellService);
let doc = appShell.hiddenDOMWindow.document;
let frame = doc.createElement("iframe");
frame.setAttribute("type", "content");
frame.setAttribute("src", "http://mochi.test:8888/browser/dom/tests/browser/browser_xhr_sandbox.js");
frame.addEventListener("load", function () {
let workerWindow = frame.contentWindow;
workerWindow.addEventListener("message", function(evt) {
is(evt.data.result, "ok", "check the sandbox code was happy");
finish();
}, true);
let sandbox = new Cu.Sandbox(workerWindow);
// inject some functions from the window into the sandbox.
// postMessage so the async code in the sandbox can report a result.
sandbox.importFunction(workerWindow.postMessage, "postMessage");
sandbox.importFunction(workerWindow.XMLHttpRequest, "XMLHttpRequest");
Cu.evalInSandbox(sandboxCode, sandbox, "1.8");
}, true);
let container = doc.body ? doc.body : doc.documentElement;
container.appendChild(frame);
}

View File

@ -52,6 +52,8 @@ MOCHITEST_CHROME_FILES = \
file_bug799299.xul \
test_bug800817.xul \
file_bug800817.xul \
test_subscript_bindings.xul \
file_subscript_bindings.js \
$(NULL)
ifeq (WINNT,$(OS_ARCH))

View File

@ -0,0 +1 @@
new window.XMLHttpRequest();

View File

@ -21,24 +21,58 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267
<![CDATA[
/** Test for Bug 741267 **/
function isXrayWrapper(x) {
return XPCNativeWrapper.unwrap(x) != x;
}
function doTest() {
var win = $("t").contentWindow;
var sandbox = Components.utils.Sandbox(win, { sandboxPrototype: win });
try {
var nl = Components.utils.evalInSandbox("NodeList", sandbox);
is(nl, String(NodeList), "'NodeList' in a sandbox should return the NodeList interface prototype object");
var css = Components.utils.evalInSandbox("CSSStyleDeclaration", sandbox);
is(css.prototype, "[object CSSStyleDeclarationPrototype]", "'CSSStyleDeclaration.prototype' in a sandbox should return the CSSStyleDeclaration interface prototype object");
} catch (e) {
ok(false, "'NodeList' shouldn't throw in a sandbox");
ok(false, "'CSSStyleDeclaration' shouldn't throw in a sandbox");
}
try {
var et = Components.utils.evalInSandbox("EventTarget", sandbox);
ok(et, "'EventTarget' in a sandbox should return the EventTarget interface prototype object");
ok(et, "'EventTarget' in a sandbox should return the EventTarget interface object");
ok(isXrayWrapper(et), "Getting an interface object on an Xray wrapper should return an Xray wrapper");
} catch (e) {
ok(false, "'EventTarget' shouldn't throw in a sandbox");
}
try {
var xhr = Components.utils.evalInSandbox("XMLHttpRequest.prototype", sandbox);
ok(xhr, "'XMLHttpRequest.prototype' in a sandbox should return the XMLHttpRequest interface prototype object");
ok(isXrayWrapper(xhr), "Getting an interface prototype object on an Xray wrapper should return an Xray wrapper");
ok(isXrayWrapper(xhr.constructor), "Getting the constructor property on an Xray wrapper of an interface prototype object should return an Xray wrapper");
} catch (e) {
ok(false, "'XMLHttpRequest.prototype' shouldn't throw in a sandbox");
}
try {
var xhr = Components.utils.evalInSandbox("XMLHttpRequest", sandbox);
is(xhr, "[object XrayWrapper " + XMLHttpRequest + "]", "'XMLHttpRequest' in a sandbox should return the XMLHttpRequest interface object");
ok(isXrayWrapper(xhr.prototype), "Getting the prototype property on an Xray wrapper of an interface object should return an Xray wrapper");
} catch (e) {
ok(false, "'XMLHttpRequest' shouldn't throw in a sandbox");
}
try {
var xhr = Components.utils.evalInSandbox("XMLHttpRequest()", sandbox);
is(xhr, "[object XMLHttpRequest]", "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
is("" + xhr, "" + XMLHttpRequest(), "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
} catch (e) {
ok(false, "'XMLHttpRequest()' shouldn't throw in a sandbox");
}
try {
var xhr = Components.utils.evalInSandbox("XMLHttpRequest.prototype.toString = function () { return 'Failed'; }; XMLHttpRequest();", sandbox);
is(xhr.toString(), "[object XrayWrapper " + XMLHttpRequest() + "]", "XMLHttpRequest.prototype.toString in the sandbox should not override the native toString behaviour");
} catch (e) {
ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox");
}
try {
Components.utils.evalInSandbox("document.defaultView.XMLHttpRequest = function() {};", sandbox);
var win = Components.utils.evalInSandbox("document.defaultView", sandbox);
var xhr = win.XMLHttpRequest();
is("" + xhr, "" + XMLHttpRequest(), "'XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
} catch (e) {
ok(false, "'XMLHttpRequest()' shouldn't throw in a sandbox");
}
@ -48,6 +82,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=741267
} catch (e) {
ok(false, "'document.createElement('canvas').getContext('2D')' shouldn't throw in a sandbox");
}
try {
var classList = Components.utils.evalInSandbox("document.body.className = 'a b'; document.body.classList", sandbox);
is(classList.toString(), "a b", "Stringifier should be called");
} catch (e) {
ok(false, "'document.createElement('canvas').getContext('2D')' shouldn't throw in a sandbox");
}
try {
var ctx = Components.utils.evalInSandbox("var ctx = document.createElement('canvas').getContext('2d'); ctx.foopy = 5; ctx", sandbox);
ok(!("foopy" in ctx), "We should have an Xray here");

View File

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=741267
-->
<window title="Mozilla Bug 741267"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<iframe id="t"></iframe>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741267"
target="_blank">Mozilla Bug 741267</a>
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for Bug 741267 **/
function doTest() {
// Resolve XMLHttpRequest on the chrome global
new XMLHttpRequest();
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
var context = { window: $("t").contentWindow };
var thrown = false;
try {
loader.loadSubScript(/.*\//.exec(window.location.href)[0] + "file_subscript_bindings.js", context);
} catch (e) {
thrown = true;
}
ok(!thrown, "'new window.XMLHttpRequest()' in a subscript shouldn't throw");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
]]>
</script>
</window>

View File

@ -39,7 +39,7 @@ struct WrapPrototypeTraits
GetProtoObject(JSContext* aCx, JSObject* aGlobal) \
{ \
using namespace mozilla::dom; \
return _class##Binding_workers::GetProtoObject(aCx, aGlobal, aGlobal); \
return _class##Binding_workers::GetProtoObject(aCx, aGlobal); \
} \
};

View File

@ -548,7 +548,7 @@ ResolveWorkerClasses(JSContext* aCx, JSHandleObject aObj, JSHandleId aId, unsign
return true;
}
JSObject* eventTarget = EventTargetBinding_workers::GetProtoObject(aCx, aObj, aObj);
JSObject* eventTarget = EventTargetBinding_workers::GetProtoObject(aCx, aObj);
if (!eventTarget) {
return false;
}

View File

@ -25,25 +25,6 @@ USING_WORKERS_NAMESPACE
using namespace mozilla::dom;
using mozilla::ErrorResult;
// These are temporary until these classes are moved to be codegenerated.
bool
WorkerResolveProperty(JSContext* cx, JSObject* wrapper, jsid id, bool set,
JSPropertyDescriptor* desc)
{
return true;
}
bool
WorkerEnumerateProperties(JSContext* cx, JSObject* wrapper,
JS::AutoIdVector& props)
{
return true;
}
NativePropertyHooks mozilla::dom::workers::sNativePropertyHooks =
{ WorkerResolveProperty, WorkerResolveProperty,
WorkerEnumerateProperties, WorkerEnumerateProperties,
NULL };
namespace {
class Worker
@ -299,8 +280,6 @@ private:
MOZ_STATIC_ASSERT(prototypes::MaxProtoChainLength == 3,
"The MaxProtoChainLength must match our manual DOMJSClasses");
// When this DOMJSClass is removed and it's the last consumer of
// sNativePropertyHooks then sNativePropertyHooks should be removed too.
DOMJSClass Worker::sClass = {
{
"Worker",
@ -314,7 +293,7 @@ DOMJSClass Worker::sClass = {
{ prototypes::id::EventTarget_workers, prototypes::id::_ID_Count,
prototypes::id::_ID_Count },
false,
&sNativePropertyHooks
&sWorkerNativePropertyHooks
}
};
@ -425,8 +404,6 @@ private:
MOZ_STATIC_ASSERT(prototypes::MaxProtoChainLength == 3,
"The MaxProtoChainLength must match our manual DOMJSClasses");
// When this DOMJSClass is removed and it's the last consumer of
// sNativePropertyHooks then sNativePropertyHooks should be removed too.
DOMJSClass ChromeWorker::sClass = {
{ "ChromeWorker",
JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2) |
@ -439,7 +416,7 @@ DOMJSClass ChromeWorker::sClass = {
{ prototypes::id::EventTarget_workers, prototypes::id::_ID_Count,
prototypes::id::_ID_Count },
false,
&sNativePropertyHooks
&sWorkerNativePropertyHooks
}
};

View File

@ -13,8 +13,6 @@
BEGIN_WORKERS_NAMESPACE
extern mozilla::dom::NativePropertyHooks sNativePropertyHooks;
namespace worker {
JSObject*

View File

@ -819,7 +819,7 @@ private:
DedicatedWorkerGlobalScope* scope =
UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj, eRegularDOMObject);
if (scope) {
mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
TraceProtoAndIfaceCache(aTrc, aObj);
scope->_trace(aTrc);
}
}
@ -852,8 +852,6 @@ private:
MOZ_STATIC_ASSERT(prototypes::MaxProtoChainLength == 3,
"The MaxProtoChainLength must match our manual DOMJSClasses");
// When this DOMJSClass is removed and it's the last consumer of
// sNativePropertyHooks then sNativePropertyHooks should be removed too.
DOMJSClass DedicatedWorkerGlobalScope::sClass = {
{
// We don't have to worry about Xray expando slots here because we'll never
@ -869,7 +867,7 @@ DOMJSClass DedicatedWorkerGlobalScope::sClass = {
{ prototypes::id::EventTarget_workers, prototypes::id::_ID_Count,
prototypes::id::_ID_Count },
false,
&sNativePropertyHooks
&sWorkerNativePropertyHooks
}
};
@ -940,7 +938,7 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
// -> Object
JSObject* eventTargetProto =
EventTargetBinding_workers::GetProtoObject(aCx, global, global);
EventTargetBinding_workers::GetProtoObject(aCx, global);
if (!eventTargetProto) {
return NULL;
}
@ -987,14 +985,10 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx)
return NULL;
}
// Init other paris-bindings. Use GetProtoObject so the proto will
// be correctly cached in the proto cache. Otherwise we'll end up
// double-calling CreateInterfaceObjects when we actually create an
// object which has these protos, which breaks things like
// instanceof.
if (!FileReaderSyncBinding_workers::GetProtoObject(aCx, global, global) ||
!XMLHttpRequestBinding_workers::GetProtoObject(aCx, global, global) ||
!XMLHttpRequestUploadBinding_workers::GetProtoObject(aCx, global, global)) {
// Init other paris-bindings.
if (!FileReaderSyncBinding_workers::GetConstructorObject(aCx, global) ||
!XMLHttpRequestBinding_workers::GetConstructorObject(aCx, global) ||
!XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, global)) {
return NULL;
}

View File

@ -3054,22 +3054,9 @@ WrapForSandbox(JSContext *cx, bool wantXrays, jsval *vp)
xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
// A proxy handler that lets us wrap callables and invoke them with
// the correct this object, while forwarding all other operations down
// to them directly.
class SandboxCallableProxyHandler : public js::Wrapper {
public:
SandboxCallableProxyHandler() : js::Wrapper(0)
{
}
virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc,
Value *vp);
};
bool
SandboxCallableProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc,
Value *vp)
xpc::SandboxCallableProxyHandler::call(JSContext *cx, JSObject *proxy,
unsigned argc, Value *vp)
{
// We forward the call to our underlying callable. The callable to forward
// to can be gotten via GetProxyCall.
@ -3099,7 +3086,7 @@ SandboxCallableProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc,
JS_ARGV(cx, vp), vp);
}
static SandboxCallableProxyHandler sandboxCallableProxyHandler;
xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
// Wrap a callable such that if we're called with oldThisObj as the
// "this" we will instead call it with newThisObj as the this.
@ -3116,7 +3103,7 @@ WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy)
// We need to pass the given callable in as the "call" and
// "construct" so we get a function proxy.
return js::NewProxyObject(cx, &sandboxCallableProxyHandler,
return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
ObjectValue(*callable), nullptr,
sandboxProtoProxy, callable, callable);
}

View File

@ -1160,8 +1160,8 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
// Init WebIDL binding constructors wanted on all XPConnect globals.
if (!TextDecoderBinding::GetProtoObject(aJSContext, global, global) ||
!TextEncoderBinding::GetProtoObject(aJSContext, global, global)) {
if (!TextDecoderBinding::GetConstructorObject(aJSContext, global) ||
!TextEncoderBinding::GetConstructorObject(aJSContext, global)) {
return UnexpectedFailure(NS_ERROR_FAILURE);
}

View File

@ -299,13 +299,19 @@ namespace dom {
extern int HandlerFamily;
inline void* ProxyFamily() { return &HandlerFamily; }
inline bool IsDOMProxy(JSObject *obj)
inline bool IsDOMProxy(JSObject *obj, const js::Class* clasp)
{
return js::IsProxy(obj) &&
MOZ_ASSERT(js::GetObjectClass(obj) == clasp);
return (js::IsObjectProxyClass(clasp) || js::IsFunctionProxyClass(clasp)) &&
js::GetProxyHandler(obj)->family() == ProxyFamily();
}
typedef bool
inline bool IsDOMProxy(JSObject *obj)
{
return IsDOMProxy(obj, js::GetObjectClass(obj));
}
typedef JSObject*
(*DefineInterface)(JSContext *cx, JSObject *global, bool *enabled);
typedef bool

View File

@ -351,7 +351,7 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
else
wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton;
} else if (mozilla::dom::IsDOMObject(obj)) {
} else if (mozilla::dom::UseDOMXray(obj)) {
wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
} else if (IsComponentsObject(obj)) {
wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,

View File

@ -49,7 +49,7 @@ XrayType
GetXrayType(JSObject *obj)
{
obj = js::UnwrapObject(obj, /* stopAtOuter = */ false);
if (mozilla::dom::IsDOMObject(obj))
if (mozilla::dom::UseDOMXray(obj))
return XrayForDOMObject;
js::Class* clasp = js::GetObjectClass(obj);
@ -229,6 +229,9 @@ public:
JSPropertyDescriptor *desc);
static bool enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
JS::AutoIdVector &props);
static bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp);
static bool construct(JSContext *cx, JSObject *wrapper, unsigned argc,
Value *argv, Value *rval);
static bool isResolving(JSContext *cx, JSObject *holder, jsid id)
{
@ -1176,17 +1179,11 @@ DOMXrayTraits::resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject
bool set, JSPropertyDescriptor *desc)
{
JSObject *obj = getTargetObject(wrapper);
const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
if (!XrayResolveNativeProperty(cx, wrapper, obj, id, desc))
return false;
do {
if (!nativeHooks->mResolveProperty(cx, wrapper, id, set, desc))
return false;
if (desc->obj) {
NS_ASSERTION(desc->obj == wrapper, "What did we resolve this on?");
return true;
}
} while ((nativeHooks = nativeHooks->mProtoHooks));
NS_ASSERTION(!desc->obj || desc->obj == wrapper,
"What did we resolve this on?");
return true;
}
@ -1202,15 +1199,11 @@ DOMXrayTraits::resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObjec
return ok;
JSObject *obj = getTargetObject(wrapper);
const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
if (!XrayResolveOwnProperty(cx, wrapper, obj, id, set, desc))
return false;
if (nativeHooks->mResolveOwnProperty) {
if (!nativeHooks->mResolveOwnProperty(cx, wrapper, id, set, desc))
return false;
NS_ASSERTION(!desc->obj || desc->obj == wrapper,
"What did we resolve this on?");
}
NS_ASSERTION(!desc->obj || desc->obj == wrapper,
"What did we resolve this on?");
return true;
}
@ -1218,23 +1211,53 @@ DOMXrayTraits::resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObjec
bool
DOMXrayTraits::enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
JS::AutoIdVector &props)
{
return XrayEnumerateProperties(cx, wrapper, getTargetObject(wrapper),
flags & (JSITER_OWNONLY | JSITER_HIDDEN),
props);
}
bool
DOMXrayTraits::call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp)
{
JSObject *obj = getTargetObject(wrapper);
const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
if (nativeHooks->mEnumerateOwnProperties &&
!nativeHooks->mEnumerateOwnProperties(cx, wrapper, props))
return false;
if (flags & (JSITER_OWNONLY | JSITER_HIDDEN))
return true;
do {
if (!nativeHooks->mEnumerateProperties(cx, wrapper, props)) {
AutoValueRooter rval(cx);
bool ok;
{
JSAutoCompartment ac(cx, obj);
if (!JS_WrapValue(cx, &vp[1]))
return false;
JS::Value* argv = JS_ARGV(cx, vp);
for (unsigned i = 0; i < argc; ++i) {
if (!JS_WrapValue(cx, &argv[i]))
return false;
}
} while ((nativeHooks = nativeHooks->mProtoHooks));
ok = JS::Call(cx, vp[1], obj, argc, argv, rval.addr());
}
if (!ok || !JS_WrapValue(cx, rval.addr()))
return false;
JS_SET_RVAL(cx, vp, rval.value());
return true;
}
bool
DOMXrayTraits::construct(JSContext *cx, JSObject *wrapper, unsigned argc,
Value *argv, Value *rval)
{
JSObject *obj = getTargetObject(wrapper);
MOZ_ASSERT(mozilla::dom::HasConstructor(obj));
JSObject *newObj;
{
JSAutoCompartment ac(cx, obj);
for (unsigned i = 0; i < argc; ++i) {
if (!JS_WrapValue(cx, &argv[i]))
return false;
}
newObj = JS_New(cx, obj, argc, argv);
}
if (!newObj || !JS_WrapObject(cx, &newObj))
return false;
rval->setObject(*newObj);
return true;
}
@ -1295,39 +1318,36 @@ XrayToString(JSContext *cx, unsigned argc, jsval *vp)
JSObject *wrapper = JS_THIS_OBJECT(cx, vp);
if (!wrapper)
return false;
if (IsWrapper(wrapper) &&
GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) {
wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
}
if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
JS_ReportError(cx, "XrayToString called on an incompatible object");
return false;
}
nsAutoString result(NS_LITERAL_STRING("[object XrayWrapper "));
JSObject *obj = &js::GetProxyPrivate(wrapper).toObject();
if (IsDOMProxy(obj)) {
JSString *wrapperStr = js::GetProxyHandler(wrapper)->obj_toString(cx, wrapper);
size_t length;
const jschar* chars = JS_GetStringCharsAndLength(cx, wrapperStr, &length);
if (!chars) {
JS_ReportOutOfMemory(cx);
return false;
}
result.Append(chars, length);
} else if (IsDOMClass(JS_GetClass(obj))) {
result.AppendLiteral("[Object ");
result.AppendASCII(JS_GetClass(obj)->name);
result.Append(']');
} else {
XPCCallContext ccx(JS_CALLER, cx, XrayTraits::getTargetObject(wrapper));
XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
char *wrapperStr = wn->ToString(ccx);
if (!wrapperStr) {
JS_ReportOutOfMemory(cx);
return false;
}
result.AppendASCII(wrapperStr);
JS_smprintf_free(wrapperStr);
}
JSObject *obj = XrayTraits::getTargetObject(wrapper);
result.Append(']');
static const char start[] = "[object XrayWrapper ";
static const char end[] = "]";
if (UseDOMXray(obj))
return NativeToString(cx, wrapper, obj, start, end, vp);
nsAutoString result;
result.AppendASCII(start);
XPCCallContext ccx(JS_CALLER, cx, obj);
XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
char *wrapperStr = wn->ToString(ccx);
if (!wrapperStr) {
JS_ReportOutOfMemory(cx);
return false;
}
result.AppendASCII(wrapperStr);
JS_smprintf_free(wrapperStr);
result.AppendASCII(end);
JSString *str = JS_NewUCStringCopyN(cx, reinterpret_cast<const jschar *>(result.get()),
result.Length());

View File

@ -122,6 +122,21 @@ public:
extern SandboxProxyHandler sandboxProxyHandler;
// A proxy handler that lets us wrap callables and invoke them with
// the correct this object, while forwarding all other operations down
// to them directly.
class SandboxCallableProxyHandler : public js::Wrapper {
public:
SandboxCallableProxyHandler() : js::Wrapper(0)
{
}
virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc,
JS::Value *vp);
};
extern SandboxCallableProxyHandler sandboxCallableProxyHandler;
class AutoSetWrapperNotShadowing;
class XPCWrappedNativeXrayTraits;