summaryrefslogtreecommitdiffstats
path: root/dom/bindings/parser
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings/parser')
-rw-r--r--dom/bindings/parser/README1
-rw-r--r--dom/bindings/parser/UPSTREAM1
-rw-r--r--dom/bindings/parser/WebIDL.py6874
-rw-r--r--dom/bindings/parser/runtests.py108
-rw-r--r--dom/bindings/parser/tests/test_any_null.py14
-rw-r--r--dom/bindings/parser/tests/test_argument_identifier_conflicts.py14
-rw-r--r--dom/bindings/parser/tests/test_argument_novoid.py14
-rw-r--r--dom/bindings/parser/tests/test_arraybuffer.py81
-rw-r--r--dom/bindings/parser/tests/test_attr.py177
-rw-r--r--dom/bindings/parser/tests/test_attr_sequence_type.py67
-rw-r--r--dom/bindings/parser/tests/test_builtin_filename.py11
-rw-r--r--dom/bindings/parser/tests/test_builtins.py41
-rw-r--r--dom/bindings/parser/tests/test_bytestring.py99
-rw-r--r--dom/bindings/parser/tests/test_callback.py34
-rw-r--r--dom/bindings/parser/tests/test_callback_interface.py94
-rw-r--r--dom/bindings/parser/tests/test_conditional_dictionary_member.py110
-rw-r--r--dom/bindings/parser/tests/test_const.py80
-rw-r--r--dom/bindings/parser/tests/test_constructor.py109
-rw-r--r--dom/bindings/parser/tests/test_constructor_no_interface_object.py36
-rw-r--r--dom/bindings/parser/tests/test_date.py15
-rw-r--r--dom/bindings/parser/tests/test_deduplicate.py15
-rw-r--r--dom/bindings/parser/tests/test_dictionary.py555
-rw-r--r--dom/bindings/parser/tests/test_distinguishability.py293
-rw-r--r--dom/bindings/parser/tests/test_double_null.py14
-rw-r--r--dom/bindings/parser/tests/test_duplicate_qualifiers.py84
-rw-r--r--dom/bindings/parser/tests/test_empty_enum.py14
-rw-r--r--dom/bindings/parser/tests/test_empty_sequence_default_value.py45
-rw-r--r--dom/bindings/parser/tests/test_enum.py93
-rw-r--r--dom/bindings/parser/tests/test_enum_duplicate_values.py13
-rw-r--r--dom/bindings/parser/tests/test_error_colno.py20
-rw-r--r--dom/bindings/parser/tests/test_error_lineno.py28
-rw-r--r--dom/bindings/parser/tests/test_exposed_extended_attribute.py222
-rw-r--r--dom/bindings/parser/tests/test_extended_attributes.py107
-rw-r--r--dom/bindings/parser/tests/test_float_types.py125
-rw-r--r--dom/bindings/parser/tests/test_forward_decl.py15
-rw-r--r--dom/bindings/parser/tests/test_global_extended_attr.py122
-rw-r--r--dom/bindings/parser/tests/test_identifier_conflict.py39
-rw-r--r--dom/bindings/parser/tests/test_implements.py216
-rw-r--r--dom/bindings/parser/tests/test_incomplete_parent.py18
-rw-r--r--dom/bindings/parser/tests/test_incomplete_types.py44
-rw-r--r--dom/bindings/parser/tests/test_interface.py405
-rw-r--r--dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py15
-rw-r--r--dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py60
-rw-r--r--dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py691
-rw-r--r--dom/bindings/parser/tests/test_lenientSetter.py58
-rw-r--r--dom/bindings/parser/tests/test_method.py178
-rw-r--r--dom/bindings/parser/tests/test_mozmap.py39
-rw-r--r--dom/bindings/parser/tests/test_namespace.py223
-rw-r--r--dom/bindings/parser/tests/test_newobject.py70
-rw-r--r--dom/bindings/parser/tests/test_nullable_equivalency.py115
-rw-r--r--dom/bindings/parser/tests/test_nullable_void.py14
-rw-r--r--dom/bindings/parser/tests/test_optional_constraints.py30
-rw-r--r--dom/bindings/parser/tests/test_overload.py60
-rw-r--r--dom/bindings/parser/tests/test_promise.py63
-rw-r--r--dom/bindings/parser/tests/test_prototype_ident.py80
-rw-r--r--dom/bindings/parser/tests/test_putForwards.py107
-rw-r--r--dom/bindings/parser/tests/test_replaceable.py58
-rw-r--r--dom/bindings/parser/tests/test_sanity.py7
-rw-r--r--dom/bindings/parser/tests/test_securecontext_extended_attribute.py332
-rw-r--r--dom/bindings/parser/tests/test_special_method_signature_mismatch.py294
-rw-r--r--dom/bindings/parser/tests/test_special_methods.py85
-rw-r--r--dom/bindings/parser/tests/test_special_methods_uniqueness.py62
-rw-r--r--dom/bindings/parser/tests/test_stringifier.py46
-rw-r--r--dom/bindings/parser/tests/test_treatNonCallableAsNull.py71
-rw-r--r--dom/bindings/parser/tests/test_typedef.py76
-rw-r--r--dom/bindings/parser/tests/test_unenumerable_own_properties.py64
-rw-r--r--dom/bindings/parser/tests/test_unforgeable.py253
-rw-r--r--dom/bindings/parser/tests/test_union.py168
-rw-r--r--dom/bindings/parser/tests/test_union_any.py14
-rw-r--r--dom/bindings/parser/tests/test_union_nullable.py53
-rw-r--r--dom/bindings/parser/tests/test_usvstring.py36
-rw-r--r--dom/bindings/parser/tests/test_variadic_callback.py10
-rw-r--r--dom/bindings/parser/tests/test_variadic_constraints.py63
73 files changed, 13962 insertions, 0 deletions
diff --git a/dom/bindings/parser/README b/dom/bindings/parser/README
new file mode 100644
index 000000000..94b64b884
--- /dev/null
+++ b/dom/bindings/parser/README
@@ -0,0 +1 @@
+A WebIDL parser written in Python to be used in Mozilla. \ No newline at end of file
diff --git a/dom/bindings/parser/UPSTREAM b/dom/bindings/parser/UPSTREAM
new file mode 100644
index 000000000..7ac589937
--- /dev/null
+++ b/dom/bindings/parser/UPSTREAM
@@ -0,0 +1 @@
+http://dev.w3.org/cvsweb/~checkout~/2006/webapi/WebIDL/Overview.html?rev=1.409;content-type=text%2Fhtml%3b+charset=utf-8 \ No newline at end of file
diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py
new file mode 100644
index 000000000..f668d7d62
--- /dev/null
+++ b/dom/bindings/parser/WebIDL.py
@@ -0,0 +1,6874 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+""" A WebIDL parser. """
+
+from ply import lex, yacc
+import re
+import os
+import traceback
+import math
+import string
+from collections import defaultdict
+
+# Machinery
+
+
+def parseInt(literal):
+ string = literal
+ sign = 0
+ base = 0
+
+ if string[0] == '-':
+ sign = -1
+ string = string[1:]
+ else:
+ sign = 1
+
+ if string[0] == '0' and len(string) > 1:
+ if string[1] == 'x' or string[1] == 'X':
+ base = 16
+ string = string[2:]
+ else:
+ base = 8
+ string = string[1:]
+ else:
+ base = 10
+
+ value = int(string, base)
+ return value * sign
+
+
+# Magic for creating enums
+def M_add_class_attribs(attribs, start):
+ def foo(name, bases, dict_):
+ for v, k in enumerate(attribs):
+ dict_[k] = start + v
+ assert 'length' not in dict_
+ dict_['length'] = start + len(attribs)
+ return type(name, bases, dict_)
+ return foo
+
+
+def enum(*names, **kw):
+ if len(kw) == 1:
+ base = kw['base'].__class__
+ start = base.length
+ else:
+ assert len(kw) == 0
+ base = object
+ start = 0
+
+ class Foo(base):
+ __metaclass__ = M_add_class_attribs(names, start)
+
+ def __setattr__(self, name, value): # this makes it read-only
+ raise NotImplementedError
+ return Foo()
+
+
+class WebIDLError(Exception):
+ def __init__(self, message, locations, warning=False):
+ self.message = message
+ self.locations = [str(loc) for loc in locations]
+ self.warning = warning
+
+ def __str__(self):
+ return "%s: %s%s%s" % (self.warning and 'warning' or 'error',
+ self.message,
+ ", " if len(self.locations) != 0 else "",
+ "\n".join(self.locations))
+
+
+class Location(object):
+ def __init__(self, lexer, lineno, lexpos, filename):
+ self._line = None
+ self._lineno = lineno
+ self._lexpos = lexpos
+ self._lexdata = lexer.lexdata
+ self._file = filename if filename else "<unknown>"
+
+ def __eq__(self, other):
+ return (self._lexpos == other._lexpos and
+ self._file == other._file)
+
+ def filename(self):
+ return self._file
+
+ def resolve(self):
+ if self._line:
+ return
+
+ startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
+ endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
+ if endofline != -1:
+ self._line = self._lexdata[startofline:endofline]
+ else:
+ self._line = self._lexdata[startofline:]
+ self._colno = self._lexpos - startofline
+
+ # Our line number seems to point to the start of self._lexdata
+ self._lineno += self._lexdata.count('\n', 0, startofline)
+
+ def get(self):
+ self.resolve()
+ return "%s line %s:%s" % (self._file, self._lineno, self._colno)
+
+ def _pointerline(self):
+ return " " * self._colno + "^"
+
+ def __str__(self):
+ self.resolve()
+ return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
+ self._line, self._pointerline())
+
+
+class BuiltinLocation(object):
+ def __init__(self, text):
+ self.msg = text + "\n"
+
+ def __eq__(self, other):
+ return (isinstance(other, BuiltinLocation) and
+ self.msg == other.msg)
+
+ def filename(self):
+ return '<builtin>'
+
+ def resolve(self):
+ pass
+
+ def get(self):
+ return self.msg
+
+ def __str__(self):
+ return self.get()
+
+
+# Data Model
+
+
+class IDLObject(object):
+ def __init__(self, location):
+ self.location = location
+ self.userData = dict()
+
+ def filename(self):
+ return self.location.filename()
+
+ def isInterface(self):
+ return False
+
+ def isNamespace(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isCallback(self):
+ return False
+
+ def isType(self):
+ return False
+
+ def isDictionary(self):
+ return False
+
+ def isUnion(self):
+ return False
+
+ def isTypedef(self):
+ return False
+
+ def getUserData(self, key, default):
+ return self.userData.get(key, default)
+
+ def setUserData(self, key, value):
+ self.userData[key] = value
+
+ def addExtendedAttributes(self, attrs):
+ assert False # Override me!
+
+ def handleExtendedAttribute(self, attr):
+ assert False # Override me!
+
+ def _getDependentObjects(self):
+ assert False # Override me!
+
+ def getDeps(self, visited=None):
+ """ Return a set of files that this object depends on. If any of
+ these files are changed the parser needs to be rerun to regenerate
+ a new IDLObject.
+
+ The visited argument is a set of all the objects already visited.
+ We must test to see if we are in it, and if so, do nothing. This
+ prevents infinite recursion."""
+
+ # NB: We can't use visited=set() above because the default value is
+ # evaluated when the def statement is evaluated, not when the function
+ # is executed, so there would be one set for all invocations.
+ if visited is None:
+ visited = set()
+
+ if self in visited:
+ return set()
+
+ visited.add(self)
+
+ deps = set()
+ if self.filename() != "<builtin>":
+ deps.add(self.filename())
+
+ for d in self._getDependentObjects():
+ deps.update(d.getDeps(visited))
+
+ return deps
+
+
+class IDLScope(IDLObject):
+ def __init__(self, location, parentScope, identifier):
+ IDLObject.__init__(self, location)
+
+ self.parentScope = parentScope
+ if identifier:
+ assert isinstance(identifier, IDLIdentifier)
+ self._name = identifier
+ else:
+ self._name = None
+
+ self._dict = {}
+ self.globalNames = set()
+ # A mapping from global name to the set of global interfaces
+ # that have that global name.
+ self.globalNameMapping = defaultdict(set)
+ self.primaryGlobalAttr = None
+ self.primaryGlobalName = None
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ if self._name:
+ return self._name.QName() + "::"
+ return "::"
+
+ def ensureUnique(self, identifier, object):
+ """
+ Ensure that there is at most one 'identifier' in scope ('self').
+ Note that object can be None. This occurs if we end up here for an
+ interface type we haven't seen yet.
+ """
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ assert not object or isinstance(object, IDLObjectWithIdentifier)
+ assert not object or object.identifier == identifier
+
+ if identifier.name in self._dict:
+ if not object:
+ return
+
+ # ensureUnique twice with the same object is not allowed
+ assert id(object) != id(self._dict[identifier.name])
+
+ replacement = self.resolveIdentifierConflict(self, identifier,
+ self._dict[identifier.name],
+ object)
+ self._dict[identifier.name] = replacement
+ return
+
+ assert object
+
+ self._dict[identifier.name] = object
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ if (isinstance(originalObject, IDLExternalInterface) and
+ isinstance(newObject, IDLExternalInterface) and
+ originalObject.identifier.name == newObject.identifier.name):
+ return originalObject
+
+ if (isinstance(originalObject, IDLExternalInterface) or
+ isinstance(newObject, IDLExternalInterface)):
+ raise WebIDLError(
+ "Name collision between "
+ "interface declarations for identifier '%s' at '%s' and '%s'"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ if (isinstance(originalObject, IDLDictionary) or
+ isinstance(newObject, IDLDictionary)):
+ raise WebIDLError(
+ "Name collision between dictionary declarations for "
+ "identifier '%s'.\n%s\n%s"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ # We do the merging of overloads here as opposed to in IDLInterface
+ # because we need to merge overloads of NamedConstructors and we need to
+ # detect conflicts in those across interfaces. See also the comment in
+ # IDLInterface.addExtendedAttributes for "NamedConstructor".
+ if (isinstance(originalObject, IDLMethod) and
+ isinstance(newObject, IDLMethod)):
+ return originalObject.addOverload(newObject)
+
+ # Default to throwing, derived classes can override.
+ conflictdesc = "\n\t%s at %s\n\t%s at %s" % (originalObject,
+ originalObject.location,
+ newObject,
+ newObject.location)
+
+ raise WebIDLError(
+ "Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
+ % (identifier.name, str(self), conflictdesc), [])
+
+ def _lookupIdentifier(self, identifier):
+ return self._dict[identifier.name]
+
+ def lookupIdentifier(self, identifier):
+ assert isinstance(identifier, IDLIdentifier)
+ assert identifier.scope == self
+ return self._lookupIdentifier(identifier)
+
+
+class IDLIdentifier(IDLObject):
+ def __init__(self, location, scope, name):
+ IDLObject.__init__(self, location)
+
+ self.name = name
+ assert isinstance(scope, IDLScope)
+ self.scope = scope
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ return self.scope.QName() + self.name
+
+ def __hash__(self):
+ return self.QName().__hash__()
+
+ def __eq__(self, other):
+ return self.QName() == other.QName()
+
+ def object(self):
+ return self.scope.lookupIdentifier(self)
+
+
+class IDLUnresolvedIdentifier(IDLObject):
+ def __init__(self, location, name, allowDoubleUnderscore=False,
+ allowForbidden=False):
+ IDLObject.__init__(self, location)
+
+ assert len(name) > 0
+
+ if name == "__noSuchMethod__":
+ raise WebIDLError("__noSuchMethod__ is deprecated", [location])
+
+ if name[:2] == "__" and name != "__content" and not allowDoubleUnderscore:
+ raise WebIDLError("Identifiers beginning with __ are reserved",
+ [location])
+ if name[0] == '_' and not allowDoubleUnderscore:
+ name = name[1:]
+ # TODO: Bug 872377, Restore "toJSON" to below list.
+ # We sometimes need custom serialization, so allow toJSON for now.
+ if (name in ["constructor", "toString"] and
+ not allowForbidden):
+ raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
+ [location])
+
+ self.name = name
+
+ def __str__(self):
+ return self.QName()
+
+ def QName(self):
+ return "<unresolved scope>::" + self.name
+
+ def resolve(self, scope, object):
+ assert isinstance(scope, IDLScope)
+ assert not object or isinstance(object, IDLObjectWithIdentifier)
+ assert not object or object.identifier == self
+
+ scope.ensureUnique(self, object)
+
+ identifier = IDLIdentifier(self.location, scope, self.name)
+ if object:
+ object.identifier = identifier
+ return identifier
+
+ def finish(self):
+ assert False # Should replace with a resolved identifier first.
+
+
+class IDLObjectWithIdentifier(IDLObject):
+ def __init__(self, location, parentScope, identifier):
+ IDLObject.__init__(self, location)
+
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+
+ self.identifier = identifier
+
+ 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):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ IDLScope.__init__(self, location, parentScope, self.identifier)
+
+
+class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
+ def __init__(self, location, identifier):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ def finish(self, scope):
+ try:
+ scope._lookupIdentifier(self.identifier)
+ except:
+ raise WebIDLError("Unresolved type '%s'." % self.identifier,
+ [self.location])
+
+ obj = self.identifier.resolve(scope, None)
+ return scope.lookupIdentifier(obj)
+
+
+class IDLExposureMixins():
+ def __init__(self, location):
+ # _exposureGlobalNames are the global names listed in our [Exposed]
+ # extended attribute. exposureSet is the exposure set as defined in the
+ # Web IDL spec: it contains interface names.
+ self._exposureGlobalNames = set()
+ self.exposureSet = set()
+ self._location = location
+ self._globalScope = None
+
+ def finish(self, scope):
+ assert scope.parentScope is None
+ self._globalScope = scope
+
+ # Verify that our [Exposed] value, if any, makes sense.
+ for globalName in self._exposureGlobalNames:
+ if globalName not in scope.globalNames:
+ raise WebIDLError("Unknown [Exposed] value %s" % globalName,
+ [self._location])
+
+ if len(self._exposureGlobalNames) == 0:
+ self._exposureGlobalNames.add(scope.primaryGlobalName)
+
+ globalNameSetToExposureSet(scope, self._exposureGlobalNames,
+ self.exposureSet)
+
+ def isExposedInWindow(self):
+ return 'Window' in self.exposureSet
+
+ def isExposedOnMainThread(self):
+ return (self.isExposedInWindow() or
+ self.isExposedInSystemGlobals())
+
+ def isExposedInAnyWorker(self):
+ return len(self.getWorkerExposureSet()) > 0
+
+ def isExposedInWorkerDebugger(self):
+ return len(self.getWorkerDebuggerExposureSet()) > 0
+
+ def isExposedInAnyWorklet(self):
+ return len(self.getWorkletExposureSet()) > 0
+
+ def isExposedInSystemGlobals(self):
+ return 'BackstagePass' in self.exposureSet
+
+ def isExposedInSomeButNotAllWorkers(self):
+ """
+ Returns true if the Exposed extended attribute for this interface
+ exposes it in some worker globals but not others. The return value does
+ not depend on whether the interface is exposed in Window or System
+ globals.
+ """
+ if not self.isExposedInAnyWorker():
+ return False
+ workerScopes = self.parentScope.globalNameMapping["Worker"]
+ return len(workerScopes.difference(self.exposureSet)) > 0
+
+ def getWorkerExposureSet(self):
+ workerScopes = self._globalScope.globalNameMapping["Worker"]
+ return workerScopes.intersection(self.exposureSet)
+
+ def getWorkletExposureSet(self):
+ workletScopes = self._globalScope.globalNameMapping["Worklet"]
+ return workletScopes.intersection(self.exposureSet)
+
+ def getWorkerDebuggerExposureSet(self):
+ workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"]
+ return workerDebuggerScopes.intersection(self.exposureSet)
+
+
+class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
+ def __init__(self, location, parentScope, identifier):
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ assert isinstance(parentScope, IDLScope)
+ self.parent = None
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ IDLExposureMixins.__init__(self, location)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+
+ def finish(self, scope):
+ IDLExposureMixins.finish(self, scope)
+ pass
+
+ def validate(self):
+ pass
+
+ def isIteratorInterface(self):
+ return False
+
+ def isExternal(self):
+ return True
+
+ def isInterface(self):
+ return True
+
+ def isConsequential(self):
+ return False
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def resolve(self, parentScope):
+ pass
+
+ def getJSImplementation(self):
+ return None
+
+ def isJSImplemented(self):
+ return False
+
+ def isProbablyShortLivingObject(self):
+ return False
+
+ def isNavigatorProperty(self):
+ return False
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLPartialInterfaceOrNamespace(IDLObject):
+ def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ IDLObject.__init__(self, location)
+ self.identifier = name
+ self.members = members
+ # propagatedExtendedAttrs are the ones that should get
+ # propagated to our non-partial interface.
+ self.propagatedExtendedAttrs = []
+ self._haveSecureContextExtendedAttribute = False
+ self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
+ self._finished = False
+ nonPartialInterfaceOrNamespace.addPartialInterface(self)
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ identifier = attr.identifier()
+
+ 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:
+ if len(member._exposureGlobalNames) != 0:
+ raise WebIDLError("[Exposed] specified on both a "
+ "partial interface member and on the "
+ "partial interface itself",
+ [member.location, attr.location])
+ member.addExtendedAttributes([attr])
+ else:
+ raise WebIDLError("Unknown extended attribute %s on partial "
+ "interface" % identifier,
+ [attr.location])
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ self._finished = True
+ 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
+
+
+def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
+ assert len(targetSet) == 0
+ if exposedAttr.hasValue():
+ targetSet.add(exposedAttr.value())
+ else:
+ assert exposedAttr.hasArgs()
+ targetSet.update(exposedAttr.args())
+
+
+def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
+ for name in nameSet:
+ exposureSet.update(globalScope.globalNameMapping[name])
+
+
+class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+ def __init__(self, location, parentScope, name, parent, members,
+ isKnownNonPartial):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert isKnownNonPartial or not parent
+ assert isKnownNonPartial or len(members) == 0
+
+ self.parent = None
+ self._callback = False
+ self._finished = False
+ self.members = []
+ self.maplikeOrSetlikeOrIterable = None
+ self._partialInterfaces = []
+ self._extendedAttrDict = {}
+ # namedConstructors needs deterministic ordering because bindings code
+ # outputs the constructs in the order that namedConstructors enumerates
+ # them.
+ self.namedConstructors = list()
+ self.implementedInterfaces = set()
+ self._consequential = False
+ self._isKnownNonPartial = False
+ # self.interfacesBasedOnSelf is the set of interfaces that inherit from
+ # self or have self as a consequential interface, including self itself.
+ # Used for distinguishability checking.
+ self.interfacesBasedOnSelf = set([self])
+ # self.interfacesImplementingSelf is the set of interfaces that directly
+ # have self as a consequential interface
+ self.interfacesImplementingSelf = set()
+ self._hasChildInterfaces = False
+ self._isOnGlobalProtoChain = False
+ # Tracking of the number of reserved slots we need for our
+ # members and those of ancestor interfaces.
+ self.totalMembersInSlots = 0
+ # Tracking of the number of own own members we have in slots
+ self._ownMembersInSlots = 0
+ # If this is an iterator interface, we need to know what iterable
+ # interface we're iterating for in order to get its nativeType.
+ self.iterableInterface = None
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+ IDLExposureMixins.__init__(self, location)
+
+ if isKnownNonPartial:
+ self.setNonPartial(location, parent, members)
+
+ def ctor(self):
+ identifier = IDLUnresolvedIdentifier(self.location, "constructor",
+ allowForbidden=True)
+ try:
+ return self._lookupIdentifier(identifier)
+ except:
+ return None
+
+ def isIterable(self):
+ return (self.maplikeOrSetlikeOrIterable and
+ self.maplikeOrSetlikeOrIterable.isIterable())
+
+ def isIteratorInterface(self):
+ return self.iterableInterface is not None
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ assert isinstance(scope, IDLScope)
+ assert isinstance(originalObject, IDLInterfaceMember)
+ assert isinstance(newObject, IDLInterfaceMember)
+
+ retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
+ originalObject, newObject)
+
+ # Might be a ctor, which isn't in self.members
+ if newObject in self.members:
+ self.members.remove(newObject)
+ return retval
+
+ def finish(self, scope):
+ if self._finished:
+ return
+
+ self._finished = True
+
+ if not self._isKnownNonPartial:
+ raise WebIDLError("Interface %s does not have a non-partial "
+ "declaration" % self.identifier.name,
+ [self.location])
+
+ IDLExposureMixins.finish(self, scope)
+
+ # Now go ahead and merge in our partial interfaces.
+ for partial in self._partialInterfaces:
+ partial.finish(scope)
+ self.addExtendedAttributes(partial.propagatedExtendedAttrs)
+ self.members.extend(partial.members)
+
+ # Generate maplike/setlike interface members. Since generated members
+ # need to be treated like regular interface members, do this before
+ # things like exposure setting.
+ for member in self.members:
+ if member.isMaplikeOrSetlikeOrIterable():
+ # Check that we only have one interface declaration (currently
+ # there can only be one maplike/setlike declaration per
+ # interface)
+ if self.maplikeOrSetlikeOrIterable:
+ raise WebIDLError("%s declaration used on "
+ "interface that already has %s "
+ "declaration" %
+ (member.maplikeOrSetlikeOrIterableType,
+ self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType),
+ [self.maplikeOrSetlikeOrIterable.location,
+ member.location])
+ self.maplikeOrSetlikeOrIterable = member
+ # If we've got a maplike or setlike declaration, we'll be building all of
+ # our required methods in Codegen. Generate members now.
+ self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented())
+
+ # Now that we've merged in our partial interfaces, set the
+ # _exposureGlobalNames on any members that don't have it set yet. Note
+ # that any partial interfaces that had [Exposed] set have already set up
+ # _exposureGlobalNames on all the members coming from them, so this is
+ # just implementing the "members default to interface that defined them"
+ # and "partial interfaces default to interface they're a partial for"
+ # rules from the spec.
+ for m in self.members:
+ # If m, or the partial interface m came from, had [Exposed]
+ # specified, it already has a nonempty exposure global names set.
+ if len(m._exposureGlobalNames) == 0:
+ m._exposureGlobalNames.update(self._exposureGlobalNames)
+
+ assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
+ parent = self.parent.finish(scope) if self.parent else None
+ if parent and isinstance(parent, IDLExternalInterface):
+ raise WebIDLError("%s inherits from %s which does not have "
+ "a definition" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location])
+ assert not parent or isinstance(parent, IDLInterface)
+
+ self.parent = parent
+
+ 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
+
+ self.totalMembersInSlots = self.parent.totalMembersInSlots
+
+ # Interfaces with [Global] or [PrimaryGlobal] must not
+ # have anything inherit from them
+ if (self.parent.getExtendedAttribute("Global") or
+ self.parent.getExtendedAttribute("PrimaryGlobal")):
+ # Note: This is not a self.parent.isOnGlobalProtoChain() check
+ # because ancestors of a [Global] interface can have other
+ # descendants.
+ raise WebIDLError("[Global] interface has another interface "
+ "inheriting from it",
+ [self.location, self.parent.location])
+
+ # Make sure that we're not exposed in places where our parent is not
+ if not self.exposureSet.issubset(self.parent.exposureSet):
+ raise WebIDLError("Interface %s is exposed in globals where its "
+ "parent interface %s is not exposed." %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ # Callbacks must not inherit from non-callbacks or inherit from
+ # anything that has consequential interfaces.
+ # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending.
+ # XXXbz Can callbacks have consequential interfaces? Spec issue pending
+ if self.isCallback():
+ if not self.parent.isCallback():
+ raise WebIDLError("Callback interface %s inheriting from "
+ "non-callback interface %s" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+ elif self.parent.isCallback():
+ raise WebIDLError("Non-callback interface %s inheriting from "
+ "callback interface %s" %
+ (self.identifier.name,
+ self.parent.identifier.name),
+ [self.location, self.parent.location])
+
+ # Interfaces which have interface objects can't inherit
+ # from [NoInterfaceObject] interfaces.
+ if (self.parent.getExtendedAttribute("NoInterfaceObject") and
+ not self.getExtendedAttribute("NoInterfaceObject")):
+ raise WebIDLError("Interface %s does not have "
+ "[NoInterfaceObject] but inherits from "
+ "interface %s which does" %
+ (self.identifier.name,
+ 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)
+
+ cycleInGraph = self.findInterfaceLoopPoint(self)
+ if cycleInGraph:
+ raise WebIDLError("Interface %s has itself as ancestor or "
+ "implemented interface" % self.identifier.name,
+ [self.location, cycleInGraph.location])
+
+ if self.isCallback():
+ # "implements" should have made sure we have no
+ # consequential interfaces.
+ assert len(self.getConsequentialInterfaces()) == 0
+ # And that we're not consequential.
+ assert not self.isConsequential()
+
+ # Now resolve() and finish() our members before importing the
+ # ones from our implemented interfaces.
+
+ # resolve() will modify self.members, so we need to iterate
+ # over a copy of the member list here.
+ for member in list(self.members):
+ member.resolve(self)
+
+ for member in self.members:
+ member.finish(scope)
+
+ # Now that we've finished our members, which has updated their exposure
+ # sets, make sure they aren't exposed in places where we are not.
+ for member in self.members:
+ if not member.exposureSet.issubset(self.exposureSet):
+ raise WebIDLError("Interface member has larger exposure set "
+ "than the interface itself",
+ [member.location, self.location])
+
+ ctor = self.ctor()
+ if ctor is not None:
+ assert len(ctor._exposureGlobalNames) == 0
+ ctor._exposureGlobalNames.update(self._exposureGlobalNames)
+ ctor.finish(scope)
+
+ for ctor in self.namedConstructors:
+ assert len(ctor._exposureGlobalNames) == 0
+ ctor._exposureGlobalNames.update(self._exposureGlobalNames)
+ ctor.finish(scope)
+
+ # Make a copy of our member list, so things that implement us
+ # can get those without all the stuff we implement ourselves
+ # admixed.
+ self.originalMembers = list(self.members)
+
+ # Import everything from our consequential interfaces into
+ # self.members. Sort our consequential interfaces by name
+ # just so we have a consistent order.
+ for iface in sorted(self.getConsequentialInterfaces(),
+ cmp=cmp,
+ key=lambda x: x.identifier.name):
+ # Flag the interface as being someone's consequential interface
+ iface.setIsConsequentialInterfaceOf(self)
+ # Verify that we're not exposed somewhere where iface is not exposed
+ if not self.exposureSet.issubset(iface.exposureSet):
+ raise WebIDLError("Interface %s is exposed in globals where its "
+ "consequential interface %s is not exposed." %
+ (self.identifier.name, iface.identifier.name),
+ [self.location, iface.location])
+
+ # If we have a maplike or setlike, and the consequential interface
+ # also does, throw an error.
+ if iface.maplikeOrSetlikeOrIterable and self.maplikeOrSetlikeOrIterable:
+ raise WebIDLError("Maplike/setlike/iterable interface %s cannot have "
+ "maplike/setlike/iterable interface %s as a "
+ "consequential interface" %
+ (self.identifier.name,
+ iface.identifier.name),
+ [self.maplikeOrSetlikeOrIterable.location,
+ iface.maplikeOrSetlikeOrIterable.location])
+ additionalMembers = iface.originalMembers
+ for additionalMember in additionalMembers:
+ for member in self.members:
+ if additionalMember.identifier.name == member.identifier.name:
+ raise WebIDLError(
+ "Multiple definitions of %s on %s coming from 'implements' statements" %
+ (member.identifier.name, self),
+ [additionalMember.location, member.location])
+ self.members.extend(additionalMembers)
+ iface.interfacesImplementingSelf.add(self)
+
+ for ancestor in self.getInheritedInterfaces():
+ ancestor.interfacesBasedOnSelf.add(self)
+ if (ancestor.maplikeOrSetlikeOrIterable is not None and
+ self.maplikeOrSetlikeOrIterable is not None):
+ raise WebIDLError("Cannot have maplike/setlike on %s that "
+ "inherits %s, which is already "
+ "maplike/setlike" %
+ (self.identifier.name,
+ ancestor.identifier.name),
+ [self.maplikeOrSetlikeOrIterable.location,
+ ancestor.maplikeOrSetlikeOrIterable.location])
+ for ancestorConsequential in ancestor.getConsequentialInterfaces():
+ ancestorConsequential.interfacesBasedOnSelf.add(self)
+
+ # Deal with interfaces marked [Unforgeable], now that we have our full
+ # member list, except unforgeables pulled in from parents. We want to
+ # do this before we set "originatingInterface" on our unforgeable
+ # members.
+ if self.getExtendedAttribute("Unforgeable"):
+ # Check that the interface already has all the things the
+ # spec would otherwise require us to synthesize and is
+ # missing the ones we plan to synthesize.
+ if not any(m.isMethod() and m.isStringifier() for m in self.members):
+ raise WebIDLError("Unforgeable interface %s does not have a "
+ "stringifier" % self.identifier.name,
+ [self.location])
+
+ for m in self.members:
+ if ((m.isMethod() and m.isJsonifier()) or
+ m.identifier.name == "toJSON"):
+ raise WebIDLError("Unforgeable interface %s has a "
+ "jsonifier so we won't be able to add "
+ "one ourselves" % self.identifier.name,
+ [self.location, m.location])
+
+ if m.identifier.name == "valueOf" and not m.isStatic():
+ raise WebIDLError("Unforgeable interface %s has a valueOf "
+ "member so we won't be able to add one "
+ "ourselves" % self.identifier.name,
+ [self.location, m.location])
+
+ for member in self.members:
+ if ((member.isAttr() or member.isMethod()) and
+ member.isUnforgeable() and
+ not hasattr(member, "originatingInterface")):
+ member.originatingInterface = self
+
+ # Compute slot indices for our members before we pull in unforgeable
+ # members from our parent. Also, maplike/setlike declarations get a
+ # slot to hold their backing object.
+ for member in self.members:
+ if ((member.isAttr() and
+ (member.getExtendedAttribute("StoreInSlot") or
+ member.getExtendedAttribute("Cached"))) or
+ member.isMaplikeOrSetlike()):
+ if member.slotIndices is None:
+ member.slotIndices = dict()
+ member.slotIndices[self.identifier.name] = self.totalMembersInSlots
+ self.totalMembersInSlots += 1
+ if member.getExtendedAttribute("StoreInSlot"):
+ self._ownMembersInSlots += 1
+
+ if self.parent:
+ # Make sure we don't shadow any of the [Unforgeable] attributes on
+ # our ancestor interfaces. We don't have to worry about
+ # consequential interfaces here, because those have already been
+ # imported into the relevant .members lists. And we don't have to
+ # worry about anything other than our parent, because it has already
+ # imported its ancestors unforgeable attributes into its member
+ # list.
+ for unforgeableMember in (member for member in self.parent.members if
+ (member.isAttr() or member.isMethod()) and
+ member.isUnforgeable()):
+ shadows = [m for m in self.members if
+ (m.isAttr() or m.isMethod()) and
+ not m.isStatic() and
+ m.identifier.name == unforgeableMember.identifier.name]
+ if len(shadows) != 0:
+ locs = [unforgeableMember.location] + [s.location for s
+ in shadows]
+ raise WebIDLError("Interface %s shadows [Unforgeable] "
+ "members of %s" %
+ (self.identifier.name,
+ ancestor.identifier.name),
+ locs)
+ # And now just stick it in our members, since we won't be
+ # inheriting this down the proto chain. If we really cared we
+ # could try to do something where we set up the unforgeable
+ # attributes/methods of ancestor interfaces, with their
+ # corresponding getters, on our interface, but that gets pretty
+ # complicated and seems unnecessary.
+ self.members.append(unforgeableMember)
+
+ # At this point, we have all of our members. If the current interface
+ # uses maplike/setlike, check for collisions anywhere in the current
+ # interface or higher in the inheritance chain.
+ if self.maplikeOrSetlikeOrIterable:
+ testInterface = self
+ isAncestor = False
+ while testInterface:
+ self.maplikeOrSetlikeOrIterable.checkCollisions(testInterface.members,
+ isAncestor)
+ isAncestor = True
+ testInterface = testInterface.parent
+
+ # Ensure that there's at most one of each {named,indexed}
+ # {getter,setter,creator,deleter}, at most one stringifier,
+ # and at most one legacycaller. Note that this last is not
+ # quite per spec, but in practice no one overloads
+ # legacycallers. Also note that in practice we disallow
+ # indexed deleters, but it simplifies some other code to
+ # treat deleter analogously to getter/setter/creator by
+ # prefixing it with "named".
+ specialMembersSeen = {}
+ for member in self.members:
+ if not member.isMethod():
+ continue
+
+ if member.isGetter():
+ memberType = "getters"
+ elif member.isSetter():
+ memberType = "setters"
+ elif member.isCreator():
+ memberType = "creators"
+ elif member.isDeleter():
+ memberType = "deleters"
+ elif member.isStringifier():
+ memberType = "stringifiers"
+ elif member.isJsonifier():
+ memberType = "jsonifiers"
+ elif member.isLegacycaller():
+ memberType = "legacycallers"
+ else:
+ continue
+
+ if (memberType != "stringifiers" and memberType != "legacycallers" and
+ memberType != "jsonifiers"):
+ if member.isNamed():
+ memberType = "named " + memberType
+ else:
+ assert member.isIndexed()
+ memberType = "indexed " + memberType
+
+ if memberType in specialMembersSeen:
+ raise WebIDLError("Multiple " + memberType + " on %s" % (self),
+ [self.location,
+ specialMembersSeen[memberType].location,
+ member.location])
+
+ specialMembersSeen[memberType] = member
+
+ if self.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ # Check that we have a named getter.
+ if "named getters" not in specialMembersSeen:
+ raise WebIDLError(
+ "Interface with [LegacyUnenumerableNamedProperties] does "
+ "not have a named getter",
+ [self.location])
+ ancestor = self.parent
+ while ancestor:
+ if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ raise WebIDLError(
+ "Interface with [LegacyUnenumerableNamedProperties] "
+ "inherits from another interface with "
+ "[LegacyUnenumerableNamedProperties]",
+ [self.location, ancestor.location])
+ ancestor = ancestor.parent
+
+ if self._isOnGlobalProtoChain:
+ # Make sure we have no named setters, creators, or deleters
+ for memberType in ["setter", "creator", "deleter"]:
+ memberId = "named " + memberType + "s"
+ if memberId in specialMembersSeen:
+ raise WebIDLError("Interface with [Global] has a named %s" %
+ memberType,
+ [self.location,
+ specialMembersSeen[memberId].location])
+ # Make sure we're not [OverrideBuiltins]
+ if self.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] also has "
+ "[OverrideBuiltins]",
+ [self.location])
+ # Mark all of our ancestors as being on the global's proto chain too
+ parent = self.parent
+ while parent:
+ # Must not inherit from an interface with [OverrideBuiltins]
+ if parent.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] inherits from "
+ "interface with [OverrideBuiltins]",
+ [self.location, parent.location])
+ parent._isOnGlobalProtoChain = True
+ parent = parent.parent
+
+ def validate(self):
+ # We don't support consequential unforgeable interfaces. Need to check
+ # this here, because in finish() an interface might not know yet that
+ # it's consequential.
+ if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
+ raise WebIDLError(
+ "%s is an unforgeable consequential interface" %
+ self.identifier.name,
+ [self.location] +
+ list(i.location for i in
+ (self.interfacesBasedOnSelf - {self})))
+
+ # We also don't support inheriting from unforgeable interfaces.
+ if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
+ locations = ([self.location] +
+ list(i.location for i in
+ self.interfacesBasedOnSelf if i.parent == self))
+ raise WebIDLError("%s is an unforgeable ancestor interface" %
+ self.identifier.name,
+ locations)
+
+ indexedGetter = None
+ hasLengthAttribute = False
+ for member in self.members:
+ member.validate()
+
+ if self.isCallback() and member.getExtendedAttribute("Replaceable"):
+ raise WebIDLError("[Replaceable] used on an attribute on "
+ "interface %s which is a callback interface" %
+ self.identifier.name,
+ [self.location, member.location])
+
+ # Check that PutForwards refers to another attribute and that no
+ # cycles exist in forwarded assignments. Also check for a
+ # integer-typed "length" attribute.
+ if member.isAttr():
+ if (member.identifier.name == "length" and
+ member.type.isInteger()):
+ hasLengthAttribute = True
+
+ iface = self
+ attr = member
+ putForwards = attr.getExtendedAttribute("PutForwards")
+ if putForwards and self.isCallback():
+ raise WebIDLError("[PutForwards] used on an attribute "
+ "on interface %s which is a callback "
+ "interface" % self.identifier.name,
+ [self.location, member.location])
+
+ while putForwards is not None:
+ forwardIface = attr.type.unroll().inner
+ fowardAttr = None
+
+ for forwardedMember in forwardIface.members:
+ if (not forwardedMember.isAttr() or
+ forwardedMember.identifier.name != putForwards[0]):
+ continue
+ if forwardedMember == member:
+ raise WebIDLError("Cycle detected in forwarded "
+ "assignments for attribute %s on "
+ "%s" %
+ (member.identifier.name, self),
+ [member.location])
+ fowardAttr = forwardedMember
+ break
+
+ if fowardAttr is None:
+ raise WebIDLError("Attribute %s on %s forwards to "
+ "missing attribute %s" %
+ (attr.identifier.name, iface, putForwards),
+ [attr.location])
+
+ iface = forwardIface
+ attr = fowardAttr
+ putForwards = attr.getExtendedAttribute("PutForwards")
+
+ # Check that the name of an [Alias] doesn't conflict with an
+ # interface member and whether we support indexed properties.
+ if member.isMethod():
+ if member.isGetter() and member.isIndexed():
+ indexedGetter = member
+
+ for alias in member.aliases:
+ if self.isOnGlobalProtoChain():
+ raise WebIDLError("[Alias] must not be used on a "
+ "[Global] interface operation",
+ [member.location])
+ if (member.getExtendedAttribute("Exposed") or
+ member.getExtendedAttribute("ChromeOnly") or
+ member.getExtendedAttribute("Pref") or
+ member.getExtendedAttribute("Func") or
+ member.getExtendedAttribute("SecureContext")):
+ raise WebIDLError("[Alias] must not be used on a "
+ "conditionally exposed operation",
+ [member.location])
+ if member.isStatic():
+ raise WebIDLError("[Alias] must not be used on a "
+ "static operation",
+ [member.location])
+ if member.isIdentifierLess():
+ raise WebIDLError("[Alias] must not be used on an "
+ "identifierless operation",
+ [member.location])
+ if member.isUnforgeable():
+ raise WebIDLError("[Alias] must not be used on an "
+ "[Unforgeable] operation",
+ [member.location])
+ for m in self.members:
+ if m.identifier.name == alias:
+ raise WebIDLError("[Alias=%s] has same name as "
+ "interface member" % alias,
+ [member.location, m.location])
+ if m.isMethod() and m != member and alias in m.aliases:
+ raise WebIDLError("duplicate [Alias=%s] definitions" %
+ alias,
+ [member.location, m.location])
+
+ if (self.getExtendedAttribute("Pref") and
+ self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an interface that is not %s-only" %
+ self.parentScope.primaryGlobalName,
+ [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,
+ # since it is also propagated to interface members.
+ if (self.isExposedConditionally(exclusions=["SecureContext"]) and
+ not self.hasInterfaceObject() and
+ not self.isNavigatorProperty()):
+ raise WebIDLError("Interface with no interface object is "
+ "exposed conditionally",
+ [self.location])
+
+ # Value iterators are only allowed on interfaces with indexed getters,
+ # and pair iterators are only allowed on interfaces without indexed
+ # getters.
+ if self.isIterable():
+ iterableDecl = self.maplikeOrSetlikeOrIterable
+ if iterableDecl.isValueIterator():
+ if not indexedGetter:
+ raise WebIDLError("Interface with value iterator does not "
+ "support indexed properties",
+ [self.location])
+
+ if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
+ raise WebIDLError("Iterable type does not match indexed "
+ "getter type",
+ [iterableDecl.location,
+ indexedGetter.location])
+
+ if not hasLengthAttribute:
+ raise WebIDLError('Interface with value iterator does not '
+ 'have an integer-typed "length" attribute',
+ [self.location])
+ else:
+ assert iterableDecl.isPairIterator()
+ if indexedGetter:
+ raise WebIDLError("Interface with pair iterator supports "
+ "indexed properties",
+ [self.location, iterableDecl.location,
+ indexedGetter.location])
+
+ def isExternal(self):
+ return False
+
+ def setIsConsequentialInterfaceOf(self, other):
+ self._consequential = True
+ self.interfacesBasedOnSelf.add(other)
+
+ def isConsequential(self):
+ return self._consequential
+
+ def setCallback(self, value):
+ self._callback = value
+
+ def isCallback(self):
+ return self._callback
+
+ def isSingleOperationInterface(self):
+ assert self.isCallback() or self.isJSImplemented()
+ return (
+ # JS-implemented things should never need the
+ # this-handling weirdness of single-operation interfaces.
+ not self.isJSImplemented() and
+ # Not inheriting from another interface
+ not self.parent and
+ # No consequential interfaces
+ len(self.getConsequentialInterfaces()) == 0 and
+ # No attributes of any kinds
+ not any(m.isAttr() for m in self.members) and
+ # There is at least one regular operation, and all regular
+ # operations have the same identifier
+ len(set(m.identifier.name for m in self.members if
+ m.isMethod() and not m.isStatic())) == 1)
+
+ def inheritanceDepth(self):
+ depth = 0
+ parent = self.parent
+ while parent:
+ depth = depth + 1
+ parent = parent.parent
+ return depth
+
+ def hasConstants(self):
+ return any(m.isConst() for m in self.members)
+
+ def hasInterfaceObject(self):
+ if self.isCallback():
+ return self.hasConstants()
+ return not hasattr(self, "_noInterfaceObject")
+
+ def hasInterfacePrototypeObject(self):
+ 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, exclusions=[]):
+ return any(((not a in exclusions) and 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:
+ identifier = attr.identifier()
+
+ # Special cased attrs
+ if identifier == "TreatNonCallableAsNull":
+ raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
+ [attr.location, self.location])
+ if identifier == "TreatNonObjectAsNull":
+ raise WebIDLError("TreatNonObjectAsNull cannot be specified on interfaces",
+ [attr.location, self.location])
+ elif identifier == "NoInterfaceObject":
+ if not attr.noArguments():
+ raise WebIDLError("[NoInterfaceObject] must take no arguments",
+ [attr.location])
+
+ if self.ctor():
+ raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
+ [self.location])
+
+ self._noInterfaceObject = True
+ elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
+ if identifier == "Constructor" and not self.hasInterfaceObject():
+ raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+ [self.location])
+
+ if identifier == "NamedConstructor" and not attr.hasValue():
+ raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
+ [attr.location])
+
+ if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
+ raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+ [self.location])
+
+ args = attr.args() if attr.hasArgs() else []
+
+ if self.identifier.name == "Promise":
+ promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
+ else:
+ promiseType = None
+ retType = IDLWrapperType(self.location, self, promiseType)
+
+ if identifier == "Constructor" or identifier == "ChromeConstructor":
+ name = "constructor"
+ allowForbidden = True
+ else:
+ name = attr.value()
+ allowForbidden = False
+
+ methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
+ allowForbidden=allowForbidden)
+
+ method = IDLMethod(self.location, methodIdentifier, retType,
+ args, static=True)
+ # Constructors are always NewObject and are always
+ # assumed to be able to throw (since there's no way to
+ # indicate otherwise) and never have any other
+ # extended attributes.
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NewObject",)),
+ IDLExtendedAttribute(self.location, ("Throws",))])
+ if identifier == "ChromeConstructor":
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
+
+ if identifier == "Constructor" or identifier == "ChromeConstructor":
+ method.resolve(self)
+ else:
+ # We need to detect conflicts for NamedConstructors across
+ # interfaces. We first call resolve on the parentScope,
+ # which will merge all NamedConstructors with the same
+ # identifier accross interfaces as overloads.
+ method.resolve(self.parentScope)
+
+ # Then we look up the identifier on the parentScope. If the
+ # result is the same as the method we're adding then it
+ # hasn't been added as an overload and it's the first time
+ # we've encountered a NamedConstructor with that identifier.
+ # If the result is not the same as the method we're adding
+ # then it has been added as an overload and we need to check
+ # whether the result is actually one of our existing
+ # NamedConstructors.
+ newMethod = self.parentScope.lookupIdentifier(method.identifier)
+ if newMethod == method:
+ self.namedConstructors.append(method)
+ elif newMethod not in self.namedConstructors:
+ raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface",
+ [method.location, newMethod.location])
+ elif (identifier == "ArrayClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ArrayClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ArrayClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif (identifier == "ExceptionClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ExceptionClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ExceptionClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif identifier == "Global":
+ if attr.hasValue():
+ self.globalNames = [attr.value()]
+ elif attr.hasArgs():
+ self.globalNames = attr.args()
+ else:
+ self.globalNames = [self.identifier.name]
+ self.parentScope.globalNames.update(self.globalNames)
+ for globalName in self.globalNames:
+ self.parentScope.globalNameMapping[globalName].add(self.identifier.name)
+ self._isOnGlobalProtoChain = True
+ elif identifier == "PrimaryGlobal":
+ if not attr.noArguments():
+ raise WebIDLError("[PrimaryGlobal] must take no arguments",
+ [attr.location])
+ if self.parentScope.primaryGlobalAttr is not None:
+ raise WebIDLError(
+ "[PrimaryGlobal] specified twice",
+ [attr.location,
+ self.parentScope.primaryGlobalAttr.location])
+ self.parentScope.primaryGlobalAttr = attr
+ self.parentScope.primaryGlobalName = self.identifier.name
+ 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
+ identifier == "Unforgeable" or
+ identifier == "UnsafeInPrerendering" or
+ identifier == "LegacyEventInit" or
+ identifier == "ProbablyShortLivingObject" or
+ identifier == "LegacyUnenumerableNamedProperties" or
+ identifier == "NonOrdinaryGetPrototypeOf"):
+ # Known extended attributes that do not take values
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr,
+ self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "JSImplementation" or
+ identifier == "HeaderFile" or
+ identifier == "NavigatorProperty" or
+ identifier == "Func" or
+ identifier == "Deprecated"):
+ # Known extended attributes that take a string value
+ if not attr.hasValue():
+ raise WebIDLError("[%s] must have a value" % identifier,
+ [attr.location])
+ else:
+ raise WebIDLError("Unknown extended attribute %s on interface" % identifier,
+ [attr.location])
+
+ attrlist = attr.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+
+class IDLNamespace(IDLInterfaceOrNamespace):
+ def __init__(self, location, parentScope, name, members, isKnownNonPartial):
+ IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
+ None, members, isKnownNonPartial)
+
+ def __str__(self):
+ return "Namespace '%s'" % self.identifier.name
+
+ def isNamespace(self):
+ return True
+
+ 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()
+
+ 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])
+
+ attrlist = attr.listValue()
+ self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
+
+
+class IDLDictionary(IDLObjectWithScope):
+ def __init__(self, location, parentScope, name, parent, members):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+ assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
+
+ self.parent = parent
+ self._finished = False
+ self.members = list(members)
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+
+ def __str__(self):
+ return "Dictionary '%s'" % self.identifier.name
+
+ def isDictionary(self):
+ return True
+
+ def canBeEmpty(self):
+ """
+ Returns true if this dictionary can be empty (that is, it has no
+ required members and neither do any of its ancestors).
+ """
+ return (all(member.optional for member in self.members) and
+ (not self.parent or self.parent.canBeEmpty()))
+
+ def finish(self, scope):
+ if self._finished:
+ return
+
+ self._finished = True
+
+ if self.parent:
+ assert isinstance(self.parent, IDLIdentifierPlaceholder)
+ oldParent = self.parent
+ self.parent = self.parent.finish(scope)
+ if not isinstance(self.parent, IDLDictionary):
+ raise WebIDLError("Dictionary %s has parent that is not a dictionary" %
+ self.identifier.name,
+ [oldParent.location, self.parent.location])
+
+ # Make sure the parent resolves all its members before we start
+ # looking at them.
+ self.parent.finish(scope)
+
+ for member in self.members:
+ member.resolve(self)
+ if not member.isComplete():
+ member.complete(scope)
+ assert member.type.isComplete()
+
+ # Members of a dictionary are sorted in lexicographic order
+ self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
+
+ inheritedMembers = []
+ ancestor = self.parent
+ while ancestor:
+ if ancestor == self:
+ raise WebIDLError("Dictionary %s has itself as an ancestor" %
+ self.identifier.name,
+ [self.identifier.location])
+ inheritedMembers.extend(ancestor.members)
+ ancestor = ancestor.parent
+
+ # Catch name duplication
+ for inheritedMember in inheritedMembers:
+ for member in self.members:
+ if member.identifier.name == inheritedMember.identifier.name:
+ raise WebIDLError("Dictionary %s has two members with name %s" %
+ (self.identifier.name, member.identifier.name),
+ [member.location, inheritedMember.location])
+
+ def validate(self):
+ def typeContainsDictionary(memberType, dictionary):
+ """
+ Returns a tuple whose:
+
+ - First element is a Boolean value indicating whether
+ memberType contains dictionary.
+
+ - Second element is:
+ A list of locations that leads from the type that was passed in
+ the memberType argument, to the dictionary being validated,
+ if the boolean value in the first element is True.
+
+ None, if the boolean value in the first element is False.
+ """
+
+ if (memberType.nullable() or
+ memberType.isSequence() or
+ memberType.isMozMap()):
+ return typeContainsDictionary(memberType.inner, dictionary)
+
+ if memberType.isDictionary():
+ if memberType.inner == dictionary:
+ return (True, [memberType.location])
+
+ (contains, locations) = dictionaryContainsDictionary(memberType.inner,
+ dictionary)
+ if contains:
+ return (True, [memberType.location] + locations)
+
+ if memberType.isUnion():
+ for member in memberType.flatMemberTypes:
+ (contains, locations) = typeContainsDictionary(member, dictionary)
+ if contains:
+ return (True, locations)
+
+ return (False, None)
+
+ def dictionaryContainsDictionary(dictMember, dictionary):
+ for member in dictMember.members:
+ (contains, locations) = typeContainsDictionary(member.type, dictionary)
+ if contains:
+ return (True, [member.location] + locations)
+
+ if dictMember.parent:
+ if dictMember.parent == dictionary:
+ return (True, [dictMember.location])
+ else:
+ (contains, locations) = dictionaryContainsDictionary(dictMember.parent, dictionary)
+ if contains:
+ return (True, [dictMember.location] + locations)
+
+ return (False, None)
+
+ for member in self.members:
+ if member.type.isDictionary() and member.type.nullable():
+ raise WebIDLError("Dictionary %s has member with nullable "
+ "dictionary type" % self.identifier.name,
+ [member.location])
+ (contains, locations) = typeContainsDictionary(member.type, self)
+ if contains:
+ raise WebIDLError("Dictionary %s has member with itself as type." %
+ self.identifier.name,
+ [member.location] + locations)
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ deps = set(self.members)
+ if (self.parent):
+ deps.add(self.parent)
+ return deps
+
+
+class IDLEnum(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, name, values):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ if len(values) != len(set(values)):
+ raise WebIDLError("Enum %s has multiple identical strings" % name.name,
+ [location])
+
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, name)
+ self._values = values
+
+ def values(self):
+ return self._values
+
+ def finish(self, scope):
+ pass
+
+ def validate(self):
+ pass
+
+ def isEnum(self):
+ return True
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLType(IDLObject):
+ Tags = enum(
+ # The integer types
+ 'int8',
+ 'uint8',
+ 'int16',
+ 'uint16',
+ 'int32',
+ 'uint32',
+ 'int64',
+ 'uint64',
+ # Additional primitive types
+ 'bool',
+ 'unrestricted_float',
+ 'float',
+ 'unrestricted_double',
+ # "double" last primitive type to match IDLBuiltinType
+ 'double',
+ # Other types
+ 'any',
+ 'domstring',
+ 'bytestring',
+ 'usvstring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'interface',
+ 'dictionary',
+ 'enum',
+ 'callback',
+ 'union',
+ 'sequence',
+ 'mozmap'
+ )
+
+ def __init__(self, location, name):
+ IDLObject.__init__(self, location)
+ self.name = name
+ self.builtin = False
+
+ def __eq__(self, other):
+ return other and self.builtin == other.builtin and self.name == other.name
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __str__(self):
+ return str(self.name)
+
+ def isType(self):
+ return True
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isBoolean(self):
+ return False
+
+ def isNumeric(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isUSVString(self):
+ return False
+
+ def isVoid(self):
+ return self.name == "Void"
+
+ def isSequence(self):
+ return False
+
+ def isMozMap(self):
+ return False
+
+ def isArrayBuffer(self):
+ return False
+
+ def isArrayBufferView(self):
+ return False
+
+ def isSharedArrayBuffer(self):
+ return False
+
+ def isTypedArray(self):
+ return False
+
+ def isCallbackInterface(self):
+ return False
+
+ def isNonCallbackInterface(self):
+ return False
+
+ def isGeckoInterface(self):
+ """ Returns a boolean indicating whether this type is an 'interface'
+ type that is implemented in Gecko. At the moment, this returns
+ true for all interface types that are not types from the TypedArray
+ spec."""
+ return self.isInterface() and not self.isSpiderMonkeyInterface()
+
+ def isSpiderMonkeyInterface(self):
+ """ Returns a boolean indicating whether this type is an 'interface'
+ type that is implemented in Spidermonkey. At the moment, this
+ only returns true for the types from the TypedArray spec. """
+ return self.isInterface() and (self.isArrayBuffer() or
+ self.isArrayBufferView() or
+ self.isSharedArrayBuffer() or
+ self.isTypedArray())
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isAny(self):
+ return self.tag() == IDLType.Tags.any
+
+ def isDate(self):
+ return self.tag() == IDLType.Tags.date
+
+ def isObject(self):
+ return self.tag() == IDLType.Tags.object
+
+ def isPromise(self):
+ return False
+
+ def isComplete(self):
+ return True
+
+ def includesRestrictedFloat(self):
+ return False
+
+ def isFloat(self):
+ return False
+
+ def isUnrestricted(self):
+ # Should only call this on float types
+ assert self.isFloat()
+
+ def isSerializable(self):
+ return False
+
+ def tag(self):
+ assert False # Override me!
+
+ def treatNonCallableAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner.callback._treatNonCallableAsNull
+
+ def treatNonObjectAsNull(self):
+ assert self.tag() == IDLType.Tags.callback
+ return self.nullable() and self.inner.callback._treatNonObjectAsNull
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def resolveType(self, parentScope):
+ pass
+
+ def unroll(self):
+ return self
+
+ def isDistinguishableFrom(self, other):
+ raise TypeError("Can't tell whether a generic type is or is not "
+ "distinguishable from other things")
+
+ def isExposedInAllOf(self, exposureSet):
+ return True
+
+
+class IDLUnresolvedType(IDLType):
+ """
+ Unresolved types are interface types
+ """
+
+ def __init__(self, location, name, promiseInnerType=None):
+ IDLType.__init__(self, location, name)
+ self._promiseInnerType = promiseInnerType
+
+ def isComplete(self):
+ return False
+
+ def complete(self, scope):
+ obj = None
+ try:
+ obj = scope._lookupIdentifier(self.name)
+ except:
+ raise WebIDLError("Unresolved type '%s'." % self.name,
+ [self.location])
+
+ assert obj
+ if obj.isType():
+ print obj
+ assert not obj.isType()
+ if obj.isTypedef():
+ assert self.name.name == obj.identifier.name
+ typedefType = IDLTypedefType(self.location, obj.innerType,
+ obj.identifier)
+ assert not typedefType.isComplete()
+ return typedefType.complete(scope)
+ elif obj.isCallback() and not obj.isInterface():
+ assert self.name.name == obj.identifier.name
+ return IDLCallbackType(self.location, obj)
+
+ if self._promiseInnerType and not self._promiseInnerType.isComplete():
+ self._promiseInnerType = self._promiseInnerType.complete(scope)
+
+ name = self.name.resolve(scope, None)
+ return IDLWrapperType(self.location, obj, self._promiseInnerType)
+
+ def isDistinguishableFrom(self, other):
+ raise TypeError("Can't tell whether an unresolved type is or is not "
+ "distinguishable from other things")
+
+
+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]
+
+ name = innerType.name
+ if innerType.isComplete():
+ name += "OrNull"
+ IDLParameterizedType.__init__(self, location, name, innerType)
+
+ def __eq__(self, other):
+ return isinstance(other, IDLNullableType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "OrNull"
+
+ def nullable(self):
+ return True
+
+ def isCallback(self):
+ return self.inner.isCallback()
+
+ def isPrimitive(self):
+ return self.inner.isPrimitive()
+
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
+ def isString(self):
+ return self.inner.isString()
+
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
+ def isUSVString(self):
+ return self.inner.isUSVString()
+
+ def isFloat(self):
+ return self.inner.isFloat()
+
+ def isUnrestricted(self):
+ return self.inner.isUnrestricted()
+
+ def isInteger(self):
+ return self.inner.isInteger()
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isSharedArrayBuffer(self):
+ return self.inner.isSharedArrayBuffer()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isPromise(self):
+ return self.inner.isPromise()
+
+ def isCallbackInterface(self):
+ return self.inner.isCallbackInterface()
+
+ def isNonCallbackInterface(self):
+ return self.inner.isNonCallbackInterface()
+
+ def isEnum(self):
+ return self.inner.isEnum()
+
+ def isUnion(self):
+ return self.inner.isUnion()
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def tag(self):
+ return self.inner.tag()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ if self.inner.nullable():
+ raise WebIDLError("The inner type of a nullable type must not be "
+ "a nullable type",
+ [self.location, self.inner.location])
+ if self.inner.isUnion():
+ if self.inner.hasNullableType:
+ raise WebIDLError("The inner type of a nullable type must not "
+ "be a union type that itself has a nullable "
+ "type as a member type", [self.location])
+
+ self.name = self.inner.name + "OrNull"
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if (other.nullable() or (other.isUnion() and other.hasNullableType) or
+ other.isDictionary()):
+ # Can't tell which type null should become
+ return False
+ return self.inner.isDistinguishableFrom(other)
+
+
+class IDLSequenceType(IDLParameterizedType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ 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():
+ self.name = self.inner.name + "Sequence"
+
+ def __eq__(self, other):
+ return isinstance(other, IDLSequenceType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "Sequence"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isUSVString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return True
+
+ def isDictionary(self):
+ return False
+
+ def isInterface(self):
+ return False
+
+ def isEnum(self):
+ return False
+
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def tag(self):
+ return IDLType.Tags.sequence
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name + "Sequence"
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if other.isPromise():
+ return False
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isInterface() or
+ other.isDictionary() or
+ other.isCallback() or other.isMozMap())
+
+
+class IDLMozMapType(IDLParameterizedType):
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ 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():
+ self.name = self.inner.name + "MozMap"
+
+ def __eq__(self, other):
+ return isinstance(other, IDLMozMapType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "MozMap"
+
+ def isMozMap(self):
+ return True
+
+ def tag(self):
+ return IDLType.Tags.mozmap
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name + "MozMap"
+ return self
+
+ def unroll(self):
+ # We do not unroll our inner. Just stop at ourselves. That
+ # lets us add headers for both ourselves and our inner as
+ # needed.
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if other.isPromise():
+ return False
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isNonCallbackInterface() or other.isSequence())
+
+ def isExposedInAllOf(self, exposureSet):
+ return self.inner.unroll().isExposedInAllOf(exposureSet)
+
+
+class IDLUnionType(IDLType):
+ def __init__(self, location, memberTypes):
+ IDLType.__init__(self, location, "")
+ self.memberTypes = memberTypes
+ self.hasNullableType = False
+ self._dictionaryType = None
+ self.flatMemberTypes = None
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
+
+ def __hash__(self):
+ assert self.isComplete()
+ return self.name.__hash__()
+
+ def isVoid(self):
+ return False
+
+ def isUnion(self):
+ return True
+
+ def isSerializable(self):
+ return all(m.isSerializable() for m in self.memberTypes)
+
+ def includesRestrictedFloat(self):
+ return any(t.includesRestrictedFloat() for t in self.memberTypes)
+
+ def tag(self):
+ return IDLType.Tags.union
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ for t in self.memberTypes:
+ t.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.flatMemberTypes is not None
+
+ def complete(self, scope):
+ def typeName(type):
+ if isinstance(type, IDLNullableType):
+ return typeName(type.inner) + "OrNull"
+ if isinstance(type, IDLWrapperType):
+ return typeName(type._identifier.object())
+ if isinstance(type, IDLObjectWithIdentifier):
+ return typeName(type.identifier)
+ return type.name
+
+ for (i, type) in enumerate(self.memberTypes):
+ if not type.isComplete():
+ self.memberTypes[i] = type.complete(scope)
+
+ self.name = "Or".join(typeName(type) for type in self.memberTypes)
+ self.flatMemberTypes = list(self.memberTypes)
+ i = 0
+ while i < len(self.flatMemberTypes):
+ if self.flatMemberTypes[i].nullable():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have more than one nullable types in a union",
+ [nullableType.location, self.flatMemberTypes[i].location])
+ if self.hasDictionaryType():
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [self._dictionaryType.location,
+ self.flatMemberTypes[i].location])
+ self.hasNullableType = True
+ nullableType = self.flatMemberTypes[i]
+ self.flatMemberTypes[i] = self.flatMemberTypes[i].inner
+ continue
+ if self.flatMemberTypes[i].isDictionary():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [nullableType.location,
+ self.flatMemberTypes[i].location])
+ self._dictionaryType = self.flatMemberTypes[i]
+ elif self.flatMemberTypes[i].isUnion():
+ self.flatMemberTypes[i:i + 1] = self.flatMemberTypes[i].memberTypes
+ continue
+ i += 1
+
+ for (i, t) in enumerate(self.flatMemberTypes[:-1]):
+ for u in self.flatMemberTypes[i + 1:]:
+ if not t.isDistinguishableFrom(u):
+ raise WebIDLError("Flat member types of a union should be "
+ "distinguishable, " + str(t) + " is not "
+ "distinguishable from " + str(u),
+ [self.location, t.location, u.location])
+
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if self.hasNullableType and other.nullable():
+ # Can't tell which type null should become
+ return False
+ if other.isUnion():
+ otherTypes = other.unroll().memberTypes
+ else:
+ otherTypes = [other]
+ # For every type in otherTypes, check that it's distinguishable from
+ # every type in our types
+ for u in otherTypes:
+ if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
+ return False
+ return True
+
+ def isExposedInAllOf(self, exposureSet):
+ # We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it.
+ for globalName in exposureSet:
+ if not any(t.unroll().isExposedInAllOf(set([globalName])) for t
+ in self.flatMemberTypes):
+ return False
+ return True
+
+ def hasDictionaryType(self):
+ return self._dictionaryType is not None
+
+ def hasPossiblyEmptyDictionaryType(self):
+ return (self._dictionaryType is not None and
+ self._dictionaryType.inner.canBeEmpty())
+
+ def _getDependentObjects(self):
+ return set(self.memberTypes)
+
+
+class IDLTypedefType(IDLType):
+ def __init__(self, location, innerType, name):
+ IDLType.__init__(self, location, name)
+ self.inner = innerType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLTypedefType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.name
+
+ def nullable(self):
+ return self.inner.nullable()
+
+ def isPrimitive(self):
+ return self.inner.isPrimitive()
+
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
+ def isString(self):
+ return self.inner.isString()
+
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
+ def isUSVString(self):
+ return self.inner.isUSVString()
+
+ def isVoid(self):
+ return self.inner.isVoid()
+
+ def isSequence(self):
+ return self.inner.isSequence()
+
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
+ def isDictionary(self):
+ return self.inner.isDictionary()
+
+ def isArrayBuffer(self):
+ return self.inner.isArrayBuffer()
+
+ def isArrayBufferView(self):
+ return self.inner.isArrayBufferView()
+
+ def isSharedArrayBuffer(self):
+ return self.inner.isSharedArrayBuffer()
+
+ def isTypedArray(self):
+ return self.inner.isTypedArray()
+
+ def isInterface(self):
+ return self.inner.isInterface()
+
+ def isCallbackInterface(self):
+ return self.inner.isCallbackInterface()
+
+ def isNonCallbackInterface(self):
+ return self.inner.isNonCallbackInterface()
+
+ def isComplete(self):
+ return False
+
+ def complete(self, parentScope):
+ if not self.inner.isComplete():
+ self.inner = self.inner.complete(parentScope)
+ assert self.inner.isComplete()
+ return self.inner
+
+ # Do we need a resolveType impl? I don't think it's particularly useful....
+
+ def tag(self):
+ return self.inner.tag()
+
+ def unroll(self):
+ return self.inner.unroll()
+
+ def isDistinguishableFrom(self, other):
+ return self.inner.isDistinguishableFrom(other)
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+
+class IDLTypedef(IDLObjectWithIdentifier):
+ def __init__(self, location, parentScope, innerType, name):
+ 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)
+
+ def finish(self, parentScope):
+ if not self.innerType.isComplete():
+ self.innerType = self.innerType.complete(parentScope)
+
+ def validate(self):
+ pass
+
+ def isTypedef(self):
+ return True
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def _getDependentObjects(self):
+ return self.innerType._getDependentObjects()
+
+
+class IDLWrapperType(IDLType):
+ def __init__(self, location, inner, promiseInnerType=None):
+ IDLType.__init__(self, location, inner.identifier.name)
+ self.inner = inner
+ self._identifier = inner.identifier
+ self.builtin = False
+ assert not promiseInnerType or inner.identifier.name == "Promise"
+ self._promiseInnerType = promiseInnerType
+
+ def __eq__(self, other):
+ return (isinstance(other, IDLWrapperType) and
+ self._identifier == other._identifier and
+ self.builtin == other.builtin)
+
+ def __str__(self):
+ return str(self.name) + " (Wrapper)"
+
+ def nullable(self):
+ return False
+
+ def isPrimitive(self):
+ return False
+
+ def isString(self):
+ return False
+
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
+ def isUSVString(self):
+ return False
+
+ def isVoid(self):
+ return False
+
+ def isSequence(self):
+ return False
+
+ def isDictionary(self):
+ return isinstance(self.inner, IDLDictionary)
+
+ def isInterface(self):
+ return (isinstance(self.inner, IDLInterface) or
+ isinstance(self.inner, IDLExternalInterface))
+
+ def isCallbackInterface(self):
+ return self.isInterface() and self.inner.isCallback()
+
+ def isNonCallbackInterface(self):
+ return self.isInterface() and not self.inner.isCallback()
+
+ def isEnum(self):
+ return isinstance(self.inner, IDLEnum)
+
+ def isPromise(self):
+ return (isinstance(self.inner, IDLInterface) and
+ self.inner.identifier.name == "Promise")
+
+ def promiseInnerType(self):
+ assert self.isPromise()
+ return self._promiseInnerType
+
+ def isSerializable(self):
+ if self.isInterface():
+ if self.inner.isExternal():
+ return False
+ return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
+ elif self.isEnum():
+ return True
+ elif self.isDictionary():
+ return all(m.type.isSerializable() for m in self.inner.members)
+ else:
+ raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
+ "is serializable" % type(self.inner), [self.location])
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolve(parentScope)
+
+ def isComplete(self):
+ return True
+
+ def tag(self):
+ if self.isInterface():
+ return IDLType.Tags.interface
+ elif self.isEnum():
+ return IDLType.Tags.enum
+ elif self.isDictionary():
+ return IDLType.Tags.dictionary
+ else:
+ assert False
+
+ def isDistinguishableFrom(self, other):
+ if self.isPromise():
+ return False
+ if other.isPromise():
+ return False
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ assert self.isInterface() or self.isEnum() or self.isDictionary()
+ if self.isEnum():
+ return (other.isPrimitive() or other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isDate())
+ if self.isDictionary() and other.nullable():
+ return False
+ if (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isDate() or other.isSequence()):
+ return True
+ if self.isDictionary():
+ return other.isNonCallbackInterface()
+
+ assert self.isInterface()
+ if other.isInterface():
+ if other.isSpiderMonkeyInterface():
+ # Just let |other| handle things
+ return other.isDistinguishableFrom(self)
+ assert self.isGeckoInterface() and other.isGeckoInterface()
+ if self.inner.isExternal() or other.unroll().inner.isExternal():
+ return self != other
+ return (len(self.inner.interfacesBasedOnSelf &
+ other.unroll().inner.interfacesBasedOnSelf) == 0 and
+ (self.isNonCallbackInterface() or
+ other.isNonCallbackInterface()))
+ if (other.isDictionary() or other.isCallback() or
+ other.isMozMap()):
+ return self.isNonCallbackInterface()
+
+ # Not much else |other| can be
+ assert other.isObject()
+ return False
+
+ def isExposedInAllOf(self, exposureSet):
+ if not self.isInterface():
+ return True
+ iface = self.inner
+ if iface.isExternal():
+ # Let's say true, though ideally we'd only do this when
+ # exposureSet contains the primary global's name.
+ return True
+ if (self.isPromise() and
+ # Check the internal type
+ not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)):
+ return False
+ return iface.exposureSet.issuperset(exposureSet)
+
+ def _getDependentObjects(self):
+ # NB: The codegen for an interface type depends on
+ # a) That the identifier is in fact an interface (as opposed to
+ # a dictionary or something else).
+ # b) The native type of the interface.
+ # If we depend on the interface object we will also depend on
+ # anything the interface depends on which is undesirable. We
+ # considered implementing a dependency just on the interface type
+ # file, but then every modification to an interface would cause this
+ # to be regenerated which is still undesirable. We decided not to
+ # depend on anything, reasoning that:
+ # 1) Changing the concrete type of the interface requires modifying
+ # Bindings.conf, which is still a global dependency.
+ # 2) Changing an interface to a dictionary (or vice versa) with the
+ # same identifier should be incredibly rare.
+ #
+ # On the other hand, if our type is a dictionary, we should
+ # depend on it, because the member types of a dictionary
+ # affect whether a method taking the dictionary as an argument
+ # takes a JSContext* argument or not.
+ if self.isDictionary():
+ return set([self.inner])
+ return set()
+
+
+class IDLBuiltinType(IDLType):
+
+ Types = enum(
+ # The integer types
+ 'byte',
+ 'octet',
+ 'short',
+ 'unsigned_short',
+ 'long',
+ 'unsigned_long',
+ 'long_long',
+ 'unsigned_long_long',
+ # Additional primitive types
+ 'boolean',
+ 'unrestricted_float',
+ 'float',
+ 'unrestricted_double',
+ # IMPORTANT: "double" must be the last primitive type listed
+ 'double',
+ # Other types
+ 'any',
+ 'domstring',
+ 'bytestring',
+ 'usvstring',
+ 'object',
+ 'date',
+ 'void',
+ # Funny stuff
+ 'ArrayBuffer',
+ 'ArrayBufferView',
+ 'SharedArrayBuffer',
+ 'Int8Array',
+ 'Uint8Array',
+ 'Uint8ClampedArray',
+ 'Int16Array',
+ 'Uint16Array',
+ 'Int32Array',
+ 'Uint32Array',
+ 'Float32Array',
+ 'Float64Array'
+ )
+
+ TagLookup = {
+ Types.byte: IDLType.Tags.int8,
+ Types.octet: IDLType.Tags.uint8,
+ Types.short: IDLType.Tags.int16,
+ Types.unsigned_short: IDLType.Tags.uint16,
+ Types.long: IDLType.Tags.int32,
+ Types.unsigned_long: IDLType.Tags.uint32,
+ Types.long_long: IDLType.Tags.int64,
+ Types.unsigned_long_long: IDLType.Tags.uint64,
+ Types.boolean: IDLType.Tags.bool,
+ Types.unrestricted_float: IDLType.Tags.unrestricted_float,
+ Types.float: IDLType.Tags.float,
+ Types.unrestricted_double: IDLType.Tags.unrestricted_double,
+ Types.double: IDLType.Tags.double,
+ Types.any: IDLType.Tags.any,
+ Types.domstring: IDLType.Tags.domstring,
+ Types.bytestring: IDLType.Tags.bytestring,
+ Types.usvstring: IDLType.Tags.usvstring,
+ Types.object: IDLType.Tags.object,
+ Types.date: IDLType.Tags.date,
+ Types.void: IDLType.Tags.void,
+ Types.ArrayBuffer: IDLType.Tags.interface,
+ Types.ArrayBufferView: IDLType.Tags.interface,
+ Types.SharedArrayBuffer: IDLType.Tags.interface,
+ Types.Int8Array: IDLType.Tags.interface,
+ Types.Uint8Array: IDLType.Tags.interface,
+ Types.Uint8ClampedArray: IDLType.Tags.interface,
+ Types.Int16Array: IDLType.Tags.interface,
+ Types.Uint16Array: IDLType.Tags.interface,
+ Types.Int32Array: IDLType.Tags.interface,
+ Types.Uint32Array: IDLType.Tags.interface,
+ Types.Float32Array: IDLType.Tags.interface,
+ Types.Float64Array: IDLType.Tags.interface
+ }
+
+ def __init__(self, location, name, type):
+ IDLType.__init__(self, location, name)
+ self.builtin = True
+ self._typeTag = type
+
+ def isPrimitive(self):
+ return self._typeTag <= IDLBuiltinType.Types.double
+
+ def isBoolean(self):
+ return self._typeTag == IDLBuiltinType.Types.boolean
+
+ def isNumeric(self):
+ return self.isPrimitive() and not self.isBoolean()
+
+ def isString(self):
+ return (self._typeTag == IDLBuiltinType.Types.domstring or
+ self._typeTag == IDLBuiltinType.Types.bytestring or
+ self._typeTag == IDLBuiltinType.Types.usvstring)
+
+ def isByteString(self):
+ return self._typeTag == IDLBuiltinType.Types.bytestring
+
+ def isDOMString(self):
+ return self._typeTag == IDLBuiltinType.Types.domstring
+
+ def isUSVString(self):
+ return self._typeTag == IDLBuiltinType.Types.usvstring
+
+ def isInteger(self):
+ return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
+
+ def isArrayBuffer(self):
+ return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
+
+ def isArrayBufferView(self):
+ return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
+
+ def isSharedArrayBuffer(self):
+ return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer
+
+ def isTypedArray(self):
+ return (self._typeTag >= IDLBuiltinType.Types.Int8Array and
+ self._typeTag <= IDLBuiltinType.Types.Float64Array)
+
+ def isInterface(self):
+ # TypedArray things are interface types per the TypedArray spec,
+ # but we handle them as builtins because SpiderMonkey implements
+ # all of it internally.
+ return (self.isArrayBuffer() or
+ self.isArrayBufferView() or
+ self.isSharedArrayBuffer() or
+ self.isTypedArray())
+
+ def isNonCallbackInterface(self):
+ # All the interfaces we can be are non-callback
+ return self.isInterface()
+
+ def isFloat(self):
+ return (self._typeTag == IDLBuiltinType.Types.float or
+ self._typeTag == IDLBuiltinType.Types.double or
+ self._typeTag == IDLBuiltinType.Types.unrestricted_float or
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double)
+
+ def isUnrestricted(self):
+ assert self.isFloat()
+ return (self._typeTag == IDLBuiltinType.Types.unrestricted_float or
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double)
+
+ def isSerializable(self):
+ return self.isPrimitive() or self.isString() or self.isDate()
+
+ def includesRestrictedFloat(self):
+ return self.isFloat() and not self.isUnrestricted()
+
+ def tag(self):
+ return IDLBuiltinType.TagLookup[self._typeTag]
+
+ def isDistinguishableFrom(self, other):
+ if other.isPromise():
+ return False
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ if self.isBoolean():
+ return (other.isNumeric() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isDate())
+ if self.isNumeric():
+ return (other.isBoolean() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isDate())
+ if self.isString():
+ return (other.isPrimitive() or other.isInterface() or
+ other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isDate())
+ if self.isAny():
+ # Can't tell "any" apart from anything
+ return False
+ if self.isObject():
+ return other.isPrimitive() or other.isString() or other.isEnum()
+ if self.isDate():
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isCallback() or
+ other.isDictionary() or other.isSequence() or
+ other.isMozMap())
+ if self.isVoid():
+ return not other.isVoid()
+ # Not much else we could be!
+ assert self.isSpiderMonkeyInterface()
+ # Like interfaces, but we know we're not a callback
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isDate() or
+ (other.isInterface() and (
+ # ArrayBuffer is distinguishable from everything
+ # that's not an ArrayBuffer or a callback interface
+ (self.isArrayBuffer() and not other.isArrayBuffer()) or
+ (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
+ # ArrayBufferView is distinguishable from everything
+ # that's not an ArrayBufferView or typed array.
+ (self.isArrayBufferView() and not other.isArrayBufferView() and
+ not other.isTypedArray()) or
+ # Typed arrays are distinguishable from everything
+ # except ArrayBufferView and the same type of typed
+ # array
+ (self.isTypedArray() and not other.isArrayBufferView() and not
+ (other.isTypedArray() and other.name == self.name)))))
+
+ def _getDependentObjects(self):
+ return set()
+
+BuiltinTypes = {
+ IDLBuiltinType.Types.byte:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
+ IDLBuiltinType.Types.byte),
+ IDLBuiltinType.Types.octet:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Octet",
+ IDLBuiltinType.Types.octet),
+ IDLBuiltinType.Types.short:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Short",
+ IDLBuiltinType.Types.short),
+ IDLBuiltinType.Types.unsigned_short:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedShort",
+ IDLBuiltinType.Types.unsigned_short),
+ IDLBuiltinType.Types.long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Long",
+ IDLBuiltinType.Types.long),
+ IDLBuiltinType.Types.unsigned_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLong",
+ IDLBuiltinType.Types.unsigned_long),
+ IDLBuiltinType.Types.long_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "LongLong",
+ IDLBuiltinType.Types.long_long),
+ IDLBuiltinType.Types.unsigned_long_long:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLongLong",
+ IDLBuiltinType.Types.unsigned_long_long),
+ IDLBuiltinType.Types.boolean:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Boolean",
+ IDLBuiltinType.Types.boolean),
+ IDLBuiltinType.Types.float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float",
+ IDLBuiltinType.Types.float),
+ IDLBuiltinType.Types.unrestricted_float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedFloat",
+ IDLBuiltinType.Types.unrestricted_float),
+ IDLBuiltinType.Types.double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Double",
+ IDLBuiltinType.Types.double),
+ IDLBuiltinType.Types.unrestricted_double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedDouble",
+ IDLBuiltinType.Types.unrestricted_double),
+ IDLBuiltinType.Types.any:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Any",
+ IDLBuiltinType.Types.any),
+ IDLBuiltinType.Types.domstring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "String",
+ IDLBuiltinType.Types.domstring),
+ IDLBuiltinType.Types.bytestring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ByteString",
+ IDLBuiltinType.Types.bytestring),
+ IDLBuiltinType.Types.usvstring:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString",
+ IDLBuiltinType.Types.usvstring),
+ IDLBuiltinType.Types.object:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object",
+ IDLBuiltinType.Types.object),
+ IDLBuiltinType.Types.date:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date",
+ IDLBuiltinType.Types.date),
+ IDLBuiltinType.Types.void:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void",
+ IDLBuiltinType.Types.void),
+ IDLBuiltinType.Types.ArrayBuffer:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBuffer",
+ IDLBuiltinType.Types.ArrayBuffer),
+ IDLBuiltinType.Types.ArrayBufferView:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView",
+ IDLBuiltinType.Types.ArrayBufferView),
+ IDLBuiltinType.Types.SharedArrayBuffer:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "SharedArrayBuffer",
+ IDLBuiltinType.Types.SharedArrayBuffer),
+ IDLBuiltinType.Types.Int8Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array",
+ IDLBuiltinType.Types.Int8Array),
+ IDLBuiltinType.Types.Uint8Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8Array",
+ IDLBuiltinType.Types.Uint8Array),
+ IDLBuiltinType.Types.Uint8ClampedArray:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8ClampedArray",
+ IDLBuiltinType.Types.Uint8ClampedArray),
+ IDLBuiltinType.Types.Int16Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int16Array",
+ IDLBuiltinType.Types.Int16Array),
+ IDLBuiltinType.Types.Uint16Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint16Array",
+ IDLBuiltinType.Types.Uint16Array),
+ IDLBuiltinType.Types.Int32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int32Array",
+ IDLBuiltinType.Types.Int32Array),
+ IDLBuiltinType.Types.Uint32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint32Array",
+ IDLBuiltinType.Types.Uint32Array),
+ IDLBuiltinType.Types.Float32Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float32Array",
+ IDLBuiltinType.Types.Float32Array),
+ IDLBuiltinType.Types.Float64Array:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
+ IDLBuiltinType.Types.Float64Array)
+}
+
+
+integerTypeSizes = {
+ IDLBuiltinType.Types.byte: (-128, 127),
+ IDLBuiltinType.Types.octet: (0, 255),
+ IDLBuiltinType.Types.short: (-32768, 32767),
+ IDLBuiltinType.Types.unsigned_short: (0, 65535),
+ IDLBuiltinType.Types.long: (-2147483648, 2147483647),
+ IDLBuiltinType.Types.unsigned_long: (0, 4294967295),
+ IDLBuiltinType.Types.long_long: (-9223372036854775808, 9223372036854775807),
+ IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615)
+}
+
+
+def matchIntegerValueToType(value):
+ for type, extremes in integerTypeSizes.items():
+ (min, max) = extremes
+ if value <= max and value >= min:
+ return BuiltinTypes[type]
+
+ return None
+
+class NoCoercionFoundError(WebIDLError):
+ """
+ A class we use to indicate generic coercion failures because none of the
+ types worked out in IDLValue.coerceToType.
+ """
+
+class IDLValue(IDLObject):
+ def __init__(self, location, type, value):
+ IDLObject.__init__(self, location)
+ self.type = type
+ assert isinstance(type, IDLType)
+
+ self.value = value
+
+ def coerceToType(self, type, location):
+ if type == self.type:
+ return self # Nothing to do
+
+ # We first check for unions to ensure that even if the union is nullable
+ # we end up with the right flat member type, not the union's type.
+ if type.isUnion():
+ # We use the flat member types here, because if we have a nullable
+ # member type, or a nested union, we want the type the value
+ # actually coerces to, not the nullable or nested union type.
+ for subtype in type.unroll().flatMemberTypes:
+ try:
+ coercedValue = self.coerceToType(subtype, location)
+ # Create a new IDLValue to make sure that we have the
+ # correct float/double type. This is necessary because we
+ # use the value's type when it is a default value of a
+ # union, and the union cares about the exact float type.
+ return IDLValue(self.location, subtype, coercedValue.value)
+ except Exception as e:
+ # Make sure to propagate out WebIDLErrors that are not the
+ # generic "hey, we could not coerce to this type at all"
+ # exception, because those are specific "coercion failed for
+ # reason X" exceptions. Note that we want to swallow
+ # non-WebIDLErrors here, because those can just happen if
+ # "type" is not something that can have a default value at
+ # all.
+ if (isinstance(e, WebIDLError) and
+ not isinstance(e, NoCoercionFoundError)):
+ raise e
+
+ # If the type allows null, rerun this matching on the inner type, except
+ # nullable enums. We handle those specially, because we want our
+ # default string values to stay strings even when assigned to a nullable
+ # enum.
+ elif type.nullable() and not type.isEnum():
+ innerValue = self.coerceToType(type.inner, location)
+ return IDLValue(self.location, type, innerValue.value)
+
+ elif self.type.isInteger() and type.isInteger():
+ # We're both integer types. See if we fit.
+
+ (min, max) = integerTypeSizes[type._typeTag]
+ if self.value <= max and self.value >= min:
+ # Promote
+ return IDLValue(self.location, type, self.value)
+ else:
+ raise WebIDLError("Value %s is out of range for type %s." %
+ (self.value, type), [location])
+ elif self.type.isInteger() and type.isFloat():
+ # Convert an integer literal into float
+ if -2**24 <= self.value <= 2**24:
+ return IDLValue(self.location, type, float(self.value))
+ else:
+ raise WebIDLError("Converting value %s to %s will lose precision." %
+ (self.value, type), [location])
+ elif self.type.isString() and type.isEnum():
+ # Just keep our string, but make sure it's a valid value for this enum
+ enum = type.unroll().inner
+ if self.value not in enum.values():
+ raise WebIDLError("'%s' is not a valid default value for enum %s"
+ % (self.value, enum.identifier.name),
+ [location, enum.location])
+ return self
+ elif self.type.isFloat() and type.isFloat():
+ if (not type.isUnrestricted() and
+ (self.value == float("inf") or self.value == float("-inf") or
+ math.isnan(self.value))):
+ raise WebIDLError("Trying to convert unrestricted value %s to non-unrestricted"
+ % self.value, [location])
+ return IDLValue(self.location, type, self.value)
+ elif self.type.isString() and type.isUSVString():
+ # Allow USVStrings to use default value just like
+ # DOMString. No coercion is required in this case as Codegen.py
+ # treats USVString just like DOMString, but with an
+ # extra normalization step.
+ assert self.type.isDOMString()
+ 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
+ # extra steps. We want to make sure that our string contains
+ # only valid characters, so we check that here.
+ valid_ascii_lit = " " + string.ascii_letters + string.digits + string.punctuation
+ for idx, c in enumerate(self.value):
+ if c not in valid_ascii_lit:
+ raise WebIDLError("Coercing this string literal %s to a ByteString is not supported yet. "
+ "Coercion failed due to an unsupported byte %d at index %d."
+ % (self.value.__repr__(), ord(c), idx), [location])
+
+ return IDLValue(self.location, type, self.value)
+
+ raise NoCoercionFoundError("Cannot coerce type %s to type %s." %
+ (self.type, type), [location])
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLNullValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if (not isinstance(type, IDLNullableType) and
+ not (type.isUnion() and type.hasNullableType) and
+ not (type.isUnion() and type.hasDictionaryType()) and
+ not type.isDictionary() and
+ not type.isAny()):
+ raise WebIDLError("Cannot coerce null value to type %s." % type,
+ [location])
+
+ nullValue = IDLNullValue(self.location)
+ if type.isUnion() and not type.nullable() and type.hasDictionaryType():
+ # We're actually a default value for the union's dictionary member.
+ # Use its type.
+ for t in type.flatMemberTypes:
+ if t.isDictionary():
+ nullValue.type = t
+ return nullValue
+ nullValue.type = type
+ return nullValue
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLEmptySequenceValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if type.isUnion():
+ # We use the flat member types here, because if we have a nullable
+ # member type, or a nested union, we want the type the value
+ # actually coerces to, not the nullable or nested union type.
+ for subtype in type.unroll().flatMemberTypes:
+ try:
+ return self.coerceToType(subtype, location)
+ except:
+ pass
+
+ if not type.isSequence():
+ raise WebIDLError("Cannot coerce empty sequence value to type %s." % type,
+ [location])
+
+ emptySequenceValue = IDLEmptySequenceValue(self.location)
+ emptySequenceValue.type = type
+ return emptySequenceValue
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLUndefinedValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if not type.isAny():
+ raise WebIDLError("Cannot coerce undefined value to type %s." % type,
+ [location])
+
+ undefinedValue = IDLUndefinedValue(self.location)
+ undefinedValue.type = type
+ return undefinedValue
+
+ def _getDependentObjects(self):
+ return set()
+
+
+class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
+
+ Tags = enum(
+ 'Const',
+ 'Attr',
+ 'Method',
+ 'MaplikeOrSetlike',
+ 'Iterable'
+ )
+
+ Special = enum(
+ 'Static',
+ 'Stringifier'
+ )
+
+ AffectsValues = ("Nothing", "Everything")
+ DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything")
+
+ def __init__(self, location, identifier, tag, extendedAttrDict=None):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+ IDLExposureMixins.__init__(self, location)
+ self.tag = tag
+ if extendedAttrDict is None:
+ self._extendedAttrDict = {}
+ else:
+ self._extendedAttrDict = extendedAttrDict
+
+ def isMethod(self):
+ return self.tag == IDLInterfaceMember.Tags.Method
+
+ def isAttr(self):
+ return self.tag == IDLInterfaceMember.Tags.Attr
+
+ def isConst(self):
+ return self.tag == IDLInterfaceMember.Tags.Const
+
+ def isMaplikeOrSetlikeOrIterable(self):
+ return (self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike or
+ self.tag == IDLInterfaceMember.Tags.Iterable)
+
+ def isMaplikeOrSetlike(self):
+ return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
+
+ def addExtendedAttributes(self, attrs):
+ for attr in attrs:
+ self.handleExtendedAttribute(attr)
+ attrlist = attr.listValue()
+ self._extendedAttrDict[attr.identifier()] = attrlist if len(attrlist) else True
+
+ def handleExtendedAttribute(self, attr):
+ pass
+
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
+ def finish(self, scope):
+ # We better be exposed _somewhere_.
+ if (len(self._exposureGlobalNames) == 0):
+ print self.identifier.name
+ assert len(self._exposureGlobalNames) != 0
+ IDLExposureMixins.finish(self, scope)
+
+ def validate(self):
+ if (self.getExtendedAttribute("Pref") and
+ self.exposureSet != set([self._globalScope.primaryGlobalName])):
+ raise WebIDLError("[Pref] used on an interface member that is not "
+ "%s-only" % self._globalScope.primaryGlobalName,
+ [self.location])
+
+ if self.isAttr() or self.isMethod():
+ if self.affects == "Everything" and self.dependsOn != "Everything":
+ raise WebIDLError("Interface member is flagged as affecting "
+ "everything but not depending on everything. "
+ "That seems rather unlikely.",
+ [self.location])
+
+ if self.getExtendedAttribute("NewObject"):
+ if self.dependsOn == "Nothing" or self.dependsOn == "DOMState":
+ raise WebIDLError("A [NewObject] method is not idempotent, "
+ "so it has to depend on something other than DOM state.",
+ [self.location])
+ if (self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")):
+ raise WebIDLError("A [NewObject] attribute shouldnt be "
+ "[Cached] or [StoreInSlot], since the point "
+ "of those is to keep returning the same "
+ "thing across multiple calls, which is not "
+ "what [NewObject] does.",
+ [self.location])
+
+ def _setDependsOn(self, dependsOn):
+ if self.dependsOn != "Everything":
+ raise WebIDLError("Trying to specify multiple different DependsOn, "
+ "Pure, or Constant extended attributes for "
+ "attribute", [self.location])
+ if dependsOn not in IDLInterfaceMember.DependsOnValues:
+ raise WebIDLError("Invalid [DependsOn=%s] on attribute" % dependsOn,
+ [self.location])
+ self.dependsOn = dependsOn
+
+ def _setAffects(self, affects):
+ if self.affects != "Everything":
+ raise WebIDLError("Trying to specify multiple different Affects, "
+ "Pure, or Constant extended attributes for "
+ "attribute", [self.location])
+ if affects not in IDLInterfaceMember.AffectsValues:
+ raise WebIDLError("Invalid [Affects=%s] on attribute" % dependsOn,
+ [self.location])
+ self.affects = affects
+
+ def _addAlias(self, alias):
+ if alias in self.aliases:
+ raise WebIDLError("Duplicate [Alias=%s] on attribute" % alias,
+ [self.location])
+ self.aliases.append(alias)
+
+
+class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
+
+ def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
+ IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
+ if keyType is not None:
+ assert isinstance(keyType, IDLType)
+ else:
+ assert valueType is not None
+ assert ifaceType in ['maplike', 'setlike', 'iterable']
+ if valueType is not None:
+ assert isinstance(valueType, IDLType)
+ self.keyType = keyType
+ self.valueType = valueType
+ self.maplikeOrSetlikeOrIterableType = ifaceType
+ self.disallowedMemberNames = []
+ self.disallowedNonMethodNames = []
+
+ def isMaplike(self):
+ return self.maplikeOrSetlikeOrIterableType == "maplike"
+
+ def isSetlike(self):
+ return self.maplikeOrSetlikeOrIterableType == "setlike"
+
+ def isIterable(self):
+ return self.maplikeOrSetlikeOrIterableType == "iterable"
+
+ def hasKeyType(self):
+ return self.keyType is not None
+
+ def hasValueType(self):
+ return self.valueType is not None
+
+ def checkCollisions(self, members, isAncestor):
+ for member in members:
+ # Check that there are no disallowed members
+ if (member.identifier.name in self.disallowedMemberNames and
+ not ((member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) or
+ (member.isAttr() and member.isMaplikeOrSetlikeAttr()))):
+ raise WebIDLError("Member '%s' conflicts "
+ "with reserved %s name." %
+ (member.identifier.name,
+ self.maplikeOrSetlikeOrIterableType),
+ [self.location, member.location])
+ # Check that there are no disallowed non-method members.
+ # Ancestor members are always disallowed here; own members
+ # are disallowed only if they're non-methods.
+ if ((isAncestor or member.isAttr() or member.isConst()) and
+ member.identifier.name in self.disallowedNonMethodNames):
+ raise WebIDLError("Member '%s' conflicts "
+ "with reserved %s method." %
+ (member.identifier.name,
+ self.maplikeOrSetlikeOrIterableType),
+ [self.location, member.location])
+
+ def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
+ chromeOnly=False, isPure=False, affectsNothing=False, newObject=False,
+ isIteratorAlias=False):
+ """
+ Create an IDLMethod based on the parameters passed in.
+
+ - members is the member list to add this function to, since this is
+ called during the member expansion portion of interface object
+ building.
+
+ - chromeOnly is only True for read-only js implemented classes, to
+ implement underscore prefixed convenience functions which would
+ otherwise not be available, unlike the case of C++ bindings.
+
+ - isPure is only True for idempotent functions, so it is not valid for
+ things like keys, values, etc. that return a new object every time.
+
+ - affectsNothing means that nothing changes due to this method, which
+ affects JIT optimization behavior
+
+ - newObject means the method creates and returns a new object.
+
+ """
+ # Only add name to lists for collision checks if it's not chrome
+ # only.
+ if chromeOnly:
+ name = "__" + name
+ else:
+ if not allowExistingOperations:
+ self.disallowedMemberNames.append(name)
+ else:
+ self.disallowedNonMethodNames.append(name)
+ # If allowExistingOperations is True, and another operation exists
+ # with the same name as the one we're trying to add, don't add the
+ # maplike/setlike operation. However, if the operation is static,
+ # then fail by way of creating the function, which will cause a
+ # naming conflict, per the spec.
+ if allowExistingOperations:
+ for m in members:
+ if m.identifier.name == name and m.isMethod() and not m.isStatic():
+ return
+ method = IDLMethod(self.location,
+ IDLUnresolvedIdentifier(self.location, name, allowDoubleUnderscore=chromeOnly),
+ returnType, args, maplikeOrSetlikeOrIterable=self)
+ # We need to be able to throw from declaration methods
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("Throws",))])
+ if chromeOnly:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
+ if isPure:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("Pure",))])
+ # Following attributes are used for keys/values/entries. Can't mark
+ # them pure, since they return a new object each time they are run.
+ if affectsNothing:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
+ IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
+ if newObject:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NewObject",))])
+ if isIteratorAlias:
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
+ members.append(method)
+
+ def resolve(self, parentScope):
+ if self.keyType:
+ self.keyType.resolveType(parentScope)
+ if self.valueType:
+ self.valueType.resolveType(parentScope)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+ if self.keyType and not self.keyType.isComplete():
+ t = self.keyType.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.keyType = t
+ if self.valueType and not self.valueType.isComplete():
+ t = self.valueType.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.valueType = t
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ def handleExtendedAttribute(self, attr):
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def _getDependentObjects(self):
+ deps = set()
+ if self.keyType:
+ deps.add(self.keyType)
+ if self.valueType:
+ deps.add(self.valueType)
+ return deps
+
+ def getForEachArguments(self):
+ return [IDLArgument(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "callback"),
+ BuiltinTypes[IDLBuiltinType.Types.object]),
+ IDLArgument(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "thisArg"),
+ BuiltinTypes[IDLBuiltinType.Types.any],
+ optional=True)]
+
+# Iterable adds ES6 iterator style functions and traits
+# (keys/values/entries/@@iterator) to an interface.
+class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
+
+ def __init__(self, location, identifier, keyType, valueType=None, scope=None):
+ IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier,
+ "iterable", keyType, valueType,
+ IDLInterfaceMember.Tags.Iterable)
+ self.iteratorType = None
+
+ def __str__(self):
+ return "declared iterable with key '%s' and value '%s'" % (self.keyType, self.valueType)
+
+ def expand(self, members, isJSImplemented):
+ """
+ In order to take advantage of all of the method machinery in Codegen,
+ we generate our functions as if they were part of the interface
+ specification during parsing.
+ """
+ # We only need to add entries/keys/values here if we're a pair iterator.
+ # Value iterators just copy these from %ArrayPrototype% instead.
+ if not self.isPairIterator():
+ return
+
+ # object entries()
+ self.addMethod("entries", members, False, self.iteratorType,
+ affectsNothing=True, newObject=True,
+ isIteratorAlias=True)
+ # object keys()
+ self.addMethod("keys", members, False, self.iteratorType,
+ affectsNothing=True, newObject=True)
+ # object values()
+ self.addMethod("values", members, False, self.iteratorType,
+ affectsNothing=True, newObject=True)
+
+ # void forEach(callback(valueType, keyType), optional any thisArg)
+ self.addMethod("forEach", members, False,
+ BuiltinTypes[IDLBuiltinType.Types.void],
+ self.getForEachArguments())
+
+ def isValueIterator(self):
+ return not self.isPairIterator()
+
+ def isPairIterator(self):
+ return self.hasKeyType()
+
+# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
+class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
+
+ def __init__(self, location, identifier, maplikeOrSetlikeType,
+ readonly, keyType, valueType):
+ IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType,
+ keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike)
+ self.readonly = readonly
+ self.slotIndices = None
+
+ # When generating JSAPI access code, we need to know the backing object
+ # type prefix to create the correct function. Generate here for reuse.
+ if self.isMaplike():
+ self.prefix = 'Map'
+ elif self.isSetlike():
+ self.prefix = 'Set'
+
+ def __str__(self):
+ return "declared '%s' with key '%s'" % (self.maplikeOrSetlikeOrIterableType, self.keyType)
+
+ def expand(self, members, isJSImplemented):
+ """
+ In order to take advantage of all of the method machinery in Codegen,
+ we generate our functions as if they were part of the interface
+ specification during parsing.
+ """
+ # Both maplike and setlike have a size attribute
+ members.append(IDLAttribute(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
+ BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
+ True,
+ maplikeOrSetlike=self))
+ self.reserved_ro_names = ["size"]
+ self.disallowedMemberNames.append("size")
+
+ # object entries()
+ self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
+ affectsNothing=True, isIteratorAlias=self.isMaplike())
+ # object keys()
+ self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
+ affectsNothing=True)
+ # object values()
+ self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
+ affectsNothing=True, isIteratorAlias=self.isSetlike())
+
+ # void forEach(callback(valueType, keyType), thisVal)
+ self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void],
+ self.getForEachArguments())
+
+ def getKeyArg():
+ return IDLArgument(self.location,
+ IDLUnresolvedIdentifier(self.location, "key"),
+ self.keyType)
+
+ # boolean has(keyType key)
+ self.addMethod("has", members, False, BuiltinTypes[IDLBuiltinType.Types.boolean],
+ [getKeyArg()], isPure=True)
+
+ if not self.readonly:
+ # void clear()
+ self.addMethod("clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void],
+ [])
+ # boolean delete(keyType key)
+ self.addMethod("delete", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()])
+
+ # Always generate underscored functions (e.g. __add, __clear) for js
+ # implemented interfaces as convenience functions.
+ if isJSImplemented:
+ # void clear()
+ self.addMethod("clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void],
+ [], chromeOnly=True)
+ # boolean delete(keyType key)
+ self.addMethod("delete", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()],
+ chromeOnly=True)
+
+ if self.isSetlike():
+ if not self.readonly:
+ # Add returns the set object it just added to.
+ # object add(keyType key)
+
+ self.addMethod("add", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()])
+ if isJSImplemented:
+ self.addMethod("add", members, True,
+ BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()],
+ chromeOnly=True)
+ return
+
+ # If we get this far, we're a maplike declaration.
+
+ # valueType get(keyType key)
+ #
+ # Note that instead of the value type, we're using any here. The
+ # validity checks should happen as things are inserted into the map,
+ # and using any as the return type makes code generation much simpler.
+ #
+ # TODO: Bug 1155340 may change this to use specific type to provide
+ # more info to JIT.
+ self.addMethod("get", members, False, BuiltinTypes[IDLBuiltinType.Types.any],
+ [getKeyArg()], isPure=True)
+
+ def getValueArg():
+ return IDLArgument(self.location,
+ IDLUnresolvedIdentifier(self.location, "value"),
+ self.valueType)
+
+ if not self.readonly:
+ self.addMethod("set", members, True, BuiltinTypes[IDLBuiltinType.Types.object],
+ [getKeyArg(), getValueArg()])
+ if isJSImplemented:
+ self.addMethod("set", members, True, BuiltinTypes[IDLBuiltinType.Types.object],
+ [getKeyArg(), getValueArg()], chromeOnly=True)
+
+class IDLConst(IDLInterfaceMember):
+ def __init__(self, location, identifier, type, value):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Const)
+
+ assert isinstance(type, IDLType)
+ if type.isDictionary():
+ raise WebIDLError("A constant cannot be of a dictionary type",
+ [self.location])
+ self.type = type
+ self.value = value
+
+ if identifier.name == "prototype":
+ raise WebIDLError("The identifier of a constant must not be 'prototype'",
+ [location])
+
+ def __str__(self):
+ return "'%s' const '%s'" % (self.type, self.identifier)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ if not self.type.isComplete():
+ type = self.type.complete(scope)
+ if not type.isPrimitive() and not type.isString():
+ locations = [self.type.location, type.location]
+ try:
+ locations.append(type.inner.location)
+ except:
+ pass
+ raise WebIDLError("Incorrect type for constant", locations)
+ self.type = type
+
+ # The value might not match the type
+ coercedValue = self.value.coerceToType(self.type, self.location)
+ assert coercedValue
+
+ self.value = coercedValue
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "Pref" or
+ identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "SecureContext"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on constant" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def _getDependentObjects(self):
+ return set([self.type, self.value])
+
+
+class IDLAttribute(IDLInterfaceMember):
+ def __init__(self, location, identifier, type, readonly, inherit=False,
+ static=False, stringifier=False, maplikeOrSetlike=None,
+ extendedAttrDict=None, navigatorObjectGetter=False):
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Attr,
+ extendedAttrDict=extendedAttrDict)
+
+ assert isinstance(type, IDLType)
+ self.type = type
+ self.readonly = readonly
+ self.inherit = inherit
+ self._static = static
+ 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
+ self.dependsOn = "Everything"
+ self.affects = "Everything"
+ self.navigatorObjectGetter = navigatorObjectGetter
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
+ [location])
+
+ if readonly and inherit:
+ raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
+ [self.location])
+
+ def isStatic(self):
+ return self._static
+
+ def forceStatic(self):
+ self._static = True
+
+ def __str__(self):
+ return "'%s' attribute '%s'" % (self.type, self.identifier)
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ if not self.type.isComplete():
+ t = self.type.complete(scope)
+
+ assert not isinstance(t, IDLUnresolvedType)
+ assert not isinstance(t, IDLTypedefType)
+ assert not isinstance(t.name, IDLUnresolvedIdentifier)
+ self.type = t
+
+ if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("An attribute cannot be of a dictionary type",
+ [self.location])
+ if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a sequence "
+ "type", [self.location])
+ if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+ "type", [self.location])
+ if self.type.isUnion():
+ for f in self.type.unroll().flatMemberTypes:
+ if f.isDictionary():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a dictionary "
+ "type", [self.location, f.location])
+ if f.isSequence():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a sequence "
+ "type", [self.location, f.location])
+ if f.isMozMap():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a MozMap "
+ "type", [self.location, f.location])
+ if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
+ raise WebIDLError("An attribute with [PutForwards] must have an "
+ "interface type as its type", [self.location])
+
+ if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
+ raise WebIDLError("An attribute with [SameObject] must have an "
+ "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")):
+ 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()):
+ raise WebIDLError("[Frozen] is only allowed on "
+ "sequence-valued, dictionary-valued, and "
+ "MozMap-valued attributes",
+ [self.location])
+ if not self.type.unroll().isExposedInAllOf(self.exposureSet):
+ raise WebIDLError("Attribute returns a type that is not exposed "
+ "everywhere where the attribute is exposed",
+ [self.location])
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "SetterThrows" and self.readonly:
+ raise WebIDLError("Readonly attributes must not be flagged as "
+ "[SetterThrows]",
+ [self.location])
+ elif (((identifier == "Throws" or identifier == "GetterThrows") and
+ self.getExtendedAttribute("StoreInSlot")) or
+ (identifier == "StoreInSlot" and
+ (self.getExtendedAttribute("Throws") or
+ self.getExtendedAttribute("GetterThrows")))):
+ raise WebIDLError("Throwing things can't be [StoreInSlot]",
+ [attr.location])
+ elif identifier == "LenientThis":
+ if not attr.noArguments():
+ raise WebIDLError("[LenientThis] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[LenientThis] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginReadable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginReadable]",
+ [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginWritable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginWritable]",
+ [attr.location, self.location])
+ self.lenientThis = True
+ elif identifier == "Unforgeable":
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject" and not self.readonly:
+ raise WebIDLError("[SameObject] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "Constant" and not self.readonly:
+ raise WebIDLError("[Constant] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "PutForwards":
+ if not self.readonly:
+ raise WebIDLError("[PutForwards] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[PutForwards] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("Replaceable") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ if not attr.hasValue():
+ raise WebIDLError("[PutForwards] takes an identifier",
+ [attr.location, self.location])
+ elif identifier == "Replaceable":
+ if not attr.noArguments():
+ raise WebIDLError("[Replaceable] must take no arguments",
+ [attr.location])
+ if not self.readonly:
+ raise WebIDLError("[Replaceable] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[Replaceable] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("PutForwards") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "LenientSetter":
+ if not attr.noArguments():
+ raise WebIDLError("[LenientSetter] must take no arguments",
+ [attr.location])
+ if not self.readonly:
+ raise WebIDLError("[LenientSetter] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[LenientSetter] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("PutForwards") is not None:
+ raise WebIDLError("[LenientSetter] and [PutForwards] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ if self.getExtendedAttribute("Replaceable") is not None:
+ raise WebIDLError("[LenientSetter] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "LenientFloat":
+ if self.readonly:
+ raise WebIDLError("[LenientFloat] used on a readonly attribute",
+ [attr.location, self.location])
+ if not self.type.includesRestrictedFloat():
+ 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 "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "Cached":
+ if self.getExtendedAttribute("StoreInSlot"):
+ raise WebIDLError("[Cached] and [StoreInSlot] must not be "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif (identifier == "CrossOriginReadable" or
+ identifier == "CrossOriginWritable"):
+ if not attr.noArguments() and identifier == "CrossOriginReadable":
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[%s] is only allowed on non-static "
+ "attributes" % identifier,
+ [attr.location, self.location])
+ if self.getExtendedAttribute("LenientThis"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [%s]" % identifier,
+ [attr.location, self.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif identifier == "Pure":
+ if not attr.noArguments():
+ raise WebIDLError("[Pure] must take no arguments",
+ [attr.location])
+ self._setDependsOn("DOMState")
+ self._setAffects("Nothing")
+ elif identifier == "Constant" or identifier == "SameObject":
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ self._setDependsOn("Nothing")
+ self._setAffects("Nothing")
+ elif identifier == "Affects":
+ if not attr.hasValue():
+ raise WebIDLError("[Affects] takes an identifier",
+ [attr.location])
+ self._setAffects(attr.value())
+ elif identifier == "DependsOn":
+ if not attr.hasValue():
+ raise WebIDLError("[DependsOn] takes an identifier",
+ [attr.location])
+ if (attr.value() != "Everything" and attr.value() != "DOMState" and
+ not self.readonly):
+ raise WebIDLError("[DependsOn=%s] only allowed on "
+ "readonly attributes" % attr.value(),
+ [attr.location, self.location])
+ self._setDependsOn(attr.value())
+ elif identifier == "UseCounter":
+ if self.stringifier:
+ raise WebIDLError("[UseCounter] must not be used on a "
+ "stringifier attribute",
+ [attr.location, self.location])
+ elif identifier == "Unscopable":
+ if not attr.noArguments():
+ raise WebIDLError("[Unscopable] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[Unscopable] is only allowed on non-static "
+ "attributes and operations",
+ [attr.location, self.location])
+ elif (identifier == "Pref" or
+ identifier == "Deprecated" or
+ identifier == "SetterThrows" or
+ identifier == "Throws" or
+ identifier == "GetterThrows" or
+ identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "SecureContext" or
+ identifier == "Frozen" or
+ identifier == "NewObject" or
+ identifier == "UnsafeInPrerendering" or
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "ReturnValueNeedsContainsHack" or
+ identifier == "BinaryName"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ 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
+
+ def isMaplikeOrSetlikeAttr(self):
+ """
+ True if this attribute was generated from an interface with
+ maplike/setlike (e.g. this is the size attribute for
+ maplike/setlike)
+ """
+ return self.maplikeOrSetlike is not None
+
+ def isUnforgeable(self):
+ return self._unforgeable
+
+ def _getDependentObjects(self):
+ return set([self.type])
+
+
+class IDLArgument(IDLObjectWithIdentifier):
+ def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
+ IDLObjectWithIdentifier.__init__(self, location, None, identifier)
+
+ assert isinstance(type, IDLType)
+ self.type = type
+
+ self.optional = optional
+ self.defaultValue = defaultValue
+ self.variadic = variadic
+ self.dictionaryMember = dictionaryMember
+ self._isComplete = False
+ self.enforceRange = False
+ self.clamp = False
+ self._allowTreatNonCallableAsNull = False
+ self._extendedAttrDict = {}
+
+ 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
+ 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
+
+ def complete(self, scope):
+ if self._isComplete:
+ return
+
+ self._isComplete = True
+
+ if not self.type.isComplete():
+ type = self.type.complete(scope)
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ self.type = type
+
+ if ((self.type.isDictionary() or
+ self.type.isUnion() and self.type.unroll().hasDictionaryType()) and
+ self.optional and not self.defaultValue and not self.variadic):
+ # Default optional non-variadic dictionaries to null,
+ # for simplicity, so the codegen doesn't have to special-case this.
+ self.defaultValue = IDLNullValue(self.location)
+ elif self.type.isAny():
+ assert (self.defaultValue is None or
+ isinstance(self.defaultValue, IDLNullValue))
+ # optional 'any' values always have a default value
+ if self.optional and not self.defaultValue and not self.variadic:
+ # Set the default value to undefined, for simplicity, so the
+ # codegen doesn't have to special-case this.
+ self.defaultValue = IDLUndefinedValue(self.location)
+
+ # Now do the coercing thing; this needs to happen after the
+ # above creation of a default value.
+ if self.defaultValue:
+ self.defaultValue = self.defaultValue.coerceToType(self.type,
+ self.location)
+ assert self.defaultValue
+
+ def allowTreatNonCallableAsNull(self):
+ return self._allowTreatNonCallableAsNull
+
+ def _getDependentObjects(self):
+ deps = set([self.type])
+ if self.defaultValue:
+ deps.add(self.defaultValue)
+ return deps
+
+ def canHaveMissingValue(self):
+ return self.optional and not self.defaultValue
+
+
+class IDLCallback(IDLObjectWithScope):
+ def __init__(self, location, parentScope, identifier, returnType, arguments):
+ assert isinstance(returnType, IDLType)
+
+ self._returnType = returnType
+ # Clone the list
+ self._arguments = list(arguments)
+
+ IDLObjectWithScope.__init__(self, location, parentScope, identifier)
+
+ for (returnType, arguments) in self.signatures():
+ for argument in arguments:
+ argument.resolve(self)
+
+ self._treatNonCallableAsNull = False
+ self._treatNonObjectAsNull = False
+
+ def isCallback(self):
+ return True
+
+ def signatures(self):
+ return [(self._returnType, self._arguments)]
+
+ def finish(self, scope):
+ if not self._returnType.isComplete():
+ type = self._returnType.complete(scope)
+
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ self._returnType = type
+
+ for argument in self._arguments:
+ if argument.type.isComplete():
+ continue
+
+ type = argument.type.complete(scope)
+
+ assert not isinstance(type, IDLUnresolvedType)
+ assert not isinstance(type, IDLTypedefType)
+ assert not isinstance(type.name, IDLUnresolvedIdentifier)
+ argument.type = type
+
+ def validate(self):
+ pass
+
+ def addExtendedAttributes(self, attrs):
+ unhandledAttrs = []
+ for attr in attrs:
+ if attr.identifier() == "TreatNonCallableAsNull":
+ self._treatNonCallableAsNull = True
+ elif attr.identifier() == "TreatNonObjectAsNull":
+ self._treatNonObjectAsNull = True
+ else:
+ unhandledAttrs.append(attr)
+ if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
+ raise WebIDLError("Cannot specify both [TreatNonCallableAsNull] "
+ "and [TreatNonObjectAsNull]", [self.location])
+ if len(unhandledAttrs) != 0:
+ IDLType.addExtendedAttributes(self, unhandledAttrs)
+
+ def _getDependentObjects(self):
+ return set([self._returnType] + self._arguments)
+
+
+class IDLCallbackType(IDLType):
+ def __init__(self, location, callback):
+ IDLType.__init__(self, location, callback.identifier.name)
+ self.callback = callback
+
+ def isCallback(self):
+ return True
+
+ def tag(self):
+ return IDLType.Tags.callback
+
+ def isDistinguishableFrom(self, other):
+ if other.isPromise():
+ return False
+ if other.isUnion():
+ # Just forward to the union; it'll deal
+ return other.isDistinguishableFrom(self)
+ return (other.isPrimitive() or other.isString() or other.isEnum() or
+ other.isNonCallbackInterface() or other.isDate() or
+ other.isSequence())
+
+ def _getDependentObjects(self):
+ return self.callback._getDependentObjects()
+
+
+class IDLMethodOverload:
+ """
+ A class that represents a single overload of a WebIDL method. This is not
+ quite the same as an element of the "effective overload set" in the spec,
+ because separate IDLMethodOverloads are not created based on arguments being
+ optional. Rather, when multiple methods have the same name, there is an
+ IDLMethodOverload for each one, all hanging off an IDLMethod representing
+ the full set of overloads.
+ """
+ def __init__(self, returnType, arguments, location):
+ self.returnType = returnType
+ # Clone the list of arguments, just in case
+ self.arguments = list(arguments)
+ self.location = location
+
+ def _getDependentObjects(self):
+ deps = set(self.arguments)
+ deps.add(self.returnType)
+ return deps
+
+
+class IDLMethod(IDLInterfaceMember, IDLScope):
+
+ Special = enum(
+ 'Getter',
+ 'Setter',
+ 'Creator',
+ 'Deleter',
+ 'LegacyCaller',
+ base=IDLInterfaceMember.Special
+ )
+
+ NamedOrIndexed = enum(
+ 'Neither',
+ 'Named',
+ 'Indexed'
+ )
+
+ def __init__(self, location, identifier, returnType, arguments,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, specialType=NamedOrIndexed.Neither,
+ legacycaller=False, stringifier=False, jsonifier=False,
+ maplikeOrSetlikeOrIterable=None):
+ # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
+ IDLInterfaceMember.__init__(self, location, identifier,
+ IDLInterfaceMember.Tags.Method)
+
+ self._hasOverloads = False
+
+ assert isinstance(returnType, IDLType)
+
+ # self._overloads is a list of IDLMethodOverloads
+ self._overloads = [IDLMethodOverload(returnType, arguments, location)]
+
+ assert isinstance(static, bool)
+ self._static = static
+ assert isinstance(getter, bool)
+ self._getter = getter
+ assert isinstance(setter, bool)
+ self._setter = setter
+ assert isinstance(creator, bool)
+ self._creator = creator
+ assert isinstance(deleter, bool)
+ self._deleter = deleter
+ assert isinstance(legacycaller, bool)
+ self._legacycaller = legacycaller
+ assert isinstance(stringifier, bool)
+ self._stringifier = stringifier
+ assert isinstance(jsonifier, bool)
+ self._jsonifier = jsonifier
+ assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
+ self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
+ self._specialType = specialType
+ self._unforgeable = False
+ self.dependsOn = "Everything"
+ self.affects = "Everything"
+ self.aliases = []
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static operation must not be 'prototype'",
+ [location])
+
+ self.assertSignatureConstraints()
+
+ def __str__(self):
+ return "Method '%s'" % self.identifier
+
+ def assertSignatureConstraints(self):
+ if self._getter or self._deleter:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ arguments = overload.arguments
+ assert len(arguments) == 1
+ assert (arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or
+ arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long])
+ assert not arguments[0].optional and not arguments[0].variadic
+ assert not self._getter or not overload.returnType.isVoid()
+
+ if self._setter or self._creator:
+ assert len(self._overloads) == 1
+ arguments = self._overloads[0].arguments
+ assert len(arguments) == 2
+ assert (arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or
+ arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long])
+ assert not arguments[0].optional and not arguments[0].variadic
+ assert not arguments[1].optional and not arguments[1].variadic
+
+ if self._stringifier:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ assert len(overload.arguments) == 0
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
+
+ if self._jsonifier:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ assert len(overload.arguments) == 0
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
+
+ def isStatic(self):
+ return self._static
+
+ def forceStatic(self):
+ self._static = True
+
+ def isGetter(self):
+ return self._getter
+
+ def isSetter(self):
+ return self._setter
+
+ def isCreator(self):
+ return self._creator
+
+ def isDeleter(self):
+ return self._deleter
+
+ def isNamed(self):
+ assert (self._specialType == IDLMethod.NamedOrIndexed.Named or
+ self._specialType == IDLMethod.NamedOrIndexed.Indexed)
+ return self._specialType == IDLMethod.NamedOrIndexed.Named
+
+ def isIndexed(self):
+ assert (self._specialType == IDLMethod.NamedOrIndexed.Named or
+ self._specialType == IDLMethod.NamedOrIndexed.Indexed)
+ return self._specialType == IDLMethod.NamedOrIndexed.Indexed
+
+ def isLegacycaller(self):
+ return self._legacycaller
+
+ def isStringifier(self):
+ return self._stringifier
+
+ def isJsonifier(self):
+ return self._jsonifier
+
+ def isMaplikeOrSetlikeOrIterableMethod(self):
+ """
+ True if this method was generated as part of a
+ maplike/setlike/etc interface (e.g. has/get methods)
+ """
+ return self.maplikeOrSetlikeOrIterable is not None
+
+ def isSpecial(self):
+ return (self.isGetter() or
+ self.isSetter() or
+ self.isCreator() or
+ self.isDeleter() or
+ self.isLegacycaller() or
+ self.isStringifier() or
+ self.isJsonifier())
+
+ def hasOverloads(self):
+ return self._hasOverloads
+
+ def isIdentifierLess(self):
+ """
+ True if the method name started with __, and if the method is not a
+ maplike/setlike method. Interfaces with maplike/setlike will generate
+ methods starting with __ for chrome only backing object access in JS
+ implemented interfaces, so while these functions use what is considered
+ an non-identifier name, they actually DO have an identifier.
+ """
+ return (self.identifier.name[:2] == "__" and
+ not self.isMaplikeOrSetlikeOrIterableMethod())
+
+ def resolve(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+ IDLScope.__init__(self, self.location, parentScope, self.identifier)
+ for (returnType, arguments) in self.signatures():
+ for argument in arguments:
+ argument.resolve(self)
+
+ def addOverload(self, method):
+ assert len(method._overloads) == 1
+
+ if self._extendedAttrDict != method ._extendedAttrDict:
+ raise WebIDLError("Extended attributes differ on different "
+ "overloads of %s" % method.identifier,
+ [self.location, method.location])
+
+ self._overloads.extend(method._overloads)
+
+ self._hasOverloads = True
+
+ if self.isStatic() != method.isStatic():
+ raise WebIDLError("Overloaded identifier %s appears with different values of the 'static' attribute" % method.identifier,
+ [method.location])
+
+ if self.isLegacycaller() != method.isLegacycaller():
+ raise WebIDLError("Overloaded identifier %s appears with different values of the 'legacycaller' attribute" % method.identifier,
+ [method.location])
+
+ # Can't overload special things!
+ assert not self.isGetter()
+ assert not method.isGetter()
+ assert not self.isSetter()
+ assert not method.isSetter()
+ assert not self.isCreator()
+ assert not method.isCreator()
+ assert not self.isDeleter()
+ assert not method.isDeleter()
+ assert not self.isStringifier()
+ assert not method.isStringifier()
+ assert not self.isJsonifier()
+ assert not method.isJsonifier()
+
+ return self
+
+ def signatures(self):
+ return [(overload.returnType, overload.arguments) for overload in
+ self._overloads]
+
+ def finish(self, scope):
+ IDLInterfaceMember.finish(self, scope)
+
+ for overload in self._overloads:
+ returnType = overload.returnType
+ if not returnType.isComplete():
+ returnType = returnType.complete(scope)
+ assert not isinstance(returnType, IDLUnresolvedType)
+ assert not isinstance(returnType, IDLTypedefType)
+ assert not isinstance(returnType.name, IDLUnresolvedIdentifier)
+ overload.returnType = returnType
+
+ for argument in overload.arguments:
+ if not argument.isComplete():
+ argument.complete(scope)
+ assert argument.type.isComplete()
+
+ # Now compute various information that will be used by the
+ # WebIDL overload resolution algorithm.
+ self.maxArgCount = max(len(s[1]) for s in self.signatures())
+ self.allowedArgCounts = [i for i in range(self.maxArgCount+1)
+ if len(self.signaturesForArgCount(i)) != 0]
+
+ def validate(self):
+ IDLInterfaceMember.validate(self)
+
+ # Make sure our overloads are properly distinguishable and don't have
+ # different argument types before the distinguishing args.
+ for argCount in self.allowedArgCounts:
+ possibleOverloads = self.overloadsForArgCount(argCount)
+ if len(possibleOverloads) == 1:
+ continue
+ distinguishingIndex = self.distinguishingIndexForArgCount(argCount)
+ for idx in range(distinguishingIndex):
+ firstSigType = possibleOverloads[0].arguments[idx].type
+ for overload in possibleOverloads[1:]:
+ if overload.arguments[idx].type != firstSigType:
+ raise WebIDLError(
+ "Signatures for method '%s' with %d arguments have "
+ "different types of arguments at index %d, which "
+ "is before distinguishing index %d" %
+ (self.identifier.name, argCount, idx,
+ distinguishingIndex),
+ [self.location, overload.location])
+
+ overloadWithPromiseReturnType = None
+ overloadWithoutPromiseReturnType = None
+ for overload in self._overloads:
+ returnType = overload.returnType
+ if not returnType.unroll().isExposedInAllOf(self.exposureSet):
+ raise WebIDLError("Overload returns a type that is not exposed "
+ "everywhere where the method is exposed",
+ [overload.location])
+
+ variadicArgument = None
+
+ arguments = overload.arguments
+ for (idx, argument) in enumerate(arguments):
+ assert argument.type.isComplete()
+
+ if ((argument.type.isDictionary() and
+ argument.type.inner.canBeEmpty())or
+ (argument.type.isUnion() and
+ argument.type.unroll().hasPossiblyEmptyDictionaryType())):
+ # Optional dictionaries and unions containing optional
+ # dictionaries at the end of the list or followed by
+ # optional arguments must be optional.
+ if (not argument.optional and
+ all(arg.optional for arg in arguments[idx+1:])):
+ raise WebIDLError("Dictionary argument or union "
+ "argument containing a dictionary "
+ "not followed by a required argument "
+ "must be optional",
+ [argument.location])
+
+ # An argument cannot be a Nullable Dictionary
+ if argument.type.nullable():
+ raise WebIDLError("An argument cannot be a nullable "
+ "dictionary or nullable union "
+ "containing a dictionary",
+ [argument.location])
+
+ # Only the last argument can be variadic
+ if variadicArgument:
+ raise WebIDLError("Variadic argument is not last argument",
+ [variadicArgument.location])
+ if argument.variadic:
+ variadicArgument = argument
+
+ if returnType.isPromise():
+ overloadWithPromiseReturnType = overload
+ else:
+ overloadWithoutPromiseReturnType = overload
+
+ # Make sure either all our overloads return Promises or none do
+ if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType:
+ raise WebIDLError("We have overloads with both Promise and "
+ "non-Promise return types",
+ [overloadWithPromiseReturnType.location,
+ overloadWithoutPromiseReturnType.location])
+
+ if overloadWithPromiseReturnType and self._legacycaller:
+ raise WebIDLError("May not have a Promise return type for a "
+ "legacycaller.",
+ [overloadWithPromiseReturnType.location])
+
+ if self.getExtendedAttribute("StaticClassOverride") and not \
+ (self.identifier.scope.isJSImplemented() and self.isStatic()):
+ raise WebIDLError("StaticClassOverride can be applied to static"
+ " methods on JS-implemented classes only.",
+ [self.location])
+
+ def overloadsForArgCount(self, argc):
+ return [overload for overload in self._overloads if
+ len(overload.arguments) == argc or
+ (len(overload.arguments) > argc and
+ all(arg.optional for arg in overload.arguments[argc:])) or
+ (len(overload.arguments) < argc and
+ len(overload.arguments) > 0 and
+ overload.arguments[-1].variadic)]
+
+ def signaturesForArgCount(self, argc):
+ return [(overload.returnType, overload.arguments) for overload
+ in self.overloadsForArgCount(argc)]
+
+ def locationsForArgCount(self, argc):
+ return [overload.location for overload in self.overloadsForArgCount(argc)]
+
+ def distinguishingIndexForArgCount(self, argc):
+ def isValidDistinguishingIndex(idx, signatures):
+ for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]):
+ for (secondRetval, secondArgs) in signatures[firstSigIndex+1:]:
+ if idx < len(firstArgs):
+ firstType = firstArgs[idx].type
+ else:
+ assert(firstArgs[-1].variadic)
+ firstType = firstArgs[-1].type
+ if idx < len(secondArgs):
+ secondType = secondArgs[idx].type
+ else:
+ assert(secondArgs[-1].variadic)
+ secondType = secondArgs[-1].type
+ if not firstType.isDistinguishableFrom(secondType):
+ return False
+ return True
+ signatures = self.signaturesForArgCount(argc)
+ for idx in range(argc):
+ if isValidDistinguishingIndex(idx, signatures):
+ return idx
+ # No valid distinguishing index. Time to throw
+ locations = self.locationsForArgCount(argc)
+ raise WebIDLError("Signatures with %d arguments for method '%s' are not "
+ "distinguishable" % (argc, self.identifier.name),
+ locations)
+
+ def handleExtendedAttribute(self, attr):
+ identifier = attr.identifier()
+ if identifier == "GetterThrows":
+ raise WebIDLError("Methods must not be flagged as "
+ "[GetterThrows]",
+ [attr.location, self.location])
+ elif identifier == "SetterThrows":
+ raise WebIDLError("Methods must not be flagged as "
+ "[SetterThrows]",
+ [attr.location, self.location])
+ elif identifier == "Unforgeable":
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "methods", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject":
+ raise WebIDLError("Methods must not be flagged as [SameObject]",
+ [attr.location, self.location])
+ elif identifier == "Constant":
+ raise WebIDLError("Methods must not be flagged as [Constant]",
+ [attr.location, self.location])
+ elif identifier == "PutForwards":
+ raise WebIDLError("Only attributes support [PutForwards]",
+ [attr.location, self.location])
+ elif identifier == "LenientSetter":
+ raise WebIDLError("Only attributes support [LenientSetter]",
+ [attr.location, self.location])
+ elif identifier == "LenientFloat":
+ # This is called before we've done overload resolution
+ assert len(self.signatures()) == 1
+ sig = self.signatures()[0]
+ if not sig[0].isVoid():
+ raise WebIDLError("[LenientFloat] used on a non-void method",
+ [attr.location, self.location])
+ if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
+ raise WebIDLError("[LenientFloat] used on an operation with no "
+ "restricted float type arguments",
+ [attr.location, self.location])
+ elif identifier == "Exposed":
+ convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
+ elif (identifier == "CrossOriginCallable" or
+ identifier == "WebGLHandlesContextLoss"):
+ # Known no-argument attributes.
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif identifier == "Pure":
+ if not attr.noArguments():
+ raise WebIDLError("[Pure] must take no arguments",
+ [attr.location])
+ self._setDependsOn("DOMState")
+ self._setAffects("Nothing")
+ elif identifier == "Affects":
+ if not attr.hasValue():
+ raise WebIDLError("[Affects] takes an identifier",
+ [attr.location])
+ self._setAffects(attr.value())
+ elif identifier == "DependsOn":
+ if not attr.hasValue():
+ raise WebIDLError("[DependsOn] takes an identifier",
+ [attr.location])
+ self._setDependsOn(attr.value())
+ elif identifier == "Alias":
+ if not attr.hasValue():
+ raise WebIDLError("[Alias] takes an identifier or string",
+ [attr.location])
+ self._addAlias(attr.value())
+ elif identifier == "UseCounter":
+ if self.isSpecial():
+ raise WebIDLError("[UseCounter] must not be used on a special "
+ "operation",
+ [attr.location, self.location])
+ elif identifier == "Unscopable":
+ if not attr.noArguments():
+ raise WebIDLError("[Unscopable] must take no arguments",
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[Unscopable] is only allowed on non-static "
+ "attributes and operations",
+ [attr.location, self.location])
+ elif (identifier == "Throws" or
+ identifier == "NewObject" or
+ identifier == "ChromeOnly" or
+ identifier == "UnsafeInPrerendering" or
+ identifier == "Pref" or
+ identifier == "Deprecated" or
+ identifier == "Func" or
+ identifier == "SecureContext" or
+ identifier == "BinaryName" or
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "StaticClassOverride"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on method" % identifier,
+ [attr.location])
+ IDLInterfaceMember.handleExtendedAttribute(self, attr)
+
+ def returnsPromise(self):
+ return self._overloads[0].returnType.isPromise()
+
+ def isUnforgeable(self):
+ return self._unforgeable
+
+ def _getDependentObjects(self):
+ deps = set()
+ for overload in self._overloads:
+ deps.update(overload._getDependentObjects())
+ return deps
+
+
+class IDLImplementsStatement(IDLObject):
+ def __init__(self, location, implementor, implementee):
+ IDLObject.__init__(self, location)
+ self.implementor = implementor
+ self.implementee = implementee
+ self._finished = False
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
+ assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
+ implementor = self.implementor.finish(scope)
+ implementee = self.implementee.finish(scope)
+ # NOTE: we depend on not setting self.implementor and
+ # self.implementee here to keep track of the original
+ # locations.
+ if not isinstance(implementor, IDLInterface):
+ raise WebIDLError("Left-hand side of 'implements' is not an "
+ "interface",
+ [self.implementor.location])
+ if implementor.isCallback():
+ raise WebIDLError("Left-hand side of 'implements' is a callback "
+ "interface",
+ [self.implementor.location])
+ if not isinstance(implementee, IDLInterface):
+ raise WebIDLError("Right-hand side of 'implements' is not an "
+ "interface",
+ [self.implementee.location])
+ if implementee.isCallback():
+ raise WebIDLError("Right-hand side of 'implements' is a callback "
+ "interface",
+ [self.implementee.location])
+ implementor.addImplementedInterface(implementee)
+ self.implementor = implementor
+ self.implementee = implementee
+
+ def validate(self):
+ pass
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+
+class IDLExtendedAttribute(IDLObject):
+ """
+ A class to represent IDL extended attributes so we can give them locations
+ """
+ def __init__(self, location, tuple):
+ IDLObject.__init__(self, location)
+ self._tuple = tuple
+
+ def identifier(self):
+ return self._tuple[0]
+
+ def noArguments(self):
+ return len(self._tuple) == 1
+
+ def hasValue(self):
+ return len(self._tuple) >= 2 and isinstance(self._tuple[1], str)
+
+ def value(self):
+ assert(self.hasValue())
+ return self._tuple[1]
+
+ def hasArgs(self):
+ return (len(self._tuple) == 2 and isinstance(self._tuple[1], list) or
+ len(self._tuple) == 3)
+
+ def args(self):
+ assert(self.hasArgs())
+ # Our args are our last element
+ return self._tuple[-1]
+
+ def listValue(self):
+ """
+ Backdoor for storing random data in _extendedAttrDict
+ """
+ return list(self._tuple)[1:]
+
+# Parser
+
+
+class Tokenizer(object):
+ tokens = [
+ "INTEGER",
+ "FLOATLITERAL",
+ "IDENTIFIER",
+ "STRING",
+ "WHITESPACE",
+ "OTHER"
+ ]
+
+ def t_FLOATLITERAL(self, t):
+ r'(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN'
+ t.value = float(t.value)
+ return t
+
+ def t_INTEGER(self, t):
+ r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
+ try:
+ # Can't use int(), because that doesn't handle octal properly.
+ t.value = parseInt(t.value)
+ except:
+ raise WebIDLError("Invalid integer literal",
+ [Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos,
+ filename=self._filename)])
+ return t
+
+ def t_IDENTIFIER(self, t):
+ r'[A-Z_a-z][0-9A-Z_a-z-]*'
+ t.type = self.keywords.get(t.value, 'IDENTIFIER')
+ return t
+
+ def t_STRING(self, t):
+ r'"[^"]*"'
+ t.value = t.value[1:-1]
+ return t
+
+ def t_WHITESPACE(self, t):
+ r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+'
+ pass
+
+ def t_ELLIPSIS(self, t):
+ r'\.\.\.'
+ t.type = self.keywords.get(t.value)
+ return t
+
+ def t_OTHER(self, t):
+ r'[^\t\n\r 0-9A-Z_a-z]'
+ t.type = self.keywords.get(t.value, 'OTHER')
+ return t
+
+ keywords = {
+ "module": "MODULE",
+ "interface": "INTERFACE",
+ "partial": "PARTIAL",
+ "dictionary": "DICTIONARY",
+ "exception": "EXCEPTION",
+ "enum": "ENUM",
+ "callback": "CALLBACK",
+ "typedef": "TYPEDEF",
+ "implements": "IMPLEMENTS",
+ "const": "CONST",
+ "null": "NULL",
+ "true": "TRUE",
+ "false": "FALSE",
+ "serializer": "SERIALIZER",
+ "stringifier": "STRINGIFIER",
+ "jsonifier": "JSONIFIER",
+ "unrestricted": "UNRESTRICTED",
+ "attribute": "ATTRIBUTE",
+ "readonly": "READONLY",
+ "inherit": "INHERIT",
+ "static": "STATIC",
+ "getter": "GETTER",
+ "setter": "SETTER",
+ "creator": "CREATOR",
+ "deleter": "DELETER",
+ "legacycaller": "LEGACYCALLER",
+ "optional": "OPTIONAL",
+ "...": "ELLIPSIS",
+ "::": "SCOPE",
+ "Date": "DATE",
+ "DOMString": "DOMSTRING",
+ "ByteString": "BYTESTRING",
+ "USVString": "USVSTRING",
+ "any": "ANY",
+ "boolean": "BOOLEAN",
+ "byte": "BYTE",
+ "double": "DOUBLE",
+ "float": "FLOAT",
+ "long": "LONG",
+ "object": "OBJECT",
+ "octet": "OCTET",
+ "Promise": "PROMISE",
+ "required": "REQUIRED",
+ "sequence": "SEQUENCE",
+ "MozMap": "MOZMAP",
+ "short": "SHORT",
+ "unsigned": "UNSIGNED",
+ "void": "VOID",
+ ":": "COLON",
+ ";": "SEMICOLON",
+ "{": "LBRACE",
+ "}": "RBRACE",
+ "(": "LPAREN",
+ ")": "RPAREN",
+ "[": "LBRACKET",
+ "]": "RBRACKET",
+ "?": "QUESTIONMARK",
+ ",": "COMMA",
+ "=": "EQUALS",
+ "<": "LT",
+ ">": "GT",
+ "ArrayBuffer": "ARRAYBUFFER",
+ "SharedArrayBuffer": "SHAREDARRAYBUFFER",
+ "or": "OR",
+ "maplike": "MAPLIKE",
+ "setlike": "SETLIKE",
+ "iterable": "ITERABLE",
+ "namespace": "NAMESPACE"
+ }
+
+ tokens.extend(keywords.values())
+
+ def t_error(self, t):
+ raise WebIDLError("Unrecognized Input",
+ [Location(lexer=self.lexer,
+ lineno=self.lexer.lineno,
+ lexpos=self.lexer.lexpos,
+ filename=self.filename)])
+
+ def __init__(self, outputdir, lexer=None):
+ if lexer:
+ self.lexer = lexer
+ else:
+ self.lexer = lex.lex(object=self,
+ outputdir=outputdir,
+ lextab='webidllex',
+ reflags=re.DOTALL)
+
+
+class SqueakyCleanLogger(object):
+ errorWhitelist = [
+ # Web IDL defines the WHITESPACE token, but doesn't actually
+ # use it ... so far.
+ "Token 'WHITESPACE' defined, but not used",
+ # And that means we have an unused token
+ "There is 1 unused token",
+ # Web IDL defines a OtherOrComma rule that's only used in
+ # ExtendedAttributeInner, which we don't use yet.
+ "Rule 'OtherOrComma' defined, but not used",
+ # And an unused rule
+ "There is 1 unused rule",
+ # And the OtherOrComma grammar symbol is unreachable.
+ "Symbol 'OtherOrComma' is unreachable",
+ # Which means the Other symbol is unreachable.
+ "Symbol 'Other' is unreachable",
+ ]
+
+ def __init__(self):
+ self.errors = []
+
+ def debug(self, msg, *args, **kwargs):
+ pass
+ info = debug
+
+ def warning(self, msg, *args, **kwargs):
+ if msg == "%s:%d: Rule %r defined, but not used" or \
+ msg == "%s:%d: Rule '%s' defined, but not used":
+ # Munge things so we don't have to hardcode filenames and
+ # line numbers in our whitelist.
+ whitelistmsg = "Rule %r defined, but not used"
+ whitelistargs = args[2:]
+ else:
+ whitelistmsg = msg
+ whitelistargs = args
+ if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist:
+ self.errors.append(msg % args)
+ error = warning
+
+ def reportGrammarErrors(self):
+ if self.errors:
+ raise WebIDLError("\n".join(self.errors), [])
+
+
+class Parser(Tokenizer):
+ def getLocation(self, p, i):
+ return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
+
+ def globalScope(self):
+ return self._globalScope
+
+ # The p_Foo functions here must match the WebIDL spec's grammar.
+ # It's acceptable to split things at '|' boundaries.
+ def p_Definitions(self, p):
+ """
+ Definitions : ExtendedAttributeList Definition Definitions
+ """
+ if p[2]:
+ p[0] = [p[2]]
+ p[2].addExtendedAttributes(p[1])
+ else:
+ assert not p[1]
+ p[0] = []
+
+ p[0].extend(p[3])
+
+ def p_DefinitionsEmpty(self, p):
+ """
+ Definitions :
+ """
+ p[0] = []
+
+ def p_Definition(self, p):
+ """
+ Definition : CallbackOrInterface
+ | Namespace
+ | Partial
+ | Dictionary
+ | Exception
+ | Enum
+ | Typedef
+ | ImplementsStatement
+ """
+ p[0] = p[1]
+ assert p[1] # We might not have implemented something ...
+
+ def p_CallbackOrInterfaceCallback(self, p):
+ """
+ CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ """
+ if p[2].isInterface():
+ assert isinstance(p[2], IDLInterface)
+ p[2].setCallback(True)
+
+ p[0] = p[2]
+
+ def p_CallbackOrInterfaceInterface(self, p):
+ """
+ CallbackOrInterface : Interface
+ """
+ p[0] = p[1]
+
+ def p_CallbackRestOrInterface(self, p):
+ """
+ CallbackRestOrInterface : CallbackRest
+ | Interface
+ """
+ assert p[1]
+ p[0] = p[1]
+
+ def handleNonPartialObject(self, location, identifier, constructor,
+ constructorArgs, nonPartialArgs):
+ """
+ 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.
+ """
+ # 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:
+ 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
+
+ # 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):
+ """
+ Interface : INTERFACE IDENTIFIER SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+
+ try:
+ if self.globalScope()._lookupIdentifier(identifier):
+ p[0] = self.globalScope()._lookupIdentifier(identifier)
+ if not isinstance(p[0], IDLExternalInterface):
+ raise WebIDLError("Name collision between external "
+ "interface declaration for identifier "
+ "%s and %s" % (identifier.name, p[0]),
+ [location, p[0].location])
+ return
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
+ pass
+
+ p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
+
+ def p_Namespace(self, p):
+ """
+ Namespace : 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.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()
+
+ nonPartialObject = None
+ try:
+ 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 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):
+ """
+ Inheritance : COLON ScopedName
+ """
+ p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
+
+ def p_InheritanceEmpty(self, p):
+ """
+ Inheritance :
+ """
+ pass
+
+ def p_InterfaceMembers(self, p):
+ """
+ InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
+ """
+ p[0] = [p[2]] if p[2] else []
+
+ assert not p[1] or p[2]
+ p[2].addExtendedAttributes(p[1])
+
+ p[0].extend(p[3])
+
+ def p_InterfaceMembersEmpty(self, p):
+ """
+ InterfaceMembers :
+ """
+ p[0] = []
+
+ def p_InterfaceMember(self, p):
+ """
+ InterfaceMember : Const
+ | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
+ """
+ p[0] = p[1]
+
+ def p_Dictionary(self, p):
+ """
+ Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ members = p[5]
+ p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
+
+ def p_DictionaryMembers(self, p):
+ """
+ DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
+ |
+ """
+ if len(p) == 1:
+ # 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):
+ """
+ DictionaryMember : Required Type IDENTIFIER Default SEMICOLON
+ """
+ # These quack a lot like optional 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,
+ dictionaryMember=True)
+
+ def p_Default(self, p):
+ """
+ Default : EQUALS DefaultValue
+ |
+ """
+ if len(p) > 1:
+ p[0] = p[2]
+ else:
+ p[0] = None
+
+ def p_DefaultValue(self, p):
+ """
+ DefaultValue : ConstValue
+ | LBRACKET RBRACKET
+ """
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ assert len(p) == 3 # Must be []
+ p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
+
+ def p_Exception(self, p):
+ """
+ Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON
+ """
+ pass
+
+ def p_Enum(self, p):
+ """
+ Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+
+ values = p[4]
+ assert values
+ p[0] = IDLEnum(location, self.globalScope(), identifier, values)
+
+ def p_EnumValueList(self, p):
+ """
+ EnumValueList : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListComma(self, p):
+ """
+ EnumValueListComma : COMMA EnumValueListString
+ """
+ p[0] = p[2]
+
+ def p_EnumValueListCommaEmpty(self, p):
+ """
+ EnumValueListComma :
+ """
+ p[0] = []
+
+ def p_EnumValueListString(self, p):
+ """
+ EnumValueListString : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListStringEmpty(self, p):
+ """
+ EnumValueListString :
+ """
+ p[0] = []
+
+ def p_CallbackRest(self, p):
+ """
+ CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+ p[0] = IDLCallback(self.getLocation(p, 1), self.globalScope(),
+ identifier, p[3], p[5])
+
+ def p_ExceptionMembers(self, p):
+ """
+ ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
+ |
+ """
+ pass
+
+ def p_Typedef(self, p):
+ """
+ Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
+ """
+ typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(),
+ p[2], p[3])
+ p[0] = typedef
+
+ def p_ImplementsStatement(self, p):
+ """
+ ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
+ """
+ assert(p[2] == "implements")
+ implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
+ implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
+ p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
+ implementee)
+
+ def p_Const(self, p):
+ """
+ Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
+ """
+ location = self.getLocation(p, 1)
+ type = p[2]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
+ value = p[5]
+ p[0] = IDLConst(location, identifier, type, value)
+
+ def p_ConstValueBoolean(self, p):
+ """
+ ConstValue : BooleanLiteral
+ """
+ location = self.getLocation(p, 1)
+ booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean]
+ p[0] = IDLValue(location, booleanType, p[1])
+
+ def p_ConstValueInteger(self, p):
+ """
+ ConstValue : INTEGER
+ """
+ location = self.getLocation(p, 1)
+
+ # We don't know ahead of time what type the integer literal is.
+ # Determine the smallest type it could possibly fit in and use that.
+ integerType = matchIntegerValueToType(p[1])
+ if integerType is None:
+ raise WebIDLError("Integer literal out of range", [location])
+
+ p[0] = IDLValue(location, integerType, p[1])
+
+ def p_ConstValueFloat(self, p):
+ """
+ ConstValue : FLOATLITERAL
+ """
+ location = self.getLocation(p, 1)
+ p[0] = IDLValue(location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1])
+
+ def p_ConstValueString(self, p):
+ """
+ ConstValue : STRING
+ """
+ location = self.getLocation(p, 1)
+ stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
+ p[0] = IDLValue(location, stringType, p[1])
+
+ def p_ConstValueNull(self, p):
+ """
+ ConstValue : NULL
+ """
+ p[0] = IDLNullValue(self.getLocation(p, 1))
+
+ def p_BooleanLiteralTrue(self, p):
+ """
+ BooleanLiteral : TRUE
+ """
+ p[0] = True
+
+ def p_BooleanLiteralFalse(self, p):
+ """
+ BooleanLiteral : FALSE
+ """
+ p[0] = False
+
+ def p_AttributeOrOperationOrMaplikeOrSetlikeOrIterable(self, p):
+ """
+ AttributeOrOperationOrMaplikeOrSetlikeOrIterable : Attribute
+ | Maplike
+ | Setlike
+ | Iterable
+ | Operation
+ """
+ p[0] = p[1]
+
+ def p_Iterable(self, p):
+ """
+ Iterable : ITERABLE LT Type GT SEMICOLON
+ | ITERABLE LT Type COMMA Type GT SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(location, "__iterable",
+ allowDoubleUnderscore=True)
+ if (len(p) > 6):
+ keyType = p[3]
+ valueType = p[5]
+ else:
+ keyType = None
+ valueType = p[3]
+
+ p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
+
+ def p_Setlike(self, p):
+ """
+ Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
+ """
+ readonly = p[1]
+ maplikeOrSetlikeType = p[2]
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(location, "__setlike",
+ allowDoubleUnderscore=True)
+ keyType = p[4]
+ valueType = keyType
+ p[0] = IDLMaplikeOrSetlike(location, identifier, maplikeOrSetlikeType,
+ readonly, keyType, valueType)
+
+ def p_Maplike(self, p):
+ """
+ Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON
+ """
+ readonly = p[1]
+ maplikeOrSetlikeType = p[2]
+ location = self.getLocation(p, 2)
+ identifier = IDLUnresolvedIdentifier(location, "__maplike",
+ allowDoubleUnderscore=True)
+ keyType = p[4]
+ valueType = p[6]
+ p[0] = IDLMaplikeOrSetlike(location, identifier, maplikeOrSetlikeType,
+ readonly, keyType, valueType)
+
+ def p_AttributeWithQualifier(self, p):
+ """
+ Attribute : Qualifier AttributeRest
+ """
+ static = IDLInterfaceMember.Special.Static in p[1]
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly,
+ static=static, stringifier=stringifier)
+
+ def p_AttributeInherited(self, p):
+ """
+ Attribute : INHERIT AttributeRest
+ """
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=True)
+
+ def p_Attribute(self, p):
+ """
+ Attribute : AttributeRest
+ """
+ (location, identifier, type, readonly) = p[1]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=False)
+
+ def p_AttributeRest(self, p):
+ """
+ AttributeRest : ReadOnly ATTRIBUTE Type AttributeName SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ readonly = p[1]
+ t = p[3]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4])
+ p[0] = (location, identifier, t, readonly)
+
+ def p_ReadOnly(self, p):
+ """
+ ReadOnly : READONLY
+ """
+ p[0] = True
+
+ def p_ReadOnlyEmpty(self, p):
+ """
+ ReadOnly :
+ """
+ p[0] = False
+
+ def p_Operation(self, p):
+ """
+ Operation : Qualifiers OperationRest
+ """
+ qualifiers = p[1]
+
+ # Disallow duplicates in the qualifier set
+ if not len(set(qualifiers)) == len(qualifiers):
+ raise WebIDLError("Duplicate qualifiers are not allowed",
+ [self.getLocation(p, 1)])
+
+ static = IDLInterfaceMember.Special.Static in p[1]
+ # If static is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not static or len(qualifiers) == 1
+
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ # If stringifier is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not stringifier or len(qualifiers) == 1
+
+ getter = True if IDLMethod.Special.Getter in p[1] else False
+ setter = True if IDLMethod.Special.Setter in p[1] else False
+ creator = True if IDLMethod.Special.Creator in p[1] else False
+ deleter = True if IDLMethod.Special.Deleter in p[1] else False
+ legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False
+
+ if getter or deleter:
+ if setter or creator:
+ raise WebIDLError("getter and deleter are incompatible with setter and creator",
+ [self.getLocation(p, 1)])
+
+ (returnType, identifier, arguments) = p[2]
+
+ assert isinstance(returnType, IDLType)
+
+ specialType = IDLMethod.NamedOrIndexed.Neither
+
+ if getter or deleter:
+ if len(arguments) != 1:
+ raise WebIDLError("%s has wrong number of arguments" %
+ ("getter" if getter else "deleter"),
+ [self.getLocation(p, 2)])
+ argType = arguments[0].type
+ if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
+ specialType = IDLMethod.NamedOrIndexed.Named
+ elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
+ specialType = IDLMethod.NamedOrIndexed.Indexed
+ if deleter:
+ raise WebIDLError("There is no such thing as an indexed deleter.",
+ [self.getLocation(p, 1)])
+ else:
+ raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
+ ("getter" if getter else "deleter"),
+ [arguments[0].location])
+ if arguments[0].optional or arguments[0].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("getter" if getter else "deleter",
+ "optional" if arguments[0].optional else "variadic"),
+ [arguments[0].location])
+ if getter:
+ if returnType.isVoid():
+ raise WebIDLError("getter cannot have void return type",
+ [self.getLocation(p, 2)])
+ if setter or creator:
+ if len(arguments) != 2:
+ raise WebIDLError("%s has wrong number of arguments" %
+ ("setter" if setter else "creator"),
+ [self.getLocation(p, 2)])
+ argType = arguments[0].type
+ if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
+ specialType = IDLMethod.NamedOrIndexed.Named
+ elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
+ specialType = IDLMethod.NamedOrIndexed.Indexed
+ else:
+ raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
+ ("setter" if setter else "creator"),
+ [arguments[0].location])
+ if arguments[0].optional or arguments[0].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("setter" if setter else "creator",
+ "optional" if arguments[0].optional else "variadic"),
+ [arguments[0].location])
+ if arguments[1].optional or arguments[1].variadic:
+ raise WebIDLError("%s cannot have %s argument" %
+ ("setter" if setter else "creator",
+ "optional" if arguments[1].optional else "variadic"),
+ [arguments[1].location])
+
+ if stringifier:
+ if len(arguments) != 0:
+ raise WebIDLError("stringifier has wrong number of arguments",
+ [self.getLocation(p, 2)])
+ if not returnType.isDOMString():
+ raise WebIDLError("stringifier must have DOMString return type",
+ [self.getLocation(p, 2)])
+
+ # identifier might be None. This is only permitted for special methods.
+ if not identifier:
+ if (not getter and not setter and not creator and
+ not deleter and not legacycaller and not stringifier):
+ raise WebIDLError("Identifier required for non-special methods",
+ [self.getLocation(p, 2)])
+
+ location = BuiltinLocation("<auto-generated-identifier>")
+ identifier = IDLUnresolvedIdentifier(
+ location,
+ "__%s%s%s%s%s%s%s" %
+ ("named" if specialType == IDLMethod.NamedOrIndexed.Named else
+ "indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "",
+ "getter" if getter else "",
+ "setter" if setter else "",
+ "deleter" if deleter else "",
+ "creator" if creator else "",
+ "legacycaller" if legacycaller else "",
+ "stringifier" if stringifier else ""),
+ allowDoubleUnderscore=True)
+
+ method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
+ static=static, getter=getter, setter=setter, creator=creator,
+ deleter=deleter, specialType=specialType,
+ legacycaller=legacycaller, stringifier=stringifier)
+ p[0] = method
+
+ def p_Stringifier(self, p):
+ """
+ Operation : STRINGIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__stringifier",
+ allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
+ arguments=[],
+ stringifier=True)
+ p[0] = method
+
+ def p_Jsonifier(self, p):
+ """
+ Operation : JSONIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__jsonifier", allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.object],
+ arguments=[],
+ jsonifier=True)
+ p[0] = method
+
+ def p_QualifierStatic(self, p):
+ """
+ Qualifier : STATIC
+ """
+ p[0] = [IDLInterfaceMember.Special.Static]
+
+ def p_QualifierStringifier(self, p):
+ """
+ Qualifier : STRINGIFIER
+ """
+ p[0] = [IDLInterfaceMember.Special.Stringifier]
+
+ def p_Qualifiers(self, p):
+ """
+ Qualifiers : Qualifier
+ | Specials
+ """
+ p[0] = p[1]
+
+ def p_Specials(self, p):
+ """
+ Specials : Special Specials
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_SpecialsEmpty(self, p):
+ """
+ Specials :
+ """
+ p[0] = []
+
+ def p_SpecialGetter(self, p):
+ """
+ Special : GETTER
+ """
+ p[0] = IDLMethod.Special.Getter
+
+ def p_SpecialSetter(self, p):
+ """
+ Special : SETTER
+ """
+ p[0] = IDLMethod.Special.Setter
+
+ def p_SpecialCreator(self, p):
+ """
+ Special : CREATOR
+ """
+ p[0] = IDLMethod.Special.Creator
+
+ def p_SpecialDeleter(self, p):
+ """
+ Special : DELETER
+ """
+ p[0] = IDLMethod.Special.Deleter
+
+ def p_SpecialLegacyCaller(self, p):
+ """
+ Special : LEGACYCALLER
+ """
+ p[0] = IDLMethod.Special.LegacyCaller
+
+ def p_OperationRest(self, p):
+ """
+ OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
+ """
+ p[0] = (p[1], p[2], p[4])
+
+ def p_OptionalIdentifier(self, p):
+ """
+ OptionalIdentifier : IDENTIFIER
+ """
+ p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ def p_OptionalIdentifierEmpty(self, p):
+ """
+ OptionalIdentifier :
+ """
+ pass
+
+ def p_ArgumentList(self, p):
+ """
+ ArgumentList : Argument Arguments
+ """
+ p[0] = [p[1]] if p[1] else []
+ p[0].extend(p[2])
+
+ def p_ArgumentListEmpty(self, p):
+ """
+ ArgumentList :
+ """
+ p[0] = []
+
+ def p_Arguments(self, p):
+ """
+ Arguments : COMMA Argument Arguments
+ """
+ p[0] = [p[2]] if p[2] else []
+ p[0].extend(p[3])
+
+ def p_ArgumentsEmpty(self, p):
+ """
+ Arguments :
+ """
+ p[0] = []
+
+ def p_Argument(self, p):
+ """
+ Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default
+ """
+ t = p[3]
+ assert isinstance(t, IDLType)
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
+
+ optional = p[2]
+ variadic = p[4]
+ defaultValue = p[6]
+
+ 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, 5), identifier, t, optional, defaultValue, variadic)
+ p[0].addExtendedAttributes(p[1])
+
+ def p_ArgumentName(self, p):
+ """
+ ArgumentName : IDENTIFIER
+ | ATTRIBUTE
+ | CALLBACK
+ | CONST
+ | CREATOR
+ | DELETER
+ | DICTIONARY
+ | ENUM
+ | EXCEPTION
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | ITERABLE
+ | LEGACYCALLER
+ | MAPLIKE
+ | PARTIAL
+ | REQUIRED
+ | SERIALIZER
+ | SETLIKE
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TYPEDEF
+ | UNRESTRICTED
+ | NAMESPACE
+ """
+ p[0] = p[1]
+
+ def p_AttributeName(self, p):
+ """
+ AttributeName : IDENTIFIER
+ | REQUIRED
+ """
+ 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
+ """
+ p[0] = True
+
+ def p_EllipsisEmpty(self, p):
+ """
+ Ellipsis :
+ """
+ p[0] = False
+
+ def p_ExceptionMember(self, p):
+ """
+ ExceptionMember : Const
+ | ExceptionField
+ """
+ pass
+
+ def p_ExceptionField(self, p):
+ """
+ ExceptionField : Type IDENTIFIER SEMICOLON
+ """
+ pass
+
+ def p_ExtendedAttributeList(self, p):
+ """
+ ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET
+ """
+ p[0] = [p[2]]
+ if p[3]:
+ p[0].extend(p[3])
+
+ def p_ExtendedAttributeListEmpty(self, p):
+ """
+ ExtendedAttributeList :
+ """
+ p[0] = []
+
+ def p_ExtendedAttribute(self, p):
+ """
+ ExtendedAttribute : ExtendedAttributeNoArgs
+ | ExtendedAttributeArgList
+ | ExtendedAttributeIdent
+ | ExtendedAttributeNamedArgList
+ | ExtendedAttributeIdentList
+ """
+ p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1])
+
+ def p_ExtendedAttributeEmpty(self, p):
+ """
+ ExtendedAttribute :
+ """
+ pass
+
+ def p_ExtendedAttributes(self, p):
+ """
+ ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes
+ """
+ p[0] = [p[2]] if p[2] else []
+ p[0].extend(p[3])
+
+ def p_ExtendedAttributesEmpty(self, p):
+ """
+ ExtendedAttributes :
+ """
+ p[0] = []
+
+ def p_Other(self, p):
+ """
+ Other : INTEGER
+ | FLOATLITERAL
+ | IDENTIFIER
+ | STRING
+ | OTHER
+ | ELLIPSIS
+ | COLON
+ | SCOPE
+ | SEMICOLON
+ | LT
+ | EQUALS
+ | GT
+ | QUESTIONMARK
+ | DATE
+ | DOMSTRING
+ | BYTESTRING
+ | USVSTRING
+ | ANY
+ | ATTRIBUTE
+ | BOOLEAN
+ | BYTE
+ | LEGACYCALLER
+ | CONST
+ | CREATOR
+ | DELETER
+ | DOUBLE
+ | EXCEPTION
+ | FALSE
+ | FLOAT
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | LONG
+ | MODULE
+ | NULL
+ | OBJECT
+ | OCTET
+ | OPTIONAL
+ | SEQUENCE
+ | MOZMAP
+ | SETTER
+ | SHORT
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TRUE
+ | TYPEDEF
+ | UNSIGNED
+ | VOID
+ """
+ pass
+
+ def p_OtherOrComma(self, p):
+ """
+ OtherOrComma : Other
+ | COMMA
+ """
+ pass
+
+ def p_TypeSingleType(self, p):
+ """
+ Type : SingleType
+ """
+ p[0] = p[1]
+
+ def p_TypeUnionType(self, p):
+ """
+ Type : UnionType Null
+ """
+ p[0] = self.handleNullable(p[1], p[2])
+
+ def p_SingleTypeNonAnyType(self, p):
+ """
+ SingleType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_SingleTypeAnyType(self, p):
+ """
+ SingleType : ANY
+ """
+ p[0] = BuiltinTypes[IDLBuiltinType.Types.any]
+
+ def p_UnionType(self, p):
+ """
+ UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
+ """
+ types = [p[2], p[4]]
+ types.extend(p[5])
+ p[0] = IDLUnionType(self.getLocation(p, 1), types)
+
+ def p_UnionMemberTypeNonAnyType(self, p):
+ """
+ UnionMemberType : NonAnyType
+ """
+ p[0] = p[1]
+
+ def p_UnionMemberType(self, p):
+ """
+ UnionMemberType : UnionType Null
+ """
+ p[0] = self.handleNullable(p[1], p[2])
+
+ def p_UnionMemberTypes(self, p):
+ """
+ UnionMemberTypes : OR UnionMemberType UnionMemberTypes
+ """
+ p[0] = [p[2]]
+ p[0].extend(p[3])
+
+ def p_UnionMemberTypesEmpty(self, p):
+ """
+ UnionMemberTypes :
+ """
+ p[0] = []
+
+ def p_NonAnyType(self, p):
+ """
+ NonAnyType : PrimitiveOrStringType Null
+ | ARRAYBUFFER Null
+ | SHAREDARRAYBUFFER Null
+ | OBJECT Null
+ """
+ if p[1] == "object":
+ type = BuiltinTypes[IDLBuiltinType.Types.object]
+ elif p[1] == "ArrayBuffer":
+ type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
+ elif p[1] == "SharedArrayBuffer":
+ type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
+ else:
+ type = BuiltinTypes[p[1]]
+
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_NonAnyTypeSequenceType(self, p):
+ """
+ NonAnyType : SEQUENCE LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLSequenceType(self.getLocation(p, 1), innerType)
+ p[0] = self.handleNullable(type, p[5])
+
+ # Note: Promise<void> is allowed, so we want to parametrize on
+ # ReturnType, not Type. Also, we want this to end up picking up
+ # the Promise interface for now, hence the games with IDLUnresolvedType.
+ def p_NonAnyTypePromiseType(self, p):
+ """
+ NonAnyType : PROMISE LT ReturnType GT Null
+ """
+ innerType = p[3]
+ promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
+ type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
+ p[0] = self.handleNullable(type, p[5])
+
+ def p_NonAnyTypeMozMapType(self, p):
+ """
+ NonAnyType : MOZMAP LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLMozMapType(self.getLocation(p, 1), innerType)
+ p[0] = self.handleNullable(type, p[5])
+
+ def p_NonAnyTypeScopedName(self, p):
+ """
+ NonAnyType : ScopedName Null
+ """
+ assert isinstance(p[1], IDLUnresolvedIdentifier)
+
+ if p[1].name == "Promise":
+ raise WebIDLError("Promise used without saying what it's "
+ "parametrized over",
+ [self.getLocation(p, 1)])
+
+ type = None
+
+ try:
+ if self.globalScope()._lookupIdentifier(p[1]):
+ obj = self.globalScope()._lookupIdentifier(p[1])
+ assert not obj.isType()
+ if obj.isTypedef():
+ type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
+ obj.identifier.name)
+ elif obj.isCallback() and not obj.isInterface():
+ type = IDLCallbackType(self.getLocation(p, 1), obj)
+ else:
+ type = IDLWrapperType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleNullable(type, p[2])
+ return
+ except:
+ pass
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_NonAnyTypeDate(self, p):
+ """
+ NonAnyType : DATE Null
+ """
+ p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date],
+ p[2])
+
+ def p_ConstType(self, p):
+ """
+ ConstType : PrimitiveOrStringType Null
+ """
+ type = BuiltinTypes[p[1]]
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_ConstTypeIdentifier(self, p):
+ """
+ ConstType : IDENTIFIER Null
+ """
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
+ p[0] = self.handleNullable(type, p[2])
+
+ def p_PrimitiveOrStringTypeUint(self, p):
+ """
+ PrimitiveOrStringType : UnsignedIntegerType
+ """
+ p[0] = p[1]
+
+ def p_PrimitiveOrStringTypeBoolean(self, p):
+ """
+ PrimitiveOrStringType : BOOLEAN
+ """
+ p[0] = IDLBuiltinType.Types.boolean
+
+ def p_PrimitiveOrStringTypeByte(self, p):
+ """
+ PrimitiveOrStringType : BYTE
+ """
+ p[0] = IDLBuiltinType.Types.byte
+
+ def p_PrimitiveOrStringTypeOctet(self, p):
+ """
+ PrimitiveOrStringType : OCTET
+ """
+ p[0] = IDLBuiltinType.Types.octet
+
+ def p_PrimitiveOrStringTypeFloat(self, p):
+ """
+ PrimitiveOrStringType : FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.float
+
+ def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_float
+
+ def p_PrimitiveOrStringTypeDouble(self, p):
+ """
+ PrimitiveOrStringType : DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.double
+
+ def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_double
+
+ def p_PrimitiveOrStringTypeDOMString(self, p):
+ """
+ PrimitiveOrStringType : DOMSTRING
+ """
+ p[0] = IDLBuiltinType.Types.domstring
+
+ def p_PrimitiveOrStringTypeBytestring(self, p):
+ """
+ PrimitiveOrStringType : BYTESTRING
+ """
+ p[0] = IDLBuiltinType.Types.bytestring
+
+ def p_PrimitiveOrStringTypeUSVString(self, p):
+ """
+ PrimitiveOrStringType : USVSTRING
+ """
+ p[0] = IDLBuiltinType.Types.usvstring
+
+ def p_UnsignedIntegerTypeUnsigned(self, p):
+ """
+ UnsignedIntegerType : UNSIGNED IntegerType
+ """
+ # Adding one to a given signed integer type gets you the unsigned type:
+ p[0] = p[2] + 1
+
+ def p_UnsignedIntegerType(self, p):
+ """
+ UnsignedIntegerType : IntegerType
+ """
+ p[0] = p[1]
+
+ def p_IntegerTypeShort(self, p):
+ """
+ IntegerType : SHORT
+ """
+ p[0] = IDLBuiltinType.Types.short
+
+ def p_IntegerTypeLong(self, p):
+ """
+ IntegerType : LONG OptionalLong
+ """
+ if p[2]:
+ p[0] = IDLBuiltinType.Types.long_long
+ else:
+ p[0] = IDLBuiltinType.Types.long
+
+ def p_OptionalLong(self, p):
+ """
+ OptionalLong : LONG
+ """
+ p[0] = True
+
+ def p_OptionalLongEmpty(self, p):
+ """
+ OptionalLong :
+ """
+ p[0] = False
+
+ def p_Null(self, p):
+ """
+ Null : QUESTIONMARK
+ |
+ """
+ if len(p) > 1:
+ p[0] = self.getLocation(p, 1)
+ else:
+ p[0] = None
+
+ def p_ReturnTypeType(self, p):
+ """
+ ReturnType : Type
+ """
+ p[0] = p[1]
+
+ def p_ReturnTypeVoid(self, p):
+ """
+ ReturnType : VOID
+ """
+ p[0] = BuiltinTypes[IDLBuiltinType.Types.void]
+
+ def p_ScopedName(self, p):
+ """
+ ScopedName : AbsoluteScopedName
+ | RelativeScopedName
+ """
+ p[0] = p[1]
+
+ def p_AbsoluteScopedName(self, p):
+ """
+ AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts
+ """
+ assert False
+ pass
+
+ def p_RelativeScopedName(self, p):
+ """
+ RelativeScopedName : IDENTIFIER ScopedNameParts
+ """
+ assert not p[2] # Not implemented!
+
+ p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
+
+ def p_ScopedNameParts(self, p):
+ """
+ ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts
+ """
+ assert False
+ pass
+
+ def p_ScopedNamePartsEmpty(self, p):
+ """
+ ScopedNameParts :
+ """
+ p[0] = None
+
+ def p_ExtendedAttributeNoArgs(self, p):
+ """
+ ExtendedAttributeNoArgs : IDENTIFIER
+ """
+ p[0] = (p[1],)
+
+ def p_ExtendedAttributeArgList(self, p):
+ """
+ ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN
+ """
+ p[0] = (p[1], p[3])
+
+ def p_ExtendedAttributeIdent(self, p):
+ """
+ ExtendedAttributeIdent : IDENTIFIER EQUALS STRING
+ | IDENTIFIER EQUALS IDENTIFIER
+ """
+ p[0] = (p[1], p[3])
+
+ def p_ExtendedAttributeNamedArgList(self, p):
+ """
+ ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN
+ """
+ p[0] = (p[1], p[3], p[5])
+
+ def p_ExtendedAttributeIdentList(self, p):
+ """
+ ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN
+ """
+ p[0] = (p[1], p[4])
+
+ def p_IdentifierList(self, p):
+ """
+ IdentifierList : IDENTIFIER Identifiers
+ """
+ idents = list(p[2])
+ idents.insert(0, p[1])
+ p[0] = idents
+
+ def p_IdentifiersList(self, p):
+ """
+ Identifiers : COMMA IDENTIFIER Identifiers
+ """
+ idents = list(p[3])
+ idents.insert(0, p[2])
+ p[0] = idents
+
+ def p_IdentifiersEmpty(self, p):
+ """
+ Identifiers :
+ """
+ p[0] = []
+
+ def p_error(self, p):
+ if not p:
+ raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both",
+ [self._filename])
+ else:
+ raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
+
+ def __init__(self, outputdir='', lexer=None):
+ Tokenizer.__init__(self, outputdir, lexer)
+
+ logger = SqueakyCleanLogger()
+ 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.
+ # Note that we _don't_ set _globalScope.primaryGlobalAttr,
+ # so we'll still be able to detect multiple PrimaryGlobal extended attributes.
+ self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
+ self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
+ self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
+ # And we add the special-cased "System" global name, which
+ # doesn't have any corresponding interfaces.
+ self._globalScope.globalNames.add("System")
+ self._globalScope.globalNameMapping["System"].add("BackstagePass")
+ self._installBuiltins(self._globalScope)
+ self._productions = []
+
+ self._filename = "<builtin>"
+ self.lexer.input(Parser._builtins)
+ self._filename = None
+
+ self.parser.parse(lexer=self.lexer, tracking=True)
+
+ def _installBuiltins(self, scope):
+ assert isinstance(scope, IDLScope)
+
+ # xrange omits the last value.
+ for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
+ builtin = BuiltinTypes[x]
+ name = builtin.name
+ typedef = IDLTypedef(BuiltinLocation("<builtin type>"), scope, builtin, name)
+
+ @ staticmethod
+ def handleNullable(type, questionMarkLocation):
+ if questionMarkLocation is not None:
+ type = IDLNullableType(questionMarkLocation, type)
+
+ return type
+
+ def parse(self, t, filename=None):
+ self.lexer.input(t)
+
+ # for tok in iter(self.lexer.token, None):
+ # print tok
+
+ self._filename = filename
+ self._productions.extend(self.parser.parse(lexer=self.lexer, tracking=True))
+ self._filename = None
+
+ def finish(self):
+ # If we have interfaces that are iterable, create their
+ # iterator interfaces and add them to the productions array.
+ interfaceStatements = []
+ for p in self._productions:
+ if isinstance(p, IDLInterface):
+ interfaceStatements.append(p)
+ if p.identifier.name == "Navigator":
+ navigatorInterface = p
+
+ iterableIteratorIface = None
+ for iface in interfaceStatements:
+ navigatorProperty = iface.getNavigatorProperty()
+ if navigatorProperty:
+ # We're generating a partial interface to add a readonly
+ # property to the Navigator interface for every interface
+ # annotated with NavigatorProperty.
+ partialInterface = IDLPartialInterfaceOrNamespace(
+ iface.location,
+ IDLUnresolvedIdentifier(iface.location, "Navigator"),
+ [ navigatorProperty ],
+ navigatorInterface)
+ self._productions.append(partialInterface)
+
+ iterable = None
+ # We haven't run finish() on the interface yet, so we don't know
+ # whether our interface is maplike/setlike/iterable or not. This
+ # means we have to loop through the members to see if we have an
+ # iterable member.
+ for m in iface.members:
+ if isinstance(m, IDLIterable):
+ iterable = m
+ break
+ if iterable and iterable.isPairIterator():
+ def simpleExtendedAttr(str):
+ return IDLExtendedAttribute(iface.location, (str, ))
+ nextMethod = IDLMethod(
+ iface.location,
+ IDLUnresolvedIdentifier(iface.location, "next"),
+ BuiltinTypes[IDLBuiltinType.Types.object], [])
+ nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
+ itr_ident = IDLUnresolvedIdentifier(iface.location,
+ iface.identifier.name + "Iterator")
+ itr_iface = IDLInterface(iface.location, self.globalScope(),
+ itr_ident, None, [nextMethod],
+ isKnownNonPartial=True)
+ itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")])
+ # Make sure the exposure set for the iterator interface is the
+ # same as the exposure set for the iterable interface, because
+ # we're going to generate methods on the iterable that return
+ # instances of the iterator.
+ itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
+ # Always append generated iterable interfaces after the
+ # interface they're a member of, otherwise nativeType generation
+ # won't work correctly.
+ itr_iface.iterableInterface = iface
+ self._productions.append(itr_iface)
+ iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
+
+ # Then, finish all the IDLImplementsStatements. In particular, we
+ # have to make sure we do those before we do the IDLInterfaces.
+ # XXX khuey hates this bit and wants to nuke it from orbit.
+ implementsStatements = [p for p in self._productions if
+ isinstance(p, IDLImplementsStatement)]
+ otherStatements = [p for p in self._productions if
+ not isinstance(p, IDLImplementsStatement)]
+ for production in implementsStatements:
+ production.finish(self.globalScope())
+ for production in otherStatements:
+ production.finish(self.globalScope())
+
+ # Do any post-finish validation we need to do
+ for production in self._productions:
+ production.validate()
+
+ # De-duplicate self._productions, without modifying its order.
+ seen = set()
+ result = []
+ for p in self._productions:
+ if p not in seen:
+ seen.add(p)
+ result.append(p)
+ return result
+
+ def reset(self):
+ return Parser(lexer=self.lexer)
+
+ # Builtin IDL defined by WebIDL
+ _builtins = """
+ typedef unsigned long long DOMTimeStamp;
+ typedef (ArrayBufferView or ArrayBuffer) BufferSource;
+ """
+
+
+def main():
+ # Parse arguments.
+ from optparse import OptionParser
+ usageString = "usage: %prog [options] files"
+ o = OptionParser(usage=usageString)
+ o.add_option("--cachedir", dest='cachedir', default=None,
+ help="Directory in which to cache lex/parse tables.")
+ o.add_option("--verbose-errors", action='store_true', default=False,
+ help="When an error happens, display the Python traceback.")
+ (options, args) = o.parse_args()
+
+ if len(args) < 1:
+ o.error(usageString)
+
+ fileList = args
+ baseDir = os.getcwd()
+
+ # Parse the WebIDL.
+ parser = Parser(options.cachedir)
+ try:
+ for filename in fileList:
+ fullPath = os.path.normpath(os.path.join(baseDir, filename))
+ f = open(fullPath, 'rb')
+ lines = f.readlines()
+ f.close()
+ print fullPath
+ parser.parse(''.join(lines), fullPath)
+ parser.finish()
+ except WebIDLError, e:
+ if options.verbose_errors:
+ traceback.print_exc()
+ else:
+ print e
+
+if __name__ == '__main__':
+ main()
diff --git a/dom/bindings/parser/runtests.py b/dom/bindings/parser/runtests.py
new file mode 100644
index 000000000..33bb59f8a
--- /dev/null
+++ b/dom/bindings/parser/runtests.py
@@ -0,0 +1,108 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, sys
+import glob
+import argparse
+import traceback
+import WebIDL
+
+class TestHarness(object):
+ def __init__(self, test, verbose):
+ self.test = test
+ self.verbose = verbose
+ self.printed_intro = False
+ self.passed = 0
+ self.failures = []
+
+ def start(self):
+ if self.verbose:
+ self.maybe_print_intro()
+
+ def finish(self):
+ if self.verbose or self.printed_intro:
+ print "Finished test %s" % self.test
+
+ def maybe_print_intro(self):
+ if not self.printed_intro:
+ print "Starting test %s" % self.test
+ self.printed_intro = True
+
+ def test_pass(self, msg):
+ self.passed += 1
+ if self.verbose:
+ print "TEST-PASS | %s" % msg
+
+ def test_fail(self, msg):
+ self.maybe_print_intro()
+ self.failures.append(msg)
+ print "TEST-UNEXPECTED-FAIL | %s" % msg
+
+ def ok(self, condition, msg):
+ if condition:
+ self.test_pass(msg)
+ else:
+ self.test_fail(msg)
+
+ def check(self, a, b, msg):
+ if a == b:
+ self.test_pass(msg)
+ else:
+ self.test_fail(msg + " | Got %s expected %s" % (a, b))
+
+def run_tests(tests, verbose):
+ testdir = os.path.join(os.path.dirname(__file__), 'tests')
+ if not tests:
+ tests = glob.iglob(os.path.join(testdir, "*.py"))
+ sys.path.append(testdir)
+
+ all_passed = 0
+ failed_tests = []
+
+ for test in tests:
+ (testpath, ext) = os.path.splitext(os.path.basename(test))
+ _test = __import__(testpath, globals(), locals(), ['WebIDLTest'])
+
+ harness = TestHarness(test, verbose)
+ harness.start()
+ try:
+ _test.WebIDLTest.__call__(WebIDL.Parser(), harness)
+ except Exception, ex:
+ harness.test_fail("Unhandled exception in test %s: %s" %
+ (testpath, ex))
+ traceback.print_exc()
+ finally:
+ harness.finish()
+ all_passed += harness.passed
+ if harness.failures:
+ failed_tests.append((test, harness.failures))
+
+ if verbose or failed_tests:
+ print
+ print 'Result summary:'
+ print 'Successful: %d' % all_passed
+ print 'Unexpected: %d' % \
+ sum(len(failures) for _, failures in failed_tests)
+ for test, failures in failed_tests:
+ print '%s:' % test
+ for failure in failures:
+ print 'TEST-UNEXPECTED-FAIL | %s' % failure
+
+def get_parser():
+ usage = """%(prog)s [OPTIONS] [TESTS]
+ Where TESTS are relative to the tests directory."""
+ parser = argparse.ArgumentParser(usage=usage)
+ parser.add_argument('-q', '--quiet', action='store_false', dest='verbose',
+ help="Don't print passing tests.", default=None)
+ parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
+ help="Run tests in verbose mode.")
+ parser.add_argument('tests', nargs="*", help="Tests to run")
+ return parser
+
+if __name__ == '__main__':
+ parser = get_parser()
+ args = parser.parse_args()
+ if args.verbose is None:
+ args.verbose = True
+ run_tests(args.tests, verbose=args.verbose)
diff --git a/dom/bindings/parser/tests/test_any_null.py b/dom/bindings/parser/tests/test_any_null.py
new file mode 100644
index 000000000..e3b690bf6
--- /dev/null
+++ b/dom/bindings/parser/tests/test_any_null.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DoubleNull {
+ attribute any? foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_argument_identifier_conflicts.py b/dom/bindings/parser/tests/test_argument_identifier_conflicts.py
new file mode 100644
index 000000000..eb1f6d3c9
--- /dev/null
+++ b/dom/bindings/parser/tests/test_argument_identifier_conflicts.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface ArgumentIdentifierConflict {
+ void foo(boolean arg1, boolean arg1);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_argument_novoid.py b/dom/bindings/parser/tests/test_argument_novoid.py
new file mode 100644
index 000000000..ef8c2229a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_argument_novoid.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface VoidArgument1 {
+ void foo(void arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_arraybuffer.py b/dom/bindings/parser/tests/test_arraybuffer.py
new file mode 100644
index 000000000..4a96c0ff5
--- /dev/null
+++ b/dom/bindings/parser/tests/test_arraybuffer.py
@@ -0,0 +1,81 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestArrayBuffer {
+ attribute ArrayBuffer bufferAttr;
+ void bufferMethod(ArrayBuffer arg1, ArrayBuffer? arg2, sequence<ArrayBuffer> arg3);
+
+ attribute ArrayBufferView viewAttr;
+ void viewMethod(ArrayBufferView arg1, ArrayBufferView? arg2, sequence<ArrayBufferView> arg3);
+
+ attribute Int8Array int8ArrayAttr;
+ void int8ArrayMethod(Int8Array arg1, Int8Array? arg2, sequence<Int8Array> arg3);
+
+ attribute Uint8Array uint8ArrayAttr;
+ void uint8ArrayMethod(Uint8Array arg1, Uint8Array? arg2, sequence<Uint8Array> arg3);
+
+ attribute Uint8ClampedArray uint8ClampedArrayAttr;
+ void uint8ClampedArrayMethod(Uint8ClampedArray arg1, Uint8ClampedArray? arg2, sequence<Uint8ClampedArray> arg3);
+
+ attribute Int16Array int16ArrayAttr;
+ void int16ArrayMethod(Int16Array arg1, Int16Array? arg2, sequence<Int16Array> arg3);
+
+ attribute Uint16Array uint16ArrayAttr;
+ void uint16ArrayMethod(Uint16Array arg1, Uint16Array? arg2, sequence<Uint16Array> arg3);
+
+ attribute Int32Array int32ArrayAttr;
+ void int32ArrayMethod(Int32Array arg1, Int32Array? arg2, sequence<Int32Array> arg3);
+
+ attribute Uint32Array uint32ArrayAttr;
+ void uint32ArrayMethod(Uint32Array arg1, Uint32Array? arg2, sequence<Uint32Array> arg3);
+
+ attribute Float32Array float32ArrayAttr;
+ void float32ArrayMethod(Float32Array arg1, Float32Array? arg2, sequence<Float32Array> arg3);
+
+ attribute Float64Array float64ArrayAttr;
+ void float64ArrayMethod(Float64Array arg1, Float64Array? arg2, sequence<Float64Array> arg3);
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(True, "TestArrayBuffer interface parsed without error")
+ harness.check(len(iface.members), 22, "Interface should have twenty two members")
+
+ members = iface.members
+
+ def checkStuff(attr, method, t):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Expect an IDLAttribute")
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Expect an IDLMethod")
+
+ harness.check(str(attr.type), t, "Expect an ArrayBuffer type")
+ harness.ok(attr.type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ (retType, arguments) = method.signatures()[0]
+ harness.ok(retType.isVoid(), "Should have a void return type")
+ harness.check(len(arguments), 3, "Expect 3 arguments")
+
+ harness.check(str(arguments[0].type), t, "Expect an ArrayBuffer type")
+ harness.ok(arguments[0].type.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[1].type), t + "OrNull", "Expect an ArrayBuffer type")
+ harness.ok(arguments[1].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+ harness.check(str(arguments[2].type), t + "Sequence", "Expect an ArrayBuffer type")
+ harness.ok(arguments[2].type.inner.isSpiderMonkeyInterface(), "Should test as a js interface")
+
+
+ checkStuff(members[0], members[1], "ArrayBuffer")
+ checkStuff(members[2], members[3], "ArrayBufferView")
+ checkStuff(members[4], members[5], "Int8Array")
+ checkStuff(members[6], members[7], "Uint8Array")
+ checkStuff(members[8], members[9], "Uint8ClampedArray")
+ checkStuff(members[10], members[11], "Int16Array")
+ checkStuff(members[12], members[13], "Uint16Array")
+ checkStuff(members[14], members[15], "Int32Array")
+ checkStuff(members[16], members[17], "Uint32Array")
+ checkStuff(members[18], members[19], "Float32Array")
+ checkStuff(members[20], members[21], "Float64Array")
diff --git a/dom/bindings/parser/tests/test_attr.py b/dom/bindings/parser/tests/test_attr.py
new file mode 100644
index 000000000..ad7aabc19
--- /dev/null
+++ b/dom/bindings/parser/tests/test_attr.py
@@ -0,0 +1,177 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ testData = [("::TestAttr%s::b", "b", "Byte%s", False),
+ ("::TestAttr%s::rb", "rb", "Byte%s", True),
+ ("::TestAttr%s::o", "o", "Octet%s", False),
+ ("::TestAttr%s::ro", "ro", "Octet%s", True),
+ ("::TestAttr%s::s", "s", "Short%s", False),
+ ("::TestAttr%s::rs", "rs", "Short%s", True),
+ ("::TestAttr%s::us", "us", "UnsignedShort%s", False),
+ ("::TestAttr%s::rus", "rus", "UnsignedShort%s", True),
+ ("::TestAttr%s::l", "l", "Long%s", False),
+ ("::TestAttr%s::rl", "rl", "Long%s", True),
+ ("::TestAttr%s::ul", "ul", "UnsignedLong%s", False),
+ ("::TestAttr%s::rul", "rul", "UnsignedLong%s", True),
+ ("::TestAttr%s::ll", "ll", "LongLong%s", False),
+ ("::TestAttr%s::rll", "rll", "LongLong%s", True),
+ ("::TestAttr%s::ull", "ull", "UnsignedLongLong%s", False),
+ ("::TestAttr%s::rull", "rull", "UnsignedLongLong%s", True),
+ ("::TestAttr%s::str", "str", "String%s", False),
+ ("::TestAttr%s::rstr", "rstr", "String%s", True),
+ ("::TestAttr%s::obj", "obj", "Object%s", False),
+ ("::TestAttr%s::robj", "robj", "Object%s", True),
+ ("::TestAttr%s::object", "object", "Object%s", False),
+ ("::TestAttr%s::f", "f", "Float%s", False),
+ ("::TestAttr%s::rf", "rf", "Float%s", True)]
+
+ parser.parse("""
+ interface TestAttr {
+ attribute byte b;
+ readonly attribute byte rb;
+ attribute octet o;
+ readonly attribute octet ro;
+ attribute short s;
+ readonly attribute short rs;
+ attribute unsigned short us;
+ readonly attribute unsigned short rus;
+ attribute long l;
+ readonly attribute long rl;
+ attribute unsigned long ul;
+ readonly attribute unsigned long rul;
+ attribute long long ll;
+ readonly attribute long long rll;
+ attribute unsigned long long ull;
+ readonly attribute unsigned long long rull;
+ attribute DOMString str;
+ readonly attribute DOMString rstr;
+ attribute object obj;
+ readonly attribute object robj;
+ attribute object _object;
+ attribute float f;
+ readonly attribute float rf;
+ };
+
+ interface TestAttrNullable {
+ attribute byte? b;
+ readonly attribute byte? rb;
+ attribute octet? o;
+ readonly attribute octet? ro;
+ attribute short? s;
+ readonly attribute short? rs;
+ attribute unsigned short? us;
+ readonly attribute unsigned short? rus;
+ attribute long? l;
+ readonly attribute long? rl;
+ attribute unsigned long? ul;
+ readonly attribute unsigned long? rul;
+ attribute long long? ll;
+ readonly attribute long long? rll;
+ attribute unsigned long long? ull;
+ readonly attribute unsigned long long? rull;
+ attribute DOMString? str;
+ readonly attribute DOMString? rstr;
+ attribute object? obj;
+ readonly attribute object? robj;
+ attribute object? _object;
+ attribute float? f;
+ readonly attribute float? rf;
+ };
+ """)
+
+ results = parser.finish()
+
+ def checkAttr(attr, QName, name, type, readonly):
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Attr is an Attr")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(attr.identifier.QName(), QName, "Attr has the right QName")
+ harness.check(attr.identifier.name, name, "Attr has the right name")
+ harness.check(str(attr.type), type, "Attr has the right type")
+ harness.check(attr.readonly, readonly, "Attr's readonly state is correct")
+
+ harness.ok(True, "TestAttr interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttr", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttr", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "", name, type % "", readonly)
+
+ iface = results[1]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestAttrNullable", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestAttrNullable", "Interface has the right name")
+ harness.check(len(iface.members), len(testData), "Expect %s members" % len(testData))
+
+ attrs = iface.members
+
+ for i in range(len(attrs)):
+ data = testData[i]
+ attr = attrs[i]
+ (QName, name, type, readonly) = data
+ checkAttr(attr, QName % "Nullable", name, type % "OrNull", readonly)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterThrows] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterThrows] on readonly attributes")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throw] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should spell [Throws] correctly")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SameObject] readonly attribute boolean foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SameObject] on attributes not of interface type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SameObject] readonly attribute A foo;
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(not threw, "Should allow [SameObject] on attributes of interface type")
diff --git a/dom/bindings/parser/tests/test_attr_sequence_type.py b/dom/bindings/parser/tests/test_attr_sequence_type.py
new file mode 100644
index 000000000..fb1b97812
--- /dev/null
+++ b/dom/bindings/parser/tests/test_attr_sequence_type.py
@@ -0,0 +1,67 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrSequenceType {
+ attribute sequence<object> foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Attribute type must not be a sequence type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrUnionWithSequenceType {
+ attribute (sequence<object> or DOMString) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union with a sequence member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrNullableUnionWithSequenceType {
+ attribute (sequence<object>? or DOMString) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union with a nullable sequence "
+ "member type")
+
+ parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface AttrUnionWithUnionWithSequenceType {
+ attribute ((sequence<object> or DOMString) or AttrUnionWithUnionWithSequenceType) foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Attribute type must not be a union type with a union member "
+ "type that has a sequence member type")
diff --git a/dom/bindings/parser/tests/test_builtin_filename.py b/dom/bindings/parser/tests/test_builtin_filename.py
new file mode 100644
index 000000000..631e52eba
--- /dev/null
+++ b/dom/bindings/parser/tests/test_builtin_filename.py
@@ -0,0 +1,11 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Test {
+ attribute long b;
+ };
+ """);
+
+ attr = parser.finish()[0].members[0]
+ harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type')
diff --git a/dom/bindings/parser/tests/test_builtins.py b/dom/bindings/parser/tests/test_builtins.py
new file mode 100644
index 000000000..f8563fc2d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_builtins.py
@@ -0,0 +1,41 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestBuiltins {
+ attribute boolean b;
+ attribute byte s8;
+ attribute octet u8;
+ attribute short s16;
+ attribute unsigned short u16;
+ attribute long s32;
+ attribute unsigned long u32;
+ attribute long long s64;
+ attribute unsigned long long u64;
+ attribute DOMTimeStamp ts;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestBuiltins interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::TestBuiltins", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestBuiltins", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 10, "Should be one production")
+
+ names = ["b", "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64", "ts"]
+ types = ["Boolean", "Byte", "Octet", "Short", "UnsignedShort", "Long", "UnsignedLong", "LongLong", "UnsignedLongLong", "UnsignedLongLong"]
+ for i in range(10):
+ attr = members[i]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestBuiltins::" + names[i], "Attr has correct QName")
+ harness.check(attr.identifier.name, names[i], "Attr has correct name")
+ harness.check(str(attr.type), types[i], "Attr type is the correct name")
+ harness.ok(attr.type.isPrimitive(), "Should be a primitive type")
diff --git a/dom/bindings/parser/tests/test_bytestring.py b/dom/bindings/parser/tests/test_bytestring.py
new file mode 100644
index 000000000..fa83e9e2d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_bytestring.py
@@ -0,0 +1,99 @@
+# -*- coding: UTF-8 -*-
+
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestByteString {
+ attribute ByteString bs;
+ attribute DOMString ds;
+ };
+ """)
+
+ results = parser.finish();
+
+ harness.ok(True, "TestByteString interface parsed without error.")
+
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::TestByteString", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestByteString", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be two productions")
+
+ attr = members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestByteString::bs", "Attr has correct QName")
+ harness.check(attr.identifier.name, "bs", "Attr has correct name")
+ harness.check(str(attr.type), "ByteString", "Attr type is the correct name")
+ harness.ok(attr.type.isByteString(), "Should be ByteString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
+
+ # now check we haven't broken DOMStrings in the process.
+ attr = members[1]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestByteString::ds", "Attr has correct QName")
+ harness.check(attr.identifier.name, "ds", "Attr has correct name")
+ harness.check(str(attr.type), "String", "Attr type is the correct name")
+ harness.ok(attr.type.isDOMString(), "Should be DOMString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isByteString(), "Should be not be ByteString type")
+
+ # Cannot represent constant ByteString in IDL.
+ threw = False
+ try:
+ parser.parse("""
+ interface ConstByteString {
+ const ByteString foo = "hello"
+ };
+ """)
+ except WebIDL.WebIDLError:
+ threw = True
+ harness.ok(threw, "Should have thrown a WebIDL error for ByteString default in interface")
+
+ # Can have optional ByteStrings with default values
+ try:
+ parser.parse("""
+ interface OptionalByteString {
+ void passByteString(optional ByteString arg = "hello");
+ };
+ """)
+ results2 = parser.finish();
+ except WebIDL.WebIDLError as e:
+ harness.ok(False,
+ "Should not have thrown a WebIDL error for ByteString "
+ "default in dictionary. " + str(e))
+
+ # Can have a default ByteString value in a dictionary
+ try:
+ parser.parse("""
+ dictionary OptionalByteStringDict {
+ ByteString item = "some string";
+ };
+ """)
+ results3 = parser.finish();
+ except WebIDL.WebIDLError as e:
+ harness.ok(False,
+ "Should not have thrown a WebIDL error for ByteString "
+ "default in dictionary. " + str(e))
+
+ # Don't allow control characters in ByteString literals
+ threw = False
+ try:
+ parser.parse("""
+ dictionary OptionalByteStringDict2 {
+ ByteString item = "\x03";
+ };
+ """)
+ results4 = parser.finish()
+ except WebIDL.WebIDLError as e:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown a WebIDL error for invalid ByteString "
+ "default in dictionary")
diff --git a/dom/bindings/parser/tests/test_callback.py b/dom/bindings/parser/tests/test_callback.py
new file mode 100644
index 000000000..4dfda1c3c
--- /dev/null
+++ b/dom/bindings/parser/tests/test_callback.py
@@ -0,0 +1,34 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestCallback {
+ attribute CallbackType? listener;
+ };
+
+ callback CallbackType = boolean (unsigned long arg);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestCallback interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestCallback", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestCallback", "Interface has the right name")
+ harness.check(len(iface.members), 1, "Expect %s members" % 1)
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ harness.ok(attr.isAttr(), "Should be an attribute")
+ harness.ok(not attr.isMethod(), "Attr is not an method")
+ harness.ok(not attr.isConst(), "Attr is not a const")
+ harness.check(attr.identifier.QName(), "::TestCallback::listener", "Attr has the right QName")
+ harness.check(attr.identifier.name, "listener", "Attr has the right name")
+ t = attr.type
+ harness.ok(not isinstance(t, WebIDL.IDLWrapperType), "Attr has the right type")
+ harness.ok(isinstance(t, WebIDL.IDLNullableType), "Attr has the right type")
+ harness.ok(t.isCallback(), "Attr has the right type")
diff --git a/dom/bindings/parser/tests/test_callback_interface.py b/dom/bindings/parser/tests/test_callback_interface.py
new file mode 100644
index 000000000..e4789dae1
--- /dev/null
+++ b/dom/bindings/parser/tests/test_callback_interface.py
@@ -0,0 +1,94 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[0]
+
+ harness.ok(iface.isCallback(), "Interface should be a callback")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface : TestInterface {
+ attribute boolean bool;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-callback parent of callback interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface : TestCallbackInterface {
+ };
+ callback interface TestCallbackInterface {
+ attribute boolean bool;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow callback parent of non-callback interface")
+
+ parser = parser.reset()
+ parser.parse("""
+ callback interface TestCallbackInterface1 {
+ void foo();
+ };
+ callback interface TestCallbackInterface2 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ };
+ callback interface TestCallbackInterface3 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ static void bar();
+ };
+ callback interface TestCallbackInterface4 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ static void bar();
+ const long baz = 5;
+ };
+ callback interface TestCallbackInterface5 {
+ static attribute boolean bool;
+ void foo();
+ };
+ callback interface TestCallbackInterface6 {
+ void foo(DOMString arg);
+ void foo(TestCallbackInterface1 arg);
+ void bar();
+ };
+ callback interface TestCallbackInterface7 {
+ static attribute boolean bool;
+ };
+ callback interface TestCallbackInterface8 {
+ attribute boolean bool;
+ };
+ callback interface TestCallbackInterface9 : TestCallbackInterface1 {
+ void foo();
+ };
+ callback interface TestCallbackInterface10 : TestCallbackInterface1 {
+ void bar();
+ };
+ """)
+ results = parser.finish()
+ for (i, iface) in enumerate(results):
+ harness.check(iface.isSingleOperationInterface(), i < 4,
+ "Interface %s should be a single operation interface" %
+ iface.identifier.name)
diff --git a/dom/bindings/parser/tests/test_conditional_dictionary_member.py b/dom/bindings/parser/tests/test_conditional_dictionary_member.py
new file mode 100644
index 000000000..433b7e501
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_const.py b/dom/bindings/parser/tests/test_const.py
new file mode 100644
index 000000000..80b6fb0e9
--- /dev/null
+++ b/dom/bindings/parser/tests/test_const.py
@@ -0,0 +1,80 @@
+import WebIDL
+
+expected = [
+ ("::TestConsts::zero", "zero", "Byte", 0),
+ ("::TestConsts::b", "b", "Byte", -1),
+ ("::TestConsts::o", "o", "Octet", 2),
+ ("::TestConsts::s", "s", "Short", -3),
+ ("::TestConsts::us", "us", "UnsignedShort", 4),
+ ("::TestConsts::l", "l", "Long", -5),
+ ("::TestConsts::ul", "ul", "UnsignedLong", 6),
+ ("::TestConsts::ull", "ull", "UnsignedLongLong", 7),
+ ("::TestConsts::ll", "ll", "LongLong", -8),
+ ("::TestConsts::t", "t", "Boolean", True),
+ ("::TestConsts::f", "f", "Boolean", False),
+ ("::TestConsts::n", "n", "BooleanOrNull", None),
+ ("::TestConsts::nt", "nt", "BooleanOrNull", True),
+ ("::TestConsts::nf", "nf", "BooleanOrNull", False),
+ ("::TestConsts::fl", "fl", "Float", 0.2),
+ ("::TestConsts::db", "db", "Double", 0.2),
+ ("::TestConsts::ufl", "ufl", "UnrestrictedFloat", 0.2),
+ ("::TestConsts::udb", "udb", "UnrestrictedDouble", 0.2),
+ ("::TestConsts::fli", "fli", "Float", 2),
+ ("::TestConsts::dbi", "dbi", "Double", 2),
+ ("::TestConsts::ufli", "ufli", "UnrestrictedFloat", 2),
+ ("::TestConsts::udbi", "udbi", "UnrestrictedDouble", 2),
+]
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestConsts {
+ const byte zero = 0;
+ const byte b = -1;
+ const octet o = 2;
+ const short s = -3;
+ const unsigned short us = 0x4;
+ const long l = -0X5;
+ const unsigned long ul = 6;
+ const unsigned long long ull = 7;
+ const long long ll = -010;
+ const boolean t = true;
+ const boolean f = false;
+ const boolean? n = null;
+ const boolean? nt = true;
+ const boolean? nf = false;
+ const float fl = 0.2;
+ const double db = 0.2;
+ const unrestricted float ufl = 0.2;
+ const unrestricted double udb = 0.2;
+ const float fli = 2;
+ const double dbi = 2;
+ const unrestricted float ufli = 2;
+ const unrestricted double udbi = 2;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestConsts interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestConsts", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestConsts", "Interface has the right name")
+ harness.check(len(iface.members), len(expected), "Expect %s members" % len(expected))
+
+ for (const, (QName, name, type, value)) in zip(iface.members, expected):
+ harness.ok(isinstance(const, WebIDL.IDLConst),
+ "Should be an IDLConst")
+ harness.ok(const.isConst(), "Const is a const")
+ harness.ok(not const.isAttr(), "Const is not an attr")
+ harness.ok(not const.isMethod(), "Const is not a method")
+ harness.check(const.identifier.QName(), QName, "Const has the right QName")
+ harness.check(const.identifier.name, name, "Const has the right name")
+ harness.check(str(const.type), type, "Const has the right type")
+ harness.ok(const.type.isPrimitive(), "All consts should be primitive")
+ harness.check(str(const.value.type), str(const.type),
+ "Const's value has the same type as the type")
+ harness.check(const.value.value, value, "Const value has the right value.")
+
diff --git a/dom/bindings/parser/tests/test_constructor.py b/dom/bindings/parser/tests/test_constructor.py
new file mode 100644
index 000000000..348204c7d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor.py
@@ -0,0 +1,109 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(argument.optional, optional, "Argument has the right optional value")
+ harness.check(argument.variadic, variadic, "Argument has the right variadic value")
+
+ def checkMethod(method, QName, name, signatures,
+ static=True, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False,
+ chromeOnly=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(method.isCreator(), creator, "Method has the correct creator value")
+ harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
+ harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
+ harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
+ harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
+ harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(str(gotRetType), expectedRetType,
+ "Method has the expected return type.")
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ parser.parse("""
+ [Constructor]
+ interface TestConstructorNoArgs {
+ };
+
+ [Constructor(DOMString name)]
+ interface TestConstructorWithArgs {
+ };
+
+ [Constructor(object foo), Constructor(boolean bar)]
+ interface TestConstructorOverloads {
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 3, "Should be three productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[2], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
+ "constructor", [("TestConstructorNoArgs (Wrapper)", [])])
+ checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
+ "constructor",
+ [("TestConstructorWithArgs (Wrapper)",
+ [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
+ checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
+ "constructor",
+ [("TestConstructorOverloads (Wrapper)",
+ [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
+ ("TestConstructorOverloads (Wrapper)",
+ [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
+
+ parser = parser.reset()
+ parser.parse("""
+ [ChromeConstructor()]
+ interface TestChromeConstructor {
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor",
+ "constructor", [("TestChromeConstructor (Wrapper)", [])],
+ chromeOnly=True)
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Constructor(),
+ ChromeConstructor(DOMString a)]
+ interface TestChromeConstructor {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
diff --git a/dom/bindings/parser/tests/test_constructor_no_interface_object.py b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
new file mode 100644
index 000000000..2b09ae71e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_constructor_no_interface_object.py
@@ -0,0 +1,36 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ [Constructor, NoInterfaceObject]
+ interface TestConstructorNoInterfaceObject {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ [NoInterfaceObject, Constructor]
+ interface TestConstructorNoInterfaceObject {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ parser.parse("""
+ [NoInterfaceObject, NamedConstructor=FooBar]
+ interface TestNamedConstructorNoInterfaceObject {
+ };
+ """)
diff --git a/dom/bindings/parser/tests/test_date.py b/dom/bindings/parser/tests/test_date.py
new file mode 100644
index 000000000..2bdfc95e1
--- /dev/null
+++ b/dom/bindings/parser/tests/test_date.py
@@ -0,0 +1,15 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface WithDates {
+ attribute Date foo;
+ void bar(Date arg);
+ void baz(sequence<Date> arg);
+ };
+ """)
+
+ results = parser.finish()
+ harness.ok(results[0].members[0].type.isDate(), "Should have Date")
+ harness.ok(results[0].members[1].signatures()[0][1][0].type.isDate(),
+ "Should have Date argument")
+ harness.ok(not results[0].members[2].signatures()[0][1][0].type.isDate(),
+ "Should have non-Date argument")
diff --git a/dom/bindings/parser/tests/test_deduplicate.py b/dom/bindings/parser/tests/test_deduplicate.py
new file mode 100644
index 000000000..6249d36fb
--- /dev/null
+++ b/dom/bindings/parser/tests/test_deduplicate.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Foo;
+ interface Bar;
+ interface Foo;
+ """);
+
+ results = parser.finish()
+
+ # There should be no duplicate interfaces in the result.
+ expectedNames = sorted(['Foo', 'Bar'])
+ actualNames = sorted(map(lambda iface: iface.identifier.name, results))
+ harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.")
diff --git a/dom/bindings/parser/tests/test_dictionary.py b/dom/bindings/parser/tests/test_dictionary.py
new file mode 100644
index 000000000..2c0fa6123
--- /dev/null
+++ b/dom/bindings/parser/tests/test_dictionary.py
@@ -0,0 +1,555 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict2 : Dict1 {
+ long child = 5;
+ Dict1 aaandAnother;
+ };
+ dictionary Dict1 {
+ long parent;
+ double otherParent;
+ };
+ """)
+ results = parser.finish()
+
+ dict1 = results[1];
+ dict2 = results[0];
+
+ harness.check(len(dict1.members), 2, "Dict1 has two members")
+ harness.check(len(dict2.members), 2, "Dict2 has four members")
+
+ harness.check(dict1.members[0].identifier.name, "otherParent",
+ "'o' comes before 'p'")
+ harness.check(dict1.members[1].identifier.name, "parent",
+ "'o' really comes before 'p'")
+ harness.check(dict2.members[0].identifier.name, "aaandAnother",
+ "'a' comes before 'c'")
+ harness.check(dict2.members[1].identifier.name, "child",
+ "'a' really comes before 'c'")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict {
+ long prop = 5;
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary")
+
+ # Now reset our parser again
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Dict1 : Dict2 {
+ long prop = 5;
+ };
+ dictionary Dict2 : Dict3 {
+ long prop2;
+ };
+ dictionary Dict3 {
+ double prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow name duplication in a dictionary and "
+ "its ancestor")
+
+ # More reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Iface {};
+ dictionary Dict : Iface {
+ long prop;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow non-dictionary parents for dictionaries")
+
+ # Even more reset
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A : B {};
+ dictionary B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in dictionary inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ [TreatNullAs=EmptyString] DOMString foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow [TreatNullAs] on dictionary members");
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Trailing dictionary arg must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo((A or DOMString) arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Trailing union arg containing a dictionary must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg followed by optional arg must be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, optional long arg2, long arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw,
+ "Dictionary arg followed by non-optional arg doesn't have to be optional")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo((A or DOMString) arg1, optional long arg2);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Union arg containing dictionary followed by optional arg must "
+ "be optional")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(A arg1, long arg2);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary arg followed by required arg can be required")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional A? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be nullable")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or long)? arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Dictionary arg must not be in a nullable union")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or long?) arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Dictionary must not be in a union with a nullable type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (long? or A) arg1);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "A nullable type must not be in a union with a dictionary")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ A? doFoo();
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary return value can be nullable")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional A arg);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Dictionary arg should actually parse")
+
+ parser = parser.reset()
+ parser.parse("""
+ dictionary A {
+ };
+ interface X {
+ void doFoo(optional (A or DOMString) arg);
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Union arg containing a dictionary should actually parse")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ Foo foo;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo3 : Foo {
+ short d;
+ };
+
+ dictionary Foo2 : Foo3 {
+ boolean c;
+ };
+
+ dictionary Foo1 : Foo2 {
+ long a;
+ };
+
+ dictionary Foo {
+ Foo1 b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Dictionary that "
+ "inherits from its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ (Foo or DOMString)[]? b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Nullable type "
+ "whose inner type includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ (DOMString or Foo) b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Union type, one of "
+ "whose member types includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ sequence<sequence<sequence<Foo>>> c;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Sequence type "
+ "whose element type includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ (DOMString or Foo)[] d;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be an Array type "
+ "whose element type includes its Dictionary.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ Foo1 b;
+ };
+
+ dictionary Foo3 {
+ Foo d;
+ };
+
+ dictionary Foo2 : Foo3 {
+ short c;
+ };
+
+ dictionary Foo1 : Foo2 {
+ long a;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a Dictionary, one of whose "
+ "members or inherited members has a type that includes "
+ "its Dictionary.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ };
+
+ dictionary Bar {
+ Foo? d;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Member type must not be a nullable dictionary")
+
+ parser = parser.reset();
+ parser.parse("""
+ dictionary Foo {
+ unrestricted float urFloat = 0;
+ unrestricted float urFloat2 = 1.1;
+ unrestricted float urFloat3 = -1.1;
+ unrestricted float? urFloat4 = null;
+ unrestricted float infUrFloat = Infinity;
+ unrestricted float negativeInfUrFloat = -Infinity;
+ unrestricted float nanUrFloat = NaN;
+
+ unrestricted double urDouble = 0;
+ unrestricted double urDouble2 = 1.1;
+ unrestricted double urDouble3 = -1.1;
+ unrestricted double? urDouble4 = null;
+ unrestricted double infUrDouble = Infinity;
+ unrestricted double negativeInfUrDouble = -Infinity;
+ unrestricted double nanUrDouble = NaN;
+ };
+ """)
+ results = parser.finish()
+ harness.ok(True, "Parsing default values for unrestricted types succeeded.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ double f = Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ double f = -Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ double f = NaN;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to NaN")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ float f = Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to Infinity")
+
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ float f = -Infinity;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to -Infinity")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ dictionary Foo {
+ float f = NaN;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Only unrestricted values can be initialized to NaN")
diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py
new file mode 100644
index 000000000..d7780c1ff
--- /dev/null
+++ b/dom/bindings/parser/tests/test_distinguishability.py
@@ -0,0 +1,293 @@
+def firstArgType(method):
+ return method.signatures()[0][1][0].type
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict {
+ };
+ callback interface Foo {
+ };
+ interface Bar {
+ // Bit of a pain to get things that have dictionary types
+ void passDict(optional Dict arg);
+ void passFoo(Foo arg);
+ void passNullableUnion((object? or DOMString) arg);
+ void passNullable(Foo? arg);
+ };
+ """)
+ results = parser.finish()
+
+ iface = results[2]
+ harness.ok(iface.isInterface(), "Should have interface")
+ dictMethod = iface.members[0]
+ ifaceMethod = iface.members[1]
+ nullableUnionMethod = iface.members[2]
+ nullableIfaceMethod = iface.members[3]
+
+ dictType = firstArgType(dictMethod)
+ ifaceType = firstArgType(ifaceMethod)
+
+ harness.ok(dictType.isDictionary(), "Should have dictionary type");
+ harness.ok(ifaceType.isInterface(), "Should have interface type");
+ harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type");
+
+ harness.ok(not dictType.isDistinguishableFrom(ifaceType),
+ "Dictionary not distinguishable from callback interface")
+ harness.ok(not ifaceType.isDistinguishableFrom(dictType),
+ "Callback interface not distinguishable from dictionary")
+
+ nullableUnionType = firstArgType(nullableUnionMethod)
+ nullableIfaceType = firstArgType(nullableIfaceMethod)
+
+ harness.ok(nullableUnionType.isUnion(), "Should have union type");
+ harness.ok(nullableIfaceType.isInterface(), "Should have interface type");
+ harness.ok(nullableIfaceType.nullable(), "Should have nullable type");
+
+ harness.ok(not nullableUnionType.isDistinguishableFrom(nullableIfaceType),
+ "Nullable type not distinguishable from union with nullable "
+ "member type")
+ harness.ok(not nullableIfaceType.isDistinguishableFrom(nullableUnionType),
+ "Union with nullable member type not distinguishable from "
+ "nullable type")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ void passKid(Kid arg);
+ void passParent(Parent arg);
+ void passGrandparent(Grandparent arg);
+ void passImplemented(Implemented arg);
+ void passImplementedParent(ImplementedParent arg);
+ void passUnrelated1(Unrelated1 arg);
+ void passUnrelated2(Unrelated2 arg);
+ void passArrayBuffer(ArrayBuffer arg);
+ void passArrayBuffer(ArrayBufferView arg);
+ };
+
+ interface Kid : Parent {};
+ interface Parent : Grandparent {};
+ interface Grandparent {};
+ interface Implemented : ImplementedParent {};
+ Parent implements Implemented;
+ interface ImplementedParent {};
+ interface Unrelated1 {};
+ interface Unrelated2 {};
+ """)
+ results = parser.finish()
+
+ iface = results[0]
+ harness.ok(iface.isInterface(), "Should have interface")
+ argTypes = [firstArgType(method) for method in iface.members]
+ unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]]
+
+ for type1 in argTypes:
+ for type2 in argTypes:
+ distinguishable = (type1 is not type2 and
+ (type1 in unrelatedTypes or
+ type2 in unrelatedTypes))
+
+ harness.check(type1.isDistinguishableFrom(type2),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s" %
+ (type1, "" if distinguishable else "not ", type2))
+ harness.check(type2.isDistinguishableFrom(type1),
+ distinguishable,
+ "Type %s should %sbe distinguishable from type %s" %
+ (type2, "" if distinguishable else "not ", type1))
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(long arg1, Dummy arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[1].members), 1,
+ "Should look like we have one method")
+ harness.check(len(results[1].members[0].signatures()), 4,
+ "Should have four signatures")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(any arg1, Dummy arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should throw when args before the distinguishing arg are not "
+ "all the same type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Dummy {};
+ interface TestIface {
+ void method(long arg1, TestIface arg2);
+ void method(long arg1, long arg2);
+ void method(any arg1, DOMString arg2);
+ void method(DOMString arg1, DOMString arg2, DOMString arg3);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should throw when there is no distinguishing index")
+
+ # Now let's test our whole distinguishability table
+ argTypes = [ "long", "short", "long?", "short?", "boolean",
+ "boolean?", "DOMString", "ByteString", "Enum", "Enum2",
+ "Interface", "Interface?",
+ "AncestorInterface", "UnrelatedInterface",
+ "ImplementedInterface", "CallbackInterface",
+ "CallbackInterface?", "CallbackInterface2",
+ "object", "Callback", "Callback2", "optional Dict",
+ "optional Dict2", "sequence<long>", "sequence<short>",
+ "MozMap<object>", "MozMap<Dict>", "MozMap<long>",
+ "Date", "Date?", "any",
+ "Promise<any>", "Promise<any>?",
+ "USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
+ "Uint8Array", "Uint16Array" ]
+ # When we can parse Date and RegExp, we need to add them here.
+
+ # Try to categorize things a bit to keep list lengths down
+ def allBut(list1, list2):
+ return [a for a in list1 if a not in list2 and
+ (a != "any" and a != "Promise<any>" and a != "Promise<any>?")]
+ numerics = [ "long", "short", "long?", "short?" ]
+ booleans = [ "boolean", "boolean?" ]
+ primitives = numerics + booleans
+ nonNumerics = allBut(argTypes, numerics)
+ nonBooleans = allBut(argTypes, booleans)
+ strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString" ]
+ nonStrings = allBut(argTypes, strings)
+ nonObjects = primitives + strings
+ objects = allBut(argTypes, nonObjects )
+ bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"]
+ sharedBufferSourceTypes = ["SharedArrayBuffer"]
+ interfaces = [ "Interface", "Interface?", "AncestorInterface",
+ "UnrelatedInterface", "ImplementedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes
+ nullables = ["long?", "short?", "boolean?", "Interface?",
+ "CallbackInterface?", "optional Dict", "optional Dict2",
+ "Date?", "any", "Promise<any>?"]
+ dates = [ "Date", "Date?" ]
+ sequences = [ "sequence<long>", "sequence<short>" ]
+ nonUserObjects = nonObjects + interfaces + dates + sequences
+ otherObjects = allBut(argTypes, nonUserObjects + ["object"])
+ notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] +
+ otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes)
+ mozMaps = [ "MozMap<object>", "MozMap<Dict>", "MozMap<long>" ]
+
+ # Build a representation of the distinguishability table as a dict
+ # of dicts, holding True values where needed, holes elsewhere.
+ data = dict();
+ for type in argTypes:
+ data[type] = dict()
+ def setDistinguishable(type, types):
+ for other in types:
+ data[type][other] = True
+
+ setDistinguishable("long", nonNumerics)
+ setDistinguishable("short", nonNumerics)
+ setDistinguishable("long?", allBut(nonNumerics, nullables))
+ setDistinguishable("short?", allBut(nonNumerics, nullables))
+ setDistinguishable("boolean", nonBooleans)
+ setDistinguishable("boolean?", allBut(nonBooleans, nullables))
+ setDistinguishable("DOMString", nonStrings)
+ setDistinguishable("ByteString", nonStrings)
+ setDistinguishable("USVString", nonStrings)
+ setDistinguishable("Enum", nonStrings)
+ setDistinguishable("Enum2", nonStrings)
+ setDistinguishable("Interface", notRelatedInterfaces)
+ setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables))
+ setDistinguishable("AncestorInterface", notRelatedInterfaces)
+ setDistinguishable("UnrelatedInterface",
+ allBut(argTypes, ["object", "UnrelatedInterface"]))
+ setDistinguishable("ImplementedInterface", notRelatedInterfaces)
+ setDistinguishable("CallbackInterface", nonUserObjects)
+ setDistinguishable("CallbackInterface?", allBut(nonUserObjects, nullables))
+ setDistinguishable("CallbackInterface2", nonUserObjects)
+ setDistinguishable("object", nonObjects)
+ setDistinguishable("Callback", nonUserObjects)
+ setDistinguishable("Callback2", nonUserObjects)
+ setDistinguishable("optional Dict", allBut(nonUserObjects, nullables))
+ setDistinguishable("optional Dict2", allBut(nonUserObjects, nullables))
+ setDistinguishable("sequence<long>",
+ allBut(argTypes, sequences + ["object"]))
+ setDistinguishable("sequence<short>",
+ allBut(argTypes, sequences + ["object"]))
+ setDistinguishable("MozMap<object>", nonUserObjects)
+ setDistinguishable("MozMap<Dict>", nonUserObjects)
+ setDistinguishable("MozMap<long>", nonUserObjects)
+ setDistinguishable("Date", allBut(argTypes, dates + ["object"]))
+ setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"]))
+ setDistinguishable("any", [])
+ setDistinguishable("Promise<any>", [])
+ setDistinguishable("Promise<any>?", [])
+ setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"]))
+ setDistinguishable("ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]))
+ setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]))
+ setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]))
+ setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"]))
+
+ def areDistinguishable(type1, type2):
+ return data[type1].get(type2, False)
+
+ def checkDistinguishability(parser, type1, type2):
+ idlTemplate = """
+ enum Enum { "a", "b" };
+ enum Enum2 { "c", "d" };
+ interface Interface : AncestorInterface {};
+ interface AncestorInterface {};
+ interface UnrelatedInterface {};
+ interface ImplementedInterface {};
+ Interface implements ImplementedInterface;
+ callback interface CallbackInterface {};
+ callback interface CallbackInterface2 {};
+ callback Callback = any();
+ callback Callback2 = long(short arg);
+ dictionary Dict {};
+ dictionary Dict2 {};
+ interface _Promise {};
+ interface TestInterface {%s
+ };
+ """
+ methodTemplate = """
+ void myMethod(%s arg);"""
+ methods = (methodTemplate % type1) + (methodTemplate % type2)
+ idl = idlTemplate % methods
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(idl)
+ results = parser.finish()
+ except:
+ threw = True
+
+ if areDistinguishable(type1, type2):
+ harness.ok(not threw,
+ "Should not throw for '%s' and '%s' because they are distinguishable" % (type1, type2))
+ else:
+ harness.ok(threw,
+ "Should throw for '%s' and '%s' because they are not distinguishable" % (type1, type2))
+
+ # Enumerate over everything in both orders, since order matters in
+ # terms of our implementation of distinguishability checks
+ for type1 in argTypes:
+ for type2 in argTypes:
+ checkDistinguishability(parser, type1, type2)
diff --git a/dom/bindings/parser/tests/test_double_null.py b/dom/bindings/parser/tests/test_double_null.py
new file mode 100644
index 000000000..700c7eade
--- /dev/null
+++ b/dom/bindings/parser/tests/test_double_null.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DoubleNull {
+ attribute byte?? foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_duplicate_qualifiers.py b/dom/bindings/parser/tests/test_duplicate_qualifiers.py
new file mode 100644
index 000000000..799f2e0e0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_duplicate_qualifiers.py
@@ -0,0 +1,84 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers1 {
+ getter getter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers2 {
+ setter setter byte foo(unsigned long index, byte value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers3 {
+ creator creator byte foo(unsigned long index, byte value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers4 {
+ deleter deleter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface DuplicateQualifiers5 {
+ getter deleter getter byte foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ results = parser.parse("""
+ interface DuplicateQualifiers6 {
+ creator setter creator byte foo(unsigned long index, byte value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_empty_enum.py b/dom/bindings/parser/tests/test_empty_enum.py
new file mode 100644
index 000000000..ee0079f06
--- /dev/null
+++ b/dom/bindings/parser/tests/test_empty_enum.py
@@ -0,0 +1,14 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum TestEmptyEnum {
+ };
+ """)
+
+ harness.ok(False, "Should have thrown!")
+ except:
+ harness.ok(True, "Parsing TestEmptyEnum enum should fail")
+
+ results = parser.finish()
diff --git a/dom/bindings/parser/tests/test_empty_sequence_default_value.py b/dom/bindings/parser/tests/test_empty_sequence_default_value.py
new file mode 100644
index 000000000..350ae72f0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_empty_sequence_default_value.py
@@ -0,0 +1,45 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface X {
+ const sequence<long> foo = [];
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Constant cannot have [] as a default value")
+
+ parser = parser.reset()
+
+ parser.parse("""
+ interface X {
+ void foo(optional sequence<long> arg = []);
+ };
+ """)
+ results = parser.finish();
+
+ harness.ok(isinstance(
+ results[0].members[0].signatures()[0][1][0].defaultValue,
+ WebIDL.IDLEmptySequenceValue),
+ "Should have IDLEmptySequenceValue as default value of argument")
+
+ parser = parser.reset()
+
+ parser.parse("""
+ dictionary X {
+ sequence<long> foo = [];
+ };
+ """)
+ results = parser.finish();
+
+ harness.ok(isinstance(results[0].members[0].defaultValue,
+ WebIDL.IDLEmptySequenceValue),
+ "Should have IDLEmptySequenceValue as default value of "
+ "dictionary member")
+
diff --git a/dom/bindings/parser/tests/test_enum.py b/dom/bindings/parser/tests/test_enum.py
new file mode 100644
index 000000000..862289391
--- /dev/null
+++ b/dom/bindings/parser/tests/test_enum.py
@@ -0,0 +1,93 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ enum TestEnum {
+ "",
+ "foo",
+ "bar"
+ };
+
+ interface TestEnumInterface {
+ TestEnum doFoo(boolean arg);
+ readonly attribute TestEnum foo;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestEnumInterfaces interface parsed without error.")
+ harness.check(len(results), 2, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLEnum),
+ "Should be an IDLEnum")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+
+ enum = results[0]
+ harness.check(enum.identifier.QName(), "::TestEnum", "Enum has the right QName")
+ harness.check(enum.identifier.name, "TestEnum", "Enum has the right name")
+ harness.check(enum.values(), ["", "foo", "bar"], "Enum has the right values")
+
+ iface = results[1]
+
+ harness.check(iface.identifier.QName(), "::TestEnumInterface", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestEnumInterface", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 2, "Should be one production")
+ harness.ok(isinstance(members[0], WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ method = members[0]
+ harness.check(method.identifier.QName(), "::TestEnumInterface::doFoo",
+ "Method has correct QName")
+ harness.check(method.identifier.name, "doFoo", "Method has correct name")
+
+ signatures = method.signatures()
+ harness.check(len(signatures), 1, "Expect one signature")
+
+ (returnType, arguments) = signatures[0]
+ harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name")
+ harness.check(len(arguments), 1, "Method has the right number of arguments")
+ arg = arguments[0]
+ harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument")
+ harness.check(str(arg.type), "Boolean", "Argument has the right type")
+
+ attr = members[1]
+ harness.check(attr.identifier.QName(), "::TestEnumInterface::foo",
+ "Attr has correct QName")
+ harness.check(attr.identifier.name, "foo", "Attr has correct name")
+
+ harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name")
+
+ # Now reset our parser
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ enum Enum {
+ "a",
+ "b",
+ "c"
+ };
+ interface TestInterface {
+ void foo(optional Enum e = "d");
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow a bogus default value for an enum")
+
+ # Now reset our parser
+ parser = parser.reset()
+ parser.parse("""
+ enum Enum {
+ "a",
+ "b",
+ "c",
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should allow trailing comma in enum")
diff --git a/dom/bindings/parser/tests/test_enum_duplicate_values.py b/dom/bindings/parser/tests/test_enum_duplicate_values.py
new file mode 100644
index 000000000..51205d209
--- /dev/null
+++ b/dom/bindings/parser/tests/test_enum_duplicate_values.py
@@ -0,0 +1,13 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum TestEnumDuplicateValue {
+ "",
+ ""
+ };
+ """)
+ harness.ok(False, "Should have thrown!")
+ except:
+ harness.ok(True, "Enum TestEnumDuplicateValue should throw")
diff --git a/dom/bindings/parser/tests/test_error_colno.py b/dom/bindings/parser/tests/test_error_colno.py
new file mode 100644
index 000000000..ca0674aec
--- /dev/null
+++ b/dom/bindings/parser/tests/test_error_colno.py
@@ -0,0 +1,20 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = 'interface ?'
+ try:
+ parser.parse(input)
+ results = parser.finish()
+ except WebIDL.WebIDLError, e:
+ threw = True
+ lines = str(e).split('\n')
+
+ harness.check(len(lines), 3, 'Expected number of lines in error message')
+ harness.check(lines[1], input, 'Second line shows error')
+ harness.check(lines[2], ' ' * (len(input) - 1) + '^',
+ 'Correct column pointer in error message')
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_error_lineno.py b/dom/bindings/parser/tests/test_error_lineno.py
new file mode 100644
index 000000000..f11222e7a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_error_lineno.py
@@ -0,0 +1,28 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Check that error messages put the '^' in the right place.
+
+ threw = False
+ input = """\
+// This is a comment.
+interface Foo {
+};
+
+/* This is also a comment. */
+interface ?"""
+ try:
+ parser.parse(input)
+ results = parser.finish()
+ except WebIDL.WebIDLError, e:
+ threw = True
+ lines = str(e).split('\n')
+
+ harness.check(len(lines), 3, 'Expected number of lines in error message')
+ harness.ok(lines[0].endswith('line 6:10'), 'First line of error should end with "line 6:10", but was "%s".' % lines[0])
+ harness.check(lines[1], 'interface ?', 'Second line of error message is the line which caused the error.')
+ harness.check(lines[2], ' ' * (len('interface ?') - 1) + '^',
+ 'Correct column pointer in error message.')
+
+ harness.ok(threw, "Should have thrown.")
+
diff --git a/dom/bindings/parser/tests/test_exposed_extended_attribute.py b/dom/bindings/parser/tests/test_exposed_extended_attribute.py
new file mode 100644
index 000000000..48957098b
--- /dev/null
+++ b/dom/bindings/parser/tests/test_exposed_extended_attribute.py
@@ -0,0 +1,222 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [PrimaryGlobal] interface Foo {};
+ [Global=(Bar1,Bar2)] interface Bar {};
+ [Global=Baz2] interface Baz {};
+
+ [Exposed=(Foo,Bar1)]
+ interface Iface {
+ void method1();
+
+ [Exposed=Bar1]
+ readonly attribute any attr;
+ };
+
+ [Exposed=Foo]
+ partial interface Iface {
+ void method2();
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 5, "Should know about five things");
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 3, "Should have three members")
+
+ harness.ok(members[0].exposureSet == set(["Foo", "Bar"]),
+ "method1 should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "method1 should have the right exposure global names")
+
+ harness.ok(members[1].exposureSet == set(["Bar"]),
+ "attr should have the right exposure set")
+ harness.ok(members[1]._exposureGlobalNames == set(["Bar1"]),
+ "attr should have the right exposure global names")
+
+ harness.ok(members[2].exposureSet == set(["Foo"]),
+ "method2 should have the right exposure set")
+ harness.ok(members[2]._exposureGlobalNames == set(["Foo"]),
+ "method2 should have the right exposure global names")
+
+ harness.ok(iface.exposureSet == set(["Foo", "Bar"]),
+ "Iface should have the right exposure set")
+ harness.ok(iface._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "Iface should have the right exposure global names")
+
+ parser = parser.reset()
+ parser.parse("""
+ [PrimaryGlobal] interface Foo {};
+ [Global=(Bar1,Bar2)] interface Bar {};
+ [Global=Baz2] interface Baz {};
+
+ interface Iface2 {
+ void method3();
+ };
+ """)
+ results = parser.finish()
+
+ harness.check(len(results), 4, "Should know about four things");
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 1, "Should have one member")
+
+ harness.ok(members[0].exposureSet == set(["Foo"]),
+ "method3 should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo"]),
+ "method3 should have the right exposure global names")
+
+ harness.ok(iface.exposureSet == set(["Foo"]),
+ "Iface2 should have the right exposure set")
+ harness.ok(iface._exposureGlobalNames == set(["Foo"]),
+ "Iface2 should have the right exposure global names")
+
+ parser = parser.reset()
+ parser.parse("""
+ [PrimaryGlobal] interface Foo {};
+ [Global=(Bar1,Bar2)] interface Bar {};
+ [Global=Baz2] interface Baz {};
+
+ [Exposed=Foo]
+ interface Iface3 {
+ void method4();
+ };
+
+ [Exposed=(Foo,Bar1)]
+ interface Mixin {
+ void method5();
+ };
+
+ Iface3 implements Mixin;
+ """)
+ results = parser.finish()
+ harness.check(len(results), 6, "Should know about six things");
+ iface = results[3]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = iface.members
+ harness.check(len(members), 2, "Should have two members")
+
+ harness.ok(members[0].exposureSet == set(["Foo"]),
+ "method4 should have the right exposure set")
+ harness.ok(members[0]._exposureGlobalNames == set(["Foo"]),
+ "method4 should have the right exposure global names")
+
+ harness.ok(members[1].exposureSet == set(["Foo", "Bar"]),
+ "method5 should have the right exposure set")
+ harness.ok(members[1]._exposureGlobalNames == set(["Foo", "Bar1"]),
+ "method5 should have the right exposure global names")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Exposed=Foo]
+ interface Bar {
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on interface.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Bar {
+ [Exposed=Foo]
+ readonly attribute bool attr;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Bar {
+ [Exposed=Foo]
+ void operation();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on operation.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Bar {
+ [Exposed=Foo]
+ const long constant = 5;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on invalid Exposed value on constant.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global] interface Foo {};
+ [Global] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ [Exposed=Bar]
+ void method();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on member exposed where its interface is not.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global] interface Foo {};
+ [Global] interface Bar {};
+
+ [Exposed=Foo]
+ interface Baz {
+ void method();
+ };
+
+ [Exposed=Bar]
+ interface Mixin {};
+
+ Baz implements Mixin;
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on LHS of implements being exposed where RHS is not.")
diff --git a/dom/bindings/parser/tests/test_extended_attributes.py b/dom/bindings/parser/tests/test_extended_attributes.py
new file mode 100644
index 000000000..85a70d98f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_extended_attributes.py
@@ -0,0 +1,107 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [NoInterfaceObject]
+ interface TestExtendedAttr {
+ [Unforgeable] readonly attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ [Pref="foo.bar",Pref=flop]
+ interface TestExtendedAttr {
+ [Pref="foo.bar"] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestLenientThis {
+ [LenientThis] attribute byte b;
+ };
+ """)
+
+ results = parser.finish()
+ harness.ok(results[0].members[0].hasLenientThis(),
+ "Should have a lenient this")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestLenientThis2 {
+ [LenientThis=something] attribute byte b;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[LenientThis] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestClamp {
+ void testClamp([Clamp] long foo);
+ void testNotClamp(long foo);
+ };
+ """)
+
+ 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,
+ "Should be clamped")
+ harness.ok(not results[0].members[1].signatures()[0][1][0].clamp,
+ "Should not be clamped")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestClamp2 {
+ void testClamp([Clamp=something] long foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[Clamp] must take no arguments")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface TestEnforceRange {
+ void testEnforceRange([EnforceRange] long foo);
+ void testNotEnforceRange(long foo);
+ };
+ """)
+
+ 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,
+ "Should be enforceRange")
+ harness.ok(not results[0].members[1].signatures()[0][1][0].enforceRange,
+ "Should not be enforceRange")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestEnforceRange2 {
+ void testEnforceRange([EnforceRange=something] long foo);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "[EnforceRange] must take no arguments")
+
diff --git a/dom/bindings/parser/tests/test_float_types.py b/dom/bindings/parser/tests/test_float_types.py
new file mode 100644
index 000000000..718f09c11
--- /dev/null
+++ b/dom/bindings/parser/tests/test_float_types.py
@@ -0,0 +1,125 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ typedef float myFloat;
+ typedef unrestricted float myUnrestrictedFloat;
+ interface FloatTypes {
+ attribute float f;
+ attribute unrestricted float uf;
+ attribute double d;
+ attribute unrestricted double ud;
+ [LenientFloat]
+ attribute float lf;
+ [LenientFloat]
+ attribute double ld;
+
+ void m1(float arg1, double arg2, float? arg3, double? arg4,
+ myFloat arg5, unrestricted float arg6,
+ unrestricted double arg7, unrestricted float? arg8,
+ unrestricted double? arg9, myUnrestrictedFloat arg10);
+ [LenientFloat]
+ void m2(float arg1, double arg2, float? arg3, double? arg4,
+ myFloat arg5, unrestricted float arg6,
+ unrestricted double arg7, unrestricted float? arg8,
+ unrestricted double? arg9, myUnrestrictedFloat arg10);
+ [LenientFloat]
+ void m3(float arg);
+ [LenientFloat]
+ void m4(double arg);
+ [LenientFloat]
+ void m5((float or FloatTypes) arg);
+ [LenientFloat]
+ void m6(sequence<float> arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 3, "Should be two typedefs and one interface.")
+ iface = results[2]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ types = [a.type for a in iface.members if a.isAttr()]
+ harness.ok(types[0].isFloat(), "'float' is a float")
+ harness.ok(not types[0].isUnrestricted(), "'float' is not unrestricted")
+ harness.ok(types[1].isFloat(), "'unrestricted float' is a float")
+ harness.ok(types[1].isUnrestricted(), "'unrestricted float' is unrestricted")
+ harness.ok(types[2].isFloat(), "'double' is a float")
+ harness.ok(not types[2].isUnrestricted(), "'double' is not unrestricted")
+ harness.ok(types[3].isFloat(), "'unrestricted double' is a float")
+ harness.ok(types[3].isUnrestricted(), "'unrestricted double' is unrestricted")
+
+ method = iface.members[6]
+ harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod")
+ argtypes = [a.type for a in method.signatures()[0][1]]
+ for (idx, type) in enumerate(argtypes):
+ harness.ok(type.isFloat(), "Type %d should be float" % idx)
+ harness.check(type.isUnrestricted(), idx >= 5,
+ "Type %d should %sbe unrestricted" % (
+ idx, "" if idx >= 4 else "not "))
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ long m(float arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on void methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ void m(unrestricted float arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ void m(sequence<unrestricted float> arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (2)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ void m((unrestricted float or FloatTypes) arg);
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on methods with unrestricted float args (3)")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface FloatTypes {
+ [LenientFloat]
+ readonly attribute float foo;
+ };
+ """)
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "[LenientFloat] only allowed on writable attributes")
diff --git a/dom/bindings/parser/tests/test_forward_decl.py b/dom/bindings/parser/tests/test_forward_decl.py
new file mode 100644
index 000000000..cac24c832
--- /dev/null
+++ b/dom/bindings/parser/tests/test_forward_decl.py
@@ -0,0 +1,15 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface ForwardDeclared;
+ interface ForwardDeclared;
+
+ interface TestForwardDecl {
+ attribute ForwardDeclared foo;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestForwardDeclared interface parsed without error.")
diff --git a/dom/bindings/parser/tests/test_global_extended_attr.py b/dom/bindings/parser/tests/test_global_extended_attr.py
new file mode 100644
index 000000000..c752cecd2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_global_extended_attr.py
@@ -0,0 +1,122 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [Global]
+ interface Foo : Bar {
+ getter any(DOMString name);
+ };
+ interface Bar {};
+ """)
+
+ results = parser.finish()
+
+ harness.ok(results[0].isOnGlobalProtoChain(),
+ "[Global] interface should be on global's proto chain")
+ harness.ok(results[1].isOnGlobalProtoChain(),
+ "[Global] interface should be on global's proto chain")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ getter any(DOMString name);
+ setter void(DOMString name, any arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "named setter")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ getter any(DOMString name);
+ creator void(DOMString name, any arg);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "named creator")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ getter any(DOMString name);
+ deleter void(DOMString name);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "named deleter")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global, OverrideBuiltins]
+ interface Foo {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "[OverrideBuiltins]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo : Bar {
+ };
+ [OverrideBuiltins]
+ interface Bar {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with an "
+ "[OverrideBuiltins] ancestor")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [Global]
+ interface Foo {
+ };
+ interface Bar : Foo {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown for [Global] used on an interface with a "
+ "descendant")
diff --git a/dom/bindings/parser/tests/test_identifier_conflict.py b/dom/bindings/parser/tests/test_identifier_conflict.py
new file mode 100644
index 000000000..b510a30c0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_identifier_conflict.py
@@ -0,0 +1,39 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ try:
+ parser.parse("""
+ enum Foo { "a" };
+ interface Foo;
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception, e:
+ harness.ok("Name collision" in e.message,
+ "Should have name collision for interface")
+
+ parser = parser.reset()
+ try:
+ parser.parse("""
+ dictionary Foo { long x; };
+ enum Foo { "a" };
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception, e:
+ harness.ok("Name collision" in e.message,
+ "Should have name collision for dictionary")
+
+ parser = parser.reset()
+ try:
+ parser.parse("""
+ enum Foo { "a" };
+ enum Foo { "b" };
+ """)
+ results = parser.finish()
+ harness.ok(False, "Should fail to parse")
+ except Exception, e:
+ harness.ok("Multiple unresolvable definitions" in e.message,
+ "Should have name collision for dictionary")
+
diff --git a/dom/bindings/parser/tests/test_implements.py b/dom/bindings/parser/tests/test_implements.py
new file mode 100644
index 000000000..04c47d92a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_implements.py
@@ -0,0 +1,216 @@
+# 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("""
+ A implements B;
+ interface B {
+ attribute long x;
+ };
+ interface A {
+ attribute long y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Should not have thrown on implements statement "
+ "before interfaces")
+ harness.check(len(results), 3, "We have three statements")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface), "B is an interface")
+ harness.check(len(results[1].members), 1, "B has one member")
+ A = results[2]
+ harness.ok(isinstance(A, WebIDL.IDLInterface), "A is an interface")
+ harness.check(len(A.members), 2, "A has two members")
+ harness.check(A.members[0].identifier.name, "y", "First member is 'y'")
+ harness.check(A.members[1].identifier.name, "x", "Second member is 'x'")
+
+ # Duplicated member names not allowed
+ threw = False
+ try:
+ parser.parse("""
+ C implements D;
+ interface D {
+ attribute long x;
+ };
+ interface C {
+ attribute long x;
+ };
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on implemented interface duplicating "
+ "a name on base interface")
+
+ # Same, but duplicated across implemented interfaces
+ threw = False
+ try:
+ parser.parse("""
+ E implements F;
+ E implements G;
+ interface F {
+ attribute long x;
+ };
+ interface G {
+ attribute long x;
+ };
+ interface E {};
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on implemented interfaces "
+ "duplicating each other's member names")
+
+ # Same, but duplicated across indirectly implemented interfaces
+ threw = False
+ try:
+ parser.parse("""
+ H implements I;
+ H implements J;
+ I implements K;
+ interface K {
+ attribute long x;
+ };
+ interface L {
+ attribute long x;
+ };
+ interface I {};
+ interface J : L {};
+ interface H {};
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on indirectly implemented interfaces "
+ "duplicating each other's member names")
+
+ # Same, but duplicated across an implemented interface and its parent
+ threw = False
+ try:
+ parser.parse("""
+ M implements N;
+ interface O {
+ attribute long x;
+ };
+ interface N : O {
+ attribute long x;
+ };
+ interface M {};
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on implemented interface and its "
+ "ancestor duplicating member names")
+
+ # Reset the parser so we can actually find things where we expect
+ # them in the list
+ parser = parser.reset()
+
+ # Diamonds should be allowed
+ threw = False
+ try:
+ parser.parse("""
+ P implements Q;
+ P implements R;
+ Q implements S;
+ R implements S;
+ interface Q {};
+ interface R {};
+ interface S {
+ attribute long x;
+ };
+ interface P {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw, "Diamond inheritance is fine")
+ harness.check(results[6].identifier.name, "S", "We should be looking at 'S'")
+ harness.check(len(results[6].members), 1, "S should have one member")
+ harness.check(results[6].members[0].identifier.name, "x",
+ "S's member should be 'x'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface {
+ };
+ TestInterface implements TestCallbackInterface;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow callback interfaces on the right-hand side "
+ "of 'implements'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ callback interface TestCallbackInterface {
+ };
+ TestCallbackInterface implements TestInterface;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow callback interfaces on the left-hand side of "
+ "'implements'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ dictionary Dict {
+ };
+ Dict implements TestInterface;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow non-interfaces on the left-hand side "
+ "of 'implements'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestInterface {
+ };
+ dictionary Dict {
+ };
+ TestInterface implements Dict;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should not allow non-interfaces on the right-hand side "
+ "of 'implements'")
+
diff --git a/dom/bindings/parser/tests/test_incomplete_parent.py b/dom/bindings/parser/tests/test_incomplete_parent.py
new file mode 100644
index 000000000..1f520a28e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_incomplete_parent.py
@@ -0,0 +1,18 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestIncompleteParent : NotYetDefined {
+ void foo();
+ };
+
+ interface NotYetDefined : EvenHigherOnTheChain {
+ };
+
+ interface EvenHigherOnTheChain {
+ };
+ """)
+
+ parser.finish()
+
+ harness.ok(True, "TestIncompleteParent interface parsed without error.")
diff --git a/dom/bindings/parser/tests/test_incomplete_types.py b/dom/bindings/parser/tests/test_incomplete_types.py
new file mode 100644
index 000000000..fdc396040
--- /dev/null
+++ b/dom/bindings/parser/tests/test_incomplete_types.py
@@ -0,0 +1,44 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestIncompleteTypes {
+ attribute FooInterface attr1;
+
+ FooInterface method1(FooInterface arg);
+ };
+
+ interface FooInterface {
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestIncompleteTypes interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestIncompleteTypes", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestIncompleteTypes", "Interface has the right name")
+ harness.check(len(iface.members), 2, "Expect 2 members")
+
+ attr = iface.members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute),
+ "Should be an IDLAttribute")
+ method = iface.members[1]
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+
+ harness.check(attr.identifier.QName(), "::TestIncompleteTypes::attr1",
+ "Attribute has the right QName")
+ harness.check(attr.type.name, "FooInterface",
+ "Previously unresolved type has the right name")
+
+ harness.check(method.identifier.QName(), "::TestIncompleteTypes::method1",
+ "Attribute has the right QName")
+ (returnType, args) = method.signatures()[0]
+ harness.check(returnType.name, "FooInterface",
+ "Previously unresolved type has the right name")
+ harness.check(args[0].type.name, "FooInterface",
+ "Previously unresolved type has the right name")
diff --git a/dom/bindings/parser/tests/test_interface.py b/dom/bindings/parser/tests/test_interface.py
new file mode 100644
index 000000000..e8ed67b54
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface.py
@@ -0,0 +1,405 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::Foo", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Foo", "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ parser.parse("interface Bar : Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface parsed without error.")
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[1]
+ harness.check(iface.identifier.QName(), "::Bar", "Interface has the right QName")
+ harness.check(iface.identifier.name, "Bar", "Interface has the right name")
+ harness.ok(isinstance(iface.parent, WebIDL.IDLInterface),
+ "Interface has a parent")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface QNameBase {
+ attribute long foo;
+ };
+
+ interface QNameDerived : QNameBase {
+ attribute long long foo;
+ attribute byte bar;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2, "Should be two productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(results[1].parent, results[0], "Inheritance chain is right")
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ harness.check(len(results[1].members), 2, "Expect 2 productions")
+ base = results[0]
+ derived = results[1]
+ harness.check(base.members[0].identifier.QName(), "::QNameBase::foo",
+ "Member has the right QName")
+ harness.check(derived.members[0].identifier.QName(), "::QNameDerived::foo",
+ "Member has the right QName")
+ harness.check(derived.members[1].identifier.QName(), "::QNameDerived::bar",
+ "Member has the right QName")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : C {};
+ interface C : B {};
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirect cycles in interface inheritance chains")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {};
+ interface B {};
+ A implements B;
+ B implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow cycles via implements")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {};
+ interface C {};
+ interface B {};
+ A implements C;
+ C implements B;
+ B implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirect cycles via implements")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B {};
+ B implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B {};
+ interface C {};
+ B implements C;
+ C implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that indirectly implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : C {};
+ interface C {};
+ C implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirectly inheriting from an interface that implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A : B {};
+ interface B : C {};
+ interface C {};
+ interface D {};
+ C implements D;
+ D implements A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow indirectly inheriting from an interface that indirectly implements us")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A;
+ interface B : A {};
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow inheriting from an interface that is only forward declared")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Constructor(long arg)]
+ interface A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ [Constructor]
+ partial interface A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ """);
+ results = parser.finish();
+ harness.check(len(results), 2,
+ "Should have two results with partial interface")
+ iface = results[0]
+ harness.check(len(iface.members), 3,
+ "Should have three members with partial interface")
+ harness.check(iface.members[0].identifier.name, "x",
+ "First member should be x with partial interface")
+ harness.check(iface.members[1].identifier.name, "foo",
+ "Second member should be foo with partial interface")
+ harness.check(len(iface.members[1].signatures()), 2,
+ "Should have two foo signatures with partial interface")
+ harness.check(iface.members[2].identifier.name, "y",
+ "Third member should be y with partial interface")
+ harness.check(len(iface.ctor().signatures()), 2,
+ "Should have two constructors with partial interface")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Constructor]
+ partial interface A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ [Constructor(long arg)]
+ interface A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ """);
+ results = parser.finish();
+ harness.check(len(results), 2,
+ "Should have two results with reversed partial interface")
+ iface = results[1]
+ harness.check(len(iface.members), 3,
+ "Should have three members with reversed partial interface")
+ harness.check(iface.members[0].identifier.name, "x",
+ "First member should be x with reversed partial interface")
+ harness.check(iface.members[1].identifier.name, "foo",
+ "Second member should be foo with reversed partial interface")
+ harness.check(len(iface.members[1].signatures()), 2,
+ "Should have two foo signatures with reversed partial interface")
+ harness.check(iface.members[2].identifier.name, "y",
+ "Third member should be y with reversed partial interface")
+ harness.check(len(iface.ctor().signatures()), 2,
+ "Should have two constructors with reversed partial interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ readonly attribute boolean x;
+ };
+ interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow two non-partial interfaces with the same name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ partial interface A {
+ readonly attribute boolean x;
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Must have a non-partial interface for a given name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ partial interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between partial interface "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between interface "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ interface A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between external interface "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ readonly attribute boolean x;
+ };
+ interface A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between external interface "
+ "and interface")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface A;
+ interface A;
+ """)
+ results = parser.finish()
+ harness.ok(len(results) == 1 and
+ isinstance(results[0], WebIDL.IDLExternalInterface),
+ "Should allow name collisions between external interface "
+ "declarations")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SomeRandomAnnotation]
+ interface A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow unknown extended attributes on interfaces")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface B {};
+ [ArrayClass]
+ interface A : B {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow [ArrayClass] on interfaces with parents")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [ArrayClass]
+ interface A {
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(not threw,
+ "Should allow [ArrayClass] on interfaces without parents")
diff --git a/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py b/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py
new file mode 100644
index 000000000..db944e7aa
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_const_identifier_conflicts.py
@@ -0,0 +1,15 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflict {
+ const byte thing1 = 1;
+ const unsigned long thing1 = 1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py b/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py
new file mode 100644
index 000000000..1a73fb917
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_identifier_conflicts_across_members.py
@@ -0,0 +1,60 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ readonly attribute long thing1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers2 {
+ readonly attribute long thing1;
+ const byte thing1 = 1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers3 {
+ getter boolean thing1(DOMString name);
+ readonly attribute long thing1;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface IdentifierConflictAcrossMembers1 {
+ const byte thing1 = 1;
+ long thing1();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
new file mode 100644
index 000000000..ee5d870c2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_interface_maplikesetlikeiterable.py
@@ -0,0 +1,691 @@
+import WebIDL
+import traceback
+def WebIDLTest(parser, harness):
+
+ def shouldPass(prefix, iface, expectedMembers, numProductions=1):
+ p = parser.reset()
+ p.parse(iface)
+ results = p.finish()
+ harness.check(len(results), numProductions,
+ "%s - Should have production count %d" % (prefix, numProductions))
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "%s - Should be an IDLInterface" % (prefix))
+ # Make a copy, since we plan to modify it
+ expectedMembers = list(expectedMembers)
+ for m in results[0].members:
+ name = m.identifier.name
+ if (name, type(m)) in expectedMembers:
+ harness.ok(True, "%s - %s - Should be a %s" % (prefix, name,
+ type(m)))
+ expectedMembers.remove((name, type(m)))
+ else:
+ harness.ok(False, "%s - %s - Unknown symbol of type %s" %
+ (prefix, name, type(m)))
+ # A bit of a hoop because we can't generate the error string if we pass
+ if len(expectedMembers) == 0:
+ harness.ok(True, "Found all the members")
+ else:
+ harness.ok(False,
+ "Expected member not found: %s of type %s" %
+ (expectedMembers[0][0], expectedMembers[0][1]))
+ return results
+
+ def shouldFail(prefix, iface):
+ try:
+ p = parser.reset()
+ p.parse(iface)
+ p.finish()
+ harness.ok(False,
+ prefix + " - Interface passed when should've failed")
+ except WebIDL.WebIDLError, e:
+ harness.ok(True,
+ prefix + " - Interface failed as expected")
+ except Exception, e:
+ harness.ok(False,
+ prefix + " - Interface failed but not as a WebIDLError exception: %s" % e)
+
+ iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
+ "values", "forEach"]]
+ setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
+ [("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
+ iterableMembers)
+ setROMembers.extend([("size", WebIDL.IDLAttribute)])
+ setRWMembers = ([(x, WebIDL.IDLMethod) for x in ["add",
+ "clear",
+ "delete"]] +
+ setROMembers)
+ setROChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
+ "__clear",
+ "__delete"]] +
+ setROMembers)
+ setRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__add",
+ "__clear",
+ "__delete"]] +
+ setRWMembers)
+ mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
+ [("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
+ iterableMembers)
+ mapROMembers.extend([("size", WebIDL.IDLAttribute)])
+ mapRWMembers = ([(x, WebIDL.IDLMethod) for x in ["set",
+ "clear",
+ "delete"]] + mapROMembers)
+ mapRWChromeMembers = ([(x, WebIDL.IDLMethod) for x in ["__set",
+ "__clear",
+ "__delete"]] +
+ mapRWMembers)
+
+ # OK, now that we've used iterableMembers to set up the above, append
+ # __iterable to it for the iterable<> case.
+ iterableMembers.append(("__iterable", WebIDL.IDLIterable))
+
+ valueIterableMembers = [("__iterable", WebIDL.IDLIterable)]
+ valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod))
+ valueIterableMembers.append(("length", WebIDL.IDLAttribute))
+
+ disallowedIterableNames = ["keys", "entries", "values"]
+ disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames
+ mapDisallowedMemberNames = ["get"] + disallowedMemberNames
+ disallowedNonMethodNames = ["clear", "delete"]
+ mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
+ setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames
+ unrelatedMembers = [("unrelatedAttribute", WebIDL.IDLAttribute),
+ ("unrelatedMethod", WebIDL.IDLMethod)]
+
+ #
+ # Simple Usage Tests
+ #
+
+ shouldPass("Iterable (key only)",
+ """
+ interface Foo1 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, valueIterableMembers + unrelatedMembers)
+
+ shouldPass("Iterable (key only) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long>;
+ readonly attribute unsigned long length;
+ getter long(unsigned long index);
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, valueIterableMembers, numProductions=2)
+
+ shouldPass("Iterable (key and value)",
+ """
+ interface Foo1 {
+ iterable<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, iterableMembers + unrelatedMembers,
+ # numProductions == 2 because of the generated iterator iface,
+ numProductions=2)
+
+ shouldPass("Iterable (key and value) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ iterable<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, iterableMembers,
+ # numProductions == 3 because of the generated iterator iface,
+ numProductions=3)
+
+ shouldPass("Maplike (readwrite)",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Maplike (readwrite)",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Maplike (readonly)",
+ """
+ interface Foo1 {
+ readonly maplike<long, long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapROMembers + unrelatedMembers)
+
+ shouldPass("Maplike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly maplike<long, long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, mapROMembers, numProductions=2)
+
+ shouldPass("Setlike (readwrite)",
+ """
+ interface Foo1 {
+ setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setRWMembers + unrelatedMembers)
+
+ shouldPass("Setlike (readwrite) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setRWMembers, numProductions=2)
+
+ shouldPass("Setlike (readonly)",
+ """
+ interface Foo1 {
+ readonly setlike<long>;
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setROMembers + unrelatedMembers)
+
+ shouldPass("Setlike (readonly) inheriting from parent",
+ """
+ interface Foo1 : Foo2 {
+ readonly setlike<long>;
+ };
+ interface Foo2 {
+ attribute long unrelatedAttribute;
+ long unrelatedMethod();
+ };
+ """, setROMembers, numProductions=2)
+
+ shouldPass("Inheritance of maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Implements with maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ };
+ Foo2 implements Foo1;
+ """, mapRWMembers, numProductions=3)
+
+ shouldPass("JS Implemented maplike interface",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ setlike<long>;
+ };
+ """, setRWChromeMembers)
+
+ shouldPass("JS Implemented maplike interface",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ """, mapRWChromeMembers)
+
+ #
+ # Multiple maplike/setlike tests
+ #
+
+ shouldFail("Two maplike/setlikes on same interface",
+ """
+ interface Foo1 {
+ setlike<long>;
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Two iterable/setlikes on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Two iterables on same interface",
+ """
+ interface Foo1 {
+ iterable<long>;
+ iterable<long, long>;
+ };
+ """)
+
+ shouldFail("Two maplike/setlikes in partials",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ partial interface Foo1 {
+ setlike<long>;
+ };
+ """)
+
+ shouldFail("Conflicting maplike/setlikes across inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ setlike<long>;
+ };
+ """)
+
+ shouldFail("Conflicting maplike/iterable across inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ iterable<long>;
+ };
+ """)
+
+ shouldFail("Conflicting maplike/setlikes across multistep inheritance",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ setlike<long>;
+ };
+ """)
+
+ shouldFail("Consequential interface with conflicting maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ setlike<long>;
+ };
+ Foo2 implements Foo1;
+ """)
+
+ shouldFail("Consequential interfaces with conflicting maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ setlike<long>;
+ };
+ interface Foo3 {
+ };
+ Foo3 implements Foo1;
+ Foo3 implements Foo2;
+ """)
+
+ #
+ # Member name collision tests
+ #
+
+ def testConflictingMembers(likeMember, conflictName, expectedMembers, methodPasses):
+ """
+ Tests for maplike/setlike member generation against conflicting member
+ names. If methodPasses is True, this means we expect the interface to
+ pass in the case of method shadowing, and expectedMembers should be the
+ list of interface members to check against on the passing interface.
+
+ """
+ if methodPasses:
+ shouldPass("Conflicting method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ [Throws]
+ void %s(long test1, double test2, double test3);
+ };
+ """ % (likeMember, conflictName), expectedMembers)
+ else:
+ shouldFail("Conflicting method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ [Throws]
+ void %s(long test1, double test2, double test3);
+ };
+ """ % (likeMember, conflictName))
+ # Inherited conflicting methods should ALWAYS fail
+ shouldFail("Conflicting inherited method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ void %s(long test1, double test2, double test3);
+ };
+ interface Foo2 : Foo1 {
+ %s;
+ };
+ """ % (conflictName, likeMember))
+ shouldFail("Conflicting static method: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static void %s(long test1, double test2, double test3);
+ };
+ """ % (likeMember, conflictName))
+ shouldFail("Conflicting attribute: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s
+ attribute double %s;
+ };
+ """ % (likeMember, conflictName))
+ shouldFail("Conflicting const: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ const double %s = 0;
+ };
+ """ % (likeMember, conflictName))
+ shouldFail("Conflicting static attribute: %s and %s" % (likeMember, conflictName),
+ """
+ interface Foo1 {
+ %s;
+ static attribute long %s;
+ };
+ """ % (likeMember, conflictName))
+
+ for member in disallowedIterableNames:
+ testConflictingMembers("iterable<long, long>", member, iterableMembers, False)
+ for member in mapDisallowedMemberNames:
+ testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
+ for member in disallowedMemberNames:
+ testConflictingMembers("setlike<long>", member, setRWMembers, False)
+ for member in mapDisallowedNonMethodNames:
+ testConflictingMembers("maplike<long, long>", member, mapRWMembers, True)
+ for member in setDisallowedNonMethodNames:
+ testConflictingMembers("setlike<long>", member, setRWMembers, True)
+
+ shouldPass("Inheritance of maplike/setlike with child member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ void entries();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Inheritance of multi-level maplike/setlike with child member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ void entries();
+ };
+ """, mapRWMembers, numProductions=3)
+
+ shouldFail("Interface with consequential maplike/setlike interface member collision",
+ """
+ interface Foo1 {
+ void entries();
+ };
+ interface Foo2 {
+ maplike<long, long>;
+ };
+ Foo1 implements Foo2;
+ """)
+
+ shouldFail("Maplike interface with consequential interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ void entries();
+ };
+ Foo1 implements Foo2;
+ """)
+
+ shouldPass("Consequential Maplike interface with inherited interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ void entries();
+ };
+ interface Foo3 : Foo2 {
+ };
+ Foo3 implements Foo1;
+ """, mapRWMembers, numProductions=4)
+
+ shouldPass("Inherited Maplike interface with consequential interface member collision",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 {
+ void entries();
+ };
+ interface Foo3 : Foo1 {
+ };
+ Foo3 implements Foo2;
+ """, mapRWMembers, numProductions=4)
+
+ shouldFail("Inheritance of name collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ void entries();
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of multi-level name collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ void entries();
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldPass("Inheritance of attribute collision with parent maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ attribute double size;
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldPass("Inheritance of multi-level attribute collision with parent maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ attribute double size;
+ };
+ """, mapRWMembers, numProductions=3)
+
+ shouldFail("Inheritance of attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of multi-level attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of attribute/rw function collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ attribute double set;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of const/rw function collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ const double set = 0;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldPass("Inheritance of rw function with same name in child maplike/setlike",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ };
+ interface Foo2 : Foo1 {
+ void clear();
+ };
+ """, mapRWMembers, numProductions=2)
+
+ shouldFail("Inheritance of unforgeable attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ [Unforgeable]
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldFail("Inheritance of multi-level unforgeable attribute collision with child maplike/setlike",
+ """
+ interface Foo1 {
+ [Unforgeable]
+ attribute double size;
+ };
+ interface Foo2 : Foo1 {
+ };
+ interface Foo3 : Foo2 {
+ maplike<long, long>;
+ };
+ """)
+
+ shouldPass("Implemented interface with readonly allowable overrides",
+ """
+ interface Foo1 {
+ readonly setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """, setROMembers + [("clear", WebIDL.IDLAttribute)])
+
+ shouldPass("JS Implemented read-only interface with readonly allowable overrides",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ readonly setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """, setROChromeMembers + [("clear", WebIDL.IDLAttribute)])
+
+ shouldFail("JS Implemented read-write interface with non-readwrite allowable overrides",
+ """
+ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1",
+ Constructor()]
+ interface Foo1 {
+ setlike<long>;
+ readonly attribute boolean clear;
+ };
+ """)
+
+ r = shouldPass("Check proper override of clear/delete/set",
+ """
+ interface Foo1 {
+ maplike<long, long>;
+ long clear(long a, long b, double c, double d);
+ long set(long a, long b, double c, double d);
+ long delete(long a, long b, double c, double d);
+ };
+ """, mapRWMembers)
+
+ for m in r[0].members:
+ if m.identifier.name in ["clear", "set", "delete"]:
+ harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name)
+ harness.check(m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name)
+ harness.ok(not m.isMaplikeOrSetlikeOrIterableMethod(),
+ "%s should not be a maplike/setlike function" % m.identifier.name)
diff --git a/dom/bindings/parser/tests/test_lenientSetter.py b/dom/bindings/parser/tests/test_lenientSetter.py
new file mode 100644
index 000000000..78a9ffe9e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_lenientSetter.py
@@ -0,0 +1,58 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+def should_throw(parser, harness, message, code):
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse(code)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown: %s" % message)
+
+
+def WebIDLTest(parser, harness):
+ # The [LenientSetter] extended attribute MUST take no arguments.
+ should_throw(parser, harness, "no arguments", """
+ interface I {
+ [LenientSetter=X] readonly attribute long A;
+ };
+ """)
+
+ # An attribute with the [LenientSetter] extended attribute MUST NOT
+ # also be declared with the [PutForwards] extended attribute.
+ should_throw(parser, harness, "PutForwards", """
+ interface I {
+ [PutForwards=B, LenientSetter] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ # An attribute with the [LenientSetter] extended attribute MUST NOT
+ # also be declared with the [Replaceable] extended attribute.
+ should_throw(parser, harness, "Replaceable", """
+ interface I {
+ [Replaceable, LenientSetter] readonly attribute J A;
+ };
+ """)
+
+ # The [LenientSetter] extended attribute MUST NOT be used on an
+ # attribute that is not read only.
+ should_throw(parser, harness, "writable attribute", """
+ interface I {
+ [LenientSetter] attribute long A;
+ };
+ """)
+
+ # The [LenientSetter] extended attribute MUST NOT be used on a
+ # static attribute.
+ should_throw(parser, harness, "static attribute", """
+ interface I {
+ [LenientSetter] static readonly attribute long A;
+ };
+ """)
diff --git a/dom/bindings/parser/tests/test_method.py b/dom/bindings/parser/tests/test_method.py
new file mode 100644
index 000000000..cf7f1b40d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_method.py
@@ -0,0 +1,178 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestMethods {
+ void basic();
+ static void basicStatic();
+ void basicWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ boolean basicBoolean();
+ static boolean basicStaticBoolean();
+ boolean basicBooleanWithSimpleArgs(boolean arg1, byte arg2, unsigned long arg3);
+ void optionalArg(optional byte? arg1, optional sequence<byte> arg2);
+ void variadicArg(byte?... arg1);
+ object getObject();
+ void setObject(object arg1);
+ void setAny(any arg1);
+ float doFloats(float arg1);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestMethods interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestMethods", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestMethods", "Interface has the right name")
+ harness.check(len(iface.members), 12, "Expect 12 members")
+
+ methods = iface.members
+
+ def checkArgument(argument, QName, name, type, optional, variadic):
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), QName, "Argument has the right QName")
+ harness.check(argument.identifier.name, name, "Argument has the right name")
+ harness.check(str(argument.type), type, "Argument has the right return type")
+ harness.check(argument.optional, optional, "Argument has the right optional value")
+ harness.check(argument.variadic, variadic, "Argument has the right variadic value")
+
+ def checkMethod(method, QName, name, signatures,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.ok(method.isMethod(), "Method is a method")
+ harness.ok(not method.isAttr(), "Method is not an attr")
+ harness.ok(not method.isConst(), "Method is not a const")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(method.isCreator(), creator, "Method has the correct creator value")
+ harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
+ harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
+ harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
+ harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
+
+ sigpairs = zip(method.signatures(), signatures)
+ for (gotSignature, expectedSignature) in sigpairs:
+ (gotRetType, gotArgs) = gotSignature
+ (expectedRetType, expectedArgs) = expectedSignature
+
+ harness.check(str(gotRetType), expectedRetType,
+ "Method has the expected return type.")
+
+ for i in range(0, len(gotArgs)):
+ (QName, name, type, optional, variadic) = expectedArgs[i]
+ checkArgument(gotArgs[i], QName, name, type, optional, variadic)
+
+ checkMethod(methods[0], "::TestMethods::basic", "basic", [("Void", [])])
+ checkMethod(methods[1], "::TestMethods::basicStatic", "basicStatic",
+ [("Void", [])], static=True)
+ checkMethod(methods[2], "::TestMethods::basicWithSimpleArgs",
+ "basicWithSimpleArgs",
+ [("Void",
+ [("::TestMethods::basicWithSimpleArgs::arg1", "arg1", "Boolean", False, False),
+ ("::TestMethods::basicWithSimpleArgs::arg2", "arg2", "Byte", False, False),
+ ("::TestMethods::basicWithSimpleArgs::arg3", "arg3", "UnsignedLong", False, False)])])
+ checkMethod(methods[3], "::TestMethods::basicBoolean", "basicBoolean", [("Boolean", [])])
+ checkMethod(methods[4], "::TestMethods::basicStaticBoolean", "basicStaticBoolean", [("Boolean", [])], static=True)
+ checkMethod(methods[5], "::TestMethods::basicBooleanWithSimpleArgs",
+ "basicBooleanWithSimpleArgs",
+ [("Boolean",
+ [("::TestMethods::basicBooleanWithSimpleArgs::arg1", "arg1", "Boolean", False, False),
+ ("::TestMethods::basicBooleanWithSimpleArgs::arg2", "arg2", "Byte", False, False),
+ ("::TestMethods::basicBooleanWithSimpleArgs::arg3", "arg3", "UnsignedLong", False, False)])])
+ checkMethod(methods[6], "::TestMethods::optionalArg",
+ "optionalArg",
+ [("Void",
+ [("::TestMethods::optionalArg::arg1", "arg1", "ByteOrNull", True, False),
+ ("::TestMethods::optionalArg::arg2", "arg2", "ByteSequence", True, False)])])
+ checkMethod(methods[7], "::TestMethods::variadicArg",
+ "variadicArg",
+ [("Void",
+ [("::TestMethods::variadicArg::arg1", "arg1", "ByteOrNull", True, True)])])
+ checkMethod(methods[8], "::TestMethods::getObject",
+ "getObject", [("Object", [])])
+ checkMethod(methods[9], "::TestMethods::setObject",
+ "setObject",
+ [("Void",
+ [("::TestMethods::setObject::arg1", "arg1", "Object", False, False)])])
+ checkMethod(methods[10], "::TestMethods::setAny",
+ "setAny",
+ [("Void",
+ [("::TestMethods::setAny::arg1", "arg1", "Any", False, False)])])
+ checkMethod(methods[11], "::TestMethods::doFloats",
+ "doFloats",
+ [("Float",
+ [("::TestMethods::doFloats::arg1", "arg1", "Float", False, False)])])
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ void foo(optional float bar = 1);
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(not threw, "Should allow integer to float type corecion")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [GetterThrows] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [GetterThrows] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [SetterThrows] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow [SetterThrows] on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ [Throw] void foo();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should spell [Throws] correctly on methods")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface A {
+ void __noSuchMethod__();
+ };
+ """)
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should not allow __noSuchMethod__ methods")
diff --git a/dom/bindings/parser/tests/test_mozmap.py b/dom/bindings/parser/tests/test_mozmap.py
new file mode 100644
index 000000000..1a36fdd62
--- /dev/null
+++ b/dom/bindings/parser/tests/test_mozmap.py
@@ -0,0 +1,39 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ dictionary Dict {};
+ interface MozMapArg {
+ void foo(MozMap<Dict> arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(len(results), 2, "Should know about two things");
+ harness.ok(isinstance(results[1], WebIDL.IDLInterface),
+ "Should have an interface here");
+ members = results[1].members
+ harness.check(len(members), 1, "Should have one member")
+ harness.ok(members[0].isMethod(), "Should have method")
+ signature = members[0].signatures()[0]
+ args = signature[1]
+ harness.check(len(args), 1, "Should have one arg")
+ harness.ok(args[0].type.isMozMap(), "Should have a MozMap type here")
+ harness.ok(args[0].type.inner.isDictionary(),
+ "Should have a dictionary inner type")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface MozMapVoidArg {
+ void foo(MozMap<void> arg);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_namespace.py b/dom/bindings/parser/tests/test_namespace.py
new file mode 100644
index 000000000..74533a177
--- /dev/null
+++ b/dom/bindings/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/dom/bindings/parser/tests/test_newobject.py b/dom/bindings/parser/tests/test_newobject.py
new file mode 100644
index 000000000..26785c6a2
--- /dev/null
+++ b/dom/bindings/parser/tests/test_newobject.py
@@ -0,0 +1,70 @@
+# Import the WebIDL module, so we can do isinstance checks and whatnot
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ # Basic functionality
+ parser.parse(
+ """
+ interface Iface {
+ [NewObject] readonly attribute Iface attr;
+ [NewObject] Iface method();
+ };
+ """)
+ results = parser.finish()
+ harness.ok(results, "Should not have thrown on basic [NewObject] usage")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Pure, NewObject] Iface method();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] methods must depend on something")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [Cached, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [Cached]")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface Iface {
+ [StoreInSlot, NewObject, Affects=Nothing] readonly attribute Iface attr;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw, "[NewObject] attributes must not be [StoreInSlot]")
diff --git a/dom/bindings/parser/tests/test_nullable_equivalency.py b/dom/bindings/parser/tests/test_nullable_equivalency.py
new file mode 100644
index 000000000..2b48b615d
--- /dev/null
+++ b/dom/bindings/parser/tests/test_nullable_equivalency.py
@@ -0,0 +1,115 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestNullableEquivalency1 {
+ attribute long a;
+ attribute long? b;
+ };
+
+ interface TestNullableEquivalency2 {
+ attribute ArrayBuffer a;
+ attribute ArrayBuffer? b;
+ };
+
+ /* Can't have dictionary-valued attributes, so can't test that here */
+
+ enum TestNullableEquivalency4Enum {
+ "Foo",
+ "Bar"
+ };
+
+ interface TestNullableEquivalency4 {
+ attribute TestNullableEquivalency4Enum a;
+ attribute TestNullableEquivalency4Enum? b;
+ };
+
+ interface TestNullableEquivalency5 {
+ attribute TestNullableEquivalency4 a;
+ attribute TestNullableEquivalency4? b;
+ };
+
+ interface TestNullableEquivalency6 {
+ attribute boolean a;
+ attribute boolean? b;
+ };
+
+ interface TestNullableEquivalency7 {
+ attribute DOMString a;
+ attribute DOMString? b;
+ };
+
+ interface TestNullableEquivalency8 {
+ attribute float a;
+ attribute float? b;
+ };
+
+ interface TestNullableEquivalency9 {
+ attribute double a;
+ attribute double? b;
+ };
+
+ interface TestNullableEquivalency10 {
+ attribute object a;
+ attribute object? b;
+ };
+ """)
+
+ for decl in parser.finish():
+ if decl.isInterface():
+ checkEquivalent(decl, harness)
+
+def checkEquivalent(iface, harness):
+ type1 = iface.members[0].type
+ type2 = iface.members[1].type
+
+ harness.check(type1.nullable(), False, 'attr1 should not be nullable')
+ harness.check(type2.nullable(), True, 'attr2 should be nullable')
+
+ # We don't know about type1, but type2, the nullable type, definitely
+ # shouldn't be builtin.
+ harness.check(type2.builtin, False, 'attr2 should not be builtin')
+
+ # Ensure that all attributes of type2 match those in type1, except for:
+ # - names on an ignore list,
+ # - names beginning with '_',
+ # - functions which throw when called with no args, and
+ # - class-level non-callables ("static variables").
+ #
+ # Yes, this is an ugly, fragile hack. But it finds bugs...
+ for attr in dir(type1):
+ if attr.startswith('_') or \
+ attr in ['nullable', 'builtin', 'filename', 'location',
+ 'inner', 'QName', 'getDeps', 'name'] or \
+ (hasattr(type(type1), attr) and not callable(getattr(type1, attr))):
+ continue
+
+ a1 = getattr(type1, attr)
+
+ if callable(a1):
+ try:
+ v1 = a1()
+ except:
+ # Can't call a1 with no args, so skip this attriute.
+ continue
+
+ try:
+ a2 = getattr(type2, attr)
+ except:
+ harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+ continue
+
+ if not callable(a2):
+ harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface))
+ continue
+
+ v2 = a2()
+ harness.check(v2, v1, '%s method return value' % attr)
+ else:
+ try:
+ a2 = getattr(type2, attr)
+ except:
+ harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface))
+ continue
+
+ harness.check(a2, a1, '%s attribute should match' % attr)
diff --git a/dom/bindings/parser/tests/test_nullable_void.py b/dom/bindings/parser/tests/test_nullable_void.py
new file mode 100644
index 000000000..961ff825e
--- /dev/null
+++ b/dom/bindings/parser/tests/test_nullable_void.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface NullableVoid {
+ void? foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_optional_constraints.py b/dom/bindings/parser/tests/test_optional_constraints.py
new file mode 100644
index 000000000..6217465ce
--- /dev/null
+++ b/dom/bindings/parser/tests/test_optional_constraints.py
@@ -0,0 +1,30 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface OptionalConstraints1 {
+ void foo(optional byte arg1, byte arg2);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(not threw,
+ "Should not have thrown on non-optional argument following "
+ "optional argument.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface OptionalConstraints2 {
+ void foo(optional byte arg1 = 1, optional byte arg2 = 2,
+ optional byte arg3, optional byte arg4 = 4,
+ optional byte arg5, optional byte arg6 = 9);
+ };
+ """)
+ results = parser.finish()
+ args = results[0].members[0].signatures()[0][1]
+ harness.check(len(args), 6, "Should have 6 arguments")
+ harness.check(args[5].defaultValue.value, 9,
+ "Should have correct default value")
diff --git a/dom/bindings/parser/tests/test_overload.py b/dom/bindings/parser/tests/test_overload.py
new file mode 100644
index 000000000..3c680ad52
--- /dev/null
+++ b/dom/bindings/parser/tests/test_overload.py
@@ -0,0 +1,60 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestOverloads {
+ void basic();
+ void basic(long arg1);
+ boolean abitharder(TestOverloads foo);
+ boolean abitharder(boolean foo);
+ void abitharder(ArrayBuffer? foo);
+ void withVariadics(long... numbers);
+ void withVariadics(TestOverloads iface);
+ void withVariadics(long num, TestOverloads iface);
+ void optionalTest();
+ void optionalTest(optional long num1, long num2);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestOverloads interface parsed without error.")
+ harness.check(len(results), 1, "Should be one production.")
+ iface = results[0]
+ harness.ok(isinstance(iface, WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ harness.check(iface.identifier.QName(), "::TestOverloads", "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestOverloads", "Interface has the right name")
+ harness.check(len(iface.members), 4, "Expect %s members" % 4)
+
+ member = iface.members[0]
+ harness.check(member.identifier.QName(), "::TestOverloads::basic", "Method has the right QName")
+ harness.check(member.identifier.name, "basic", "Method has the right name")
+ harness.check(member.hasOverloads(), True, "Method has overloads")
+
+ signatures = member.signatures()
+ harness.check(len(signatures), 2, "Method should have 2 signatures")
+
+ (retval, argumentSet) = signatures[0]
+
+ harness.check(str(retval), "Void", "Expect a void retval")
+ harness.check(len(argumentSet), 0, "Expect an empty argument set")
+
+ (retval, argumentSet) = signatures[1]
+ harness.check(str(retval), "Void", "Expect a void retval")
+ harness.check(len(argumentSet), 1, "Expect an argument set with one argument")
+
+ argument = argumentSet[0]
+ harness.ok(isinstance(argument, WebIDL.IDLArgument),
+ "Should be an IDLArgument")
+ harness.check(argument.identifier.QName(), "::TestOverloads::basic::arg1", "Argument has the right QName")
+ harness.check(argument.identifier.name, "arg1", "Argument has the right name")
+ harness.check(str(argument.type), "Long", "Argument has the right type")
+
+ member = iface.members[3]
+ harness.check(len(member.overloadsForArgCount(0)), 1,
+ "Only one overload for no args")
+ harness.check(len(member.overloadsForArgCount(1)), 0,
+ "No overloads for one arg")
+ harness.check(len(member.overloadsForArgCount(2)), 1,
+ "Only one overload for two args")
diff --git a/dom/bindings/parser/tests/test_promise.py b/dom/bindings/parser/tests/test_promise.py
new file mode 100644
index 000000000..55bc07680
--- /dev/null
+++ b/dom/bindings/parser/tests/test_promise.py
@@ -0,0 +1,63 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ legacycaller Promise<any> foo();
+ };
+ """)
+ results = parser.finish()
+
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow Promise return values for legacycaller.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ Promise<any> foo();
+ long foo(long arg);
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow overloads which have both Promise and "
+ "non-Promise return types.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ long foo(long arg);
+ Promise<any> foo();
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow overloads which have both Promise and "
+ "non-Promise return types.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface _Promise {};
+ interface A {
+ Promise<any> foo();
+ Promise<any> foo(long arg);
+ };
+ """)
+ results = parser.finish();
+
+ harness.ok(True,
+ "Should allow overloads which only have Promise and return "
+ "types.")
diff --git a/dom/bindings/parser/tests/test_prototype_ident.py b/dom/bindings/parser/tests/test_prototype_ident.py
new file mode 100644
index 000000000..d3932b54f
--- /dev/null
+++ b/dom/bindings/parser/tests/test_prototype_ident.py
@@ -0,0 +1,80 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface TestIface {
+ static attribute boolean prototype;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "The identifier of a static attribute must not be 'prototype'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestIface {
+ static boolean prototype();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "The identifier of a static operation must not be 'prototype'")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface TestIface {
+ const boolean prototype = true;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "The identifier of a constant must not be 'prototype'")
+
+ # Make sure that we can parse non-static attributes with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ attribute boolean prototype;
+ };
+ """)
+ results = parser.finish()
+
+ testIface = results[0];
+ harness.check(testIface.members[0].isStatic(), False, "Attribute should not be static")
+ harness.check(testIface.members[0].identifier.name, "prototype", "Attribute identifier should be 'prototype'")
+
+ # Make sure that we can parse non-static operations with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse("""
+ interface TestIface {
+ boolean prototype();
+ };
+ """)
+ results = parser.finish()
+
+ testIface = results[0];
+ harness.check(testIface.members[0].isStatic(), False, "Operation should not be static")
+ harness.check(testIface.members[0].identifier.name, "prototype", "Operation identifier should be 'prototype'")
+
+ # Make sure that we can parse dictionary members with 'prototype' as identifier.
+ parser = parser.reset()
+ parser.parse("""
+ dictionary TestDict {
+ boolean prototype;
+ };
+ """)
+ results = parser.finish()
+
+ testDict = results[0];
+ harness.check(testDict.members[0].identifier.name, "prototype", "Dictionary member should be 'prototype'")
+
diff --git a/dom/bindings/parser/tests/test_putForwards.py b/dom/bindings/parser/tests/test_putForwards.py
new file mode 100644
index 000000000..86a1bf115
--- /dev/null
+++ b/dom/bindings/parser/tests/test_putForwards.py
@@ -0,0 +1,107 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] readonly attribute long A;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] readonly attribute J A;
+ };
+ interface J {
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=B] static readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ callback interface I {
+ [PutForwards=B] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface I {
+ [PutForwards=C] readonly attribute J A;
+ [PutForwards=C] readonly attribute J B;
+ };
+ interface J {
+ [PutForwards=D] readonly attribute K C;
+ };
+ interface K {
+ [PutForwards=A] readonly attribute I D;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_replaceable.py b/dom/bindings/parser/tests/test_replaceable.py
new file mode 100644
index 000000000..93ee42ed9
--- /dev/null
+++ b/dom/bindings/parser/tests/test_replaceable.py
@@ -0,0 +1,58 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+def should_throw(parser, harness, message, code):
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse(code)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown: %s" % message)
+
+
+def WebIDLTest(parser, harness):
+ # The [Replaceable] extended attribute MUST take no arguments.
+ should_throw(parser, harness, "no arguments", """
+ interface I {
+ [Replaceable=X] readonly attribute long A;
+ };
+ """)
+
+ # An attribute with the [Replaceable] extended attribute MUST NOT also be
+ # declared with the [PutForwards] extended attribute.
+ should_throw(parser, harness, "PutForwards", """
+ interface I {
+ [PutForwards=B, Replaceable] readonly attribute J A;
+ };
+ interface J {
+ attribute long B;
+ };
+ """)
+
+ # The [Replaceable] extended attribute MUST NOT be used on an attribute
+ # that is not read only.
+ should_throw(parser, harness, "writable attribute", """
+ interface I {
+ [Replaceable] attribute long A;
+ };
+ """)
+
+ # The [Replaceable] extended attribute MUST NOT be used on a static
+ # attribute.
+ should_throw(parser, harness, "static attribute", """
+ interface I {
+ [Replaceable] static readonly attribute long A;
+ };
+ """)
+
+ # The [Replaceable] extended attribute MUST NOT be used on an attribute
+ # declared on a callback interface.
+ should_throw(parser, harness, "callback interface", """
+ callback interface I {
+ [Replaceable] readonly attribute long A;
+ };
+ """)
diff --git a/dom/bindings/parser/tests/test_sanity.py b/dom/bindings/parser/tests/test_sanity.py
new file mode 100644
index 000000000..d3184c007
--- /dev/null
+++ b/dom/bindings/parser/tests/test_sanity.py
@@ -0,0 +1,7 @@
+def WebIDLTest(parser, harness):
+ parser.parse("")
+ parser.finish()
+ harness.ok(True, "Parsing nothing doesn't throw.")
+ parser.parse("interface Foo {};")
+ parser.finish()
+ harness.ok(True, "Parsing a silly interface doesn't throw.")
diff --git a/dom/bindings/parser/tests/test_securecontext_extended_attribute.py b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
new file mode 100644
index 000000000..084f19fa7
--- /dev/null
+++ b/dom/bindings/parser/tests/test_securecontext_extended_attribute.py
@@ -0,0 +1,332 @@
+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]")
+
+ # Test SecureContext and NoInterfaceObject
+ parser = parser.reset()
+ parser.parse("""
+ [NoInterfaceObject, SecureContext]
+ interface TestSecureContextNoInterfaceObject {
+ void testSecureMethod(byte foo);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results[0].members), 1, "TestSecureContextNoInterfaceObject should have only one member")
+ harness.ok(results[0].getExtendedAttribute("SecureContext"),
+ "Interface should have [SecureContext] extended attribute")
+ harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"),
+ "Interface member should have [SecureContext] extended attribute")
diff --git a/dom/bindings/parser/tests/test_special_method_signature_mismatch.py b/dom/bindings/parser/tests/test_special_method_signature_mismatch.py
new file mode 100644
index 000000000..5ea1743d3
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_method_signature_mismatch.py
@@ -0,0 +1,294 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch1 {
+ getter long long foo(long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch2 {
+ getter void foo(unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch3 {
+ getter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch4 {
+ getter boolean foo(unsigned long... index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch5 {
+ getter boolean foo(optional unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch6 {
+ getter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch7 {
+ deleter long long foo(long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch9 {
+ deleter boolean foo(unsigned long index, boolean extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch10 {
+ deleter boolean foo(unsigned long... index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch11 {
+ deleter boolean foo(optional unsigned long index);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch12 {
+ deleter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch13 {
+ setter long long foo(long index, long long value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch15 {
+ setter boolean foo(unsigned long index, boolean value, long long extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch16 {
+ setter boolean foo(unsigned long index, boolean... value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch17 {
+ setter boolean foo(unsigned long index, optional boolean value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch18 {
+ setter boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch20 {
+ creator long long foo(long index, long long value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch22 {
+ creator boolean foo(unsigned long index, boolean value, long long extraArg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch23 {
+ creator boolean foo(unsigned long index, boolean... value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch24 {
+ creator boolean foo(unsigned long index, optional boolean value);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodSignatureMismatch25 {
+ creator boolean foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_special_methods.py b/dom/bindings/parser/tests/test_special_methods.py
new file mode 100644
index 000000000..5b45c3399
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_methods.py
@@ -0,0 +1,85 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface SpecialMethods {
+ getter long long (unsigned long index);
+ setter long long (unsigned long index, long long value);
+ creator long long (unsigned long index, long long value);
+ getter boolean (DOMString name);
+ setter boolean (DOMString name, boolean value);
+ creator boolean (DOMString name, boolean value);
+ deleter boolean (DOMString name);
+ };
+
+ interface SpecialMethodsCombination {
+ setter creator long long (unsigned long index, long long value);
+ getter deleter boolean (DOMString name);
+ setter creator boolean (DOMString name, boolean value);
+ };
+ """)
+
+ results = parser.finish()
+
+ def checkMethod(method, QName, name,
+ static=False, getter=False, setter=False, creator=False,
+ deleter=False, legacycaller=False, stringifier=False):
+ harness.ok(isinstance(method, WebIDL.IDLMethod),
+ "Should be an IDLMethod")
+ harness.check(method.identifier.QName(), QName, "Method has the right QName")
+ harness.check(method.identifier.name, name, "Method has the right name")
+ harness.check(method.isStatic(), static, "Method has the correct static value")
+ harness.check(method.isGetter(), getter, "Method has the correct getter value")
+ harness.check(method.isSetter(), setter, "Method has the correct setter value")
+ harness.check(method.isCreator(), creator, "Method has the correct creator value")
+ harness.check(method.isDeleter(), deleter, "Method has the correct deleter value")
+ harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
+ harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
+
+ harness.check(len(results), 2, "Expect 2 interfaces")
+
+ iface = results[0]
+ harness.check(len(iface.members), 7, "Expect 7 members")
+
+ checkMethod(iface.members[0], "::SpecialMethods::__indexedgetter", "__indexedgetter",
+ getter=True)
+ checkMethod(iface.members[1], "::SpecialMethods::__indexedsetter", "__indexedsetter",
+ setter=True)
+ checkMethod(iface.members[2], "::SpecialMethods::__indexedcreator", "__indexedcreator",
+ creator=True)
+ checkMethod(iface.members[3], "::SpecialMethods::__namedgetter", "__namedgetter",
+ getter=True)
+ checkMethod(iface.members[4], "::SpecialMethods::__namedsetter", "__namedsetter",
+ setter=True)
+ checkMethod(iface.members[5], "::SpecialMethods::__namedcreator", "__namedcreator",
+ creator=True)
+ checkMethod(iface.members[6], "::SpecialMethods::__nameddeleter", "__nameddeleter",
+ deleter=True)
+
+ iface = results[1]
+ harness.check(len(iface.members), 3, "Expect 3 members")
+
+ checkMethod(iface.members[0], "::SpecialMethodsCombination::__indexedsettercreator",
+ "__indexedsettercreator", setter=True, creator=True)
+ checkMethod(iface.members[1], "::SpecialMethodsCombination::__namedgetterdeleter",
+ "__namedgetterdeleter", getter=True, deleter=True)
+ checkMethod(iface.members[2], "::SpecialMethodsCombination::__namedsettercreator",
+ "__namedsettercreator", setter=True, creator=True)
+
+ parser = parser.reset();
+
+ threw = False
+ try:
+ parser.parse(
+ """
+ interface IndexedDeleter {
+ deleter void(unsigned long index);
+ };
+ """)
+ parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "There are no indexed deleters")
+
+
diff --git a/dom/bindings/parser/tests/test_special_methods_uniqueness.py b/dom/bindings/parser/tests/test_special_methods_uniqueness.py
new file mode 100644
index 000000000..42e2c5bb7
--- /dev/null
+++ b/dom/bindings/parser/tests/test_special_methods_uniqueness.py
@@ -0,0 +1,62 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ getter deleter boolean (DOMString name);
+ getter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ deleter boolean (DOMString name);
+ getter deleter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ setter creator boolean (DOMString name);
+ creator boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ threw = False
+ try:
+ parser.parse("""
+ interface SpecialMethodUniqueness1 {
+ setter boolean (DOMString name);
+ creator setter boolean (DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_stringifier.py b/dom/bindings/parser/tests/test_stringifier.py
new file mode 100644
index 000000000..14c2c5226
--- /dev/null
+++ b/dom/bindings/parser/tests/test_stringifier.py
@@ -0,0 +1,46 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.ok(isinstance(results[0].members[0], WebIDL.IDLMethod),
+ "Stringifer should be method")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ stringifier;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow two 'stringifier;'")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ interface TestStringifier {
+ stringifier;
+ stringifier DOMString foo();
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should not allow a 'stringifier;' and a 'stringifier()'")
+
diff --git a/dom/bindings/parser/tests/test_treatNonCallableAsNull.py b/dom/bindings/parser/tests/test_treatNonCallableAsNull.py
new file mode 100644
index 000000000..7a0bde8a6
--- /dev/null
+++ b/dom/bindings/parser/tests/test_treatNonCallableAsNull.py
@@ -0,0 +1,71 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ [TreatNonCallableAsNull] callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull1 {
+ attribute Function? onfoo;
+ attribute Function onbar;
+ };
+ """)
+
+ results = parser.finish()
+
+ iface = results[1]
+ attr = iface.members[0]
+ harness.check(attr.type.treatNonCallableAsNull(), True, "Got the expected value")
+ attr = iface.members[1]
+ harness.check(attr.type.treatNonCallableAsNull(), False, "Got the expected value")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ interface TestTreatNonCallableAsNull2 {
+ [TreatNonCallableAsNull] attribute Function onfoo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ callback Function = any(any... arguments);
+
+ [TreatNonCallableAsNull]
+ interface TestTreatNonCallableAsNull3 {
+ attribute Function onfoo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+
+ threw = False
+ try:
+ parser.parse("""
+ [TreatNonCallableAsNull, TreatNonObjectAsNull]
+ callback Function = any(any... arguments);
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_typedef.py b/dom/bindings/parser/tests/test_typedef.py
new file mode 100644
index 000000000..8921985c5
--- /dev/null
+++ b/dom/bindings/parser/tests/test_typedef.py
@@ -0,0 +1,76 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ typedef long mylong;
+ typedef long? mynullablelong;
+ interface Foo {
+ const mylong X = 5;
+ const mynullablelong Y = 7;
+ const mynullablelong Z = null;
+ void foo(mylong arg);
+ };
+ """)
+
+ results = parser.finish()
+
+ harness.check(results[2].members[1].type.name, "LongOrNull",
+ "Should expand typedefs")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef long? mynullablelong;
+ interface Foo {
+ void foo(mynullablelong? Y);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable arg.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ typedef long? mynullablelong;
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on nullable inside nullable const.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Foo {
+ const mynullablelong? X = 5;
+ };
+ typedef long? mynullablelong;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on nullable inside nullable const typedef "
+ "after interface.")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Foo {
+ const mylong X = 5;
+ };
+ typedef long mylong;
+ """)
+
+ results = parser.finish()
+
+ harness.check(results[0].members[0].type.name, "Long",
+ "Should expand typedefs that come before interface")
diff --git a/dom/bindings/parser/tests/test_unenumerable_own_properties.py b/dom/bindings/parser/tests/test_unenumerable_own_properties.py
new file mode 100644
index 000000000..d017d5ce0
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unenumerable_own_properties.py
@@ -0,0 +1,64 @@
+def WebIDLTest(parser, harness):
+
+ parser.parse(
+ """
+ interface Foo {};
+ [LegacyUnenumerableNamedProperties]
+ interface Bar : Foo {
+ getter long(DOMString name);
+ };
+ interface Baz : Bar {
+ getter long(DOMString name);
+ };
+ """);
+ results = parser.finish();
+ harness.check(len(results), 3, "Should have three interfaces")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyUnenumerableNamedProperties]
+ interface NoNamedGetter {
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyUnenumerableNamedProperties=Foo]
+ interface ShouldNotHaveArg {
+ getter long(DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [LegacyUnenumerableNamedProperties]
+ interface Foo {
+ getter long(DOMString name);
+ };
+ interface Bar : Foo {};
+ [LegacyUnenumerableNamedProperties]
+ interface Baz : Bar {
+ getter long(DOMString name);
+ };
+ """)
+
+ results = parser.finish()
+ except Exception, x:
+ threw = True
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_unforgeable.py b/dom/bindings/parser/tests/test_unforgeable.py
new file mode 100644
index 000000000..3787e8c6a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -0,0 +1,253 @@
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ const short foo = 10;
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties even if we have a constant with "
+ "the same name.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ static attribute short foo;
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties even if we have a static attribute "
+ "with the same name.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ static void foo();
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should be able to inherit from an interface with "
+ "[Unforgeable] properties even if we have a static operation "
+ "with the same name.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ void foo();
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable attribute on "
+ "parent with operation.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ void foo();
+ };
+ interface Parent {
+ [Unforgeable] void foo();
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable operation on "
+ "parent with operation.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ attribute short foo;
+ };
+ interface Parent {
+ [Unforgeable] readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable attribute on "
+ "parent with attribute.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ attribute short foo;
+ };
+ interface Parent {
+ [Unforgeable] void foo();
+ };
+ """)
+
+ results = parser.finish()
+ except Exception,x:
+ threw = True
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable operation on "
+ "parent with attribute.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent {};
+ interface Consequential {
+ [Unforgeable] readonly attribute long foo;
+ };
+ Parent implements Consequential;
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 4,
+ "Should be able to inherit from an interface with a "
+ "consequential interface with [Unforgeable] properties.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ void foo();
+ };
+ interface Parent {};
+ interface Consequential {
+ [Unforgeable] readonly attribute long foo;
+ };
+ Parent implements Consequential;
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown when shadowing unforgeable attribute "
+ "of parent's consequential interface.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent : GrandParent {};
+ interface GrandParent {};
+ interface Consequential {
+ [Unforgeable] readonly attribute long foo;
+ };
+ GrandParent implements Consequential;
+ interface ChildConsequential {
+ void foo();
+ };
+ Child implements ChildConsequential;
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown when our consequential interface shadows unforgeable attribute "
+ "of ancestor's consequential interface.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface Child : Parent {
+ };
+ interface Parent : GrandParent {};
+ interface GrandParent {};
+ interface Consequential {
+ [Unforgeable] void foo();
+ };
+ GrandParent implements Consequential;
+ interface ChildConsequential {
+ void foo();
+ };
+ Child implements ChildConsequential;
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown when our consequential interface shadows unforgeable operation "
+ "of ancestor's consequential interface.")
+
+ parser = parser.reset();
+ parser.parse("""
+ interface iface {
+ [Unforgeable] attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ harness.check(len(results), 1,
+ "Should allow writable [Unforgeable] attribute.")
+
+ parser = parser.reset();
+ threw = False
+ try:
+ parser.parse("""
+ interface iface {
+ [Unforgeable] static readonly attribute long foo;
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown for static [Unforgeable] attribute.")
diff --git a/dom/bindings/parser/tests/test_union.py b/dom/bindings/parser/tests/test_union.py
new file mode 100644
index 000000000..9c4f2a56a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union.py
@@ -0,0 +1,168 @@
+import WebIDL
+import itertools
+import string
+
+# We'd like to use itertools.chain but it's 2.6 or higher.
+def chain(*iterables):
+ # chain('ABC', 'DEF') --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
+
+# We'd like to use itertools.combinations but it's 2.6 or higher.
+def combinations(iterable, r):
+ # combinations('ABCD', 2) --> AB AC AD BC BD CD
+ # combinations(range(4), 3) --> 012 013 023 123
+ pool = tuple(iterable)
+ n = len(pool)
+ if r > n:
+ return
+ indices = range(r)
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != i + n - r:
+ break
+ else:
+ return
+ indices[i] += 1
+ for j in range(i+1, r):
+ indices[j] = indices[j-1] + 1
+ yield tuple(pool[i] for i in indices)
+
+# We'd like to use itertools.combinations_with_replacement but it's 2.7 or
+# higher.
+def combinations_with_replacement(iterable, r):
+ # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
+ pool = tuple(iterable)
+ n = len(pool)
+ if not n and r:
+ return
+ indices = [0] * r
+ yield tuple(pool[i] for i in indices)
+ while True:
+ for i in reversed(range(r)):
+ if indices[i] != n - 1:
+ break
+ else:
+ return
+ indices[i:] = [indices[i] + 1] * (r - i)
+ yield tuple(pool[i] for i in indices)
+
+def WebIDLTest(parser, harness):
+ types = ["float",
+ "double",
+ "short",
+ "unsigned short",
+ "long",
+ "unsigned long",
+ "long long",
+ "unsigned long long",
+ "boolean",
+ "byte",
+ "octet",
+ "DOMString",
+ "ByteString",
+ "USVString",
+ #"sequence<float>",
+ "object",
+ "ArrayBuffer",
+ #"Date",
+ "TestInterface1",
+ "TestInterface2"]
+
+ testPre = """
+ interface TestInterface1 {
+ };
+ interface TestInterface2 {
+ };
+ """
+
+ interface = testPre + """
+ interface PrepareForTest {
+ """
+ for (i, type) in enumerate(types):
+ interface += string.Template("""
+ readonly attribute ${type} attr${i};
+ """).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+
+ parser.parse(interface)
+ results = parser.finish()
+
+ iface = results[2]
+
+ parser = parser.reset()
+
+ def typesAreDistinguishable(t):
+ return all(u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+ def typesAreNotDistinguishable(t):
+ return any(not u[0].isDistinguishableFrom(u[1]) for u in combinations(t, 2))
+ def unionTypeName(t):
+ if len(t) > 2:
+ t[0:2] = [unionTypeName(t[0:2])]
+ return "(" + " or ".join(t) + ")"
+
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def unionTypes(typeCombinations, predicate):
+ for c in typeCombinations:
+ if predicate(t[1] for t in c):
+ yield unionTypeName([t[0] for t in c])
+
+ # We limit invalid union types with a union member type to the subset of 3
+ # types with one invalid combination.
+ # typeCombinations is an iterable of tuples containing the name of the type
+ # as a string and the parsed IDL type.
+ def invalidUnionWithUnion(typeCombinations):
+ for c in typeCombinations:
+ if (typesAreNotDistinguishable((c[0][1], c[1][1])) and
+ typesAreDistinguishable((c[1][1], c[2][1])) and
+ typesAreDistinguishable((c[0][1], c[2][1]))):
+ yield unionTypeName([t[0] for t in c])
+
+ # Create a list of tuples containing the name of the type as a string and
+ # the parsed IDL type.
+ types = zip(types, (a.type for a in iface.members))
+
+ validUnionTypes = chain(unionTypes(combinations(types, 2), typesAreDistinguishable),
+ unionTypes(combinations(types, 3), typesAreDistinguishable))
+ invalidUnionTypes = chain(unionTypes(combinations_with_replacement(types, 2), typesAreNotDistinguishable),
+ invalidUnionWithUnion(combinations(types, 3)))
+ interface = testPre + """
+ interface TestUnion {
+ """
+ for (i, type) in enumerate(validUnionTypes):
+ interface += string.Template("""
+ void method${i}(${type} arg);
+ ${type} returnMethod${i}();
+ attribute ${type} attr${i};
+ void optionalMethod${i}(${type}? arg);
+ """).substitute(i=i, type=type)
+ interface += """
+ };
+ """
+ parser.parse(interface)
+ results = parser.finish()
+
+ parser = parser.reset()
+
+ for invalid in invalidUnionTypes:
+ interface = testPre + string.Template("""
+ interface TestUnion {
+ void method(${type} arg);
+ };
+ """).substitute(type=invalid)
+
+ threw = False
+ try:
+ parser.parse(interface)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
+
+ parser = parser.reset()
diff --git a/dom/bindings/parser/tests/test_union_any.py b/dom/bindings/parser/tests/test_union_any.py
new file mode 100644
index 000000000..e34cadab4
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union_any.py
@@ -0,0 +1,14 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface AnyNotInUnion {
+ void foo((any or DOMString) arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown.")
diff --git a/dom/bindings/parser/tests/test_union_nullable.py b/dom/bindings/parser/tests/test_union_nullable.py
new file mode 100644
index 000000000..08430a94a
--- /dev/null
+++ b/dom/bindings/parser/tests/test_union_nullable.py
@@ -0,0 +1,53 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface OneNullableInUnion {
+ void foo((object? or DOMString?) arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Two nullable member types of a union should have thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse("""
+ interface NullableInNullableUnion {
+ void foo((object? or DOMString)? arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "A nullable union type with a nullable member type should have "
+ "thrown.")
+
+ parser.reset()
+ threw = False
+
+ try:
+ parser.parse("""
+ interface NullableInUnionNullableUnionHelper {
+ };
+ interface NullableInUnionNullableUnion {
+ void foo(((object? or DOMString) or NullableInUnionNullableUnionHelper)? arg);
+ };
+ """)
+
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "A nullable union type with a nullable member type should have "
+ "thrown.")
diff --git a/dom/bindings/parser/tests/test_usvstring.py b/dom/bindings/parser/tests/test_usvstring.py
new file mode 100644
index 000000000..3a1369abd
--- /dev/null
+++ b/dom/bindings/parser/tests/test_usvstring.py
@@ -0,0 +1,36 @@
+# -*- coding: UTF-8 -*-
+
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ interface TestUSVString {
+ attribute USVString svs;
+ };
+ """)
+
+ results = parser.finish();
+
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterface),
+ "Should be an IDLInterface")
+ iface = results[0]
+ harness.check(iface.identifier.QName(), "::TestUSVString",
+ "Interface has the right QName")
+ harness.check(iface.identifier.name, "TestUSVString",
+ "Interface has the right name")
+ harness.check(iface.parent, None, "Interface has no parent")
+
+ members = iface.members
+ harness.check(len(members), 1, "Should be one member")
+
+ attr = members[0]
+ harness.ok(isinstance(attr, WebIDL.IDLAttribute), "Should be an IDLAttribute")
+ harness.check(attr.identifier.QName(), "::TestUSVString::svs",
+ "Attr has correct QName")
+ harness.check(attr.identifier.name, "svs", "Attr has correct name")
+ harness.check(str(attr.type), "USVString",
+ "Attr type is the correct name")
+ harness.ok(attr.type.isUSVString(), "Should be USVString type")
+ harness.ok(attr.type.isString(), "Should be String collective type")
+ harness.ok(not attr.type.isDOMString(), "Should be not be DOMString type")
diff --git a/dom/bindings/parser/tests/test_variadic_callback.py b/dom/bindings/parser/tests/test_variadic_callback.py
new file mode 100644
index 000000000..d9a78db20
--- /dev/null
+++ b/dom/bindings/parser/tests/test_variadic_callback.py
@@ -0,0 +1,10 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("""
+ callback TestVariadicCallback = any(any... arguments);
+ """)
+
+ results = parser.finish()
+
+ harness.ok(True, "TestVariadicCallback callback parsed without error.")
diff --git a/dom/bindings/parser/tests/test_variadic_constraints.py b/dom/bindings/parser/tests/test_variadic_constraints.py
new file mode 100644
index 000000000..7448e40d5
--- /dev/null
+++ b/dom/bindings/parser/tests/test_variadic_constraints.py
@@ -0,0 +1,63 @@
+def WebIDLTest(parser, harness):
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints1 {
+ void foo(byte... arg1, byte arg2);
+ };
+ """)
+ results = parser.finish()
+
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on variadic argument followed by required "
+ "argument.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints2 {
+ void foo(byte... arg1, optional byte arg2);
+ };
+ """)
+ results = parser.finish();
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on variadic argument followed by optional "
+ "argument.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints3 {
+ void foo(optional byte... arg1);
+ };
+ """)
+ results = parser.finish()
+
+ except:
+ threw = True
+
+ harness.ok(threw,
+ "Should have thrown on variadic argument explicitly flagged as "
+ "optional.")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface VariadicConstraints4 {
+ void foo(byte... arg1 = 0);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+
+ harness.ok(threw, "Should have thrown on variadic argument with default value.")