summaryrefslogtreecommitdiffstats
path: root/dom/bindings/Configuration.py
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings/Configuration.py')
-rw-r--r--dom/bindings/Configuration.py791
1 files changed, 791 insertions, 0 deletions
diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py
new file mode 100644
index 000000000..5c96580a1
--- /dev/null
+++ b/dom/bindings/Configuration.py
@@ -0,0 +1,791 @@
+# 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/.
+
+from WebIDL import IDLImplementsStatement
+import os
+from collections import defaultdict
+
+autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
+
+
+class DescriptorProvider:
+ """
+ A way of getting descriptors for interface names. Subclasses must
+ have a getDescriptor method callable with the interface name only.
+ """
+ def __init__(self):
+ pass
+
+
+class Configuration(DescriptorProvider):
+ """
+ Represents global configuration state based on IDL parse data and
+ the configuration file.
+ """
+ def __init__(self, filename, parseData, generatedEvents=[]):
+ DescriptorProvider.__init__(self)
+
+ # Read the configuration file.
+ glbl = {}
+ execfile(filename, glbl)
+ config = glbl['DOMInterfaces']
+
+ # Build descriptors for all the interfaces we have in the parse data.
+ # This allows callers to specify a subset of interfaces by filtering
+ # |parseData|.
+ self.descriptors = []
+ self.interfaces = {}
+ self.descriptorsByName = {}
+ self.optimizedOutDescriptorNames = set()
+ self.generatedEvents = generatedEvents
+ self.maxProtoChainLength = 0
+ for thing in parseData:
+ if isinstance(thing, IDLImplementsStatement):
+ # Our build system doesn't support dep build involving
+ # addition/removal of "implements" statements that appear in a
+ # different .webidl file than their LHS interface. Make sure we
+ # don't have any of those.
+ #
+ # But whitelist a RHS that is LegacyQueryInterface,
+ # since people shouldn't be adding any of those.
+ if (thing.implementor.filename() != thing.filename() and
+ thing.implementee.identifier.name != "LegacyQueryInterface"):
+ raise TypeError(
+ "The binding build system doesn't really support "
+ "'implements' statements which don't appear in the "
+ "file in which the left-hand side of the statement is "
+ "defined. Don't do this unless your right-hand side "
+ "is LegacyQueryInterface.\n"
+ "%s\n"
+ "%s" %
+ (thing.location, thing.implementor.location))
+
+ assert not thing.isType()
+
+ if not thing.isInterface() and not thing.isNamespace():
+ continue
+ iface = thing
+ self.interfaces[iface.identifier.name] = iface
+ if iface.identifier.name not in config:
+ # Completely skip consequential interfaces with no descriptor
+ # if they have no interface object because chances are we
+ # don't need to do anything interesting with them.
+ if iface.isConsequential() and not iface.hasInterfaceObject():
+ self.optimizedOutDescriptorNames.add(iface.identifier.name)
+ continue
+ entry = {}
+ else:
+ entry = config[iface.identifier.name]
+ assert not isinstance(entry, list)
+ desc = Descriptor(self, iface, entry)
+ self.descriptors.append(desc)
+ # Setting up descriptorsByName while iterating through interfaces
+ # means we can get the nativeType of iterable interfaces without
+ # having to do multiple loops.
+ assert desc.interface.identifier.name not in self.descriptorsByName
+ self.descriptorsByName[desc.interface.identifier.name] = desc
+
+ # Keep the descriptor list sorted for determinism.
+ self.descriptors.sort(lambda x, y: cmp(x.name, y.name))
+
+
+ self.descriptorsByFile = {}
+ for d in self.descriptors:
+ self.descriptorsByFile.setdefault(d.interface.filename(),
+ []).append(d)
+
+ self.enums = [e for e in parseData if e.isEnum()]
+
+ self.dictionaries = [d for d in parseData if d.isDictionary()]
+ self.callbacks = [c for c in parseData if
+ c.isCallback() and not c.isInterface()]
+
+ # Dictionary mapping from a union type name to a set of filenames where
+ # union types with that name are used.
+ self.filenamesPerUnion = defaultdict(set)
+
+ # Dictionary mapping from a filename to a list of types for
+ # the union types used in that file. If a union type is used
+ # in multiple files then it will be added to the list for the
+ # None key. Note that the list contains a type for every use
+ # of a union type, so there can be multiple entries with union
+ # types that have the same name.
+ self.unionsPerFilename = defaultdict(list)
+
+ for (t, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
+ while True:
+ if t.isMozMap():
+ t = t.inner
+ elif t.unroll() != t:
+ t = t.unroll()
+ elif t.isPromise():
+ t = t.promiseInnerType()
+ else:
+ break
+ if t.isUnion():
+ filenamesForUnion = self.filenamesPerUnion[t.name]
+ if t.filename() not in filenamesForUnion:
+ # We have a to be a bit careful: some of our built-in
+ # typedefs are for unions, and those unions end up with
+ # "<unknown>" as the filename. If that happens, we don't
+ # want to try associating this union with one particular
+ # filename, since there isn't one to associate it with,
+ # really.
+ if t.filename() == "<unknown>":
+ uniqueFilenameForUnion = None
+ elif len(filenamesForUnion) == 0:
+ # This is the first file that we found a union with this
+ # name in, record the union as part of the file.
+ uniqueFilenameForUnion = t.filename()
+ else:
+ # We already found a file that contains a union with
+ # this name.
+ if len(filenamesForUnion) == 1:
+ # This is the first time we found a union with this
+ # name in another file.
+ for f in filenamesForUnion:
+ # Filter out unions with this name from the
+ # unions for the file where we previously found
+ # them.
+ unionsForFilename = self.unionsPerFilename[f]
+ unionsForFilename = filter(lambda u: u.name != t.name,
+ unionsForFilename)
+ if len(unionsForFilename) == 0:
+ del self.unionsPerFilename[f]
+ else:
+ self.unionsPerFilename[f] = unionsForFilename
+ # Unions with this name appear in multiple files, record
+ # the filename as None, so that we can detect that.
+ uniqueFilenameForUnion = None
+ self.unionsPerFilename[uniqueFilenameForUnion].append(t)
+ filenamesForUnion.add(t.filename())
+
+ def getInterface(self, ifname):
+ return self.interfaces[ifname]
+
+ def getDescriptors(self, **filters):
+ """Gets the descriptors that match the given filters."""
+ curr = self.descriptors
+ # Collect up our filters, because we may have a webIDLFile filter that
+ # we always want to apply first.
+ tofilter = []
+ for key, val in filters.iteritems():
+ if key == 'webIDLFile':
+ # Special-case this part to make it fast, since most of our
+ # getDescriptors calls are conditioned on a webIDLFile. We may
+ # not have this key, in which case we have no descriptors
+ # either.
+ curr = self.descriptorsByFile.get(val, [])
+ continue
+ elif key == 'hasInterfaceObject':
+ getter = lambda x: (not x.interface.isExternal() and
+ x.interface.hasInterfaceObject())
+ elif key == 'hasInterfacePrototypeObject':
+ getter = lambda x: (not x.interface.isExternal() and
+ x.interface.hasInterfacePrototypeObject())
+ elif key == 'hasInterfaceOrInterfacePrototypeObject':
+ getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject()
+ elif key == 'isCallback':
+ getter = lambda x: x.interface.isCallback()
+ elif key == 'isExternal':
+ getter = lambda x: x.interface.isExternal()
+ elif key == 'isJSImplemented':
+ getter = lambda x: x.interface.isJSImplemented()
+ elif key == 'isNavigatorProperty':
+ getter = lambda x: x.interface.isNavigatorProperty()
+ elif key == 'isExposedInAnyWorker':
+ getter = lambda x: x.interface.isExposedInAnyWorker()
+ elif key == 'isExposedInWorkerDebugger':
+ getter = lambda x: x.interface.isExposedInWorkerDebugger()
+ elif key == 'isExposedInAnyWorklet':
+ getter = lambda x: x.interface.isExposedInAnyWorklet()
+ elif key == 'isExposedInSystemGlobals':
+ getter = lambda x: x.interface.isExposedInSystemGlobals()
+ elif key == 'isExposedInWindow':
+ getter = lambda x: x.interface.isExposedInWindow()
+ else:
+ # Have to watch out: just closing over "key" is not enough,
+ # since we're about to mutate its value
+ getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
+ tofilter.append((getter, val))
+ for f in tofilter:
+ curr = filter(lambda x: f[0](x) == f[1], curr)
+ return curr
+
+ def getEnums(self, webIDLFile):
+ return filter(lambda e: e.filename() == webIDLFile, self.enums)
+
+ def getDictionaries(self, webIDLFile):
+ return filter(lambda d: d.filename() == webIDLFile, self.dictionaries)
+
+ def getCallbacks(self, webIDLFile):
+ return filter(lambda c: c.filename() == webIDLFile, self.callbacks)
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name.
+ """
+ # We may have optimized out this descriptor, but the chances of anyone
+ # asking about it are then slim. Put the check for that _after_ we've
+ # done our normal lookup. But that means we have to do our normal
+ # lookup in a way that will not throw if it fails.
+ d = self.descriptorsByName.get(interfaceName, None)
+ if d:
+ return d
+
+ if interfaceName in self.optimizedOutDescriptorNames:
+ raise NoSuchDescriptorError(
+ "No descriptor for '%s', which is a mixin ([NoInterfaceObject] "
+ "and a consequential interface) without an explicit "
+ "Bindings.conf annotation." % interfaceName)
+
+ raise NoSuchDescriptorError("For " + interfaceName + " found no matches")
+
+
+class NoSuchDescriptorError(TypeError):
+ def __init__(self, str):
+ TypeError.__init__(self, str)
+
+
+def methodReturnsJSObject(method):
+ assert method.isMethod()
+ if method.returnsPromise():
+ return True
+
+ for signature in method.signatures():
+ returnType = signature[0]
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return True
+
+ return False
+
+
+def MemberIsUnforgeable(member, descriptor):
+ # Note: "or" and "and" return either their LHS or RHS, not
+ # necessarily booleans. Make sure to return a boolean from this
+ # method, because callers will compare its return value to
+ # booleans.
+ return bool((member.isAttr() or member.isMethod()) and
+ not member.isStatic() and
+ (member.isUnforgeable() or
+ descriptor.interface.getExtendedAttribute("Unforgeable")))
+
+
+class Descriptor(DescriptorProvider):
+ """
+ Represents a single descriptor for an interface. See Bindings.conf.
+ """
+ def __init__(self, config, interface, desc):
+ DescriptorProvider.__init__(self)
+ self.config = config
+ self.interface = interface
+
+ self.wantsXrays = (not interface.isExternal() and
+ interface.isExposedInWindow())
+
+ if self.wantsXrays:
+ # We could try to restrict self.wantsXrayExpandoClass further. For
+ # example, we could set it to false if all of our slots store
+ # Gecko-interface-typed things, because we don't use Xray expando
+ # slots for those. But note that we would need to check the types
+ # of not only the members of "interface" but also of all its
+ # ancestors, because those can have members living in our slots too.
+ # For now, do the simple thing.
+ self.wantsXrayExpandoClass = (interface.totalMembersInSlots != 0)
+
+ # Read the desc, and fill in the relevant defaults.
+ ifaceName = self.interface.identifier.name
+ # For generated iterator interfaces for other iterable interfaces, we
+ # just use IterableIterator as the native type, templated on the
+ # nativeType of the iterable interface. That way we can have a
+ # templated implementation for all the duplicated iterator
+ # functionality.
+ if self.interface.isIteratorInterface():
+ itrName = self.interface.iterableInterface.identifier.name
+ itrDesc = self.getDescriptor(itrName)
+ nativeTypeDefault = iteratorNativeType(itrDesc)
+
+ elif self.interface.isExternal():
+ nativeTypeDefault = "nsIDOM" + ifaceName
+ else:
+ nativeTypeDefault = "mozilla::dom::" + ifaceName
+
+ self.nativeType = desc.get('nativeType', nativeTypeDefault)
+ # Now create a version of nativeType that doesn't have extra
+ # mozilla::dom:: at the beginning.
+ prettyNativeType = self.nativeType.split("::")
+ if prettyNativeType[0] == "mozilla":
+ prettyNativeType.pop(0)
+ if prettyNativeType[0] == "dom":
+ prettyNativeType.pop(0)
+ self.prettyNativeType = "::".join(prettyNativeType)
+
+ self.jsImplParent = desc.get('jsImplParent', self.nativeType)
+
+ # Do something sane for JSObject
+ if self.nativeType == "JSObject":
+ headerDefault = "js/TypeDecls.h"
+ elif self.interface.isCallback() or self.interface.isJSImplemented():
+ # A copy of CGHeaders.getDeclarationFilename; we can't
+ # import it here, sadly.
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(self.interface.filename())
+ headerDefault = basename.replace('.webidl', 'Binding.h')
+ else:
+ if not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
+ headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
+ elif self.interface.isIteratorInterface():
+ headerDefault = "mozilla/dom/IterableIterator.h"
+ else:
+ headerDefault = self.nativeType
+ headerDefault = headerDefault.replace("::", "/") + ".h"
+ self.headerFile = desc.get('headerFile', headerDefault)
+ self.headerIsDefault = self.headerFile == headerDefault
+ if self.jsImplParent == self.nativeType:
+ self.jsImplParentHeader = self.headerFile
+ else:
+ self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
+
+ self.notflattened = desc.get('notflattened', False)
+ self.register = desc.get('register', True)
+
+ self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False)
+
+ # If we're concrete, we need to crawl our ancestor interfaces and mark
+ # them as having a concrete descendant.
+ self.concrete = (not self.interface.isExternal() and
+ not self.interface.isCallback() and
+ not self.interface.isNamespace() and
+ desc.get('concrete', True))
+ self.hasUnforgeableMembers = (self.concrete and
+ any(MemberIsUnforgeable(m, self) for m in
+ self.interface.members))
+ self.operations = {
+ 'IndexedGetter': None,
+ 'IndexedSetter': None,
+ 'IndexedCreator': None,
+ 'IndexedDeleter': None,
+ 'NamedGetter': None,
+ 'NamedSetter': None,
+ 'NamedCreator': None,
+ 'NamedDeleter': None,
+ 'Stringifier': None,
+ 'LegacyCaller': None,
+ 'Jsonifier': None
+ }
+
+ # Stringifiers and jsonifiers need to be set up whether an interface is
+ # concrete or not, because they're actually prototype methods and hence
+ # can apply to instances of descendant interfaces. Legacy callers and
+ # named/indexed operations only need to be set up on concrete
+ # interfaces, since they affect the JSClass we end up using, not the
+ # prototype object.
+ def addOperation(operation, m):
+ if not self.operations[operation]:
+ self.operations[operation] = m
+
+ # Since stringifiers go on the prototype, we only need to worry
+ # about our own stringifier, not those of our ancestor interfaces.
+ if not self.interface.isExternal():
+ for m in self.interface.members:
+ if m.isMethod() and m.isStringifier():
+ addOperation('Stringifier', m)
+ if m.isMethod() and m.isJsonifier():
+ addOperation('Jsonifier', m)
+
+ if self.concrete:
+ self.proxy = False
+ iface = self.interface
+ for m in iface.members:
+ # Don't worry about inheriting legacycallers either: in
+ # practice these are on most-derived prototypes.
+ if m.isMethod() and m.isLegacycaller():
+ if not m.isIdentifierLess():
+ raise TypeError("We don't support legacycaller with "
+ "identifier.\n%s" % m.location)
+ if len(m.signatures()) != 1:
+ raise TypeError("We don't support overloaded "
+ "legacycaller.\n%s" % m.location)
+ addOperation('LegacyCaller', m)
+ while iface:
+ for m in iface.members:
+ if not m.isMethod():
+ continue
+
+ def addIndexedOrNamedOperation(operation, m):
+ if m.isIndexed():
+ operation = 'Indexed' + operation
+ else:
+ assert m.isNamed()
+ operation = 'Named' + operation
+ addOperation(operation, m)
+
+ if m.isGetter():
+ addIndexedOrNamedOperation('Getter', m)
+ if m.isSetter():
+ addIndexedOrNamedOperation('Setter', m)
+ if m.isCreator():
+ addIndexedOrNamedOperation('Creator', m)
+ if m.isDeleter():
+ addIndexedOrNamedOperation('Deleter', m)
+ if m.isLegacycaller() and iface != self.interface:
+ raise TypeError("We don't support legacycaller on "
+ "non-leaf interface %s.\n%s" %
+ (iface, iface.location))
+
+ iface.setUserData('hasConcreteDescendant', True)
+ iface = iface.parent
+
+ self.proxy = (self.supportsIndexedProperties() or
+ (self.supportsNamedProperties() and
+ not self.hasNamedPropertiesObject) or
+ self.hasNonOrdinaryGetPrototypeOf())
+
+ if self.proxy:
+ if (not self.operations['IndexedGetter'] and
+ (self.operations['IndexedSetter'] or
+ self.operations['IndexedDeleter'] or
+ self.operations['IndexedCreator'])):
+ raise SyntaxError("%s supports indexed properties but does "
+ "not have an indexed getter.\n%s" %
+ (self.interface, self.interface.location))
+ if (not self.operations['NamedGetter'] and
+ (self.operations['NamedSetter'] or
+ self.operations['NamedDeleter'] or
+ self.operations['NamedCreator'])):
+ raise SyntaxError("%s supports named properties but does "
+ "not have a named getter.\n%s" %
+ (self.interface, self.interface.location))
+ iface = self.interface
+ while iface:
+ iface.setUserData('hasProxyDescendant', True)
+ iface = iface.parent
+
+ if desc.get('wantsQI', None) is not None:
+ self._wantsQI = desc.get('wantsQI', None)
+ self.wrapperCache = (not self.interface.isCallback() and
+ not self.interface.isIteratorInterface() and
+ desc.get('wrapperCache', True))
+ # Nasty temporary hack for supporting both DOM and SpiderMonkey promises
+ # without too much pain
+ if self.interface.identifier.name == "Promise":
+ assert self.wrapperCache
+ # But really, we're only wrappercached if we have an interface
+ # object (that is, when we're not using SpiderMonkey promises).
+ self.wrapperCache = self.interface.hasInterfaceObject()
+
+ self.name = interface.identifier.name
+
+ # self.extendedAttributes is a dict of dicts, keyed on
+ # all/getterOnly/setterOnly and then on member name. Values are an
+ # array of extended attributes.
+ self.extendedAttributes = {'all': {}, 'getterOnly': {}, 'setterOnly': {}}
+
+ def addExtendedAttribute(attribute, config):
+ def add(key, members, attribute):
+ for member in members:
+ self.extendedAttributes[key].setdefault(member, []).append(attribute)
+
+ if isinstance(config, dict):
+ for key in ['all', 'getterOnly', 'setterOnly']:
+ add(key, config.get(key, []), attribute)
+ elif isinstance(config, list):
+ add('all', config, attribute)
+ else:
+ assert isinstance(config, str)
+ if config == '*':
+ iface = self.interface
+ while iface:
+ add('all', map(lambda m: m.name, iface.members), attribute)
+ iface = iface.parent
+ else:
+ add('all', [config], attribute)
+
+ if self.interface.isJSImplemented():
+ addExtendedAttribute('implicitJSContext', ['constructor'])
+ else:
+ for attribute in ['implicitJSContext']:
+ addExtendedAttribute(attribute, desc.get(attribute, {}))
+
+ if self.interface.identifier.name == 'Navigator':
+ for m in self.interface.members:
+ if m.isAttr() and m.navigatorObjectGetter:
+ # These getters call ConstructNavigatorObject to construct
+ # the value, and ConstructNavigatorObject needs a JSContext.
+ self.extendedAttributes['all'].setdefault(m.identifier.name, []).append('implicitJSContext')
+
+ self._binaryNames = desc.get('binaryNames', {})
+ self._binaryNames.setdefault('__legacycaller', 'LegacyCall')
+ self._binaryNames.setdefault('__stringifier', 'Stringify')
+
+ if not self.interface.isExternal():
+ def isTestInterface(iface):
+ return (iface.identifier.name in ["TestInterface",
+ "TestJSImplInterface",
+ "TestRenamedInterface"])
+
+ for member in self.interface.members:
+ if not member.isAttr() and not member.isMethod():
+ continue
+ binaryName = member.getExtendedAttribute("BinaryName")
+ if binaryName:
+ assert isinstance(binaryName, list)
+ assert len(binaryName) == 1
+ self._binaryNames.setdefault(member.identifier.name,
+ binaryName[0])
+
+ # Build the prototype chain.
+ self.prototypeChain = []
+ parent = interface
+ while parent:
+ self.prototypeChain.insert(0, parent.identifier.name)
+ parent = parent.parent
+ config.maxProtoChainLength = max(config.maxProtoChainLength,
+ len(self.prototypeChain))
+
+ def binaryNameFor(self, name):
+ return self._binaryNames.get(name, name)
+
+ @property
+ def prototypeNameChain(self):
+ return map(lambda p: self.getDescriptor(p).name, self.prototypeChain)
+
+ @property
+ def parentPrototypeName(self):
+ if len(self.prototypeChain) == 1:
+ return None
+ return self.getDescriptor(self.prototypeChain[-2]).name
+
+ def hasInterfaceOrInterfacePrototypeObject(self):
+
+ # Forward-declared interfaces don't need either interface object or
+ # interface prototype object as they're going to use QI.
+ if self.interface.isExternal():
+ return False
+
+ return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject()
+
+ @property
+ def hasNamedPropertiesObject(self):
+ if self.interface.isExternal():
+ return False
+
+ return self.isGlobal() and self.supportsNamedProperties()
+
+ def getExtendedAttributes(self, member, getter=False, setter=False):
+ def ensureValidThrowsExtendedAttribute(attr):
+ if (attr is not None and attr is not True):
+ raise TypeError("Unknown value for 'Throws': " + attr[0])
+
+ def maybeAppendInfallibleToAttrs(attrs, throws):
+ ensureValidThrowsExtendedAttribute(throws)
+ if throws is None:
+ attrs.append("infallible")
+
+ name = member.identifier.name
+ throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
+ if member.isMethod():
+ # JSObject-returning [NewObject] methods must be fallible,
+ # since they have to (fallibly) allocate the new JSObject.
+ if (member.getExtendedAttribute("NewObject") and
+ methodReturnsJSObject(member)):
+ throws = True
+ attrs = self.extendedAttributes['all'].get(name, [])
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ assert member.isAttr()
+ assert bool(getter) != bool(setter)
+ key = 'getterOnly' if getter else 'setterOnly'
+ attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
+ if throws is None:
+ throwsAttr = "GetterThrows" if getter else "SetterThrows"
+ throws = member.getExtendedAttribute(throwsAttr)
+ maybeAppendInfallibleToAttrs(attrs, throws)
+ return attrs
+
+ def supportsIndexedProperties(self):
+ return self.operations['IndexedGetter'] is not None
+
+ def supportsNamedProperties(self):
+ return self.operations['NamedGetter'] is not None
+
+ def hasNonOrdinaryGetPrototypeOf(self):
+ return self.interface.getExtendedAttribute("NonOrdinaryGetPrototypeOf")
+
+ def needsHeaderInclude(self):
+ """
+ An interface doesn't need a header file if it is not concrete, not
+ pref-controlled, has no prototype object, has no static methods or
+ attributes and has no parent. The parent matters because we assert
+ things about refcounting that depend on the actual underlying type if we
+ have a parent.
+
+ """
+ return (self.interface.isExternal() or self.concrete or
+ self.interface.hasInterfacePrototypeObject() or
+ any((m.isAttr() or m.isMethod()) and m.isStatic() for m in self.interface.members) or
+ self.interface.parent)
+
+ def hasThreadChecks(self):
+ # isExposedConditionally does not necessarily imply thread checks
+ # (since at least [SecureContext] is independent of them), but we're
+ # only used to decide whether to include nsThreadUtils.h, so we don't
+ # worry about that.
+ return ((self.isExposedConditionally() and
+ not self.interface.isExposedInWindow()) or
+ self.interface.isExposedInSomeButNotAllWorkers())
+
+ def isExposedConditionally(self):
+ return (self.interface.isExposedConditionally() or
+ self.interface.isExposedInSomeButNotAllWorkers())
+
+ def needsXrayResolveHooks(self):
+ """
+ Generally, any interface with NeedResolve needs Xray
+ resolveOwnProperty and enumerateOwnProperties hooks. But for
+ the special case of plugin-loading elements, we do NOT want
+ those, because we don't want to instantiate plug-ins simply
+ due to chrome touching them and that's all those hooks do on
+ those elements. So we special-case those here.
+ """
+ return (self.interface.getExtendedAttribute("NeedResolve") and
+ self.interface.identifier.name not in ["HTMLObjectElement",
+ "HTMLEmbedElement",
+ "HTMLAppletElement"])
+ def needsXrayNamedDeleterHook(self):
+ return self.operations["NamedDeleter"] is not None
+
+ def needsSpecialGenericOps(self):
+ """
+ Returns true if this descriptor requires generic ops other than
+ GenericBindingMethod/GenericBindingGetter/GenericBindingSetter.
+
+ In practice we need to do this if our this value might be an XPConnect
+ object or if we need to coerce null/undefined to the global.
+ """
+ return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain()
+
+ def isGlobal(self):
+ """
+ Returns true if this is the primary interface for a global object
+ of some sort.
+ """
+ return (self.interface.getExtendedAttribute("Global") or
+ self.interface.getExtendedAttribute("PrimaryGlobal"))
+
+ @property
+ def namedPropertiesEnumerable(self):
+ """
+ Returns whether this interface should have enumerable named properties
+ """
+ assert self.proxy
+ assert self.supportsNamedProperties()
+ iface = self.interface
+ while iface:
+ if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ return False
+ iface = iface.parent
+ return True
+
+ @property
+ def registersGlobalNamesOnWindow(self):
+ return (not self.interface.isExternal() and
+ self.interface.hasInterfaceObject() and
+ self.interface.isExposedInWindow() and
+ self.register)
+
+ def getDescriptor(self, interfaceName):
+ """
+ Gets the appropriate descriptor for the given interface name.
+ """
+ return self.config.getDescriptor(interfaceName)
+
+
+# Some utility methods
+def getTypesFromDescriptor(descriptor):
+ """
+ Get all argument and return types for all members of the descriptor
+ """
+ members = [m for m in descriptor.interface.members]
+ if descriptor.interface.ctor():
+ members.append(descriptor.interface.ctor())
+ members.extend(descriptor.interface.namedConstructors)
+ signatures = [s for m in members if m.isMethod() for s in m.signatures()]
+ types = []
+ for s in signatures:
+ assert len(s) == 2
+ (returnType, arguments) = s
+ types.append(returnType)
+ types.extend(a.type for a in arguments)
+
+ types.extend(a.type for a in members if a.isAttr())
+
+ if descriptor.interface.maplikeOrSetlikeOrIterable:
+ maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
+ if maplikeOrSetlikeOrIterable.hasKeyType():
+ types.append(maplikeOrSetlikeOrIterable.keyType)
+ if maplikeOrSetlikeOrIterable.hasValueType():
+ types.append(maplikeOrSetlikeOrIterable.valueType)
+ return types
+
+
+def getFlatTypes(types):
+ retval = set()
+ for type in types:
+ type = type.unroll()
+ if type.isUnion():
+ retval |= set(type.flatMemberTypes)
+ else:
+ retval.add(type)
+ return retval
+
+
+def getTypesFromDictionary(dictionary):
+ """
+ Get all member types for this dictionary
+ """
+ types = []
+ curDict = dictionary
+ while curDict:
+ types.extend([m.type for m in curDict.members])
+ curDict = curDict.parent
+ return types
+
+
+def getTypesFromCallback(callback):
+ """
+ Get the types this callback depends on: its return type and the
+ types of its arguments.
+ """
+ sig = callback.signatures()[0]
+ types = [sig[0]] # Return type
+ types.extend(arg.type for arg in sig[1]) # Arguments
+ return types
+
+
+def getAllTypes(descriptors, dictionaries, callbacks):
+ """
+ Generate all the types we're dealing with. For each type, a tuple
+ containing type, dictionary is yielded. The dictionary can be None if the
+ type does not come from a dictionary.
+ """
+ for d in descriptors:
+ if d.interface.isExternal():
+ continue
+ for t in getTypesFromDescriptor(d):
+ yield (t, None)
+ for dictionary in dictionaries:
+ for t in getTypesFromDictionary(dictionary):
+ yield (t, dictionary)
+ for callback in callbacks:
+ for t in getTypesFromCallback(callback):
+ yield (t, None)
+
+def iteratorNativeType(descriptor):
+ assert descriptor.interface.isIterable()
+ iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
+ assert iterableDecl.isPairIterator()
+ return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType