diff options
author | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-07-12 13:58:26 -0700 |
---|---|---|
committer | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-07-12 13:58:26 -0700 |
commit | aa811be8164a4dc872c438738f897ac605bd093d (patch) | |
tree | 69736378335bb5edb38209c94ecfff307fea69ad /components/script/dom/bindings/codegen/parser | |
parent | b5c9e8a58e5d010f0ea5d9bcd733a745f32b0d17 (diff) | |
download | servo-aa811be8164a4dc872c438738f897ac605bd093d.tar.gz servo-aa811be8164a4dc872c438738f897ac605bd093d.zip |
webidl: Update parser
Some patches failed to apply. I'll apply manually after this.
Diffstat (limited to 'components/script/dom/bindings/codegen/parser')
4 files changed, 1167 insertions, 280 deletions
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index acedc38ae5d..a0dededf912 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -158,6 +158,9 @@ class IDLObject(object): def isInterface(self): return False + def isNamespace(self): + return False + def isEnum(self): return False @@ -585,8 +588,8 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins): return set() -class IDLPartialInterface(IDLObject): - def __init__(self, location, name, members, nonPartialInterface): +class IDLPartialInterfaceOrNamespace(IDLObject): + def __init__(self, location, name, members, nonPartialInterfaceOrNamespace): assert isinstance(name, IDLUnresolvedIdentifier) IDLObject.__init__(self, location) @@ -595,9 +598,10 @@ class IDLPartialInterface(IDLObject): # propagatedExtendedAttrs are the ones that should get # propagated to our non-partial interface. self.propagatedExtendedAttrs = [] - self._nonPartialInterface = nonPartialInterface + self._haveSecureContextExtendedAttribute = False + self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace self._finished = False - nonPartialInterface.addPartialInterface(self) + nonPartialInterfaceOrNamespace.addPartialInterface(self) def addExtendedAttributes(self, attrs): for attr in attrs: @@ -605,6 +609,16 @@ class IDLPartialInterface(IDLObject): if identifier in ["Constructor", "NamedConstructor"]: self.propagatedExtendedAttrs.append(attr) + elif identifier == "SecureContext": + self._haveSecureContextExtendedAttribute = True + # This gets propagated to all our members. + for member in self.members: + if member.getExtendedAttribute("SecureContext"): + raise WebIDLError("[SecureContext] specified on both a " + "partial interface member and on the " + "partial interface itself", + [member.location, attr.location]) + member.addExtendedAttributes([attr]) elif identifier == "Exposed": # This just gets propagated to all our members. for member in self.members: @@ -623,9 +637,23 @@ class IDLPartialInterface(IDLObject): if self._finished: return self._finished = True - # Need to make sure our non-partial interface gets finished so it can - # report cases when we only have partial interfaces. - self._nonPartialInterface.finish(scope) + if (not self._haveSecureContextExtendedAttribute and + self._nonPartialInterfaceOrNamespace.getExtendedAttribute("SecureContext")): + # This gets propagated to all our members. + for member in self.members: + if member.getExtendedAttribute("SecureContext"): + raise WebIDLError("[SecureContext] specified on both a " + "partial interface member and on the " + "non-partial interface", + [member.location, + self._nonPartialInterfaceOrNamespace.location]) + member.addExtendedAttributes( + [IDLExtendedAttribute(self._nonPartialInterfaceOrNamespace.location, + ("SecureContext",))]) + # Need to make sure our non-partial interface or namespace gets + # finished so it can report cases when we only have partial + # interfaces/namespaces. + self._nonPartialInterfaceOrNamespace.finish(scope) def validate(self): pass @@ -645,7 +673,7 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet): exposureSet.update(globalScope.globalNameMapping[name]) -class IDLInterface(IDLObjectWithScope, IDLExposureMixins): +class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): def __init__(self, location, parentScope, name, parent, members, isKnownNonPartial): assert isinstance(parentScope, IDLScope) @@ -691,9 +719,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): if isKnownNonPartial: self.setNonPartial(location, parent, members) - def __str__(self): - return "Interface '%s'" % self.identifier.name - def ctor(self): identifier = IDLUnresolvedIdentifier(self.location, "constructor", allowForbidden=True) @@ -789,6 +814,20 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): assert iter(self.members) + if self.isNamespace(): + assert not self.parent + for m in self.members: + if m.isAttr() or m.isMethod(): + if m.isStatic(): + raise WebIDLError("Don't mark things explicitly static " + "in namespaces", + [self.location, m.location]) + # Just mark all our methods/attributes as static. The other + # option is to duplicate the relevant InterfaceMembers + # production bits but modified to produce static stuff to + # start with, but that sounds annoying. + m.forceStatic() + if self.parent: self.parent.finish(scope) self.parent._hasChildInterfaces = True @@ -843,6 +882,17 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): self.parent.identifier.name), [self.location, self.parent.location]) + # Interfaces that are not [SecureContext] can't inherit + # from [SecureContext] interfaces. + if (self.parent.getExtendedAttribute("SecureContext") and + not self.getExtendedAttribute("SecureContext")): + raise WebIDLError("Interface %s does not have " + "[SecureContext] but inherits from " + "interface %s which does" % + (self.identifier.name, + self.parent.identifier.name), + [self.location, self.parent.location]) + for iface in self.implementedInterfaces: iface.finish(scope) @@ -1211,6 +1261,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): member.getExtendedAttribute("ChromeOnly") or member.getExtendedAttribute("Pref") or member.getExtendedAttribute("Func") or + member.getExtendedAttribute("SecureContext") or member.getExtendedAttribute("AvailableIn") or member.getExtendedAttribute("CheckAnyPermissions") or member.getExtendedAttribute("CheckAllPermissions")): @@ -1285,9 +1336,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): [self.location, iterableDecl.location, indexedGetter.location]) - def isInterface(self): - return True - def isExternal(self): return False @@ -1338,7 +1386,172 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): return not hasattr(self, "_noInterfaceObject") def hasInterfacePrototypeObject(self): - return not self.isCallback() and self.getUserData('hasConcreteDescendant', False) + return (not self.isCallback() and not self.isNamespace() + and self.getUserData('hasConcreteDescendant', False)) + + def addImplementedInterface(self, implementedInterface): + assert(isinstance(implementedInterface, IDLInterface)) + self.implementedInterfaces.add(implementedInterface) + + def getInheritedInterfaces(self): + """ + Returns a list of the interfaces this interface inherits from + (not including this interface itself). The list is in order + from most derived to least derived. + """ + assert(self._finished) + if not self.parent: + return [] + parentInterfaces = self.parent.getInheritedInterfaces() + parentInterfaces.insert(0, self.parent) + return parentInterfaces + + def getConsequentialInterfaces(self): + assert(self._finished) + # The interfaces we implement directly + consequentialInterfaces = set(self.implementedInterfaces) + + # And their inherited interfaces + for iface in self.implementedInterfaces: + consequentialInterfaces |= set(iface.getInheritedInterfaces()) + + # And now collect up the consequential interfaces of all of those + temp = set() + for iface in consequentialInterfaces: + temp |= iface.getConsequentialInterfaces() + + return consequentialInterfaces | temp + + def findInterfaceLoopPoint(self, otherInterface): + """ + Finds an interface, amongst our ancestors and consequential interfaces, + that inherits from otherInterface or implements otherInterface + directly. If there is no such interface, returns None. + """ + if self.parent: + if self.parent == otherInterface: + return self + loopPoint = self.parent.findInterfaceLoopPoint(otherInterface) + if loopPoint: + return loopPoint + if otherInterface in self.implementedInterfaces: + return self + for iface in self.implementedInterfaces: + loopPoint = iface.findInterfaceLoopPoint(otherInterface) + if loopPoint: + return loopPoint + return None + + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) + + def setNonPartial(self, location, parent, members): + assert not parent or isinstance(parent, IDLIdentifierPlaceholder) + if self._isKnownNonPartial: + raise WebIDLError("Two non-partial definitions for the " + "same %s" % + ("interface" if self.isInterface() + else "namespace"), + [location, self.location]) + self._isKnownNonPartial = True + # Now make it look like we were parsed at this new location, since + # that's the place where the interface is "really" defined + self.location = location + assert not self.parent + self.parent = parent + # Put the new members at the beginning + self.members = members + self.members + + def addPartialInterface(self, partial): + assert self.identifier.name == partial.identifier.name + self._partialInterfaces.append(partial) + + def getJSImplementation(self): + classId = self.getExtendedAttribute("JSImplementation") + if not classId: + return classId + assert isinstance(classId, list) + assert len(classId) == 1 + return classId[0] + + def isJSImplemented(self): + return bool(self.getJSImplementation()) + + def isProbablyShortLivingObject(self): + current = self + while current: + if current.getExtendedAttribute("ProbablyShortLivingObject"): + return True + current = current.parent + return False + + def isNavigatorProperty(self): + naviProp = self.getExtendedAttribute("NavigatorProperty") + if not naviProp: + return False + assert len(naviProp) == 1 + assert isinstance(naviProp, list) + assert len(naviProp[0]) != 0 + return True + + def getNavigatorProperty(self): + naviProp = self.getExtendedAttribute("NavigatorProperty") + if not naviProp: + return None + assert len(naviProp) == 1 + assert isinstance(naviProp, list) + assert len(naviProp[0]) != 0 + conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes + attr = IDLAttribute(self.location, + IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]), + IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)), + True, + extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes }, + navigatorObjectGetter=True) + attr._exposureGlobalNames = self._exposureGlobalNames + # We're abusing Constant a little bit here, because we need Cached. The + # getter will create a new object every time, but we're never going to + # clear the cached value. + extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )), + IDLExtendedAttribute(self.location, ("Cached", )), + IDLExtendedAttribute(self.location, ("Constant", )) ] + attr.addExtendedAttributes(extendedAttrs) + return attr + + def hasChildInterfaces(self): + return self._hasChildInterfaces + + def isOnGlobalProtoChain(self): + return self._isOnGlobalProtoChain + + def _getDependentObjects(self): + deps = set(self.members) + deps.update(self.implementedInterfaces) + if self.parent: + deps.add(self.parent) + return deps + + def hasMembersInSlots(self): + return self._ownMembersInSlots != 0 + + conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn", + "SecureContext", + "CheckAnyPermissions", + "CheckAllPermissions" ] + def isExposedConditionally(self): + return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes) + +class IDLInterface(IDLInterfaceOrNamespace): + def __init__(self, location, parentScope, name, parent, members, + isKnownNonPartial): + IDLInterfaceOrNamespace.__init__(self, location, parentScope, name, + parent, members, isKnownNonPartial) + + def __str__(self): + return "Interface '%s'" % self.identifier.name + + def isInterface(self): + return True def addExtendedAttributes(self, attrs): for attr in attrs: @@ -1469,6 +1682,18 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): self.parentScope.globalNames.add(self.identifier.name) self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name) self._isOnGlobalProtoChain = True + elif identifier == "SecureContext": + if not attr.noArguments(): + raise WebIDLError("[%s] must take no arguments" % identifier, + [attr.location]) + # This gets propagated to all our members. + for member in self.members: + if member.getExtendedAttribute("SecureContext"): + raise WebIDLError("[SecureContext] specified on both " + "an interface member and on the " + "interface itself", + [member.location, attr.location]) + member.addExtendedAttributes([attr]) elif (identifier == "NeedResolve" or identifier == "OverrideBuiltins" or identifier == "ChromeOnly" or @@ -1506,154 +1731,46 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): attrlist = attr.listValue() self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True - def addImplementedInterface(self, implementedInterface): - assert(isinstance(implementedInterface, IDLInterface)) - self.implementedInterfaces.add(implementedInterface) - - def getInheritedInterfaces(self): - """ - Returns a list of the interfaces this interface inherits from - (not including this interface itself). The list is in order - from most derived to least derived. - """ - assert(self._finished) - if not self.parent: - return [] - parentInterfaces = self.parent.getInheritedInterfaces() - parentInterfaces.insert(0, self.parent) - return parentInterfaces - - def getConsequentialInterfaces(self): - assert(self._finished) - # The interfaces we implement directly - consequentialInterfaces = set(self.implementedInterfaces) - - # And their inherited interfaces - for iface in self.implementedInterfaces: - consequentialInterfaces |= set(iface.getInheritedInterfaces()) - - # And now collect up the consequential interfaces of all of those - temp = set() - for iface in consequentialInterfaces: - temp |= iface.getConsequentialInterfaces() - - return consequentialInterfaces | temp - - def findInterfaceLoopPoint(self, otherInterface): - """ - Finds an interface, amongst our ancestors and consequential interfaces, - that inherits from otherInterface or implements otherInterface - directly. If there is no such interface, returns None. - """ - if self.parent: - if self.parent == otherInterface: - return self - loopPoint = self.parent.findInterfaceLoopPoint(otherInterface) - if loopPoint: - return loopPoint - if otherInterface in self.implementedInterfaces: - return self - for iface in self.implementedInterfaces: - loopPoint = iface.findInterfaceLoopPoint(otherInterface) - if loopPoint: - return loopPoint - return None - - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - - def setNonPartial(self, location, parent, members): - assert not parent or isinstance(parent, IDLIdentifierPlaceholder) - if self._isKnownNonPartial: - raise WebIDLError("Two non-partial definitions for the " - "same interface", - [location, self.location]) - self._isKnownNonPartial = True - # Now make it look like we were parsed at this new location, since - # that's the place where the interface is "really" defined - self.location = location - assert not self.parent - self.parent = parent - # Put the new members at the beginning - self.members = members + self.members - - def addPartialInterface(self, partial): - assert self.identifier.name == partial.identifier.name - self._partialInterfaces.append(partial) - def getJSImplementation(self): - classId = self.getExtendedAttribute("JSImplementation") - if not classId: - return classId - assert isinstance(classId, list) - assert len(classId) == 1 - return classId[0] - - def isJSImplemented(self): - return bool(self.getJSImplementation()) +class IDLNamespace(IDLInterfaceOrNamespace): + def __init__(self, location, parentScope, name, members, isKnownNonPartial): + IDLInterfaceOrNamespace.__init__(self, location, parentScope, name, + None, members, isKnownNonPartial) - def isProbablyShortLivingObject(self): - current = self - while current: - if current.getExtendedAttribute("ProbablyShortLivingObject"): - return True - current = current.parent - return False + def __str__(self): + return "Namespace '%s'" % self.identifier.name - def isNavigatorProperty(self): - naviProp = self.getExtendedAttribute("NavigatorProperty") - if not naviProp: - return False - assert len(naviProp) == 1 - assert isinstance(naviProp, list) - assert len(naviProp[0]) != 0 + def isNamespace(self): return True - def getNavigatorProperty(self): - naviProp = self.getExtendedAttribute("NavigatorProperty") - if not naviProp: - return None - assert len(naviProp) == 1 - assert isinstance(naviProp, list) - assert len(naviProp[0]) != 0 - conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterface.conditionExtendedAttributes - attr = IDLAttribute(self.location, - IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]), - IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)), - True, - extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes }, - navigatorObjectGetter=True) - attr._exposureGlobalNames = self._exposureGlobalNames - # We're abusing Constant a little bit here, because we need Cached. The - # getter will create a new object every time, but we're never going to - # clear the cached value. - extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )), - IDLExtendedAttribute(self.location, ("Cached", )), - IDLExtendedAttribute(self.location, ("Constant", )) ] - attr.addExtendedAttributes(extendedAttrs) - return attr - - def hasChildInterfaces(self): - return self._hasChildInterfaces - - def isOnGlobalProtoChain(self): - return self._isOnGlobalProtoChain - - def _getDependentObjects(self): - deps = set(self.members) - deps.update(self.implementedInterfaces) - if self.parent: - deps.add(self.parent) - return deps + def addExtendedAttributes(self, attrs): + # The set of things namespaces support is small enough it's simpler + # to factor out into a separate method than it is to sprinkle + # isNamespace() checks all through + # IDLInterfaceOrNamespace.addExtendedAttributes. + for attr in attrs: + identifier = attr.identifier() - def hasMembersInSlots(self): - return self._ownMembersInSlots != 0 + if identifier == "Exposed": + convertExposedAttrToGlobalNameSet(attr, + self._exposureGlobalNames) + elif identifier == "ClassString": + # Takes a string value to override the default "Object" if + # desired. + if not attr.hasValue(): + raise WebIDLError("[%s] must have a value" % identifier, + [attr.location]) + elif identifier == "ProtoObjectHack": + if not attr.noArguments(): + raise WebIDLError("[%s] must not have arguments" % identifier, + [attr.location]) + else: + raise WebIDLError("Unknown extended attribute %s on namespace" % + identifier, + [attr.location]) - conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn", - "CheckAnyPermissions", - "CheckAllPermissions" ] - def isExposedConditionally(self): - return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes) + attrlist = attr.listValue() + self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True class IDLDictionary(IDLObjectWithScope): @@ -2066,7 +2183,30 @@ class IDLUnresolvedType(IDLType): "distinguishable from other things") -class IDLNullableType(IDLType): +class IDLParameterizedType(IDLType): + def __init__(self, location, name, innerType): + IDLType.__init__(self, location, name) + self.builtin = False + self.inner = innerType + + def includesRestrictedFloat(self): + return self.inner.includesRestrictedFloat() + + def resolveType(self, parentScope): + assert isinstance(parentScope, IDLScope) + self.inner.resolveType(parentScope) + + def isComplete(self): + return self.inner.isComplete() + + def unroll(self): + return self.inner.unroll() + + def _getDependentObjects(self): + return self.inner._getDependentObjects() + + +class IDLNullableType(IDLParameterizedType): def __init__(self, location, innerType): assert not innerType.isVoid() assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] @@ -2074,9 +2214,7 @@ class IDLNullableType(IDLType): name = innerType.name if innerType.isComplete(): name += "OrNull" - IDLType.__init__(self, location, name) - self.inner = innerType - self.builtin = False + IDLParameterizedType.__init__(self, location, name, innerType) def __eq__(self, other): return isinstance(other, IDLNullableType) and self.inner == other.inner @@ -2117,9 +2255,6 @@ class IDLNullableType(IDLType): def isUnrestricted(self): return self.inner.isUnrestricted() - def includesRestrictedFloat(self): - return self.inner.includesRestrictedFloat() - def isInteger(self): return self.inner.isInteger() @@ -2174,13 +2309,6 @@ class IDLNullableType(IDLType): def tag(self): return self.inner.tag() - def resolveType(self, parentScope): - assert isinstance(parentScope, IDLScope) - self.inner.resolveType(parentScope) - - def isComplete(self): - return self.inner.isComplete() - def complete(self, scope): self.inner = self.inner.complete(scope) if self.inner.nullable(): @@ -2196,9 +2324,6 @@ class IDLNullableType(IDLType): self.name = self.inner.name + "OrNull" return self - def unroll(self): - return self.inner.unroll() - def isDistinguishableFrom(self, other): if (other.nullable() or (other.isUnion() and other.hasNullableType) or other.isDictionary()): @@ -2206,17 +2331,12 @@ class IDLNullableType(IDLType): return False return self.inner.isDistinguishableFrom(other) - def _getDependentObjects(self): - return self.inner._getDependentObjects() - -class IDLSequenceType(IDLType): +class IDLSequenceType(IDLParameterizedType): def __init__(self, location, parameterType): assert not parameterType.isVoid() - IDLType.__init__(self, location, parameterType.name) - self.inner = parameterType - self.builtin = False + IDLParameterizedType.__init__(self, location, parameterType.name, parameterType) # Need to set self.name up front if our inner type is already complete, # since in that case our .complete() won't be called. if self.inner.isComplete(): @@ -2267,27 +2387,14 @@ class IDLSequenceType(IDLType): def isSerializable(self): return self.inner.isSerializable() - def includesRestrictedFloat(self): - return self.inner.includesRestrictedFloat() - def tag(self): return IDLType.Tags.sequence - def resolveType(self, parentScope): - assert isinstance(parentScope, IDLScope) - self.inner.resolveType(parentScope) - - def isComplete(self): - return self.inner.isComplete() - def complete(self, scope): self.inner = self.inner.complete(scope) self.name = self.inner.name + "Sequence" return self - def unroll(self): - return self.inner.unroll() - def isDistinguishableFrom(self, other): if other.isPromise(): return False @@ -2299,20 +2406,12 @@ class IDLSequenceType(IDLType): other.isDictionary() or other.isCallback() or other.isMozMap()) - def _getDependentObjects(self): - return self.inner._getDependentObjects() - -class IDLMozMapType(IDLType): - # XXXbz This is pretty similar to IDLSequenceType in various ways. - # And maybe to IDLNullableType. Should we have a superclass for - # "type containing this other type"? Bug 1015318. +class IDLMozMapType(IDLParameterizedType): def __init__(self, location, parameterType): assert not parameterType.isVoid() - IDLType.__init__(self, location, parameterType.name) - self.inner = parameterType - self.builtin = False + IDLParameterizedType.__init__(self, location, parameterType.name, parameterType) # Need to set self.name up front if our inner type is already complete, # since in that case our .complete() won't be called. if self.inner.isComplete(): @@ -2327,19 +2426,9 @@ class IDLMozMapType(IDLType): def isMozMap(self): return True - def includesRestrictedFloat(self): - return self.inner.includesRestrictedFloat() - def tag(self): return IDLType.Tags.mozmap - def resolveType(self, parentScope): - assert isinstance(parentScope, IDLScope) - self.inner.resolveType(parentScope) - - def isComplete(self): - return self.inner.isComplete() - def complete(self, scope): self.inner = self.inner.complete(scope) self.name = self.inner.name + "MozMap" @@ -2363,9 +2452,6 @@ class IDLMozMapType(IDLType): def isExposedInAllOf(self, exposureSet): return self.inner.unroll().isExposedInAllOf(exposureSet) - def _getDependentObjects(self): - return self.inner._getDependentObjects() - class IDLUnionType(IDLType): def __init__(self, location, memberTypes): @@ -3877,6 +3963,7 @@ class IDLConst(IDLInterfaceMember): elif (identifier == "Pref" or identifier == "ChromeOnly" or identifier == "Func" or + identifier == "SecureContext" or identifier == "AvailableIn" or identifier == "CheckAnyPermissions" or identifier == "CheckAllPermissions"): @@ -3903,7 +3990,7 @@ class IDLAttribute(IDLInterfaceMember): self.type = type self.readonly = readonly self.inherit = inherit - self.static = static + self._static = static self.lenientThis = False self._unforgeable = False self.stringifier = stringifier @@ -3925,7 +4012,10 @@ class IDLAttribute(IDLInterfaceMember): [self.location]) def isStatic(self): - return self.static + return self._static + + def forceStatic(self): + self._static = True def __str__(self): return "'%s' attribute '%s'" % (self.type, self.identifier) @@ -3979,16 +4069,53 @@ class IDLAttribute(IDLInterfaceMember): "interface type as its type", [self.location]) def validate(self): + def typeContainsChromeOnlyDictionaryMember(type): + if (type.nullable() or + type.isSequence() or + type.isMozMap()): + return typeContainsChromeOnlyDictionaryMember(type.inner) + + if type.isUnion(): + for memberType in type.flatMemberTypes: + (contains, location) = typeContainsChromeOnlyDictionaryMember(memberType) + if contains: + return (True, location) + + if type.isDictionary(): + dictionary = type.inner + while dictionary: + (contains, location) = dictionaryContainsChromeOnlyMember(dictionary) + if contains: + return (True, location) + dictionary = dictionary.parent + + return (False, None) + + def dictionaryContainsChromeOnlyMember(dictionary): + for member in dictionary.members: + if member.getExtendedAttribute("ChromeOnly"): + return (True, member.location) + (contains, location) = typeContainsChromeOnlyDictionaryMember(member.type) + if contains: + return (True, location) + return (False, None) + IDLInterfaceMember.validate(self) - if ((self.getExtendedAttribute("Cached") or - self.getExtendedAttribute("StoreInSlot")) and - not self.affects == "Nothing"): - raise WebIDLError("Cached attributes and attributes stored in " - "slots must be Constant or Pure or " - "Affects=Nothing, since the getter won't always " - "be called.", - [self.location]) + if (self.getExtendedAttribute("Cached") or + self.getExtendedAttribute("StoreInSlot")): + if not self.affects == "Nothing": + raise WebIDLError("Cached attributes and attributes stored in " + "slots must be Constant or Pure or " + "Affects=Nothing, since the getter won't always " + "be called.", + [self.location]) + (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type) + if contains: + raise WebIDLError("[Cached] and [StoreInSlot] must not be used " + "on an attribute whose type contains a " + "[ChromeOnly] dictionary member", + [self.location, location]) if self.getExtendedAttribute("Frozen"): if (not self.type.isSequence() and not self.type.isDictionary() and not self.type.isMozMap()): @@ -4177,6 +4304,7 @@ class IDLAttribute(IDLInterfaceMember): identifier == "GetterThrows" or identifier == "ChromeOnly" or identifier == "Func" or + identifier == "SecureContext" or identifier == "Frozen" or identifier == "AvailableIn" or identifier == "NewObject" or @@ -4233,6 +4361,7 @@ class IDLArgument(IDLObjectWithIdentifier): self.enforceRange = False self.clamp = False self._allowTreatNonCallableAsNull = False + self._extendedAttrDict = {} assert not variadic or optional assert not variadic or not defaultValue @@ -4262,11 +4391,22 @@ class IDLArgument(IDLObjectWithIdentifier): self.enforceRange = True elif identifier == "TreatNonCallableAsNull": self._allowTreatNonCallableAsNull = True + elif (self.dictionaryMember and + (identifier == "ChromeOnly" or identifier == "Func")): + if not self.optional: + raise WebIDLError("[%s] must not be used on a required " + "dictionary member" % identifier, + [attribute.location]) else: raise WebIDLError("Unhandled extended attribute on %s" % ("a dictionary member" if self.dictionaryMember else "an argument"), [attribute.location]) + attrlist = attribute.listValue() + self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True + + def getExtendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) def isComplete(self): return self._isComplete @@ -4537,6 +4677,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope): def isStatic(self): return self._static + def forceStatic(self): + self._static = True + def isGetter(self): return self._getter @@ -4888,6 +5031,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope): identifier == "Pref" or identifier == "Deprecated" or identifier == "Func" or + identifier == "SecureContext" or identifier == "AvailableIn" or identifier == "CheckAnyPermissions" or identifier == "CheckAllPermissions" or @@ -5115,7 +5259,8 @@ class Tokenizer(object): "or": "OR", "maplike": "MAPLIKE", "setlike": "SETLIKE", - "iterable": "ITERABLE" + "iterable": "ITERABLE", + "namespace": "NAMESPACE" } tokens.extend(keywords.values()) @@ -5212,7 +5357,8 @@ class Parser(Tokenizer): def p_Definition(self, p): """ Definition : CallbackOrInterface - | PartialInterface + | Namespace + | Partial | Dictionary | Exception | Enum @@ -5246,33 +5392,54 @@ class Parser(Tokenizer): assert p[1] p[0] = p[1] - def p_Interface(self, p): + def handleNonPartialObject(self, location, identifier, constructor, + constructorArgs, nonPartialArgs): """ - Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON + This handles non-partial objects (interfaces and namespaces) by + checking for an existing partial object, and promoting it to + non-partial as needed. The return value is the non-partial object. + + constructorArgs are all the args for the constructor except the last + one: isKnownNonPartial. + + nonPartialArgs are the args for the setNonPartial call. """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[5] - parent = p[3] + # The name of the class starts with "IDL", so strip that off. + # Also, starts with a capital letter after that, so nix that + # as well. + prettyname = constructor.__name__[3:].lower() try: existingObj = self.globalScope()._lookupIdentifier(identifier) if existingObj: - p[0] = existingObj - if not isinstance(p[0], IDLInterface): - raise WebIDLError("Interface has the same name as " - "non-interface object", - [location, p[0].location]) - p[0].setNonPartial(location, parent, members) - return + if not isinstance(existingObj, constructor): + raise WebIDLError("%s has the same name as " + "non-%s object" % + (prettyname.capitalize(), prettyname), + [location, existingObj.location]) + existingObj.setNonPartial(*nonPartialArgs) + return existingObj except Exception, ex: if isinstance(ex, WebIDLError): raise ex pass - iface = IDLInterface(location, self.globalScope(), identifier, parent, - members, isKnownNonPartial=True) - p[0] = iface + # True for isKnownNonPartial + return constructor(*(constructorArgs + [True])) + + def p_Interface(self, p): + """ + Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[5] + parent = p[3] + + p[0] = self.handleNonPartialObject( + location, identifier, IDLInterface, + [location, self.globalScope(), identifier, parent, members], + [location, parent, members]) def p_InterfaceForwardDecl(self, p): """ @@ -5297,34 +5464,100 @@ class Parser(Tokenizer): p[0] = IDLExternalInterface(location, self.globalScope(), identifier) - def p_PartialInterface(self, p): + def p_Namespace(self, p): """ - PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON + Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON """ - location = self.getLocation(p, 2) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) - members = p[5] + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[4] + + p[0] = self.handleNonPartialObject( + location, identifier, IDLNamespace, + [location, self.globalScope(), identifier, members], + [location, None, members]) + + def p_Partial(self, p): + """ + Partial : PARTIAL PartialDefinition + """ + p[0] = p[2] + + def p_PartialDefinition(self, p): + """ + PartialDefinition : PartialInterface + | PartialNamespace + """ + p[0] = p[1] + + def handlePartialObject(self, location, identifier, nonPartialConstructor, + nonPartialConstructorArgs, + partialConstructorArgs): + """ + This handles partial objects (interfaces and namespaces) by checking for + an existing non-partial object, and adding ourselves to it as needed. + The return value is our partial object. For now we just use + IDLPartialInterfaceOrNamespace for partial objects. + + nonPartialConstructorArgs are all the args for the non-partial + constructor except the last two: members and isKnownNonPartial. + + partialConstructorArgs are the arguments for the + IDLPartialInterfaceOrNamespace constructor, except the last one (the + non-partial object). + """ + # The name of the class starts with "IDL", so strip that off. + # Also, starts with a capital letter after that, so nix that + # as well. + prettyname = nonPartialConstructor.__name__[3:].lower() - nonPartialInterface = None + nonPartialObject = None try: - nonPartialInterface = self.globalScope()._lookupIdentifier(identifier) - if nonPartialInterface: - if not isinstance(nonPartialInterface, IDLInterface): - raise WebIDLError("Partial interface has the same name as " - "non-interface object", - [location, nonPartialInterface.location]) + nonPartialObject = self.globalScope()._lookupIdentifier(identifier) + if nonPartialObject: + if not isinstance(nonPartialObject, nonPartialConstructor): + raise WebIDLError("Partial %s has the same name as " + "non-%s object" % + (prettyname, prettyname), + [location, nonPartialObject.location]) except Exception, ex: if isinstance(ex, WebIDLError): raise ex pass - if not nonPartialInterface: - nonPartialInterface = IDLInterface(location, self.globalScope(), - identifier, None, - [], isKnownNonPartial=False) - partialInterface = IDLPartialInterface(location, identifier, members, - nonPartialInterface) - p[0] = partialInterface + if not nonPartialObject: + nonPartialObject = nonPartialConstructor( + # No members, False for isKnownNonPartial + *(nonPartialConstructorArgs + [[], False])) + partialInterface = IDLPartialInterfaceOrNamespace( + *(partialConstructorArgs + [nonPartialObject])) + return partialInterface + + def p_PartialInterface(self, p): + """ + PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[4] + + p[0] = self.handlePartialObject( + location, identifier, IDLInterface, + [location, self.globalScope(), identifier, None], + [location, identifier, members]) + + def p_PartialNamespace(self, p): + """ + PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + members = p[4] + + p[0] = self.handlePartialObject( + location, identifier, IDLNamespace, + [location, self.globalScope(), identifier], + [location, identifier, members]) def p_Inheritance(self, p): """ @@ -5985,6 +6218,7 @@ class Parser(Tokenizer): | JSONIFIER | TYPEDEF | UNRESTRICTED + | NAMESPACE """ p[0] = p[1] @@ -6585,19 +6819,20 @@ class Parser(Tokenizer): Tokenizer.__init__(self, outputdir, lexer) logger = SqueakyCleanLogger() - self.parser = yacc.yacc(module=self, - outputdir=outputdir, - tabmodule='webidlyacc', - errorlog=logger, - debug=False - # Pickling the grammar is a speedup in - # some cases (older Python?) but a - # significant slowdown in others. - # We're not pickling for now, until it - # becomes a speedup again. - # , picklefile='WebIDLGrammar.pkl' - ) - logger.reportGrammarErrors() + try: + self.parser = yacc.yacc(module=self, + outputdir=outputdir, + tabmodule='webidlyacc', + errorlog=logger + # Pickling the grammar is a speedup in + # some cases (older Python?) but a + # significant slowdown in others. + # We're not pickling for now, until it + # becomes a speedup again. + # , picklefile='WebIDLGrammar.pkl' + ) + finally: + logger.reportGrammarErrors() self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None) # To make our test harness work, pretend like we have a primary global already. @@ -6668,10 +6903,11 @@ class Parser(Tokenizer): # We're generating a partial interface to add a readonly # property to the Navigator interface for every interface # annotated with NavigatorProperty. - partialInterface = IDLPartialInterface(iface.location, - IDLUnresolvedIdentifier(iface.location, "Navigator"), - [ navigatorProperty ], - navigatorInterface) + partialInterface = IDLPartialInterfaceOrNamespace( + iface.location, + IDLUnresolvedIdentifier(iface.location, "Navigator"), + [ navigatorProperty ], + navigatorInterface) self._productions.append(partialInterface) iterable = None diff --git a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py new file mode 100644 index 00000000000..433b7e501a4 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py @@ -0,0 +1,110 @@ +def WebIDLTest(parser, harness): + parser.parse(""" + dictionary Dict { + any foo; + [ChromeOnly] any bar; + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should have a dictionary") + members = results[0].members; + harness.check(len(members), 2, "Should have two members") + # Note that members are ordered lexicographically, so "bar" comes + # before "foo". + harness.ok(members[0].getExtendedAttribute("ChromeOnly"), + "First member is not ChromeOnly") + harness.ok(not members[1].getExtendedAttribute("ChromeOnly"), + "Second member is ChromeOnly") + + parser = parser.reset() + parser.parse(""" + dictionary Dict { + any foo; + any bar; + }; + + interface Iface { + [Constant, Cached] readonly attribute Dict dict; + }; + """) + results = parser.finish() + harness.check(len(results), 2, "Should have a dictionary and an interface") + + parser = parser.reset() + exception = None + try: + parser.parse(""" + dictionary Dict { + any foo; + [ChromeOnly] any bar; + }; + + interface Iface { + [Constant, Cached] readonly attribute Dict dict; + }; + """) + results = parser.finish() + except Exception, exception: + pass + + harness.ok(exception, "Should have thrown.") + harness.check(exception.message, + "[Cached] and [StoreInSlot] must not be used on an attribute " + "whose type contains a [ChromeOnly] dictionary member", + "Should have thrown the right exception") + + parser = parser.reset() + exception = None + try: + parser.parse(""" + dictionary ParentDict { + [ChromeOnly] any bar; + }; + + dictionary Dict : ParentDict { + any foo; + }; + + interface Iface { + [Constant, Cached] readonly attribute Dict dict; + }; + """) + results = parser.finish() + except Exception, exception: + pass + + harness.ok(exception, "Should have thrown (2).") + harness.check(exception.message, + "[Cached] and [StoreInSlot] must not be used on an attribute " + "whose type contains a [ChromeOnly] dictionary member", + "Should have thrown the right exception (2)") + + parser = parser.reset() + exception = None + try: + parser.parse(""" + dictionary GrandParentDict { + [ChromeOnly] any baz; + }; + + dictionary ParentDict : GrandParentDict { + any bar; + }; + + dictionary Dict : ParentDict { + any foo; + }; + + interface Iface { + [Constant, Cached] readonly attribute Dict dict; + }; + """) + results = parser.finish() + except Exception, exception: + pass + + harness.ok(exception, "Should have thrown (3).") + harness.check(exception.message, + "[Cached] and [StoreInSlot] must not be used on an attribute " + "whose type contains a [ChromeOnly] dictionary member", + "Should have thrown the right exception (3)") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_namespace.py b/components/script/dom/bindings/codegen/parser/tests/test_namespace.py new file mode 100644 index 00000000000..74533a1770e --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_namespace.py @@ -0,0 +1,223 @@ +def WebIDLTest(parser, harness): + parser.parse( + """ + namespace MyNamespace { + attribute any foo; + any bar(); + }; + """) + + results = parser.finish() + harness.check(len(results), 1, "Should have a thing.") + harness.ok(results[0].isNamespace(), "Our thing should be a namespace"); + harness.check(len(results[0].members), 2, + "Should have two things in our namespace") + harness.ok(results[0].members[0].isAttr(), "First member is attribute") + harness.ok(results[0].members[0].isStatic(), "Attribute should be static") + harness.ok(results[0].members[1].isMethod(), "Second member is method") + harness.ok(results[0].members[1].isStatic(), "Operation should be static") + + parser = parser.reset() + parser.parse( + """ + namespace MyNamespace { + attribute any foo; + }; + partial namespace MyNamespace { + any bar(); + }; + """) + + results = parser.finish() + harness.check(len(results), 2, "Should have things.") + harness.ok(results[0].isNamespace(), "Our thing should be a namespace"); + harness.check(len(results[0].members), 2, + "Should have two things in our namespace") + harness.ok(results[0].members[0].isAttr(), "First member is attribute") + harness.ok(results[0].members[0].isStatic(), "Attribute should be static"); + harness.ok(results[0].members[1].isMethod(), "Second member is method") + harness.ok(results[0].members[1].isStatic(), "Operation should be static"); + + parser = parser.reset() + parser.parse( + """ + partial namespace MyNamespace { + any bar(); + }; + namespace MyNamespace { + attribute any foo; + }; + """) + + results = parser.finish() + harness.check(len(results), 2, "Should have things.") + harness.ok(results[1].isNamespace(), "Our thing should be a namespace"); + harness.check(len(results[1].members), 2, + "Should have two things in our namespace") + harness.ok(results[1].members[0].isAttr(), "First member is attribute") + harness.ok(results[1].members[0].isStatic(), "Attribute should be static"); + harness.ok(results[1].members[1].isMethod(), "Second member is method") + harness.ok(results[1].members[1].isStatic(), "Operation should be static"); + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + namespace MyNamespace { + static attribute any foo; + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + namespace MyNamespace { + static any bar(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + namespace MyNamespace { + any bar(); + }; + + interface MyNamespace { + any baz(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface MyNamespace { + any baz(); + }; + + namespace MyNamespace { + any bar(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + namespace MyNamespace { + any baz(); + }; + + namespace MyNamespace { + any bar(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + partial namespace MyNamespace { + any baz(); + }; + + interface MyNamespace { + any bar(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + namespace MyNamespace { + any bar(); + }; + + partial interface MyNamespace { + any baz(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + partial interface MyNamespace { + any baz(); + }; + + namespace MyNamespace { + any bar(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface MyNamespace { + any bar(); + }; + + partial namespace MyNamespace { + any baz(); + }; + """) + + results = parser.finish() + except Exception, x: + threw = True + harness.ok(threw, "Should have thrown.") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py new file mode 100644 index 00000000000..d907d08449f --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py @@ -0,0 +1,318 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + [SecureContext] + interface TestSecureContextOnInterface { + const octet TEST_CONSTANT = 0; + readonly attribute byte testAttribute; + void testMethod(byte foo); + }; + partial interface TestSecureContextOnInterface { + const octet TEST_CONSTANT_2 = 0; + readonly attribute byte testAttribute2; + void testMethod2(byte foo); + }; + """) + results = parser.finish() + harness.check(len(results[0].members), 6, "TestSecureContextOnInterface should have six members") + harness.ok(results[0].getExtendedAttribute("SecureContext"), + "Interface should have [SecureContext] extended attribute") + harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to constant members") + harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to attribute members") + harness.ok(results[0].members[2].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to method members") + harness.ok(results[0].members[3].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to constant members from partial interface") + harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to attribute members from partial interface") + harness.ok(results[0].members[5].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to method members from partial interface") + + # Same thing, but with the partial interface specified first: + parser = parser.reset() + parser.parse(""" + partial interface TestSecureContextOnInterfaceAfterPartialInterface { + const octet TEST_CONSTANT_2 = 0; + readonly attribute byte testAttribute2; + void testMethod2(byte foo); + }; + [SecureContext] + interface TestSecureContextOnInterfaceAfterPartialInterface { + const octet TEST_CONSTANT = 0; + readonly attribute byte testAttribute; + void testMethod(byte foo); + }; + """) + results = parser.finish() + harness.check(len(results[1].members), 6, "TestSecureContextOnInterfaceAfterPartialInterface should have six members") + harness.ok(results[1].getExtendedAttribute("SecureContext"), + "Interface should have [SecureContext] extended attribute") + harness.ok(results[1].members[0].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to constant members") + harness.ok(results[1].members[1].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to attribute members") + harness.ok(results[1].members[2].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to method members") + harness.ok(results[1].members[3].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to constant members from partial interface") + harness.ok(results[1].members[4].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to attribute members from partial interface") + harness.ok(results[1].members[5].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to method members from partial interface") + + parser = parser.reset() + parser.parse(""" + interface TestSecureContextOnPartialInterface { + const octet TEST_CONSTANT = 0; + readonly attribute byte testAttribute; + void testMethod(byte foo); + }; + [SecureContext] + partial interface TestSecureContextOnPartialInterface { + const octet TEST_CONSTANT_2 = 0; + readonly attribute byte testAttribute2; + void testMethod2(byte foo); + }; + """) + results = parser.finish() + harness.check(len(results[0].members), 6, "TestSecureContextOnPartialInterface should have six members") + harness.ok(results[0].getExtendedAttribute("SecureContext") is None, + "[SecureContext] should not propagate from a partial interface to the interface") + harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None, + "[SecureContext] should not propagate from a partial interface to the interface's constant members") + harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None, + "[SecureContext] should not propagate from a partial interface to the interface's attribute members") + harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None, + "[SecureContext] should not propagate from a partial interface to the interface's method members") + harness.ok(results[0].members[3].getExtendedAttribute("SecureContext"), + "Constant members from [SecureContext] partial interface should be [SecureContext]") + harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"), + "Attribute members from [SecureContext] partial interface should be [SecureContext]") + harness.ok(results[0].members[5].getExtendedAttribute("SecureContext"), + "Method members from [SecureContext] partial interface should be [SecureContext]") + + parser = parser.reset() + parser.parse(""" + interface TestSecureContextOnInterfaceMembers { + const octet TEST_NON_SECURE_CONSTANT_1 = 0; + [SecureContext] + const octet TEST_SECURE_CONSTANT = 1; + const octet TEST_NON_SECURE_CONSTANT_2 = 2; + readonly attribute byte testNonSecureAttribute1; + [SecureContext] + readonly attribute byte testSecureAttribute; + readonly attribute byte testNonSecureAttribute2; + void testNonSecureMethod1(byte foo); + [SecureContext] + void testSecureMethod(byte foo); + void testNonSecureMethod2(byte foo); + }; + """) + results = parser.finish() + harness.check(len(results[0].members), 9, "TestSecureContextOnInterfaceMembers should have nine members") + harness.ok(results[0].getExtendedAttribute("SecureContext") is None, + "[SecureContext] on members should not propagate up to the interface") + harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None, + "Constant should not have [SecureContext] extended attribute") + harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"), + "Constant should have [SecureContext] extended attribute") + harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None, + "Constant should not have [SecureContext] extended attribute") + harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None, + "Attribute should not have [SecureContext] extended attribute") + harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"), + "Attribute should have [SecureContext] extended attribute") + harness.ok(results[0].members[5].getExtendedAttribute("SecureContext") is None, + "Attribute should not have [SecureContext] extended attribute") + harness.ok(results[0].members[6].getExtendedAttribute("SecureContext") is None, + "Method should not have [SecureContext] extended attribute") + harness.ok(results[0].members[7].getExtendedAttribute("SecureContext"), + "Method should have [SecureContext] extended attribute") + harness.ok(results[0].members[8].getExtendedAttribute("SecureContext") is None, + "Method should not have [SecureContext] extended attribute") + + parser = parser.reset() + parser.parse(""" + interface TestSecureContextOnPartialInterfaceMembers { + }; + partial interface TestSecureContextOnPartialInterfaceMembers { + const octet TEST_NON_SECURE_CONSTANT_1 = 0; + [SecureContext] + const octet TEST_SECURE_CONSTANT = 1; + const octet TEST_NON_SECURE_CONSTANT_2 = 2; + readonly attribute byte testNonSecureAttribute1; + [SecureContext] + readonly attribute byte testSecureAttribute; + readonly attribute byte testNonSecureAttribute2; + void testNonSecureMethod1(byte foo); + [SecureContext] + void testSecureMethod(byte foo); + void testNonSecureMethod2(byte foo); + }; + """) + results = parser.finish() + harness.check(len(results[0].members), 9, "TestSecureContextOnPartialInterfaceMembers should have nine members") + harness.ok(results[0].members[0].getExtendedAttribute("SecureContext") is None, + "Constant from partial interface should not have [SecureContext] extended attribute") + harness.ok(results[0].members[1].getExtendedAttribute("SecureContext"), + "Constant from partial interface should have [SecureContext] extended attribute") + harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None, + "Constant from partial interface should not have [SecureContext] extended attribute") + harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None, + "Attribute from partial interface should not have [SecureContext] extended attribute") + harness.ok(results[0].members[4].getExtendedAttribute("SecureContext"), + "Attribute from partial interface should have [SecureContext] extended attribute") + harness.ok(results[0].members[5].getExtendedAttribute("SecureContext") is None, + "Attribute from partial interface should not have [SecureContext] extended attribute") + harness.ok(results[0].members[6].getExtendedAttribute("SecureContext") is None, + "Method from partial interface should not have [SecureContext] extended attribute") + harness.ok(results[0].members[7].getExtendedAttribute("SecureContext"), + "Method from partial interface should have [SecureContext] extended attribute") + harness.ok(results[0].members[8].getExtendedAttribute("SecureContext") is None, + "Method from partial interface should not have [SecureContext] extended attribute") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [SecureContext=something] + interface TestSecureContextTakesNoValue1 { + const octet TEST_SECURE_CONSTANT = 0; + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[SecureContext] must take no arguments (testing on interface)") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestSecureContextForOverloads1 { + [SecureContext] + void testSecureMethod(byte foo); + }; + partial interface TestSecureContextForOverloads1 { + void testSecureMethod(byte foo, byte bar); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "If [SecureContext] appears on an overloaded operation, then it MUST appear on all overloads") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestSecureContextForOverloads2 { + [SecureContext] + void testSecureMethod(byte foo); + }; + partial interface TestSecureContextForOverloads2 { + [SecureContext] + void testSecureMethod(byte foo, byte bar); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(not threw, "[SecureContext] can appear on an overloaded operation if it appears on all overloads") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [SecureContext] + interface TestSecureContextOnInterfaceAndMember { + [SecureContext] + void testSecureMethod(byte foo); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[SecureContext] must not appear on an interface and interface member") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface TestSecureContextOnPartialInterfaceAndMember { + }; + [SecureContext] + partial interface TestSecureContextOnPartialInterfaceAndMember { + [SecureContext] + void testSecureMethod(byte foo); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[SecureContext] must not appear on a partial interface and one of the partial interface's member's") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [SecureContext] + interface TestSecureContextOnInterfaceAndPartialInterfaceMember { + }; + partial interface TestSecureContextOnInterfaceAndPartialInterfaceMember { + [SecureContext] + void testSecureMethod(byte foo); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[SecureContext] must not appear on an interface and one of its partial interface's member's") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [SecureContext] + interface TestSecureContextOnInheritedInterface { + }; + interface TestSecureContextNotOnInheritingInterface : TestSecureContextOnInheritedInterface { + void testSecureMethod(byte foo); + }; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[SecureContext] must appear on interfaces that inherit from another [SecureContext] interface") + + # Test 'implements'. The behavior tested here may have to change depending + # on the resolution of https://github.com/heycam/webidl/issues/118 + parser = parser.reset() + parser.parse(""" + [SecureContext] + interface TestSecureContextInterfaceThatImplementsNonSecureContextInterface { + const octet TEST_CONSTANT = 0; + }; + interface TestNonSecureContextInterface { + const octet TEST_CONSTANT_2 = 0; + readonly attribute byte testAttribute2; + void testMethod2(byte foo); + }; + TestSecureContextInterfaceThatImplementsNonSecureContextInterface implements TestNonSecureContextInterface; + """) + results = parser.finish() + harness.check(len(results[0].members), 4, "TestSecureContextInterfaceThatImplementsNonSecureContextInterface should have two members") + harness.ok(results[0].getExtendedAttribute("SecureContext"), + "Interface should have [SecureContext] extended attribute") + harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"), + "[SecureContext] should propagate from interface to constant members even when other members are copied from a non-[SecureContext] interface") + harness.ok(results[0].members[1].getExtendedAttribute("SecureContext") is None, + "Constants copied from non-[SecureContext] interface should not be [SecureContext]") + harness.ok(results[0].members[2].getExtendedAttribute("SecureContext") is None, + "Attributes copied from non-[SecureContext] interface should not be [SecureContext]") + harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None, + "Methods copied from non-[SecureContext] interface should not be [SecureContext]") + |