Commit 34783894 authored by Peter Van der Beken's avatar Peter Van der Beken
Browse files

Bug 1906744 - Check if constructor is enabled before installing named property. a=RyanVM

parent da601af0
Loading
Loading
Loading
Loading
+24 −5
Original line number Diff line number Diff line
@@ -532,14 +532,33 @@ class SystemCallerGuarantee {
  operator CallerType() const { return CallerType::System; }
};

enum class DefineInterfaceProperty {
  No,
  CheckExposure,
  Always,
};

class ProtoAndIfaceCache;
typedef void (*CreateInterfaceObjectsMethod)(JSContext* aCx,
                                             JS::Handle<JSObject*> aGlobal,
                                             ProtoAndIfaceCache& aCache,
                                             bool aDefineOnGlobal);
using CreateInterfaceObjectsMethod =
    void (*)(JSContext*, JS::Handle<JSObject*>, ProtoAndIfaceCache&,
             DefineInterfaceProperty aDefineOnGlobal);

// GetPerInterfaceObjectHandle has 3 possible behaviours for defining the named
// properties on the global for an interface or namespace when it creates an
// interface or namespace object. aDefineOnGlobal can be used to pick the
// behaviour. GetPerInterfaceObjectHandle either:
//
//  * does not define any properties on the global object
//    (for DefineInterfaceProperty::No),
//  * checks whether the interface is exposed in the global object before
//    defining properties (for DefineInterfaceProperty::CheckExposure),
//  * always defines properties (for DefineInterfaceProperty::Always).
//
// Callers should be careful when passing DefineInterfaceProperty::Always and
// make sure to check exposure themselves if needed.
JS::Handle<JSObject*> GetPerInterfaceObjectHandle(
    JSContext* aCx, size_t aSlotId, CreateInterfaceObjectsMethod aCreator,
    bool aDefineOnGlobal);
    DefineInterfaceProperty aDefineOnGlobal);

}  // namespace dom
}  // namespace mozilla
+5 −5
Original line number Diff line number Diff line
@@ -3734,8 +3734,8 @@ bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
    // JS::GetRealmGlobalOrNull should not be returning null here, because we
    // have live objects in the Realm.
    JSAutoRealm ar(aCx, JS::GetRealmGlobalOrNull(realm));
    aDesiredProto.set(
        GetPerInterfaceObjectHandle(aCx, aProtoId, aCreator, true));
    aDesiredProto.set(GetPerInterfaceObjectHandle(
        aCx, aProtoId, aCreator, DefineInterfaceProperty::CheckExposure));
    if (!aDesiredProto) {
      return false;
    }
@@ -3866,8 +3866,8 @@ bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
  // makes sense to start with: https://github.com/whatwg/html/issues/3575
  {
    JSAutoRealm ar(aCx, newTarget);
    JS::Handle<JSObject*> constructor =
        GetPerInterfaceObjectHandle(aCx, aConstructorId, aCreator, true);
    JS::Handle<JSObject*> constructor = GetPerInterfaceObjectHandle(
        aCx, aConstructorId, aCreator, DefineInterfaceProperty::CheckExposure);
    if (!constructor) {
      return false;
    }
@@ -4281,7 +4281,7 @@ JSObject* UnprivilegedJunkScopeOrWorkerGlobal(const fallible_t&) {

JS::Handle<JSObject*> GetPerInterfaceObjectHandle(
    JSContext* aCx, size_t aSlotId, CreateInterfaceObjectsMethod aCreator,
    bool aDefineOnGlobal) {
    DefineInterfaceProperty aDefineOnGlobal) {
  /* Make sure our global is sane.  Hopefully we can remove this sometime */
  JSObject* global = JS::CurrentGlobalOrNull(aCx);
  if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+8 −0
Original line number Diff line number Diff line
@@ -3259,6 +3259,14 @@ class StringIdChars {
already_AddRefed<Promise> CreateRejectedPromiseFromThrownException(
    JSContext* aCx, ErrorResult& aError);

template <auto ConstructorEnabled>
inline bool ShouldExpose(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
                         DefineInterfaceProperty aDefine) {
  return aDefine == DefineInterfaceProperty::Always ||
         (aDefine == DefineInterfaceProperty::CheckExposure &&
          ConstructorEnabled(aCx, aGlobal));
}

}  // namespace binding_detail

}  // namespace dom
+89 −73
Original line number Diff line number Diff line
@@ -3514,7 +3514,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
            Argument("JSContext*", "aCx"),
            Argument("JS::Handle<JSObject*>", "aGlobal"),
            Argument("ProtoAndIfaceCache&", "aProtoAndIfaceCache"),
            Argument("bool", "aDefineOnGlobal"),
            Argument("DefineInterfaceProperty", "aDefineOnGlobal"),
        ]
        CGAbstractMethod.__init__(
            self, descriptor, "CreateInterfaceObjects", "void", args, static=static
@@ -3545,6 +3545,19 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
        getConstructorProto += "(aCx)"
        needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
        if needInterfaceObject and self.descriptor.isExposedConditionally():
            # This code might be called when we're trying to create an object
            # in a non-system compartment, for example when system code is
            # calling a constructor through Xrays. In that case we do want to
            # create an interface object in the non-system compartment, but we
            # don't want to expose the name on the non-system global if the
            # interface itself is marked as ChromeOnly.
            defineOnGlobal = (
                "ShouldExpose<%s::ConstructorEnabled>(aCx, aGlobal, aDefineOnGlobal)"
                % toBindingNamespace(self.descriptor.name)
            )
        else:
            defineOnGlobal = "aDefineOnGlobal != DefineInterfaceProperty::No"
        needInterfacePrototypeObject = (
            self.descriptor.interface.hasInterfacePrototypeObject()
        )
@@ -3644,7 +3657,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
                                        interfaceCache,
                                        ${properties},
                                        ${chromeProperties},
                                        "${name}", aDefineOnGlobal,
                                        "${name}",
                                        ${defineOnGlobal},
                                        ${unscopableNames},
                                        ${isGlobal},
                                        ${legacyWindowAliases},
@@ -3662,6 +3676,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
            properties=properties,
            chromeProperties=chromeProperties,
            name=name,
            defineOnGlobal=defineOnGlobal,
            unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
            isGlobal=toStringBool(isGlobal),
            legacyWindowAliases="legacyWindowAliases"
@@ -3896,6 +3911,40 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
        ).define()
class CGCreateAndDefineOnGlobalMethod(CGAbstractMethod):
    """
    A method for creating the interface or namespace object and defining
    properties for it on the global.
    """
    def __init__(self, descriptor):
        CGAbstractMethod.__init__(
            self,
            descriptor,
            "CreateAndDefineOnGlobal",
            "bool",
            [
                Argument("JSContext*", "aCx"),
            ],
            inline=True,
        )
    def definition_body(self):
        return fill(
            """
            // Get the interface or namespace object for this class. This will
            // create the object as needed and always define the properties for
            // it on the global. The caller should make sure the interface or
            // namespace is exposed on the global before calling this.
            return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
                                               &CreateInterfaceObjects,
                                               DefineInterfaceProperty::Always);
            """,
            name=self.descriptor.name,
        )
class CGGetProtoObjectHandleMethod(CGAbstractMethod):
    """
    A method for getting the interface prototype object.
@@ -3920,7 +3969,7 @@ class CGGetProtoObjectHandleMethod(CGAbstractMethod):
               object as needed. */
            return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
                                               &CreateInterfaceObjects,
                                               /* aDefineOnGlobal = */ true);
                                               DefineInterfaceProperty::CheckExposure);
            """,
            name=self.descriptor.name,
@@ -3958,7 +4007,6 @@ class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
            "JS::Handle<JSObject*>",
            [
                Argument("JSContext*", "aCx"),
                Argument("bool", "aDefineOnGlobal", "true"),
            ],
            inline=True,
        )
@@ -3971,7 +4019,7 @@ class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
            return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
                                               &CreateInterfaceObjects,
                                               aDefineOnGlobal);
                                               DefineInterfaceProperty::CheckExposure);
            """,
            name=self.descriptor.name,
        )
@@ -16678,6 +16726,11 @@ class CGDescriptor(CGThing):
        if descriptor.interface.hasInterfaceObject():
            cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
            cgThings.append(CGGetConstructorObjectMethod(descriptor))
            cgThings.append(
                CGCreateAndDefineOnGlobalMethod(
                    descriptor,
                )
            )
        # See whether we need to generate cross-origin property arrays.
        if needCrossOriginPropertyArrays:
@@ -17819,6 +17872,21 @@ class CGDictionary(CGThing):
        return all(CGDictionary.typeSafeToJSONify(m.type) for m in dictionary.members)
def RegisterNonWindowBindings(descriptors):
    conditions = []
    for desc in descriptors:
        bindingNS = toBindingNamespace(desc.name)
        condition = "!%s::CreateAndDefineOnGlobal(aCx)" % bindingNS
        if desc.isExposedConditionally():
            condition = "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
        conditions.append(condition)
    lines = [
        CGIfWrapper(CGGeneric("return false;\n"), condition) for condition in conditions
    ]
    lines.append(CGGeneric("return true;\n"))
    return CGList(lines, "\n").define()
class CGRegisterWorkerBindings(CGAbstractMethod):
    def __init__(self, config):
        CGAbstractMethod.__init__(
@@ -17831,24 +17899,11 @@ class CGRegisterWorkerBindings(CGAbstractMethod):
        self.config = config
    def definition_body(self):
        descriptors = self.config.getDescriptors(
        return RegisterNonWindowBindings(
            self.config.getDescriptors(
                hasInterfaceObject=True, isExposedInAnyWorker=True, register=True
            )
        conditions = []
        for desc in descriptors:
            bindingNS = toBindingNamespace(desc.name)
            condition = "!%s::GetConstructorObject(aCx)" % bindingNS
            if desc.isExposedConditionally():
                condition = (
                    "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
        )
            conditions.append(condition)
        lines = [
            CGIfWrapper(CGGeneric("return false;\n"), condition)
            for condition in conditions
        ]
        lines.append(CGGeneric("return true;\n"))
        return CGList(lines, "\n").define()
class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
@@ -17863,24 +17918,11 @@ class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
        self.config = config
    def definition_body(self):
        descriptors = self.config.getDescriptors(
        return RegisterNonWindowBindings(
            self.config.getDescriptors(
                hasInterfaceObject=True, isExposedInWorkerDebugger=True, register=True
            )
        conditions = []
        for desc in descriptors:
            bindingNS = toBindingNamespace(desc.name)
            condition = "!%s::GetConstructorObject(aCx)" % bindingNS
            if desc.isExposedConditionally():
                condition = (
                    "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
        )
            conditions.append(condition)
        lines = [
            CGIfWrapper(CGGeneric("return false;\n"), condition)
            for condition in conditions
        ]
        lines.append(CGGeneric("return true;\n"))
        return CGList(lines, "\n").define()
class CGRegisterWorkletBindings(CGAbstractMethod):
@@ -17895,24 +17937,11 @@ class CGRegisterWorkletBindings(CGAbstractMethod):
        self.config = config
    def definition_body(self):
        descriptors = self.config.getDescriptors(
        return RegisterNonWindowBindings(
            self.config.getDescriptors(
                hasInterfaceObject=True, isExposedInAnyWorklet=True, register=True
            )
        conditions = []
        for desc in descriptors:
            bindingNS = toBindingNamespace(desc.name)
            condition = "!%s::GetConstructorObject(aCx)" % bindingNS
            if desc.isExposedConditionally():
                condition = (
                    "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
        )
            conditions.append(condition)
        lines = [
            CGIfWrapper(CGGeneric("return false;\n"), condition)
            for condition in conditions
        ]
        lines.append(CGGeneric("return true;\n"))
        return CGList(lines, "\n").define()
class CGRegisterShadowRealmBindings(CGAbstractMethod):
@@ -17927,24 +17956,11 @@ class CGRegisterShadowRealmBindings(CGAbstractMethod):
        self.config = config
    def definition_body(self):
        descriptors = self.config.getDescriptors(
        return RegisterNonWindowBindings(
            self.config.getDescriptors(
                hasInterfaceObject=True, isExposedInShadowRealms=True, register=True
            )
        conditions = []
        for desc in descriptors:
            bindingNS = toBindingNamespace(desc.name)
            condition = "!%s::GetConstructorObject(aCx)" % bindingNS
            if desc.isExposedConditionally():
                condition = (
                    "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
        )
            conditions.append(condition)
        lines = [
            CGIfWrapper(CGGeneric("return false;\n"), condition)
            for condition in conditions
        ]
        lines.append(CGGeneric("return true;\n"))
        return CGList(lines, "\n").define()
def BindingNamesOffsetEnum(name):
+9 −3
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ static JSObject* FindNamedConstructorForXray(
    JSContext* aCx, JS::Handle<jsid> aId, const WebIDLNameTableEntry* aEntry) {
  JSObject* interfaceObject =
      GetPerInterfaceObjectHandle(aCx, aEntry->mConstructorId, aEntry->mCreate,
                                  /* aDefineOnGlobal = */ false);
                                  DefineInterfaceProperty::No);
  if (!interfaceObject) {
    return nullptr;
  }
@@ -155,10 +155,13 @@ bool WebIDLGlobalNameHash::DefineIfEnabled(
    return true;
  }

  // We've already checked whether the interface is enabled (see
  // checkEnabledForScope above), so it's fine to pass
  // DefineInterfaceProperty::Always here.
  JS::Rooted<JSObject*> interfaceObject(
      aCx,
      GetPerInterfaceObjectHandle(aCx, entry->mConstructorId, entry->mCreate,
                                  /* aDefineOnGlobal = */ true));
                                  DefineInterfaceProperty::Always));
  if (NS_WARN_IF(!interfaceObject)) {
    return Throw(aCx, NS_ERROR_FAILURE);
  }
@@ -229,9 +232,12 @@ bool WebIDLGlobalNameHash::ResolveForSystemGlobal(JSContext* aCx,
  // Look up the corresponding entry in the name table, and resolve if enabled.
  const WebIDLNameTableEntry* entry = GetEntry(aId.toLinearString());
  if (entry && (!entry->mEnabled || entry->mEnabled(aCx, aObj))) {
    // We've already checked whether the interface is enabled (see
    // entry->mEnabled above), so it's fine to pass
    // DefineInterfaceProperty::Always here.
    if (NS_WARN_IF(!GetPerInterfaceObjectHandle(
            aCx, entry->mConstructorId, entry->mCreate,
            /* aDefineOnGlobal = */ true))) {
            DefineInterfaceProperty::Always))) {
      return Throw(aCx, NS_ERROR_FAILURE);
    }

Loading