aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/bindings/codegen')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py26
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py347
-rw-r--r--components/script/dom/bindings/codegen/parser/pref-main-thread.patch27
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py238
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py8
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_typedef_identifier_conflict.py16
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")