diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index c862247f150c..c47e7d3a8f14 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -3540,6 +3540,14 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, JS::Rooted global(cx); bool defineOnXray = xpc::WrapperFactory::IsXrayWrapper(obj); if (defineOnXray) { + // Check whether to define this property on the Xray first. This allows + // consumers to opt in to defining on the xray even if they don't want + // to define on the underlying global. + if (name_struct->mConstructorEnabled && + !(*name_struct->mConstructorEnabled)(cx, obj)) { + return NS_OK; + } + global = js::CheckedUnwrap(obj, /* stopAtOuter = */ false); if (!global) { return NS_ERROR_DOM_SECURITY_ERR; @@ -3549,36 +3557,36 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, global = obj; } - // Check whether our constructor is enabled after we unwrap Xrays, since - // we don't want to define an interface on the Xray if it's disabled in - // the target global, even if it's enabled in the Xray's global. - if (name_struct->mConstructorEnabled && - !(*name_struct->mConstructorEnabled)(cx, global)) { + // Check whether to define on the global too. Note that at this point cx + // is in the compartment of global even if we were coming in via an Xray. + bool defineOnGlobal = !name_struct->mConstructorEnabled || + (*name_struct->mConstructorEnabled)(cx, global); + + if (!defineOnGlobal && !defineOnXray) { return NS_OK; } - bool enabled; - JS::Rooted interfaceObject(cx, define(cx, global, id, &enabled)); - if (enabled) { - if (!interfaceObject) { + JS::Rooted interfaceObject(cx, define(cx, global, id, + defineOnGlobal)); + 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.address()) || + !JS_DefinePropertyById(cx, obj, id, + JS::ObjectValue(*interfaceObject), JS_PropertyStub, + JS_StrictPropertyStub, 0)) { return NS_ERROR_FAILURE; } - if (defineOnXray) { - // This really should be handled by the Xray for the window. - ac.destroy(); - if (!JS_WrapObject(cx, interfaceObject.address()) || - !JS_DefinePropertyById(cx, obj, id, - JS::ObjectValue(*interfaceObject), JS_PropertyStub, - JS_StrictPropertyStub, 0)) { - return NS_ERROR_FAILURE; - } - } - - *did_resolve = true; - - return NS_OK; } + + *did_resolve = true; + + return NS_OK; } } diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 7d4ffbca03d7..348875d34508 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -371,7 +371,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle global, JS::Handle proto, const NativeProperties* properties, const NativeProperties* chromeOnlyProperties, - const char* name) + const char* name, bool defineOnGlobal) { JS::Rooted constructor(cx); if (constructorClass) { @@ -455,7 +455,7 @@ CreateInterfaceObject(JSContext* cx, JS::Handle global, return NULL; } - if (!DefineConstructor(cx, global, name, constructor)) { + if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) { return nullptr; } @@ -471,8 +471,9 @@ CreateInterfaceObject(JSContext* cx, JS::Handle global, JS::ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_PERMANENT | JSPROP_READONLY) || - !DefineConstructor(cx, global, namedConstructors->mName, - namedConstructor)) { + (defineOnGlobal && + !DefineConstructor(cx, global, namedConstructors->mName, + namedConstructor))) { return nullptr; } js::SetReservedSlot(constructor, namedConstructorSlot++, @@ -562,7 +563,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle global, JS::Heap* constructorCache, const DOMClass* domClass, const NativeProperties* properties, const NativeProperties* chromeOnlyProperties, - const char* name) + const char* name, bool defineOnGlobal) { MOZ_ASSERT(protoClass || constructorClass || constructor, "Need at least one class or a constructor!"); @@ -612,7 +613,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle global, interface = CreateInterfaceObject(cx, global, constructorProto, constructorClass, constructor, ctorNargs, namedConstructors, proto, - properties, chromeOnlyProperties, name); + properties, chromeOnlyProperties, name, + defineOnGlobal); if (!interface) { if (protoCache) { // If we fail we need to make sure to clear the value of protoCache we diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index dee67a55ad05..625bec08b319 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -364,6 +364,12 @@ struct NamedConstructor * on objects in chrome compartments. This must be null if the * interface doesn't have any ChromeOnly properties or if the * object is being created in non-chrome compartment. + * defineOnGlobal controls whether properties should be defined on the given + * global for the interface object (if any) and named + * constructors (if any) for this interface. This can be + * false in situations where we want the properties to only + * appear on privileged Xrays but not on the unprivileged + * underlying global. * * At least one of protoClass, constructorClass or constructor should be * non-null. If constructorClass or constructor are non-null, the resulting @@ -380,7 +386,7 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle global, JS::Heap* constructorCache, const DOMClass* domClass, const NativeProperties* regularProperties, const NativeProperties* chromeOnlyProperties, - const char* name); + const char* name, bool defineOnGlobal); /* * Define the unforgeable attributes on an object. diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 3b701803f8f9..a5d688c4c244 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1664,7 +1664,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): def __init__(self, descriptor, properties): args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle', 'aGlobal'), - Argument('JS::Heap*', 'protoAndIfaceArray')] + Argument('JS::Heap*', 'aProtoAndIfaceArray'), + Argument('bool', 'aDefineOnGlobal')] CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args) self.properties = properties def definition_body(self): @@ -1791,13 +1792,13 @@ if (!unforgeableHolder) { if needInterfacePrototypeObject: protoClass = "&PrototypeClass.mBase" - protoCache = "&protoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name + protoCache = "&aProtoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name else: protoClass = "nullptr" protoCache = "nullptr" if needInterfaceObject: interfaceClass = "&InterfaceObjectClass.mBase" - interfaceCache = "&protoAndIfaceArray[constructors::id::%s]" % self.descriptor.name + interfaceCache = "&aProtoAndIfaceArray[constructors::id::%s]" % self.descriptor.name else: # We don't have slots to store the named constructors. assert len(self.descriptor.interface.namedConstructors) == 0 @@ -1828,7 +1829,7 @@ if (!unforgeableHolder) { " %s,\n" " %s,\n" " %s,\n" - " %s);" % ( + " %s, aDefineOnGlobal);" % ( protoClass, protoCache, interfaceClass, constructHookHolder, constructArgs, namedConstructors, @@ -1840,7 +1841,7 @@ if (!unforgeableHolder) { if UseHolderForUnforgeable(self.descriptor): assert needInterfacePrototypeObject setUnforgeableHolder = CGGeneric( - "JSObject* proto = protoAndIfaceArray[prototypes::id::%s];\n" + "JSObject* proto = aProtoAndIfaceArray[prototypes::id::%s];\n" "if (proto) {\n" " js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,\n" " JS::ObjectValue(*unforgeableHolder));\n" @@ -1858,9 +1859,9 @@ class CGGetPerInterfaceObject(CGAbstractMethod): A method for getting a per-interface object (a prototype object or interface constructor object). """ - def __init__(self, descriptor, name, idPrefix=""): + def __init__(self, descriptor, name, idPrefix="", extraArgs=[]): args = [Argument('JSContext*', 'aCx'), - Argument('JS::Handle', 'aGlobal')] + Argument('JS::Handle', 'aGlobal')] + extraArgs CGAbstractMethod.__init__(self, descriptor, name, 'JS::Handle', args, inline=True) self.id = idPrefix + "id::" + self.descriptor.name @@ -1874,7 +1875,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod): /* Check to see whether the interface objects are already installed */ JS::Heap* protoAndIfaceArray = GetProtoAndIfaceArray(aGlobal); if (!protoAndIfaceArray[%s]) { - CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray); + CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray, aDefineOnGlobal); } /* @@ -1897,15 +1898,18 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject): def definition_body(self): return """ /* Get the interface prototype object for this class. This will create the - object as needed. */""" + CGGetPerInterfaceObject.definition_body(self) + object as needed. */ + bool aDefineOnGlobal = true;""" + CGGetPerInterfaceObject.definition_body(self) class CGGetConstructorObjectMethod(CGGetPerInterfaceObject): """ A method for getting the interface constructor object. """ def __init__(self, descriptor): - CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject", - "constructors::") + CGGetPerInterfaceObject.__init__( + self, descriptor, "GetConstructorObject", + "constructors::", + extraArgs=[Argument("bool", "aDefineOnGlobal", "true")]) def definition_body(self): return """ /* Get the interface object for this class. This will create the object as @@ -1920,7 +1924,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle', 'aGlobal'), Argument('JS::Handle', 'id'), - Argument('bool*', 'aEnabled')] + Argument('bool', 'aDefineOnGlobal')] CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args) def declare(self): @@ -1935,7 +1939,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): def definition_body(self): if len(self.descriptor.interface.namedConstructors) > 0: - getConstructor = """ JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal); + getConstructor = """ JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal); if (!interfaceObject) { return nullptr; } @@ -1947,10 +1951,8 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): } return interfaceObject;""" else: - getConstructor = " return GetConstructorObject(aCx, aGlobal);" - return (""" *aEnabled = true; - -""" + getConstructor) + getConstructor = " return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);" + return getConstructor class CGConstructorEnabledViaPrefEnabled(CGAbstractMethod): """ diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index dedaded837b0..5a4f1a35d99a 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -457,7 +457,7 @@ inline bool IsDOMProxy(JSObject *obj) typedef JSObject* (*DefineInterface)(JSContext *cx, JS::Handle global, - JS::Handle id, bool *enabled); + JS::Handle id, bool defineOnGlobal); typedef JSObject* (*ConstructNavigatorProperty)(JSContext *cx, JS::Handle naviObj);