diff options
Diffstat (limited to 'components/script/dom/bindings/codegen')
6 files changed, 493 insertions, 169 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 1db23e50992..dea6e104213 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -573,9 +573,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, isAutoRooted=False, invalidEnumValueFatal=True, defaultValue=None, - treatNullAs="Default", - isEnforceRange=False, - isClamp=False, exceptionCode=None, allowTreatNonObjectAsNull=False, isCallbackReturnValue=False, @@ -603,12 +600,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, If defaultValue is not None, it's the IDL default value for this conversion - If isEnforceRange is true, we're converting an integer and throwing if the - value is out of range. - - If isClamp is true, we're converting an integer and clamping if the - value is out of range. - If allowTreatNonObjectAsNull is true, then [TreatNonObjectAsNull] extended attributes on nullable callback functions will be honored. @@ -631,6 +622,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # We should not have a defaultValue if we know we're an object assert not isDefinitelyObject or defaultValue is None + isEnforceRange = type.enforceRange + isClamp = type.clamp + if type.treatNullAsEmpty: + treatNullAs = "EmptyString" + else: + treatNullAs = "Default" + # If exceptionCode is not set, we'll just rethrow the exception we got. # Note that we can't just set failureCode to exceptionCode, because setting # failureCode will prevent pending exceptions from being set in cases when @@ -1301,9 +1299,6 @@ class CGArgumentConverter(CGThing): descriptorProvider, invalidEnumValueFatal=invalidEnumValueFatal, defaultValue=argument.defaultValue, - treatNullAs=argument.treatNullAs, - isEnforceRange=argument.enforceRange, - isClamp=argument.clamp, isMember="Variadic" if argument.variadic else False, isAutoRooted=type_needs_auto_root(argument.type), allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull()) @@ -3508,9 +3503,6 @@ class FakeArgument(): self.variadic = False self.defaultValue = None self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull - self.treatNullAs = interfaceMember.treatNullAs - self.enforceRange = False - self.clamp = False def allowTreatNonCallableAsNull(self): return self._allowTreatNonObjectAsNull @@ -4874,7 +4866,7 @@ class CGProxySpecialOperation(CGPerSignatureCall): # arguments[0] is the index or name of the item that we're setting. argument = arguments[1] info = getJSToNativeConversionInfo( - argument.type, descriptor, treatNullAs=argument.treatNullAs, + argument.type, descriptor, exceptionCode="return false;") template = info.template declType = info.declType @@ -6886,7 +6878,7 @@ class CGCallbackInterface(CGCallback): class FakeMember(): def __init__(self): - self.treatNullAs = "Default" + pass def isStatic(self): return False diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index 95cf21a65ed..5879f8b0064 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -420,48 +420,11 @@ class IDLObjectWithIdentifier(IDLObject): if parentScope: self.resolve(parentScope) - self.treatNullAs = "Default" - def resolve(self, parentScope): assert isinstance(parentScope, IDLScope) assert isinstance(self.identifier, IDLUnresolvedIdentifier) self.identifier.resolve(parentScope, self) - def checkForStringHandlingExtendedAttributes(self, attrs, - isDictionaryMember=False, - isOptional=False): - """ - A helper function to deal with TreatNullAs. Returns the list - of attrs it didn't handle itself. - """ - assert isinstance(self, IDLArgument) or isinstance(self, IDLAttribute) - unhandledAttrs = list() - for attr in attrs: - if not attr.hasValue(): - unhandledAttrs.append(attr) - continue - - identifier = attr.identifier() - value = attr.value() - if identifier == "TreatNullAs": - if not self.type.isDOMString() or self.type.nullable(): - raise WebIDLError("[TreatNullAs] is only allowed on " - "arguments or attributes whose type is " - "DOMString", - [self.location]) - if isDictionaryMember: - raise WebIDLError("[TreatNullAs] is not allowed for " - "dictionary members", [self.location]) - if value != 'EmptyString': - raise WebIDLError("[TreatNullAs] must take the identifier " - "'EmptyString', not '%s'" % value, - [self.location]) - self.treatNullAs = value - else: - unhandledAttrs.append(attr) - - return unhandledAttrs - class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope): def __init__(self, location, parentScope, identifier): @@ -2090,9 +2053,15 @@ class IDLType(IDLObject): IDLObject.__init__(self, location) self.name = name self.builtin = False + self.clamp = False + self.treatNullAsEmpty = False + self.enforceRange = False + self._extendedAttrDict = {} def __eq__(self, other): - return other and self.builtin == other.builtin and self.name == other.name + return (other and self.builtin == other.builtin and self.name == other.name and + self.clamp == other.clamp and self.enforceRange == other.enforceRange and + self.treatNullAsEmpty == other.treatNullAsEmpty) def __ne__(self, other): return not self == other @@ -2218,12 +2187,14 @@ class IDLType(IDLObject): assert self.tag() == IDLType.Tags.callback return self.nullable() and self.inner.callback._treatNonObjectAsNull - def addExtendedAttributes(self, attrs): - if len(attrs) != 0: - raise WebIDLError("There are no extended attributes that are " - "allowed on types, for now (but this is " - "changing; see bug 1359269)", + def withExtendedAttributes(self, attrs): + if len(attrs) > 0: + raise WebIDLError("Extended attributes on types only supported for builtins", [attrs[0].location, self.location]) + return self + + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) def resolveType(self, parentScope): pass @@ -2244,8 +2215,9 @@ class IDLUnresolvedType(IDLType): Unresolved types are interface types """ - def __init__(self, location, name): + def __init__(self, location, name, attrs=[]): IDLType.__init__(self, location, name) + self.extraTypeAttributes = attrs def isComplete(self): return False @@ -2267,7 +2239,7 @@ class IDLUnresolvedType(IDLType): typedefType = IDLTypedefType(self.location, obj.innerType, obj.identifier) assert not typedefType.isComplete() - return typedefType.complete(scope) + return typedefType.complete(scope).withExtendedAttributes(self.extraTypeAttributes) elif obj.isCallback() and not obj.isInterface(): assert self.name.name == obj.identifier.name return IDLCallbackType(obj.location, obj) @@ -2275,6 +2247,9 @@ class IDLUnresolvedType(IDLType): name = self.name.resolve(scope, None) return IDLWrapperType(self.location, obj) + def withExtendedAttributes(self, attrs): + return IDLUnresolvedType(self.location, self.name, attrs) + def isDistinguishableFrom(self, other): raise TypeError("Can't tell whether an unresolved type is or is not " "distinguishable from other things") @@ -2790,12 +2765,17 @@ class IDLTypedefType(IDLType): def _getDependentObjects(self): return self.inner._getDependentObjects() + def withExtendedAttributes(self, attrs): + return IDLTypedefType(self.location, self.inner.withExtendedAttributes(attrs), self.name) + class IDLTypedef(IDLObjectWithIdentifier): def __init__(self, location, parentScope, innerType, name): + # Set self.innerType first, because IDLObjectWithIdentifier.__init__ + # will call our __str__, which wants to use it. + self.innerType = innerType identifier = IDLUnresolvedIdentifier(location, name) IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) - self.innerType = innerType def __str__(self): return "Typedef %s %s" % (self.identifier.name, self.innerType) @@ -3107,10 +3087,59 @@ class IDLBuiltinType(IDLType): Types.ReadableStream: IDLType.Tags.interface, } - def __init__(self, location, name, type): + def __init__(self, location, name, type, clamp=False, enforceRange=False, treatNullAsEmpty=False, + attrLocation=[]): + """ + The mutually exclusive clamp/enforceRange/treatNullAsEmpty arguments are used to create instances + of this type with the appropriate attributes attached. Use .clamped(), .rangeEnforced(), and .treatNullAs(). + + attrLocation is an array of source locations of these attributes for error reporting. + """ IDLType.__init__(self, location, name) self.builtin = True self._typeTag = type + self._clamped = None + self._rangeEnforced = None + self._withTreatNullAs = None + if self.isNumeric(): + if clamp: + self.clamp = True + self.name = "Clamped" + self.name + self._extendedAttrDict["Clamp"] = True + elif enforceRange: + self.enforceRange = True + self.name = "RangeEnforced" + self.name + self._extendedAttrDict["EnforceRange"] = True + elif clamp or enforceRange: + raise WebIDLError("Non-numeric types cannot be [Clamp] or [EnforceRange]", attrLocation) + if self.isDOMString(): + if treatNullAsEmpty: + self.treatNullAsEmpty = True + self.name = "NullIsEmpty" + self.name + self._extendedAttrDict["TreatNullAs"] = ["EmptyString"] + elif treatNullAsEmpty: + raise WebIDLError("Non-string types cannot be [TreatNullAs]", attrLocation) + + def clamped(self, attrLocation): + if not self._clamped: + self._clamped = IDLBuiltinType(self.location, self.name, + self._typeTag, clamp=True, + attrLocation=attrLocation) + return self._clamped + + def rangeEnforced(self, attrLocation): + if not self._rangeEnforced: + self._rangeEnforced = IDLBuiltinType(self.location, self.name, + self._typeTag, enforceRange=True, + attrLocation=attrLocation) + return self._rangeEnforced + + def withTreatNullAs(self, attrLocation): + if not self._withTreatNullAs: + self._withTreatNullAs = IDLBuiltinType(self.location, self.name, + self._typeTag, treatNullAsEmpty=True, + attrLocation=attrLocation) + return self._withTreatNullAs def isPrimitive(self): return self._typeTag <= IDLBuiltinType.Types.double @@ -3246,6 +3275,45 @@ class IDLBuiltinType(IDLType): def _getDependentObjects(self): return set() + def withExtendedAttributes(self, attrs): + ret = self + for attribute in attrs: + identifier = attribute.identifier() + if identifier == "Clamp": + if not attribute.noArguments(): + raise WebIDLError("[Clamp] must take no arguments", + [attribute.location]) + if ret.enforceRange or self.enforceRange: + raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", + [self.location, attribute.location]) + ret = self.clamped([self.location, attribute.location]) + elif identifier == "EnforceRange": + if not attribute.noArguments(): + raise WebIDLError("[EnforceRange] must take no arguments", + [attribute.location]) + if ret.clamp or self.clamp: + raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", + [self.location, attribute.location]) + ret = self.rangeEnforced([self.location, attribute.location]) + elif identifier == "TreatNullAs": + if not self.isDOMString(): + raise WebIDLError("[TreatNullAs] only allowed on DOMStrings", + [self.location, attribute.location]) + assert not self.nullable() + if not attribute.hasValue(): + raise WebIDLError("[TreatNullAs] must take an identifier argument" + [attribute.location]) + value = attribute.value() + if value != 'EmptyString': + raise WebIDLError("[TreatNullAs] must take the identifier " + "'EmptyString', not '%s'" % value, + [attribute.location]) + ret = self.withTreatNullAs([self.location, attribute.location]) + else: + raise WebIDLError("Unhandled extended attribute on type", + [self.location, attribute.location]) + return ret + BuiltinTypes = { IDLBuiltinType.Types.byte: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte", @@ -3460,6 +3528,10 @@ class IDLValue(IDLObject): # extra normalization step. assert self.type.isDOMString() return self + elif self.type.isDOMString() and type.treatNullAsEmpty: + # TreatNullAsEmpty is a different type for resolution reasons, + # however once you have a value it doesn't matter + return self elif self.type.isString() and type.isByteString(): # Allow ByteStrings to use a default value like DOMString. # No coercion is required as Codegen.py will handle the @@ -4096,8 +4168,6 @@ class IDLAttribute(IDLInterfaceMember): self.lenientThis = False self._unforgeable = False self.stringifier = stringifier - self.enforceRange = False - self.clamp = False self.slotIndices = None assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike) self.maplikeOrSetlike = maplikeOrSetlike @@ -4134,6 +4204,9 @@ class IDLAttribute(IDLInterfaceMember): assert not isinstance(t.name, IDLUnresolvedIdentifier) self.type = t + if self.readonly and (self.type.clamp or self.type.enforceRange or self.type.treatNullAsEmpty): + raise WebIDLError("A readonly attribute cannot be [Clamp] or [EnforceRange]", + [self.location]) if self.type.isDictionary() and not self.getExtendedAttribute("Cached"): raise WebIDLError("An attribute cannot be of a dictionary type", [self.location]) @@ -4357,16 +4430,6 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("[LenientFloat] used on an attribute with a " "non-restricted-float type", [attr.location, self.location]) - elif identifier == "EnforceRange": - if self.readonly: - raise WebIDLError("[EnforceRange] used on a readonly attribute", - [attr.location, self.location]) - self.enforceRange = True - elif identifier == "Clamp": - if self.readonly: - raise WebIDLError("[Clamp] used on a readonly attribute", - [attr.location, self.location]) - self.clamp = True elif identifier == "StoreInSlot": if self.getExtendedAttribute("Cached"): raise WebIDLError("[StoreInSlot] and [Cached] must not be " @@ -4468,10 +4531,6 @@ class IDLAttribute(IDLInterfaceMember): self.type.resolveType(parentScope) IDLObjectWithIdentifier.resolve(self, parentScope) - def addExtendedAttributes(self, attrs): - attrs = self.checkForStringHandlingExtendedAttributes(attrs) - IDLInterfaceMember.addExtendedAttributes(self, attrs) - def hasLenientThis(self): return self.lenientThis @@ -4491,7 +4550,7 @@ class IDLAttribute(IDLInterfaceMember): class IDLArgument(IDLObjectWithIdentifier): - def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False): + def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False, allowTypeAttributes=False): IDLObjectWithIdentifier.__init__(self, location, None, identifier) assert isinstance(type, IDLType) @@ -4502,37 +4561,19 @@ class IDLArgument(IDLObjectWithIdentifier): self.variadic = variadic self.dictionaryMember = dictionaryMember self._isComplete = False - self.enforceRange = False - self.clamp = False self._allowTreatNonCallableAsNull = False self._extendedAttrDict = {} + self.allowTypeAttributes = allowTypeAttributes assert not variadic or optional assert not variadic or not defaultValue def addExtendedAttributes(self, attrs): - attrs = self.checkForStringHandlingExtendedAttributes( - attrs, - isDictionaryMember=self.dictionaryMember, - isOptional=self.optional) for attribute in attrs: identifier = attribute.identifier() - if identifier == "Clamp": - if not attribute.noArguments(): - raise WebIDLError("[Clamp] must take no arguments", - [attribute.location]) - if self.enforceRange: - raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", - [self.location]) - self.clamp = True - elif identifier == "EnforceRange": - if not attribute.noArguments(): - raise WebIDLError("[EnforceRange] must take no arguments", - [attribute.location]) - if self.clamp: - raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", - [self.location]) - self.enforceRange = True + if self.allowTypeAttributes and (identifier == "EnforceRange" or identifier == "Clamp" or + identifier == "TreatNullAs"): + self.type = self.type.withExtendedAttributes([attribute]) elif identifier == "TreatNonCallableAsNull": self._allowTreatNonCallableAsNull = True elif (self.dictionaryMember and @@ -4583,6 +4624,8 @@ class IDLArgument(IDLObjectWithIdentifier): # codegen doesn't have to special-case this. self.defaultValue = IDLUndefinedValue(self.location) + if self.dictionaryMember and self.type.treatNullAsEmpty: + raise WebIDLError("Dictionary members cannot be [TreatNullAs]", [self.location]) # Now do the coercing thing; this needs to happen after the # above creation of a default value. if self.defaultValue: @@ -5811,31 +5854,42 @@ class Parser(Tokenizer): # We're at the end of the list p[0] = [] return - # Add our extended attributes p[2].addExtendedAttributes(p[1]) p[0] = [p[2]] p[0].extend(p[3]) - def p_DictionaryMember(self, p): + def p_DictionaryMemberRequired(self, p): """ - DictionaryMember : Required Type IDENTIFIER Default SEMICOLON + DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON """ - # These quack a lot like optional arguments, so just treat them that way. + # These quack a lot like required arguments, so just treat them that way. t = p[2] assert isinstance(t, IDLType) identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) - defaultValue = p[4] - optional = not p[1] - - if not optional and defaultValue: - raise WebIDLError("Required dictionary members can't have a default value.", - [self.getLocation(p, 4)]) p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, - optional=optional, - defaultValue=defaultValue, variadic=False, + optional=False, + defaultValue=None, variadic=False, dictionaryMember=True) + def p_DictionaryMember(self, p): + """ + DictionaryMember : Type IDENTIFIER Default SEMICOLON + """ + # These quack a lot like optional arguments, so just treat them that way. + t = p[1] + assert isinstance(t, IDLType) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + defaultValue = p[3] + + # Any attributes that precede this may apply to the type, so + # we configure the argument to forward type attributes down instead of producing + # a parse error + p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, + optional=True, + defaultValue=defaultValue, variadic=False, + dictionaryMember=True, allowTypeAttributes=True) + def p_Default(self, p): """ Default : EQUALS DefaultValue @@ -5923,7 +5977,7 @@ class Parser(Tokenizer): def p_Typedef(self, p): """ - Typedef : TYPEDEF Type IDENTIFIER SEMICOLON + Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON """ typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(), p[2], p[3]) @@ -6016,8 +6070,8 @@ class Parser(Tokenizer): def p_Iterable(self, p): """ - Iterable : ITERABLE LT Type GT SEMICOLON - | ITERABLE LT Type COMMA Type GT SEMICOLON + Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON + | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON """ location = self.getLocation(p, 2) identifier = IDLUnresolvedIdentifier(location, "__iterable", @@ -6033,7 +6087,7 @@ class Parser(Tokenizer): def p_Setlike(self, p): """ - Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON + Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON """ readonly = p[1] maplikeOrSetlikeType = p[2] @@ -6047,7 +6101,7 @@ class Parser(Tokenizer): def p_Maplike(self, p): """ - Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON + Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON """ readonly = p[1] maplikeOrSetlikeType = p[2] @@ -6085,7 +6139,7 @@ class Parser(Tokenizer): def p_AttributeRest(self, p): """ - AttributeRest : ReadOnly ATTRIBUTE Type AttributeName SEMICOLON + AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON """ location = self.getLocation(p, 2) readonly = p[1] @@ -6339,32 +6393,47 @@ class Parser(Tokenizer): def p_Argument(self, p): """ - Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default + Argument : ExtendedAttributeList ArgumentRest """ - t = p[3] + p[0] = p[2] + p[0].addExtendedAttributes(p[1]) + + def p_ArgumentRestOptional(self, p): + """ + ArgumentRest : OPTIONAL TypeWithExtendedAttributes ArgumentName Default + """ + t = p[2] assert isinstance(t, IDLType) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5]) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) - optional = p[2] - variadic = p[4] - defaultValue = p[6] + defaultValue = p[4] - if not optional and defaultValue: - raise WebIDLError("Mandatory arguments can't have a default value.", - [self.getLocation(p, 6)]) # We can't test t.isAny() here and give it a default value as needed, # since at this point t is not a fully resolved type yet (e.g. it might # be a typedef). We'll handle the 'any' case in IDLArgument.complete. - if variadic: - if optional: - raise WebIDLError("Variadic arguments should not be marked optional.", - [self.getLocation(p, 2)]) - optional = variadic + p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, True, defaultValue, False) - p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic) - p[0].addExtendedAttributes(p[1]) + def p_ArgumentRest(self, p): + """ + ArgumentRest : Type Ellipsis ArgumentName + """ + t = p[1] + assert isinstance(t, IDLType) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) + + variadic = p[2] + + # We can't test t.isAny() here and give it a default value as needed, + # since at this point t is not a fully resolved type yet (e.g. it might + # be a typedef). We'll handle the 'any' case in IDLArgument.complete. + + # variadic implies optional + # Any attributes that precede this may apply to the type, so + # we configure the argument to forward type attributes down instead of producing + # a parse error + p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, variadic, None, variadic, allowTypeAttributes=True) def p_ArgumentName(self, p): """ @@ -6403,30 +6472,6 @@ class Parser(Tokenizer): """ p[0] = p[1] - def p_Optional(self, p): - """ - Optional : OPTIONAL - """ - p[0] = True - - def p_OptionalEmpty(self, p): - """ - Optional : - """ - p[0] = False - - def p_Required(self, p): - """ - Required : REQUIRED - """ - p[0] = True - - def p_RequiredEmpty(self, p): - """ - Required : - """ - p[0] = False - def p_Ellipsis(self, p): """ Ellipsis : ELLIPSIS @@ -6567,6 +6612,12 @@ class Parser(Tokenizer): """ p[0] = self.handleNullable(p[1], p[2]) + def p_TypeWithExtendedAttributes(self, p): + """ + TypeWithExtendedAttributes : ExtendedAttributeList Type + """ + p[0] = p[2].withExtendedAttributes(p[1]) + def p_SingleTypeNonAnyType(self, p): """ SingleType : NonAnyType @@ -6589,9 +6640,9 @@ class Parser(Tokenizer): def p_UnionMemberTypeNonAnyType(self, p): """ - UnionMemberType : NonAnyType + UnionMemberType : ExtendedAttributeList NonAnyType """ - p[0] = p[1] + p[0] = p[2].withExtendedAttributes(p[1]) def p_UnionMemberType(self, p): """ @@ -6641,7 +6692,7 @@ class Parser(Tokenizer): def p_NonAnyTypeSequenceType(self, p): """ - NonAnyType : SEQUENCE LT Type GT Null + NonAnyType : SEQUENCE LT TypeWithExtendedAttributes GT Null """ innerType = p[3] type = IDLSequenceType(self.getLocation(p, 1), innerType) @@ -6657,7 +6708,7 @@ class Parser(Tokenizer): def p_NonAnyTypeRecordType(self, p): """ - NonAnyType : RECORD LT StringType COMMA Type GT Null + NonAnyType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null """ keyType = p[3] valueType = p[5] diff --git a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch b/components/script/dom/bindings/codegen/parser/pref-main-thread.patch new file mode 100644 index 00000000000..a90d0593693 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/pref-main-thread.patch @@ -0,0 +1,27 @@ +--- WebIDL.py ++++ WebIDL.py +@@ -1362,12 +1362,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): + for bindingAlias in member.bindingAliases: + checkDuplicateNames(member, bindingAlias, "BindingAlias") + +- +- if self.getExtendedAttribute("Pref") and self.isExposedOffMainThread(): +- raise WebIDLError("[Pref] used on an interface that is not " +- "main-thread-only", +- [self.location]) +- + # Conditional exposure makes no sense for interfaces with no + # interface object, unless they're navigator properties. + # And SecureContext makes sense for interfaces with no interface object, +@@ -3619,11 +3613,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): + IDLExposureMixins.finish(self, scope) + + def validate(self): +- if self.getExtendedAttribute("Pref") and self.isExposedOffMainThread(): +- raise WebIDLError("[Pref] used on an interface member that is not " +- "main-thread-only", +- [self.location]) +- + if self.isAttr() or self.isMethod(): + if self.affects == "Everything" and self.dependsOn != "Everything": + raise WebIDLError("Interface member is flagged as affecting " diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py new file mode 100644 index 00000000000..1128d58317a --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py @@ -0,0 +1,238 @@ +# Import the WebIDL module, so we can do isinstance checks and whatnot +import WebIDL + +def WebIDLTest(parser, harness): + # Basic functionality + threw = False + try: + parser.parse(""" + typedef [EnforceRange] long Foo; + typedef [Clamp] long Bar; + typedef [TreatNullAs=EmptyString] DOMString Baz; + dictionary A { + required [EnforceRange] long a; + required [Clamp] long b; + [ChromeOnly, EnforceRange] long c; + Foo d; + }; + interface B { + attribute Foo typedefFoo; + attribute [EnforceRange] long foo; + attribute [Clamp] long bar; + attribute [TreatNullAs=EmptyString] DOMString baz; + void method([EnforceRange] long foo, [Clamp] long bar, + [TreatNullAs=EmptyString] DOMString baz); + void method2(optional [EnforceRange] long foo, optional [Clamp] long bar, + optional [TreatNullAs=EmptyString] DOMString baz); + }; + interface Setlike { + setlike<[Clamp] long>; + }; + interface Maplike { + maplike<[Clamp] long, [EnforceRange] long>; + }; + interface Iterable { + iterable<[Clamp] long, [EnforceRange] long>; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(not threw, "Should not have thrown on parsing normal") + if not threw: + harness.check(results[0].innerType.enforceRange, True, "Foo is [EnforceRange]") + harness.check(results[1].innerType.clamp, True, "Bar is [Clamp]") + harness.check(results[2].innerType.treatNullAsEmpty, True, "Baz is [TreatNullAs=EmptyString]") + A = results[3] + harness.check(A.members[0].type.enforceRange, True, "A.a is [EnforceRange]") + harness.check(A.members[1].type.clamp, True, "A.b is [Clamp]") + harness.check(A.members[2].type.enforceRange, True, "A.c is [EnforceRange]") + harness.check(A.members[3].type.enforceRange, True, "A.d is [EnforceRange]") + B = results[4] + harness.check(B.members[0].type.enforceRange, True, "B.typedefFoo is [EnforceRange]") + harness.check(B.members[1].type.enforceRange, True, "B.foo is [EnforceRange]") + harness.check(B.members[2].type.clamp, True, "B.bar is [Clamp]") + harness.check(B.members[3].type.treatNullAsEmpty, True, "B.baz is [TreatNullAs=EmptyString]") + method = B.members[4].signatures()[0][1] + harness.check(method[0].type.enforceRange, True, "foo argument of method is [EnforceRange]") + harness.check(method[1].type.clamp, True, "bar argument of method is [Clamp]") + harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method is [TreatNullAs=EmptyString]") + method2 = B.members[5].signatures()[0][1] + harness.check(method[0].type.enforceRange, True, "foo argument of method2 is [EnforceRange]") + harness.check(method[1].type.clamp, True, "bar argument of method2 is [Clamp]") + harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method2 is [TreatNullAs=EmptyString]") + + ATTRIBUTES = [("[Clamp]", "long"), ("[EnforceRange]", "long"), ("[TreatNullAs=EmptyString]", "DOMString")] + TEMPLATES = [ + ("required dictionary members", """ + dictionary Foo { + %s required %s foo; + }; + """), + ("optional arguments", """ + interface Foo { + void foo(%s optional %s foo); + }; + """), + ("typedefs", """ + %s typedef %s foo; + """), + ("attributes", """ + interface Foo { + %s attribute %s foo; + }; + """), + ("readonly attributes", """ + interface Foo { + readonly attribute %s %s foo; + }; + """), + ("readonly unresolved attributes", """ + interface Foo { + readonly attribute Bar baz; + }; + typedef %s %s Bar; + """) + ]; + + for (name, template) in TEMPLATES: + parser = parser.reset() + threw = False + try: + parser.parse(template % ("", "long")) + parser.finish() + except: + threw = True + harness.ok(not threw, "Template for %s parses without attributes" % name) + for (attribute, type) in ATTRIBUTES: + parser = parser.reset() + threw = False + try: + parser.parse(template % (attribute, type)) + parser.finish() + except: + threw = True + harness.ok(threw, + "Should not allow %s on %s" % (attribute, name)) + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp, EnforceRange] long Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange, Clamp] long Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange]") + + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp] long Foo; + typedef [EnforceRange] Foo bar; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange] long Foo; + typedef [Clamp] Foo bar; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp] DOMString Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [Clamp] on DOMString") + + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange] DOMString Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [EnforceRange] on DOMString") + + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [TreatNullAs=EmptyString] long Foo; + """) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [TreatNullAs] on long") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + void foo([Clamp] Bar arg); + }; + typedef long Bar; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow type attributes on unresolved types") + harness.check(results[0].members[0].signatures()[0][1][0].type.clamp, True, + "Unresolved types with type attributes should correctly resolve with attributes") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Foo { + void foo(Bar arg); + }; + typedef [Clamp] long Bar; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "Should allow type attributes on typedefs") + harness.check(results[0].members[0].signatures()[0][1][0].type.clamp, True, + "Unresolved types that resolve to typedefs with attributes should correctly resolve with attributes") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py index 85a70d98f2c..97184ec2478 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py @@ -56,9 +56,9 @@ def WebIDLTest(parser, harness): results = parser.finish() # Pull out the first argument out of the arglist of the first (and # only) signature. - harness.ok(results[0].members[0].signatures()[0][1][0].clamp, + harness.ok(results[0].members[0].signatures()[0][1][0].type.clamp, "Should be clamped") - harness.ok(not results[0].members[1].signatures()[0][1][0].clamp, + harness.ok(not results[0].members[1].signatures()[0][1][0].type.clamp, "Should not be clamped") parser = parser.reset() @@ -86,9 +86,9 @@ def WebIDLTest(parser, harness): results = parser.finish() # Pull out the first argument out of the arglist of the first (and # only) signature. - harness.ok(results[0].members[0].signatures()[0][1][0].enforceRange, + harness.ok(results[0].members[0].signatures()[0][1][0].type.enforceRange, "Should be enforceRange") - harness.ok(not results[0].members[1].signatures()[0][1][0].enforceRange, + harness.ok(not results[0].members[1].signatures()[0][1][0].type.enforceRange, "Should not be enforceRange") parser = parser.reset() diff --git a/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py b/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py new file mode 100644 index 00000000000..0ea38ce437b --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py @@ -0,0 +1,16 @@ +def WebIDLTest(parser, harness): + exception = None + try: + parser.parse( + """ + typedef long foo; + typedef long foo; + """) + + results = parser.finish() + except Exception as e: + exception = e + + harness.ok(exception, "Should have thrown.") + harness.ok("Multiple unresolvable definitions of identifier 'foo'" in str(exception), + "Should have a sane exception message") |