Bug 1664775 [wpt PR 25515] - Fieldset NG: Fix hit-testing for the rendered legend...
[gecko.git] / dom / bindings / Configuration.py
blob522eb9d478a190b7927b01d3b9931cae840ec864
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 # You can obtain one at http://mozilla.org/MPL/2.0/.
5 from WebIDL import IDLIncludesStatement
6 import io
7 import os
8 import six
9 from collections import defaultdict
11 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
14 class DescriptorProvider:
15 """
16 A way of getting descriptors for interface names. Subclasses must
17 have a getDescriptor method callable with the interface name only.
19 Subclasses must also have a getConfig() method that returns a
20 Configuration.
21 """
22 def __init__(self):
23 pass
26 def isChildPath(path, basePath):
27 path = os.path.normpath(path)
28 return os.path.commonprefix((path, basePath)) == basePath
31 class Configuration(DescriptorProvider):
32 """
33 Represents global configuration state based on IDL parse data and
34 the configuration file.
35 """
36 def __init__(self, filename, webRoots, parseData, generatedEvents=[]):
37 DescriptorProvider.__init__(self)
39 # Read the configuration file.
40 glbl = {}
41 exec(io.open(filename, encoding='utf-8').read(), glbl)
42 config = glbl['DOMInterfaces']
44 webRoots = tuple(map(os.path.normpath, webRoots))
45 def isInWebIDLRoot(path):
46 return any(isChildPath(path, root) for root in webRoots)
48 # Build descriptors for all the interfaces we have in the parse data.
49 # This allows callers to specify a subset of interfaces by filtering
50 # |parseData|.
51 self.descriptors = []
52 self.interfaces = {}
53 self.descriptorsByName = {}
54 self.dictionariesByName = {}
55 self.generatedEvents = generatedEvents
56 self.maxProtoChainLength = 0
57 for thing in parseData:
58 if isinstance(thing, IDLIncludesStatement):
59 # Our build system doesn't support dep build involving
60 # addition/removal of "includes" statements that appear in a
61 # different .webidl file than their LHS interface. Make sure we
62 # don't have any of those. See similar block below for partial
63 # interfaces!
64 if (thing.interface.filename() != thing.filename()):
65 raise TypeError(
66 "The binding build system doesn't really support "
67 "'includes' statements which don't appear in the "
68 "file in which the left-hand side of the statement is "
69 "defined.\n"
70 "%s\n"
71 "%s" %
72 (thing.location, thing.interface.location))
74 assert not thing.isType()
76 if (not thing.isInterface() and not thing.isNamespace() and
77 not thing.isInterfaceMixin()):
78 continue
79 # Our build system doesn't support dep builds involving
80 # addition/removal of partial interfaces/namespaces/mixins that
81 # appear in a different .webidl file than the
82 # interface/namespace/mixin they are extending. Make sure we don't
83 # have any of those. See similar block above for "includes"
84 # statements!
85 if not thing.isExternal():
86 for partial in thing.getPartials():
87 if partial.filename() != thing.filename():
88 raise TypeError(
89 "The binding build system doesn't really support "
90 "partial interfaces/namespaces/mixins which don't "
91 "appear in the file in which the "
92 "interface/namespace/mixin they are extending is "
93 "defined. Don't do this.\n"
94 "%s\n"
95 "%s" %
96 (partial.location, thing.location))
98 # The rest of the logic doesn't apply to mixins.
99 if thing.isInterfaceMixin():
100 continue
102 iface = thing
103 if not iface.isExternal():
104 if not (iface.getExtendedAttribute("ChromeOnly") or
105 iface.getExtendedAttribute("Func") == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"] or
106 not iface.hasInterfaceObject() or
107 isInWebIDLRoot(iface.filename())):
108 raise TypeError(
109 "Interfaces which are exposed to the web may only be "
110 "defined in a DOM WebIDL root %r. Consider marking "
111 "the interface [ChromeOnly] or "
112 "[Func='nsContentUtils::IsCallerChromeOrFuzzingEnabled'] "
113 "if you do not want it exposed to the web.\n"
114 "%s" %
115 (webRoots, iface.location))
117 self.interfaces[iface.identifier.name] = iface
119 entry = config.get(iface.identifier.name, {})
120 assert not isinstance(entry, list)
122 desc = Descriptor(self, iface, entry)
123 self.descriptors.append(desc)
124 # Setting up descriptorsByName while iterating through interfaces
125 # means we can get the nativeType of iterable interfaces without
126 # having to do multiple loops.
127 assert desc.interface.identifier.name not in self.descriptorsByName
128 self.descriptorsByName[desc.interface.identifier.name] = desc
130 # Keep the descriptor list sorted for determinism.
131 self.descriptors.sort(key=lambda x: x.name)
134 self.descriptorsByFile = {}
135 for d in self.descriptors:
136 self.descriptorsByFile.setdefault(d.interface.filename(),
137 []).append(d)
139 self.enums = [e for e in parseData if e.isEnum()]
141 self.dictionaries = [d for d in parseData if d.isDictionary()]
142 self.dictionariesByName = { d.identifier.name: d for d in self.dictionaries }
144 self.callbacks = [c for c in parseData if
145 c.isCallback() and not c.isInterface()]
147 # Dictionary mapping from a union type name to a set of filenames where
148 # union types with that name are used.
149 self.filenamesPerUnion = defaultdict(set)
151 # Dictionary mapping from a filename to a list of types for
152 # the union types used in that file. If a union type is used
153 # in multiple files then it will be added to the list for the
154 # None key. Note that the list contains a type for every use
155 # of a union type, so there can be multiple entries with union
156 # types that have the same name.
157 self.unionsPerFilename = defaultdict(list)
159 for (t, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
160 t = findInnermostType(t)
161 if t.isUnion():
162 filenamesForUnion = self.filenamesPerUnion[t.name]
163 if t.filename() not in filenamesForUnion:
164 # We have a to be a bit careful: some of our built-in
165 # typedefs are for unions, and those unions end up with
166 # "<unknown>" as the filename. If that happens, we don't
167 # want to try associating this union with one particular
168 # filename, since there isn't one to associate it with,
169 # really.
170 if t.filename() == "<unknown>":
171 uniqueFilenameForUnion = None
172 elif len(filenamesForUnion) == 0:
173 # This is the first file that we found a union with this
174 # name in, record the union as part of the file.
175 uniqueFilenameForUnion = t.filename()
176 else:
177 # We already found a file that contains a union with
178 # this name.
179 if len(filenamesForUnion) == 1:
180 # This is the first time we found a union with this
181 # name in another file.
182 for f in filenamesForUnion:
183 # Filter out unions with this name from the
184 # unions for the file where we previously found
185 # them.
186 unionsForFilename = [
187 u for u in self.unionsPerFilename[f]
188 if u.name != t.name
190 if len(unionsForFilename) == 0:
191 del self.unionsPerFilename[f]
192 else:
193 self.unionsPerFilename[f] = unionsForFilename
194 # Unions with this name appear in multiple files, record
195 # the filename as None, so that we can detect that.
196 uniqueFilenameForUnion = None
197 self.unionsPerFilename[uniqueFilenameForUnion].append(t)
198 filenamesForUnion.add(t.filename())
200 for d in getDictionariesConvertedToJS(self.descriptors, self.dictionaries,
201 self.callbacks):
202 d.needsConversionToJS = True
204 for d in getDictionariesConvertedFromJS(self.descriptors, self.dictionaries,
205 self.callbacks):
206 d.needsConversionFromJS = True
208 def getInterface(self, ifname):
209 return self.interfaces[ifname]
211 def getDescriptors(self, **filters):
212 """Gets the descriptors that match the given filters."""
213 curr = self.descriptors
214 # Collect up our filters, because we may have a webIDLFile filter that
215 # we always want to apply first.
216 tofilter = [ (lambda x: x.interface.isExternal(), False) ]
217 for key, val in six.iteritems(filters):
218 if key == 'webIDLFile':
219 # Special-case this part to make it fast, since most of our
220 # getDescriptors calls are conditioned on a webIDLFile. We may
221 # not have this key, in which case we have no descriptors
222 # either.
223 curr = self.descriptorsByFile.get(val, [])
224 continue
225 elif key == 'hasInterfaceObject':
226 getter = lambda x: x.interface.hasInterfaceObject()
227 elif key == 'hasInterfacePrototypeObject':
228 getter = lambda x: x.interface.hasInterfacePrototypeObject()
229 elif key == 'hasInterfaceOrInterfacePrototypeObject':
230 getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject()
231 elif key == 'isCallback':
232 getter = lambda x: x.interface.isCallback()
233 elif key == 'isJSImplemented':
234 getter = lambda x: x.interface.isJSImplemented()
235 elif key == 'isExposedInAnyWorker':
236 getter = lambda x: x.interface.isExposedInAnyWorker()
237 elif key == 'isExposedInWorkerDebugger':
238 getter = lambda x: x.interface.isExposedInWorkerDebugger()
239 elif key == 'isExposedInAnyWorklet':
240 getter = lambda x: x.interface.isExposedInAnyWorklet()
241 elif key == 'isExposedInWindow':
242 getter = lambda x: x.interface.isExposedInWindow()
243 elif key == 'isSerializable':
244 getter = lambda x: x.interface.isSerializable()
245 else:
246 # Have to watch out: just closing over "key" is not enough,
247 # since we're about to mutate its value
248 getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
249 tofilter.append((getter, val))
250 for f in tofilter:
251 curr = [x for x in curr if f[0](x) == f[1]]
252 return curr
254 def getEnums(self, webIDLFile):
255 return [e for e in self.enums if e.filename() == webIDLFile]
257 def getDictionaries(self, webIDLFile):
258 return [d for d in self.dictionaries if d.filename() == webIDLFile]
260 def getCallbacks(self, webIDLFile):
261 return [c for c in self.callbacks if c.filename() == webIDLFile]
263 def getDescriptor(self, interfaceName):
265 Gets the appropriate descriptor for the given interface name.
267 # We may have optimized out this descriptor, but the chances of anyone
268 # asking about it are then slim. Put the check for that _after_ we've
269 # done our normal lookup. But that means we have to do our normal
270 # lookup in a way that will not throw if it fails.
271 d = self.descriptorsByName.get(interfaceName, None)
272 if d:
273 return d
275 raise NoSuchDescriptorError("For " + interfaceName + " found no matches")
277 def getConfig(self):
278 return self
280 def getDictionariesConvertibleToJS(self):
281 return [d for d in self.dictionaries if d.needsConversionToJS]
283 def getDictionariesConvertibleFromJS(self):
284 return [d for d in self.dictionaries if d.needsConversionFromJS]
286 def getDictionaryIfExists(self, dictionaryName):
287 return self.dictionariesByName.get(dictionaryName, None)
290 class NoSuchDescriptorError(TypeError):
291 def __init__(self, str):
292 TypeError.__init__(self, str)
295 def methodReturnsJSObject(method):
296 assert method.isMethod()
298 for signature in method.signatures():
299 returnType = signature[0]
300 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
301 return True
303 return False
306 def MemberIsUnforgeable(member, descriptor):
307 # Note: "or" and "and" return either their LHS or RHS, not
308 # necessarily booleans. Make sure to return a boolean from this
309 # method, because callers will compare its return value to
310 # booleans.
311 return bool((member.isAttr() or member.isMethod()) and
312 not member.isStatic() and
313 (member.isUnforgeable() or
314 descriptor.interface.getExtendedAttribute("Unforgeable")))
317 class Descriptor(DescriptorProvider):
319 Represents a single descriptor for an interface. See Bindings.conf.
321 def __init__(self, config, interface, desc):
322 DescriptorProvider.__init__(self)
323 self.config = config
324 self.interface = interface
326 self.wantsXrays = (not interface.isExternal() and
327 interface.isExposedInWindow())
329 if self.wantsXrays:
330 # We could try to restrict self.wantsXrayExpandoClass further. For
331 # example, we could set it to false if all of our slots store
332 # Gecko-interface-typed things, because we don't use Xray expando
333 # slots for those. But note that we would need to check the types
334 # of not only the members of "interface" but also of all its
335 # ancestors, because those can have members living in our slots too.
336 # For now, do the simple thing.
337 self.wantsXrayExpandoClass = (interface.totalMembersInSlots != 0)
339 # Read the desc, and fill in the relevant defaults.
340 ifaceName = self.interface.identifier.name
341 # For generated iterator interfaces for other iterable interfaces, we
342 # just use IterableIterator as the native type, templated on the
343 # nativeType of the iterable interface. That way we can have a
344 # templated implementation for all the duplicated iterator
345 # functionality.
346 if self.interface.isIteratorInterface():
347 itrName = self.interface.iterableInterface.identifier.name
348 itrDesc = self.getDescriptor(itrName)
349 nativeTypeDefault = iteratorNativeType(itrDesc)
351 elif self.interface.isExternal():
352 nativeTypeDefault = "nsIDOM" + ifaceName
353 else:
354 nativeTypeDefault = "mozilla::dom::" + ifaceName
356 self.nativeType = desc.get('nativeType', nativeTypeDefault)
357 # Now create a version of nativeType that doesn't have extra
358 # mozilla::dom:: at the beginning.
359 prettyNativeType = self.nativeType.split("::")
360 if prettyNativeType[0] == "mozilla":
361 prettyNativeType.pop(0)
362 if prettyNativeType[0] == "dom":
363 prettyNativeType.pop(0)
364 self.prettyNativeType = "::".join(prettyNativeType)
366 self.jsImplParent = desc.get('jsImplParent', self.nativeType)
368 # Do something sane for JSObject
369 if self.nativeType == "JSObject":
370 headerDefault = "js/TypeDecls.h"
371 elif self.interface.isCallback() or self.interface.isJSImplemented():
372 # A copy of CGHeaders.getDeclarationFilename; we can't
373 # import it here, sadly.
374 # Use our local version of the header, not the exported one, so that
375 # test bindings, which don't export, will work correctly.
376 basename = os.path.basename(self.interface.filename())
377 headerDefault = basename.replace('.webidl', 'Binding.h')
378 else:
379 if not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
380 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
381 elif self.interface.isIteratorInterface():
382 headerDefault = "mozilla/dom/IterableIterator.h"
383 else:
384 headerDefault = self.nativeType
385 headerDefault = headerDefault.replace("::", "/") + ".h"
386 self.headerFile = desc.get('headerFile', headerDefault)
387 self.headerIsDefault = self.headerFile == headerDefault
388 if self.jsImplParent == self.nativeType:
389 self.jsImplParentHeader = self.headerFile
390 else:
391 self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
393 self.notflattened = desc.get('notflattened', False)
394 self.register = desc.get('register', True)
396 # If we're concrete, we need to crawl our ancestor interfaces and mark
397 # them as having a concrete descendant.
398 concreteDefault = (not self.interface.isExternal() and
399 not self.interface.isCallback() and
400 not self.interface.isNamespace() and
401 # We're going to assume that leaf interfaces are
402 # concrete; otherwise what's the point? Also
403 # interfaces with constructors had better be
404 # concrete; otherwise how can you construct them?
405 (not self.interface.hasChildInterfaces() or
406 self.interface.ctor() is not None))
408 self.concrete = desc.get('concrete', concreteDefault)
409 self.hasUnforgeableMembers = (self.concrete and
410 any(MemberIsUnforgeable(m, self) for m in
411 self.interface.members))
412 self.operations = {
413 'IndexedGetter': None,
414 'IndexedSetter': None,
415 'IndexedDeleter': None,
416 'NamedGetter': None,
417 'NamedSetter': None,
418 'NamedDeleter': None,
419 'Stringifier': None,
420 'LegacyCaller': None,
423 self.hasDefaultToJSON = False
425 # Stringifiers need to be set up whether an interface is
426 # concrete or not, because they're actually prototype methods and hence
427 # can apply to instances of descendant interfaces. Legacy callers and
428 # named/indexed operations only need to be set up on concrete
429 # interfaces, since they affect the JSClass we end up using, not the
430 # prototype object.
431 def addOperation(operation, m):
432 if not self.operations[operation]:
433 self.operations[operation] = m
435 # Since stringifiers go on the prototype, we only need to worry
436 # about our own stringifier, not those of our ancestor interfaces.
437 if not self.interface.isExternal():
438 for m in self.interface.members:
439 if m.isMethod() and m.isStringifier():
440 addOperation('Stringifier', m)
441 if m.isMethod() and m.isDefaultToJSON():
442 self.hasDefaultToJSON = True
444 # We keep track of instrumente props for all non-external interfaces.
445 self.instrumentedProps = []
446 instrumentedProps = self.interface.getExtendedAttribute("InstrumentedProps")
447 if instrumentedProps:
448 # It's actually a one-element list, with the list
449 # we want as the only element.
450 self.instrumentedProps = instrumentedProps[0]
452 # Check that we don't have duplicated instrumented props.
453 uniqueInstrumentedProps = set(self.instrumentedProps)
454 if len(uniqueInstrumentedProps) != len(self.instrumentedProps):
455 duplicates = [p for p in uniqueInstrumentedProps if
456 self.instrumentedProps.count(p) > 1]
457 raise TypeError("Duplicated instrumented properties: %s.\n%s" %
458 (duplicates, self.interface.location))
460 if self.concrete:
461 self.proxy = False
462 iface = self.interface
463 for m in iface.members:
464 # Don't worry about inheriting legacycallers either: in
465 # practice these are on most-derived prototypes.
466 if m.isMethod() and m.isLegacycaller():
467 if not m.isIdentifierLess():
468 raise TypeError("We don't support legacycaller with "
469 "identifier.\n%s" % m.location)
470 if len(m.signatures()) != 1:
471 raise TypeError("We don't support overloaded "
472 "legacycaller.\n%s" % m.location)
473 addOperation('LegacyCaller', m)
475 while iface:
476 for m in iface.members:
477 if not m.isMethod():
478 continue
480 def addIndexedOrNamedOperation(operation, m):
481 if m.isIndexed():
482 operation = 'Indexed' + operation
483 else:
484 assert m.isNamed()
485 operation = 'Named' + operation
486 addOperation(operation, m)
488 if m.isGetter():
489 addIndexedOrNamedOperation('Getter', m)
490 if m.isSetter():
491 addIndexedOrNamedOperation('Setter', m)
492 if m.isDeleter():
493 addIndexedOrNamedOperation('Deleter', m)
494 if m.isLegacycaller() and iface != self.interface:
495 raise TypeError("We don't support legacycaller on "
496 "non-leaf interface %s.\n%s" %
497 (iface, iface.location))
499 iface.setUserData('hasConcreteDescendant', True)
500 iface = iface.parent
502 self.proxy = (self.supportsIndexedProperties() or
503 (self.supportsNamedProperties() and
504 not self.hasNamedPropertiesObject) or
505 self.isMaybeCrossOriginObject())
507 if self.proxy:
508 if (self.isMaybeCrossOriginObject() and
509 (self.supportsIndexedProperties() or
510 self.supportsNamedProperties())):
511 raise TypeError("We don't support named or indexed "
512 "properties on maybe-cross-origin objects. "
513 "This lets us assume that their proxy "
514 "hooks are never called via Xrays. "
515 "Fix %s.\n%s" %
516 (self.interface, self.interface.location))
518 if (not self.operations['IndexedGetter'] and
519 (self.operations['IndexedSetter'] or
520 self.operations['IndexedDeleter'])):
521 raise SyntaxError("%s supports indexed properties but does "
522 "not have an indexed getter.\n%s" %
523 (self.interface, self.interface.location))
524 if (not self.operations['NamedGetter'] and
525 (self.operations['NamedSetter'] or
526 self.operations['NamedDeleter'])):
527 raise SyntaxError("%s supports named properties but does "
528 "not have a named getter.\n%s" %
529 (self.interface, self.interface.location))
530 iface = self.interface
531 while iface:
532 iface.setUserData('hasProxyDescendant', True)
533 iface = iface.parent
535 if desc.get('wantsQI', None) is not None:
536 self._wantsQI = desc.get('wantsQI', None)
537 self.wrapperCache = (not self.interface.isCallback() and
538 not self.interface.isIteratorInterface() and
539 desc.get('wrapperCache', True))
541 self.name = interface.identifier.name
543 # self.implicitJSContext is a list of names of methods and attributes
544 # that need a JSContext.
545 if self.interface.isJSImplemented():
546 self.implicitJSContext = ['constructor']
547 else:
548 self.implicitJSContext = desc.get('implicitJSContext', [])
549 assert isinstance(self.implicitJSContext, list)
551 self._binaryNames = {}
553 if not self.interface.isExternal():
554 def maybeAddBinaryName(member):
555 binaryName = member.getExtendedAttribute("BinaryName")
556 if binaryName:
557 assert isinstance(binaryName, list)
558 assert len(binaryName) == 1
559 self._binaryNames.setdefault(member.identifier.name,
560 binaryName[0])
561 for member in self.interface.members:
562 if not member.isAttr() and not member.isMethod():
563 continue
564 maybeAddBinaryName(member);
566 ctor = self.interface.ctor()
567 if ctor:
568 maybeAddBinaryName(ctor)
570 # Some default binary names for cases when nothing else got set.
571 self._binaryNames.setdefault('__legacycaller', 'LegacyCall')
572 self._binaryNames.setdefault('__stringifier', 'Stringify')
574 # Build the prototype chain.
575 self.prototypeChain = []
576 self.needsMissingPropUseCounters = False
577 parent = interface
578 while parent:
579 self.needsMissingPropUseCounters = (
580 self.needsMissingPropUseCounters or
581 parent.getExtendedAttribute("InstrumentedProps"))
582 self.prototypeChain.insert(0, parent.identifier.name)
583 parent = parent.parent
584 config.maxProtoChainLength = max(config.maxProtoChainLength,
585 len(self.prototypeChain))
587 def binaryNameFor(self, name):
588 return self._binaryNames.get(name, name)
590 @property
591 def prototypeNameChain(self):
592 return [self.getDescriptor(p).name for p in self.prototypeChain]
594 @property
595 def parentPrototypeName(self):
596 if len(self.prototypeChain) == 1:
597 return None
598 return self.getDescriptor(self.prototypeChain[-2]).name
600 def hasInterfaceOrInterfacePrototypeObject(self):
601 return (self.interface.hasInterfaceObject() or
602 self.interface.hasInterfacePrototypeObject())
604 @property
605 def hasNamedPropertiesObject(self):
606 return self.isGlobal() and self.supportsNamedProperties()
608 def getExtendedAttributes(self, member, getter=False, setter=False):
609 def ensureValidBoolExtendedAttribute(attr, name):
610 if (attr is not None and attr is not True):
611 raise TypeError("Unknown value for '%s': %s" % (name, attr[0]))
613 def ensureValidThrowsExtendedAttribute(attr):
614 ensureValidBoolExtendedAttribute(attr, "Throws")
616 def ensureValidCanOOMExtendedAttribute(attr):
617 ensureValidBoolExtendedAttribute(attr, "CanOOM")
619 def ensureValidNeedsSubjectPrincipalExtendedAttribute(attr):
620 ensureValidBoolExtendedAttribute(attr, "NeedsSubjectPrincipal")
622 def maybeAppendInfallibleToAttrs(attrs, throws):
623 ensureValidThrowsExtendedAttribute(throws)
624 if throws is None:
625 attrs.append("infallible")
627 def maybeAppendCanOOMToAttrs(attrs, canOOM):
628 ensureValidCanOOMExtendedAttribute(canOOM)
629 if canOOM is not None:
630 attrs.append("canOOM")
632 def maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal):
633 if (needsSubjectPrincipal is not None and
634 needsSubjectPrincipal is not True and
635 needsSubjectPrincipal != ["NonSystem"]):
636 raise TypeError("Unknown value for 'NeedsSubjectPrincipal': %s" %
637 needsSubjectPrincipal[0])
639 if needsSubjectPrincipal is not None:
640 attrs.append("needsSubjectPrincipal")
641 if needsSubjectPrincipal == ["NonSystem"]:
642 attrs.append("needsNonSystemSubjectPrincipal")
644 name = member.identifier.name
645 throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
646 canOOM = member.getExtendedAttribute("CanOOM")
647 needsSubjectPrincipal = member.getExtendedAttribute("NeedsSubjectPrincipal")
648 attrs = []
649 if name in self.implicitJSContext:
650 attrs.append('implicitJSContext')
651 if member.isMethod():
652 # JSObject-returning [NewObject] methods must be fallible,
653 # since they have to (fallibly) allocate the new JSObject.
654 if member.getExtendedAttribute("NewObject"):
655 if member.returnsPromise():
656 throws = True
657 elif methodReturnsJSObject(member):
658 canOOM = True
659 maybeAppendInfallibleToAttrs(attrs, throws)
660 maybeAppendCanOOMToAttrs(attrs, canOOM)
661 maybeAppendNeedsSubjectPrincipalToAttrs(attrs,
662 needsSubjectPrincipal)
663 return attrs
665 assert member.isAttr()
666 assert bool(getter) != bool(setter)
667 if throws is None:
668 throwsAttr = "GetterThrows" if getter else "SetterThrows"
669 throws = member.getExtendedAttribute(throwsAttr)
670 maybeAppendInfallibleToAttrs(attrs, throws)
671 if canOOM is None:
672 canOOMAttr = "GetterCanOOM" if getter else "SetterCanOOM"
673 canOOM = member.getExtendedAttribute(canOOMAttr)
674 maybeAppendCanOOMToAttrs(attrs, canOOM)
675 if needsSubjectPrincipal is None:
676 needsSubjectPrincipalAttr = (
677 "GetterNeedsSubjectPrincipal" if getter else "SetterNeedsSubjectPrincipal")
678 needsSubjectPrincipal = member.getExtendedAttribute(
679 needsSubjectPrincipalAttr)
680 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
681 return attrs
683 def supportsIndexedProperties(self):
684 return self.operations['IndexedGetter'] is not None
686 def lengthNeedsCallerType(self):
688 Determine whether our length getter needs a caller type; this is needed
689 in some indexed-getter proxy algorithms. The idea is that if our
690 indexed getter needs a caller type, our automatically-generated Length()
691 calls need one too.
693 assert self.supportsIndexedProperties()
694 indexedGetter = self.operations['IndexedGetter']
695 return indexedGetter.getExtendedAttribute("NeedsCallerType")
697 def supportsNamedProperties(self):
698 return self.operations['NamedGetter'] is not None
700 def supportedNamesNeedCallerType(self):
702 Determine whether our GetSupportedNames call needs a caller type. The
703 idea is that if your named getter needs a caller type, then so does
704 GetSupportedNames.
706 assert self.supportsNamedProperties()
707 namedGetter = self.operations['NamedGetter']
708 return namedGetter.getExtendedAttribute("NeedsCallerType")
710 def isMaybeCrossOriginObject(self):
711 # If we're isGlobal and have cross-origin members, we're a Window, and
712 # that's not a cross-origin object. The WindowProxy is.
713 return self.concrete and self.interface.hasCrossOriginMembers and not self.isGlobal()
715 def needsHeaderInclude(self):
717 An interface doesn't need a header file if it is not concrete, not
718 pref-controlled, has no prototype object, has no static methods or
719 attributes and has no parent. The parent matters because we assert
720 things about refcounting that depend on the actual underlying type if we
721 have a parent.
724 return (self.interface.isExternal() or self.concrete or
725 self.interface.hasInterfacePrototypeObject() or
726 any((m.isAttr() or m.isMethod()) and m.isStatic() for m in self.interface.members) or
727 self.interface.parent)
729 def hasThreadChecks(self):
730 # isExposedConditionally does not necessarily imply thread checks
731 # (since at least [SecureContext] is independent of them), but we're
732 # only used to decide whether to include nsThreadUtils.h, so we don't
733 # worry about that.
734 return ((self.isExposedConditionally() and
735 not self.interface.isExposedInWindow()) or
736 self.interface.isExposedInSomeButNotAllWorkers())
738 def hasCEReactions(self):
739 return any(m.getExtendedAttribute("CEReactions") for m in self.interface.members)
741 def isExposedConditionally(self):
742 return (self.interface.isExposedConditionally() or
743 self.interface.isExposedInSomeButNotAllWorkers())
745 def needsXrayResolveHooks(self):
747 Generally, any interface with NeedResolve needs Xray
748 resolveOwnProperty and enumerateOwnProperties hooks. But for
749 the special case of plugin-loading elements, we do NOT want
750 those, because we don't want to instantiate plug-ins simply
751 due to chrome touching them and that's all those hooks do on
752 those elements. So we special-case those here.
754 return (self.interface.getExtendedAttribute("NeedResolve") and
755 self.interface.identifier.name not in ["HTMLObjectElement",
756 "HTMLEmbedElement"])
757 def needsXrayNamedDeleterHook(self):
758 return self.operations["NamedDeleter"] is not None
760 def isGlobal(self):
762 Returns true if this is the primary interface for a global object
763 of some sort.
765 return self.interface.getExtendedAttribute("Global")
767 @property
768 def namedPropertiesEnumerable(self):
770 Returns whether this interface should have enumerable named properties
772 assert self.proxy
773 assert self.supportsNamedProperties()
774 iface = self.interface
775 while iface:
776 if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
777 return False
778 iface = iface.parent
779 return True
781 @property
782 def registersGlobalNamesOnWindow(self):
783 return (self.interface.hasInterfaceObject() and
784 self.interface.isExposedInWindow() and
785 self.register)
787 def getDescriptor(self, interfaceName):
789 Gets the appropriate descriptor for the given interface name.
791 return self.config.getDescriptor(interfaceName)
793 def getConfig(self):
794 return self.config
797 # Some utility methods
798 def getTypesFromDescriptor(descriptor, includeArgs=True, includeReturns=True):
800 Get argument and/or return types for all members of the descriptor. By
801 default returns all argument types (which includes types of writable
802 attributes) and all return types (which includes types of all attributes).
804 assert(includeArgs or includeReturns) # Must want _something_.
805 members = [m for m in descriptor.interface.members]
806 if descriptor.interface.ctor():
807 members.append(descriptor.interface.ctor())
808 members.extend(descriptor.interface.namedConstructors)
809 signatures = [s for m in members if m.isMethod() for s in m.signatures()]
810 types = []
811 for s in signatures:
812 assert len(s) == 2
813 (returnType, arguments) = s
814 if includeReturns:
815 types.append(returnType)
816 if includeArgs:
817 types.extend(a.type for a in arguments)
819 types.extend(a.type for a in members if
820 (a.isAttr() and (includeReturns or
821 (includeArgs and not a.readonly))))
823 if descriptor.interface.maplikeOrSetlikeOrIterable:
824 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
825 if maplikeOrSetlikeOrIterable.isMaplike():
826 # The things we expand into may or may not correctly indicate in
827 # their formal IDL types what things we have as return values. For
828 # example, "keys" returns the moral equivalent of sequence<keyType>
829 # but just claims to return "object". Similarly, "values" returns
830 # the moral equivalent of sequence<valueType> but claims to return
831 # "object". And due to bug 1155340, "get" claims to return "any"
832 # instead of the right type. So let's just manually work around
833 # that lack of specificity. For our arguments, we already enforce
834 # the right types at the IDL level, so those will get picked up
835 # correctly.
836 assert maplikeOrSetlikeOrIterable.hasKeyType()
837 assert maplikeOrSetlikeOrIterable.hasValueType()
838 if includeReturns:
839 types.append(maplikeOrSetlikeOrIterable.keyType)
840 types.append(maplikeOrSetlikeOrIterable.valueType)
841 elif maplikeOrSetlikeOrIterable.isSetlike():
842 assert maplikeOrSetlikeOrIterable.hasKeyType()
843 assert maplikeOrSetlikeOrIterable.hasValueType()
844 assert maplikeOrSetlikeOrIterable.keyType == maplikeOrSetlikeOrIterable.valueType
845 # As in the maplike case, we don't always declare our return values
846 # quite correctly.
847 if includeReturns:
848 types.append(maplikeOrSetlikeOrIterable.keyType)
849 else:
850 assert maplikeOrSetlikeOrIterable.isIterable()
851 # As in the maplike/setlike cases we don't do a good job of
852 # declaring our actual return types, while our argument types, if
853 # any, are declared fine.
854 if includeReturns:
855 if maplikeOrSetlikeOrIterable.hasKeyType():
856 types.append(maplikeOrSetlikeOrIterable.keyType)
857 if maplikeOrSetlikeOrIterable.hasValueType():
858 types.append(maplikeOrSetlikeOrIterable.valueType)
860 return types
863 def getTypesFromDictionary(dictionary):
865 Get all member types for this dictionary
867 types = []
868 curDict = dictionary
869 while curDict:
870 types.extend([m.type for m in curDict.members])
871 curDict = curDict.parent
872 return types
875 def getTypesFromCallback(callback):
877 Get the types this callback depends on: its return type and the
878 types of its arguments.
880 sig = callback.signatures()[0]
881 types = [sig[0]] # Return type
882 types.extend(arg.type for arg in sig[1]) # Arguments
883 return types
886 def getAllTypes(descriptors, dictionaries, callbacks):
888 Generate all the types we're dealing with. For each type, a tuple
889 containing type, dictionary is yielded. The dictionary can be None if the
890 type does not come from a dictionary.
892 for d in descriptors:
893 if d.interface.isExternal():
894 continue
895 for t in getTypesFromDescriptor(d):
896 yield (t, None)
897 for dictionary in dictionaries:
898 for t in getTypesFromDictionary(dictionary):
899 yield (t, dictionary)
900 for callback in callbacks:
901 for t in getTypesFromCallback(callback):
902 yield (t, None)
904 def iteratorNativeType(descriptor):
905 assert descriptor.interface.isIterable()
906 iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
907 assert iterableDecl.isPairIterator()
908 return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
911 def findInnermostType(t):
913 Find the innermost type of the given type, unwrapping Promise and Record
914 types, as well as everything that unroll() unwraps.
916 while True:
917 if t.isRecord():
918 t = t.inner
919 elif t.unroll() != t:
920 t = t.unroll()
921 elif t.isPromise():
922 t = t.promiseInnerType()
923 else:
924 return t
927 def getDependentDictionariesFromDictionary(d):
929 Find all the dictionaries contained in the given dictionary, as ancestors or
930 members. This returns a generator.
932 while d:
933 yield d
934 for member in d.members:
935 for next in getDictionariesFromType(member.type):
936 yield next
937 d = d.parent
940 def getDictionariesFromType(type):
942 Find all the dictionaries contained in type. This can be used to find
943 dictionaries that need conversion to JS (by looking at types that get
944 converted to JS) or dictionaries that need conversion from JS (by looking at
945 types that get converted from JS).
947 This returns a generator.
949 type = findInnermostType(type)
950 if type.isUnion():
951 # Look for dictionaries in all the member types
952 for t in type.flatMemberTypes:
953 for next in getDictionariesFromType(t):
954 yield next
955 elif type.isDictionary():
956 # Find the dictionaries that are itself, any of its ancestors, or
957 # contained in any of its member types.
958 for d in getDependentDictionariesFromDictionary(type.inner):
959 yield d
962 def getDictionariesConvertedToJS(descriptors, dictionaries, callbacks):
963 for desc in descriptors:
964 if desc.interface.isExternal():
965 continue
967 if desc.interface.isJSImplemented():
968 # For a JS-implemented interface, we need to-JS
969 # conversions for all the types involved.
970 for t in getTypesFromDescriptor(desc):
971 for d in getDictionariesFromType(t):
972 yield d
973 elif desc.interface.isCallback():
974 # For callbacks we only want to include the arguments, since that's
975 # where the to-JS conversion happens.
976 for t in getTypesFromDescriptor(desc, includeReturns=False):
977 for d in getDictionariesFromType(t):
978 yield d
979 else:
980 # For normal interfaces, we only want to include return values,
981 # since that's where to-JS conversion happens.
982 for t in getTypesFromDescriptor(desc, includeArgs=False):
983 for d in getDictionariesFromType(t):
984 yield d
986 for callback in callbacks:
987 # We only want to look at the arguments
988 sig = callback.signatures()[0]
989 for arg in sig[1]:
990 for d in getDictionariesFromType(arg.type):
991 yield d
993 for dictionary in dictionaries:
994 if dictionary.needsConversionToJS:
995 # It's explicitly flagged as needing to-JS conversion, and all its
996 # dependent dictionaries will need to-JS conversion too.
997 for d in getDependentDictionariesFromDictionary(dictionary):
998 yield d
1001 def getDictionariesConvertedFromJS(descriptors, dictionaries, callbacks):
1002 for desc in descriptors:
1003 if desc.interface.isExternal():
1004 continue
1006 if desc.interface.isJSImplemented():
1007 # For a JS-implemented interface, we need from-JS conversions for
1008 # all the types involved.
1009 for t in getTypesFromDescriptor(desc):
1010 for d in getDictionariesFromType(t):
1011 yield d
1012 elif desc.interface.isCallback():
1013 # For callbacks we only want to include the return value, since
1014 # that's where teh from-JS conversion happens.
1015 for t in getTypesFromDescriptor(desc, includeArgs=False):
1016 for d in getDictionariesFromType(t):
1017 yield d
1018 else:
1019 # For normal interfaces, we only want to include arguments values,
1020 # since that's where from-JS conversion happens.
1021 for t in getTypesFromDescriptor(desc, includeReturns=False):
1022 for d in getDictionariesFromType(t):
1023 yield d
1025 for callback in callbacks:
1026 # We only want to look at the return value
1027 sig = callback.signatures()[0]
1028 for d in getDictionariesFromType(sig[0]):
1029 yield d
1031 for dictionary in dictionaries:
1032 if dictionary.needsConversionFromJS:
1033 # It's explicitly flagged as needing from-JS conversion, and all its
1034 # dependent dictionaries will need from-JS conversion too.
1035 for d in getDependentDictionariesFromDictionary(dictionary):
1036 yield d