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/.
8 from collections
import defaultdict
10 from WebIDL
import IDLIncludesStatement
12 autogenerated_comment
= "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
15 def toStringBool(arg
):
17 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
19 return str(not not arg
).lower()
22 class DescriptorProvider
:
24 A way of getting descriptors for interface names. Subclasses must
25 have a getDescriptor method callable with the interface name only.
27 Subclasses must also have a getConfig() method that returns a
35 def isChildPath(path
, basePath
):
36 path
= os
.path
.normpath(path
)
37 return os
.path
.commonprefix((path
, basePath
)) == basePath
40 class Configuration(DescriptorProvider
):
42 Represents global configuration state based on IDL parse data and
43 the configuration file.
46 class IDLAttrGetterOrSetterTemplate
:
47 class TemplateAdditionalArg
:
48 def __init__(self
, type, name
, value
=None):
53 def __init__(self
, template
, getter
, setter
, argument
, attrName
):
54 self
.descriptor
= None
55 self
.usedInOtherInterfaces
= False
59 Configuration
.IDLAttrGetterOrSetterTemplate
.TemplateAdditionalArg(
63 self
.attrNameString
= attrName
66 class TemplateIDLAttribute
:
67 def __init__(self
, attr
):
69 assert not attr
.isMaplikeOrSetlikeAttr()
70 assert not attr
.slotIndices
72 self
.identifier
= attr
.identifier
74 self
.extendedAttributes
= attr
.getExtendedAttributes()
75 self
.slotIndices
= None
77 def getExtendedAttribute(self
, name
):
78 return self
.extendedAttributes
.get(name
)
83 def isMaplikeOrSetlikeAttr(self
):
92 def __init__(self
, filename
, webRoots
, parseData
, generatedEvents
=[]):
93 DescriptorProvider
.__init
__(self
)
95 # Read the configuration file.
97 exec(io
.open(filename
, encoding
="utf-8").read(), glbl
)
98 config
= glbl
["DOMInterfaces"]
100 self
.attributeTemplates
= dict()
101 attributeTemplatesByInterface
= dict()
102 for interface
, templates
in glbl
["TemplatedAttributes"].items():
103 for template
in templates
:
104 name
= template
.get("template")
105 t
= Configuration
.IDLAttrGetterOrSetterTemplate(**template
)
106 self
.attributeTemplates
[name
] = t
107 attributeTemplatesByInterface
.setdefault(interface
, list()).append(t
)
109 webRoots
= tuple(map(os
.path
.normpath
, webRoots
))
111 def isInWebIDLRoot(path
):
112 return any(isChildPath(path
, root
) for root
in webRoots
)
114 # Build descriptors for all the interfaces we have in the parse data.
115 # This allows callers to specify a subset of interfaces by filtering
117 self
.descriptors
= []
119 self
.descriptorsByName
= {}
120 self
.dictionariesByName
= {}
121 self
.generatedEvents
= generatedEvents
122 self
.maxProtoChainLength
= 0
123 for thing
in parseData
:
124 if isinstance(thing
, IDLIncludesStatement
):
125 # Our build system doesn't support dep build involving
126 # addition/removal of "includes" statements that appear in a
127 # different .webidl file than their LHS interface. Make sure we
128 # don't have any of those. See similar block below for partial
130 if thing
.interface
.filename
!= thing
.filename
:
132 "The binding build system doesn't really support "
133 "'includes' statements which don't appear in the "
134 "file in which the left-hand side of the statement is "
137 "%s" % (thing
.location
, thing
.interface
.location
)
140 assert not thing
.isType()
143 not thing
.isInterface()
144 and not thing
.isNamespace()
145 and not thing
.isInterfaceMixin()
148 # Our build system doesn't support dep builds involving
149 # addition/removal of partial interfaces/namespaces/mixins that
150 # appear in a different .webidl file than the
151 # interface/namespace/mixin they are extending. Make sure we don't
152 # have any of those. See similar block above for "includes"
154 if not thing
.isExternal():
155 for partial
in thing
.getPartials():
156 if partial
.filename
!= thing
.filename
:
158 "The binding build system doesn't really support "
159 "partial interfaces/namespaces/mixins which don't "
160 "appear in the file in which the "
161 "interface/namespace/mixin they are extending is "
162 "defined. Don't do this.\n"
164 "%s" % (partial
.location
, thing
.location
)
167 # The rest of the logic doesn't apply to mixins.
168 if thing
.isInterfaceMixin():
172 if not iface
.isExternal():
174 iface
.getExtendedAttribute("ChromeOnly")
175 or iface
.getExtendedAttribute("Func")
176 == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"]
177 or not iface
.hasInterfaceObject()
178 or isInWebIDLRoot(iface
.filename
)
181 "Interfaces which are exposed to the web may only be "
182 "defined in a DOM WebIDL root %r. Consider marking "
183 "the interface [ChromeOnly] or "
184 "[Func='nsContentUtils::IsCallerChromeOrFuzzingEnabled'] "
185 "if you do not want it exposed to the web.\n"
186 "%s" % (webRoots
, iface
.location
)
189 self
.interfaces
[iface
.identifier
.name
] = iface
191 entry
= config
.pop(iface
.identifier
.name
, {})
192 assert not isinstance(entry
, list)
198 attributeTemplatesByInterface
.get(iface
.identifier
.name
),
200 self
.descriptors
.append(desc
)
201 # Setting up descriptorsByName while iterating through interfaces
202 # means we can get the nativeType of iterable interfaces without
203 # having to do multiple loops.
204 assert desc
.interface
.identifier
.name
not in self
.descriptorsByName
205 self
.descriptorsByName
[desc
.interface
.identifier
.name
] = desc
208 raise NoSuchDescriptorError(
209 "Bindings.conf contains entries for "
211 + " that aren't declared as interfaces in the .webidl files."
214 # Keep the descriptor list sorted for determinism.
215 self
.descriptors
.sort(key
=lambda x
: x
.name
)
217 self
.descriptorsByFile
= {}
218 for d
in self
.descriptors
:
219 self
.descriptorsByFile
.setdefault(d
.interface
.filename
, []).append(d
)
221 self
.enums
= [e
for e
in parseData
if e
.isEnum()]
223 self
.dictionaries
= [d
for d
in parseData
if d
.isDictionary()]
224 self
.dictionariesByName
= {d
.identifier
.name
: d
for d
in self
.dictionaries
}
227 c
for c
in parseData
if c
.isCallback() and not c
.isInterface()
230 # Dictionary mapping from a union type name to a set of filenames where
231 # union types with that name are used.
232 self
.filenamesPerUnion
= defaultdict(set)
234 # Dictionary mapping from a filename to a list of types for
235 # the union types used in that file. If a union type is used
236 # in multiple files then it will be added to the list for the
237 # None key. Note that the list contains a type for every use
238 # of a union type, so there can be multiple entries with union
239 # types that have the same name.
240 self
.unionsPerFilename
= defaultdict(list)
243 filenamesForUnion
= self
.filenamesPerUnion
[t
.name
]
244 if t
.filename
not in filenamesForUnion
:
245 # We have a to be a bit careful: some of our built-in
246 # typedefs are for unions, and those unions end up with
247 # "<unknown>" as the filename. If that happens, we don't
248 # want to try associating this union with one particular
249 # filename, since there isn't one to associate it with,
251 if t
.filename
== "<unknown>":
252 uniqueFilenameForUnion
= None
253 elif len(filenamesForUnion
) == 0:
254 # This is the first file that we found a union with this
255 # name in, record the union as part of the file.
256 uniqueFilenameForUnion
= t
.filename
258 # We already found a file that contains a union with
260 if len(filenamesForUnion
) == 1:
261 # This is the first time we found a union with this
262 # name in another file.
263 for f
in filenamesForUnion
:
264 # Filter out unions with this name from the
265 # unions for the file where we previously found
267 unionsForFilename
= [
268 u
for u
in self
.unionsPerFilename
[f
] if u
.name
!= t
.name
270 if len(unionsForFilename
) == 0:
271 del self
.unionsPerFilename
[f
]
273 self
.unionsPerFilename
[f
] = unionsForFilename
274 # Unions with this name appear in multiple files, record
275 # the filename as None, so that we can detect that.
276 uniqueFilenameForUnion
= None
277 self
.unionsPerFilename
[uniqueFilenameForUnion
].append(t
)
278 filenamesForUnion
.add(t
.filename
)
281 t
= findInnermostType(t
)
284 for m
in t
.flatMemberTypes
:
287 for t
, _
in getAllTypes(self
.descriptors
, self
.dictionaries
, self
.callbacks
):
290 for d
in getDictionariesConvertedToJS(
291 self
.descriptors
, self
.dictionaries
, self
.callbacks
293 d
.needsConversionToJS
= True
295 for d
in getDictionariesConvertedFromJS(
296 self
.descriptors
, self
.dictionaries
, self
.callbacks
298 d
.needsConversionFromJS
= True
300 # Collect all the global names exposed on a Window object (to implement
301 # the hash for looking up these names when resolving a property).
302 self
.windowGlobalNames
= []
303 for desc
in self
.getDescriptors(registersGlobalNamesOnWindow
=True):
304 self
.windowGlobalNames
.append((desc
.name
, desc
))
305 self
.windowGlobalNames
.extend(
306 (n
.identifier
.name
, desc
) for n
in desc
.interface
.legacyFactoryFunctions
308 self
.windowGlobalNames
.extend(
309 (n
, desc
) for n
in desc
.interface
.legacyWindowAliases
312 # Collect a sorted list of strings that we want to concatenate into
313 # one big string and a dict mapping each string to its offset in the
314 # concatenated string.
316 # We want the names of all the interfaces with a prototype (for
317 # implementing @@toStringTag).
319 d
.interface
.getClassName()
320 for d
in self
.getDescriptors(hasInterfaceOrInterfacePrototypeObject
=True)
323 # Now also add the names from windowGlobalNames, we need them for the
324 # perfect hash that we build for these.
325 names
.update(n
[0] for n
in self
.windowGlobalNames
)
327 # Sorting is not strictly necessary, but makes the generated code a bit
329 names
= sorted(names
)
331 # We can't rely on being able to pass initial=0 to itertools.accumulate
332 # because it was only added in version 3.8, so define an accumulate
333 # function that chains the initial value into the iterator.
334 def accumulate(iterable
, initial
):
335 return itertools
.accumulate(itertools
.chain([initial
], iterable
))
337 # Calculate the offset of each name in the concatenated string. Note that
338 # we need to add 1 to the length to account for the null terminating each
340 offsets
= accumulate(map(lambda n
: len(n
) + 1, names
), initial
=0)
341 self
.namesStringOffsets
= list(zip(names
, offsets
))
343 allTemplatedAttributes
= (
345 for d
in self
.descriptors
346 if not d
.interface
.isExternal()
347 for m
in d
.interface
.members
348 if m
.isAttr() and m
.getExtendedAttribute("BindingTemplate") is not None
350 # attributesPerTemplate will have the template names as keys, and a
351 # list of tuples as values. Every tuple contains an IDLAttribute and a
353 attributesPerTemplate
= dict()
354 for m
, d
in allTemplatedAttributes
:
355 t
= m
.getExtendedAttribute("BindingTemplate")
356 if isinstance(t
[0], list):
358 l
= attributesPerTemplate
.setdefault(t
[0], list())
359 # We want the readonly attributes last, because we use the first
360 # attribute in the list as the canonical attribute for the
361 # template, and if there are any writable attributes the
362 # template should have support for that.
368 for name
, attributes
in attributesPerTemplate
.items():
369 # We use the first attribute to generate a canonical implementation
370 # of getter and setter.
371 firstAttribute
, firstDescriptor
= attributes
[0]
372 template
= self
.attributeTemplates
.get(name
)
375 "Unknown BindingTemplate with name %s for %s on %s"
378 firstAttribute
.identifier
.name
,
379 firstDescriptor
.interface
.identifier
.name
,
383 # This mimics a real IDL attribute for templated bindings.
385 template
.attr
= Configuration
.TemplateIDLAttribute(firstAttribute
)
387 def filterExtendedAttributes(extendedAttributes
):
388 # These are the extended attributes that we allow to have
389 # different values among all attributes that use the same
391 ignoredAttributes
= {
404 lambda i
: i
[0] not in ignoredAttributes
,
405 extendedAttributes
.items(),
409 firstExtAttrs
= filterExtendedAttributes(
410 firstAttribute
.getExtendedAttributes()
413 for a
, d
in attributes
:
414 # We want to make sure all getters or setters grouped by a
415 # template have the same WebIDL signatures, so make sure
416 # their types are the same.
417 if template
.attr
.type != a
.type:
419 "%s on %s and %s on %s have different type, but they're using the same template %s."
421 firstAttribute
.identifier
.name
,
422 firstDescriptor
.interface
.identifier
.name
,
424 d
.interface
.identifier
.name
,
429 extAttrs
= filterExtendedAttributes(a
.getExtendedAttributes())
430 if template
.attr
.extendedAttributes
!= extAttrs
:
431 for k
in extAttrs
.keys() - firstExtAttrs
.keys():
433 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
436 d
.interface
.identifier
.name
,
438 firstAttribute
.identifier
.name
,
439 firstDescriptor
.interface
.identifier
.name
,
443 for k
in firstExtAttrs
.keys() - extAttrs
.keys():
445 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
447 firstAttribute
.identifier
.name
,
448 firstDescriptor
.interface
.identifier
.name
,
451 d
.interface
.identifier
.name
,
455 for k
, v
in firstExtAttrs
.items():
458 "%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s."
460 firstAttribute
.identifier
.name
,
461 firstDescriptor
.interface
.identifier
.name
,
463 d
.interface
.identifier
.name
,
469 def sameThrows(getter
=False, setter
=False):
470 extAttrs1
= firstDescriptor
.getExtendedAttributes(
471 firstAttribute
, getter
=getter
, setter
=setter
473 extAttrs2
= d
.getExtendedAttributes(a
, getter
=getter
, setter
=setter
)
474 return ("needsErrorResult" in extAttrs1
) == (
475 "needsErrorResult" in extAttrs2
478 if not sameThrows(getter
=True) or (
479 not a
.readonly
and not sameThrows(setter
=True)
482 "%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s."
484 firstAttribute
.identifier
.name
,
485 firstDescriptor
.interface
.identifier
.name
,
487 d
.interface
.identifier
.name
,
492 for name
, template
in self
.attributeTemplates
.items():
493 if template
.attr
is None:
494 print("Template %s is unused, please remove it." % name
)
496 def getInterface(self
, ifname
):
497 return self
.interfaces
[ifname
]
499 def getDescriptors(self
, **filters
):
500 """Gets the descriptors that match the given filters."""
501 curr
= self
.descriptors
502 # Collect up our filters, because we may have a webIDLFile filter that
503 # we always want to apply first.
504 tofilter
= [(lambda x
: x
.interface
.isExternal(), False)]
505 for key
, val
in filters
.items():
506 if key
== "webIDLFile":
507 # Special-case this part to make it fast, since most of our
508 # getDescriptors calls are conditioned on a webIDLFile. We may
509 # not have this key, in which case we have no descriptors
511 curr
= self
.descriptorsByFile
.get(val
, [])
513 elif key
== "hasInterfaceObject":
516 return x
.interface
.hasInterfaceObject()
518 elif key
== "hasInterfacePrototypeObject":
521 return x
.interface
.hasInterfacePrototypeObject()
523 elif key
== "hasInterfaceOrInterfacePrototypeObject":
526 return x
.hasInterfaceOrInterfacePrototypeObject()
528 elif key
== "isCallback":
531 return x
.interface
.isCallback()
533 elif key
== "isJSImplemented":
536 return x
.interface
.isJSImplemented()
538 elif key
== "isExposedInAnyWorker":
541 return x
.interface
.isExposedInAnyWorker()
543 elif key
== "isExposedInWorkerDebugger":
546 return x
.interface
.isExposedInWorkerDebugger()
548 elif key
== "isExposedInAnyWorklet":
551 return x
.interface
.isExposedInAnyWorklet()
553 elif key
== "isExposedInWindow":
556 return x
.interface
.isExposedInWindow()
558 elif key
== "isExposedInShadowRealms":
561 return x
.interface
.isExposedInShadowRealms()
563 elif key
== "isSerializable":
566 return x
.interface
.isSerializable()
569 # Have to watch out: just closing over "key" is not enough,
570 # since we're about to mutate its value
571 getter
= (lambda attrName
: lambda x
: getattr(x
, attrName
))(key
)
572 tofilter
.append((getter
, val
))
574 curr
= [x
for x
in curr
if f
[0](x
) == f
[1]]
577 def getEnums(self
, webIDLFile
):
578 return [e
for e
in self
.enums
if e
.filename
== webIDLFile
]
580 def getDictionaries(self
, webIDLFile
):
581 return [d
for d
in self
.dictionaries
if d
.filename
== webIDLFile
]
583 def getCallbacks(self
, webIDLFile
):
584 return [c
for c
in self
.callbacks
if c
.filename
== webIDLFile
]
586 def getDescriptor(self
, interfaceName
):
588 Gets the appropriate descriptor for the given interface name.
590 # We may have optimized out this descriptor, but the chances of anyone
591 # asking about it are then slim. Put the check for that _after_ we've
592 # done our normal lookup. But that means we have to do our normal
593 # lookup in a way that will not throw if it fails.
594 d
= self
.descriptorsByName
.get(interfaceName
, None)
598 raise NoSuchDescriptorError("For " + interfaceName
+ " found no matches")
603 def getDictionariesConvertibleToJS(self
):
604 return [d
for d
in self
.dictionaries
if d
.needsConversionToJS
]
606 def getDictionariesConvertibleFromJS(self
):
607 return [d
for d
in self
.dictionaries
if d
.needsConversionFromJS
]
609 def getDictionaryIfExists(self
, dictionaryName
):
610 return self
.dictionariesByName
.get(dictionaryName
, None)
613 class NoSuchDescriptorError(TypeError):
614 def __init__(self
, str):
615 TypeError.__init
__(self
, str)
618 def methodReturnsJSObject(method
):
619 assert method
.isMethod()
621 for signature
in method
.signatures():
622 returnType
= signature
[0]
623 if returnType
.isObject() or returnType
.isSpiderMonkeyInterface():
629 def MemberIsLegacyUnforgeable(member
, descriptor
):
630 # Note: "or" and "and" return either their LHS or RHS, not
631 # necessarily booleans. Make sure to return a boolean from this
632 # method, because callers will compare its return value to
635 (member
.isAttr() or member
.isMethod())
636 and not member
.isStatic()
638 member
.isLegacyUnforgeable()
639 or descriptor
.interface
.getExtendedAttribute("LegacyUnforgeable")
644 class Descriptor(DescriptorProvider
):
646 Represents a single descriptor for an interface. See Bindings.conf.
649 def __init__(self
, config
, interface
, desc
, attributeTemplates
):
650 DescriptorProvider
.__init
__(self
)
652 self
.interface
= interface
653 self
.attributeTemplates
= attributeTemplates
654 if self
.attributeTemplates
is not None:
655 for t
in self
.attributeTemplates
:
658 self
.wantsXrays
= not interface
.isExternal() and interface
.isExposedInWindow()
661 # We could try to restrict self.wantsXrayExpandoClass further. For
662 # example, we could set it to false if all of our slots store
663 # Gecko-interface-typed things, because we don't use Xray expando
664 # slots for those. But note that we would need to check the types
665 # of not only the members of "interface" but also of all its
666 # ancestors, because those can have members living in our slots too.
667 # For now, do the simple thing.
668 self
.wantsXrayExpandoClass
= interface
.totalMembersInSlots
!= 0
670 # Read the desc, and fill in the relevant defaults.
671 ifaceName
= self
.interface
.identifier
.name
672 # For generated iterator interfaces for other iterable interfaces, we
673 # just use IterableIterator as the native type, templated on the
674 # nativeType of the iterable interface. That way we can have a
675 # templated implementation for all the duplicated iterator
677 if self
.interface
.isIteratorInterface():
678 itrName
= self
.interface
.iterableInterface
.identifier
.name
679 itrDesc
= self
.getDescriptor(itrName
)
680 nativeTypeDefault
= iteratorNativeType(itrDesc
)
681 elif self
.interface
.isAsyncIteratorInterface():
682 itrName
= self
.interface
.asyncIterableInterface
.identifier
.name
683 itrDesc
= self
.getDescriptor(itrName
)
684 nativeTypeDefault
= iteratorNativeType(itrDesc
)
686 elif self
.interface
.isExternal():
687 nativeTypeDefault
= "nsIDOM" + ifaceName
689 nativeTypeDefault
= "mozilla::dom::" + ifaceName
691 self
.nativeType
= desc
.get("nativeType", nativeTypeDefault
)
692 # Now create a version of nativeType that doesn't have extra
693 # mozilla::dom:: at the beginning.
694 prettyNativeType
= self
.nativeType
.split("::")
695 if prettyNativeType
[0] == "mozilla":
696 prettyNativeType
.pop(0)
697 if prettyNativeType
[0] == "dom":
698 prettyNativeType
.pop(0)
699 self
.prettyNativeType
= "::".join(prettyNativeType
)
701 self
.jsImplParent
= desc
.get("jsImplParent", self
.nativeType
)
703 # Do something sane for JSObject
704 if self
.nativeType
== "JSObject":
705 headerDefault
= "js/TypeDecls.h"
706 elif self
.interface
.isCallback() or self
.interface
.isJSImplemented():
707 # A copy of CGHeaders.getDeclarationFilename; we can't
708 # import it here, sadly.
709 # Use our local version of the header, not the exported one, so that
710 # test bindings, which don't export, will work correctly.
711 basename
= os
.path
.basename(self
.interface
.filename
)
712 headerDefault
= basename
.replace(".webidl", "Binding.h")
714 if not self
.interface
.isExternal() and self
.interface
.getExtendedAttribute(
717 headerDefault
= self
.interface
.getExtendedAttribute("HeaderFile")[0]
719 self
.interface
.isIteratorInterface()
720 or self
.interface
.isAsyncIteratorInterface()
722 headerDefault
= "mozilla/dom/IterableIterator.h"
724 headerDefault
= self
.nativeType
725 headerDefault
= headerDefault
.replace("::", "/") + ".h"
726 self
.headerFile
= desc
.get("headerFile", headerDefault
)
727 self
.headerIsDefault
= self
.headerFile
== headerDefault
728 if self
.jsImplParent
== self
.nativeType
:
729 self
.jsImplParentHeader
= self
.headerFile
731 self
.jsImplParentHeader
= self
.jsImplParent
.replace("::", "/") + ".h"
733 self
.notflattened
= desc
.get("notflattened", False)
734 self
.register
= desc
.get("register", True)
736 # If we're concrete, we need to crawl our ancestor interfaces and mark
737 # them as having a concrete descendant.
739 not self
.interface
.isExternal()
740 and not self
.interface
.isCallback()
741 and not self
.interface
.isNamespace()
743 # We're going to assume that leaf interfaces are
744 # concrete; otherwise what's the point? Also
745 # interfaces with constructors had better be
746 # concrete; otherwise how can you construct them?
748 not self
.interface
.hasChildInterfaces()
749 or self
.interface
.ctor() is not None
753 self
.concrete
= desc
.get("concrete", concreteDefault
)
754 self
.hasLegacyUnforgeableMembers
= self
.concrete
and any(
755 MemberIsLegacyUnforgeable(m
, self
) for m
in self
.interface
.members
758 "IndexedGetter": None,
759 "IndexedSetter": None,
760 "IndexedDeleter": None,
763 "NamedDeleter": None,
765 "LegacyCaller": None,
768 self
.hasDefaultToJSON
= False
770 # Stringifiers need to be set up whether an interface is
771 # concrete or not, because they're actually prototype methods and hence
772 # can apply to instances of descendant interfaces. Legacy callers and
773 # named/indexed operations only need to be set up on concrete
774 # interfaces, since they affect the JSClass we end up using, not the
776 def addOperation(operation
, m
):
777 if not self
.operations
[operation
]:
778 self
.operations
[operation
] = m
780 # Since stringifiers go on the prototype, we only need to worry
781 # about our own stringifier, not those of our ancestor interfaces.
782 if not self
.interface
.isExternal():
783 for m
in self
.interface
.members
:
784 if m
.isMethod() and m
.isStringifier():
785 addOperation("Stringifier", m
)
786 if m
.isMethod() and m
.isDefaultToJSON():
787 self
.hasDefaultToJSON
= True
789 # We keep track of instrumente props for all non-external interfaces.
790 self
.instrumentedProps
= []
791 instrumentedProps
= self
.interface
.getExtendedAttribute("InstrumentedProps")
792 if instrumentedProps
:
793 # It's actually a one-element list, with the list
794 # we want as the only element.
795 self
.instrumentedProps
= instrumentedProps
[0]
797 # Check that we don't have duplicated instrumented props.
798 uniqueInstrumentedProps
= set(self
.instrumentedProps
)
799 if len(uniqueInstrumentedProps
) != len(self
.instrumentedProps
):
802 for p
in uniqueInstrumentedProps
803 if self
.instrumentedProps
.count(p
) > 1
806 "Duplicated instrumented properties: %s.\n%s"
807 % (duplicates
, self
.interface
.location
)
812 iface
= self
.interface
813 for m
in iface
.members
:
814 # Don't worry about inheriting legacycallers either: in
815 # practice these are on most-derived prototypes.
816 if m
.isMethod() and m
.isLegacycaller():
817 if not m
.isIdentifierLess():
819 "We don't support legacycaller with "
820 "identifier.\n%s" % m
.location
822 if len(m
.signatures()) != 1:
824 "We don't support overloaded "
825 "legacycaller.\n%s" % m
.location
827 addOperation("LegacyCaller", m
)
830 for m
in iface
.members
:
834 def addIndexedOrNamedOperation(operation
, m
):
836 operation
= "Indexed" + operation
839 operation
= "Named" + operation
840 addOperation(operation
, m
)
843 addIndexedOrNamedOperation("Getter", m
)
845 addIndexedOrNamedOperation("Setter", m
)
847 addIndexedOrNamedOperation("Deleter", m
)
848 if m
.isLegacycaller() and iface
!= self
.interface
:
850 "We don't support legacycaller on "
851 "non-leaf interface %s.\n%s" % (iface
, iface
.location
)
854 iface
.setUserData("hasConcreteDescendant", True)
858 self
.supportsIndexedProperties()
860 self
.supportsNamedProperties() and not self
.hasNamedPropertiesObject
862 or self
.isMaybeCrossOriginObject()
866 if self
.isMaybeCrossOriginObject() and (
867 self
.supportsIndexedProperties() or self
.supportsNamedProperties()
870 "We don't support named or indexed "
871 "properties on maybe-cross-origin objects. "
872 "This lets us assume that their proxy "
873 "hooks are never called via Xrays. "
874 "Fix %s.\n%s" % (self
.interface
, self
.interface
.location
)
877 if not self
.operations
["IndexedGetter"] and (
878 self
.operations
["IndexedSetter"]
879 or self
.operations
["IndexedDeleter"]
882 "%s supports indexed properties but does "
883 "not have an indexed getter.\n%s"
884 % (self
.interface
, self
.interface
.location
)
886 if not self
.operations
["NamedGetter"] and (
887 self
.operations
["NamedSetter"] or self
.operations
["NamedDeleter"]
890 "%s supports named properties but does "
891 "not have a named getter.\n%s"
892 % (self
.interface
, self
.interface
.location
)
894 iface
= self
.interface
896 iface
.setUserData("hasProxyDescendant", True)
899 if desc
.get("wantsQI", None) is not None:
900 self
._wantsQI
= desc
.get("wantsQI", None)
901 self
.wrapperCache
= (
902 not self
.interface
.isCallback()
903 and not self
.interface
.isIteratorInterface()
904 and not self
.interface
.isAsyncIteratorInterface()
905 and desc
.get("wrapperCache", True)
908 self
.name
= interface
.identifier
.name
910 # self.implicitJSContext is a list of names of methods and attributes
911 # that need a JSContext.
912 if self
.interface
.isJSImplemented():
913 self
.implicitJSContext
= ["constructor"]
915 self
.implicitJSContext
= desc
.get("implicitJSContext", [])
916 assert isinstance(self
.implicitJSContext
, list)
918 self
._binaryNames
= {}
920 if not self
.interface
.isExternal():
922 def maybeAddBinaryName(member
):
923 binaryName
= member
.getExtendedAttribute("BinaryName")
925 assert isinstance(binaryName
, list)
926 assert len(binaryName
) == 1
927 self
._binaryNames
.setdefault(
928 (member
.identifier
.name
, member
.isStatic()), binaryName
[0]
931 for member
in self
.interface
.members
:
932 if not member
.isAttr() and not member
.isMethod():
934 maybeAddBinaryName(member
)
936 ctor
= self
.interface
.ctor()
938 maybeAddBinaryName(ctor
)
940 # Some default binary names for cases when nothing else got set.
941 self
._binaryNames
.setdefault(("__legacycaller", False), "LegacyCall")
942 self
._binaryNames
.setdefault(("__stringifier", False), "Stringify")
944 # Build the prototype chain.
945 self
.prototypeChain
= []
946 self
.needsMissingPropUseCounters
= False
949 self
.needsMissingPropUseCounters
= (
950 self
.needsMissingPropUseCounters
951 or parent
.getExtendedAttribute("InstrumentedProps")
953 self
.prototypeChain
.insert(0, parent
.identifier
.name
)
954 parent
= parent
.parent
955 config
.maxProtoChainLength
= max(
956 config
.maxProtoChainLength
, len(self
.prototypeChain
)
959 self
.hasOrdinaryObjectPrototype
= desc
.get("hasOrdinaryObjectPrototype", False)
961 def binaryNameFor(self
, name
, isStatic
):
962 return self
._binaryNames
.get((name
, isStatic
), name
)
965 def prototypeNameChain(self
):
966 return [self
.getDescriptor(p
).name
for p
in self
.prototypeChain
]
969 def parentPrototypeName(self
):
970 if len(self
.prototypeChain
) == 1:
972 return self
.getDescriptor(self
.prototypeChain
[-2]).name
974 def hasInterfaceOrInterfacePrototypeObject(self
):
976 self
.interface
.hasInterfaceObject()
977 or self
.interface
.hasInterfacePrototypeObject()
981 def hasNamedPropertiesObject(self
):
982 return self
.isGlobal() and self
.supportsNamedProperties()
984 def getExtendedAttributes(self
, member
, getter
=False, setter
=False):
985 def ensureValidBoolExtendedAttribute(attr
, name
):
986 if attr
is not None and attr
is not True:
987 raise TypeError("Unknown value for '%s': %s" % (name
, attr
[0]))
989 def ensureValidThrowsExtendedAttribute(attr
):
990 ensureValidBoolExtendedAttribute(attr
, "Throws")
992 def ensureValidCanOOMExtendedAttribute(attr
):
993 ensureValidBoolExtendedAttribute(attr
, "CanOOM")
995 def maybeAppendNeedsErrorResultToAttrs(attrs
, throws
):
996 ensureValidThrowsExtendedAttribute(throws
)
997 if throws
is not None:
998 attrs
.append("needsErrorResult")
1000 def maybeAppendCanOOMToAttrs(attrs
, canOOM
):
1001 ensureValidCanOOMExtendedAttribute(canOOM
)
1002 if canOOM
is not None:
1003 attrs
.append("canOOM")
1005 def maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
):
1007 needsSubjectPrincipal
is not None
1008 and needsSubjectPrincipal
is not True
1009 and needsSubjectPrincipal
!= ["NonSystem"]
1012 "Unknown value for 'NeedsSubjectPrincipal': %s"
1013 % needsSubjectPrincipal
[0]
1016 if needsSubjectPrincipal
is not None:
1017 attrs
.append("needsSubjectPrincipal")
1018 if needsSubjectPrincipal
== ["NonSystem"]:
1019 attrs
.append("needsNonSystemSubjectPrincipal")
1021 name
= member
.identifier
.name
1022 throws
= self
.interface
.isJSImplemented() or member
.getExtendedAttribute(
1025 canOOM
= member
.getExtendedAttribute("CanOOM")
1026 needsSubjectPrincipal
= member
.getExtendedAttribute("NeedsSubjectPrincipal")
1028 if name
in self
.implicitJSContext
:
1029 attrs
.append("implicitJSContext")
1030 if member
.isMethod():
1031 if self
.interface
.isAsyncIteratorInterface() and name
== "next":
1032 attrs
.append("implicitJSContext")
1033 # JSObject-returning [NewObject] methods must be fallible,
1034 # since they have to (fallibly) allocate the new JSObject.
1035 if member
.getExtendedAttribute("NewObject"):
1036 if member
.returnsPromise():
1038 elif methodReturnsJSObject(member
):
1040 maybeAppendNeedsErrorResultToAttrs(attrs
, throws
)
1041 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
1042 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
)
1045 assert member
.isAttr()
1046 assert bool(getter
) != bool(setter
)
1048 throwsAttr
= "GetterThrows" if getter
else "SetterThrows"
1049 throws
= member
.getExtendedAttribute(throwsAttr
)
1050 maybeAppendNeedsErrorResultToAttrs(attrs
, throws
)
1052 canOOMAttr
= "GetterCanOOM" if getter
else "SetterCanOOM"
1053 canOOM
= member
.getExtendedAttribute(canOOMAttr
)
1054 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
1055 if needsSubjectPrincipal
is None:
1056 needsSubjectPrincipalAttr
= (
1057 "GetterNeedsSubjectPrincipal"
1059 else "SetterNeedsSubjectPrincipal"
1061 needsSubjectPrincipal
= member
.getExtendedAttribute(
1062 needsSubjectPrincipalAttr
1064 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
)
1067 def supportsIndexedProperties(self
):
1068 return self
.operations
["IndexedGetter"] is not None
1070 def lengthNeedsCallerType(self
):
1072 Determine whether our length getter needs a caller type; this is needed
1073 in some indexed-getter proxy algorithms. The idea is that if our
1074 indexed getter needs a caller type, our automatically-generated Length()
1077 assert self
.supportsIndexedProperties()
1078 indexedGetter
= self
.operations
["IndexedGetter"]
1079 return indexedGetter
.getExtendedAttribute("NeedsCallerType")
1081 def supportsNamedProperties(self
):
1082 return self
.operations
["NamedGetter"] is not None
1084 def supportedNamesNeedCallerType(self
):
1086 Determine whether our GetSupportedNames call needs a caller type. The
1087 idea is that if your named getter needs a caller type, then so does
1090 assert self
.supportsNamedProperties()
1091 namedGetter
= self
.operations
["NamedGetter"]
1092 return namedGetter
.getExtendedAttribute("NeedsCallerType")
1094 def isMaybeCrossOriginObject(self
):
1095 # If we're isGlobal and have cross-origin members, we're a Window, and
1096 # that's not a cross-origin object. The WindowProxy is.
1099 and self
.interface
.hasCrossOriginMembers
1100 and not self
.isGlobal()
1103 def needsHeaderInclude(self
):
1105 An interface doesn't need a header file if it is not concrete, not
1106 pref-controlled, has no prototype object, has no static methods or
1107 attributes and has no parent. The parent matters because we assert
1108 things about refcounting that depend on the actual underlying type if we
1113 self
.interface
.isExternal()
1115 or self
.interface
.hasInterfacePrototypeObject()
1117 (m
.isAttr() or m
.isMethod()) and m
.isStatic()
1118 for m
in self
.interface
.members
1120 or self
.interface
.parent
1123 def hasThreadChecks(self
):
1124 # isExposedConditionally does not necessarily imply thread checks
1125 # (since at least [SecureContext] is independent of them), but we're
1126 # only used to decide whether to include nsThreadUtils.h, so we don't
1129 self
.isExposedConditionally() and not self
.interface
.isExposedInWindow()
1130 ) or self
.interface
.isExposedInSomeButNotAllWorkers()
1132 def hasCEReactions(self
):
1134 m
.getExtendedAttribute("CEReactions") for m
in self
.interface
.members
1137 def isExposedConditionally(self
):
1139 self
.interface
.isExposedConditionally()
1140 or self
.interface
.isExposedInSomeButNotAllWorkers()
1143 def needsXrayResolveHooks(self
):
1145 Generally, any interface with NeedResolve needs Xray
1146 resolveOwnProperty and enumerateOwnProperties hooks. But for
1147 the special case of plugin-loading elements, we do NOT want
1148 those, because we don't want to instantiate plug-ins simply
1149 due to chrome touching them and that's all those hooks do on
1150 those elements. So we special-case those here.
1152 return self
.interface
.getExtendedAttribute(
1154 ) and self
.interface
.identifier
.name
not in [
1155 "HTMLObjectElement",
1159 def needsXrayNamedDeleterHook(self
):
1160 return self
.operations
["NamedDeleter"] is not None
1164 Returns true if this is the primary interface for a global object
1167 return self
.interface
.getExtendedAttribute("Global")
1170 def namedPropertiesEnumerable(self
):
1172 Returns whether this interface should have enumerable named properties
1175 assert self
.supportsNamedProperties()
1176 iface
= self
.interface
1178 if iface
.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
1180 iface
= iface
.parent
1184 def registersGlobalNamesOnWindow(self
):
1186 self
.interface
.hasInterfaceObject()
1187 and self
.interface
.isExposedInWindow()
1191 def getDescriptor(self
, interfaceName
):
1193 Gets the appropriate descriptor for the given interface name.
1195 return self
.config
.getDescriptor(interfaceName
)
1197 def getConfig(self
):
1201 # Some utility methods
1202 def getTypesFromDescriptor(descriptor
, includeArgs
=True, includeReturns
=True):
1204 Get argument and/or return types for all members of the descriptor. By
1205 default returns all argument types (which includes types of writable
1206 attributes) and all return types (which includes types of all attributes).
1208 assert includeArgs
or includeReturns
# Must want _something_.
1209 members
= [m
for m
in descriptor
.interface
.members
]
1210 if descriptor
.interface
.ctor():
1211 members
.append(descriptor
.interface
.ctor())
1212 members
.extend(descriptor
.interface
.legacyFactoryFunctions
)
1213 signatures
= [s
for m
in members
if m
.isMethod() for s
in m
.signatures()]
1215 for s
in signatures
:
1217 (returnType
, arguments
) = s
1219 types
.append(returnType
)
1221 types
.extend(a
.type for a
in arguments
)
1226 if (a
.isAttr() and (includeReturns
or (includeArgs
and not a
.readonly
)))
1229 if descriptor
.interface
.maplikeOrSetlikeOrIterable
:
1230 maplikeOrSetlikeOrIterable
= descriptor
.interface
.maplikeOrSetlikeOrIterable
1231 if maplikeOrSetlikeOrIterable
.isMaplike():
1232 # The things we expand into may or may not correctly indicate in
1233 # their formal IDL types what things we have as return values. For
1234 # example, "keys" returns the moral equivalent of sequence<keyType>
1235 # but just claims to return "object". Similarly, "values" returns
1236 # the moral equivalent of sequence<valueType> but claims to return
1237 # "object". And due to bug 1155340, "get" claims to return "any"
1238 # instead of the right type. So let's just manually work around
1239 # that lack of specificity. For our arguments, we already enforce
1240 # the right types at the IDL level, so those will get picked up
1242 assert maplikeOrSetlikeOrIterable
.hasKeyType()
1243 assert maplikeOrSetlikeOrIterable
.hasValueType()
1245 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
1246 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
1247 elif maplikeOrSetlikeOrIterable
.isSetlike():
1248 assert maplikeOrSetlikeOrIterable
.hasKeyType()
1249 assert maplikeOrSetlikeOrIterable
.hasValueType()
1251 maplikeOrSetlikeOrIterable
.keyType
1252 == maplikeOrSetlikeOrIterable
.valueType
1254 # As in the maplike case, we don't always declare our return values
1257 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
1260 maplikeOrSetlikeOrIterable
.isIterable()
1261 or maplikeOrSetlikeOrIterable
.isAsyncIterable()
1263 # As in the maplike/setlike cases we don't do a good job of
1264 # declaring our actual return types, while our argument types, if
1265 # any, are declared fine.
1267 if maplikeOrSetlikeOrIterable
.hasKeyType():
1268 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
1269 if maplikeOrSetlikeOrIterable
.hasValueType():
1270 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
1275 def getTypesFromDictionary(dictionary
):
1277 Get all member types for this dictionary
1279 return [m
.type for m
in dictionary
.members
]
1282 def getTypesFromCallback(callback
):
1284 Get the types this callback depends on: its return type and the
1285 types of its arguments.
1287 sig
= callback
.signatures()[0]
1288 types
= [sig
[0]] # Return type
1289 types
.extend(arg
.type for arg
in sig
[1]) # Arguments
1293 def getAllTypes(descriptors
, dictionaries
, callbacks
):
1295 Generate all the types we're dealing with. For each type, a tuple
1296 containing type, dictionary is yielded. The dictionary can be None if the
1297 type does not come from a dictionary.
1299 for d
in descriptors
:
1300 if d
.interface
.isExternal():
1302 for t
in getTypesFromDescriptor(d
):
1304 for dictionary
in dictionaries
:
1305 for t
in getTypesFromDictionary(dictionary
):
1306 yield (t
, dictionary
)
1307 for callback
in callbacks
:
1308 for t
in getTypesFromCallback(callback
):
1312 # For sync value iterators, we use default array implementation, for async
1313 # iterators and sync pair iterators, we use AsyncIterableIterator or
1314 # IterableIterator instead.
1315 def iteratorNativeType(descriptor
):
1316 assert descriptor
.interface
.isIterable() or descriptor
.interface
.isAsyncIterable()
1317 iterableDecl
= descriptor
.interface
.maplikeOrSetlikeOrIterable
1318 assert iterableDecl
.isPairIterator() or descriptor
.interface
.isAsyncIterable()
1319 if descriptor
.interface
.isIterable():
1320 return "mozilla::dom::IterableIterator<%s>" % descriptor
.nativeType
1321 needReturnMethod
= toStringBool(
1322 descriptor
.interface
.maplikeOrSetlikeOrIterable
.getExtendedAttribute(
1323 "GenerateReturnMethod"
1327 return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
1328 descriptor
.nativeType
,
1333 def findInnermostType(t
):
1335 Find the innermost type of the given type, unwrapping Promise and Record
1336 types, as well as everything that unroll() unwraps.
1341 elif t
.unroll() != t
:
1344 t
= t
.promiseInnerType()
1349 def getDependentDictionariesFromDictionary(d
):
1351 Find all the dictionaries contained in the given dictionary, as ancestors or
1352 members. This returns a generator.
1356 for member
in d
.members
:
1357 for next
in getDictionariesFromType(member
.type):
1362 def getDictionariesFromType(type):
1364 Find all the dictionaries contained in type. This can be used to find
1365 dictionaries that need conversion to JS (by looking at types that get
1366 converted to JS) or dictionaries that need conversion from JS (by looking at
1367 types that get converted from JS).
1369 This returns a generator.
1371 type = findInnermostType(type)
1373 # Look for dictionaries in all the member types
1374 for t
in type.flatMemberTypes
:
1375 for next
in getDictionariesFromType(t
):
1377 elif type.isDictionary():
1378 # Find the dictionaries that are itself, any of its ancestors, or
1379 # contained in any of its member types.
1380 for d
in getDependentDictionariesFromDictionary(type.inner
):
1384 def getDictionariesConvertedToJS(descriptors
, dictionaries
, callbacks
):
1385 for desc
in descriptors
:
1386 if desc
.interface
.isExternal():
1389 if desc
.interface
.isJSImplemented():
1390 # For a JS-implemented interface, we need to-JS
1391 # conversions for all the types involved.
1392 for t
in getTypesFromDescriptor(desc
):
1393 for d
in getDictionariesFromType(t
):
1395 elif desc
.interface
.isCallback():
1396 # For callbacks we only want to include the arguments, since that's
1397 # where the to-JS conversion happens.
1398 for t
in getTypesFromDescriptor(desc
, includeReturns
=False):
1399 for d
in getDictionariesFromType(t
):
1402 # For normal interfaces, we only want to include return values,
1403 # since that's where to-JS conversion happens.
1404 for t
in getTypesFromDescriptor(desc
, includeArgs
=False):
1405 for d
in getDictionariesFromType(t
):
1408 for callback
in callbacks
:
1409 # We only want to look at the arguments
1410 sig
= callback
.signatures()[0]
1412 for d
in getDictionariesFromType(arg
.type):
1415 for dictionary
in dictionaries
:
1416 if dictionary
.needsConversionToJS
:
1417 # It's explicitly flagged as needing to-JS conversion, and all its
1418 # dependent dictionaries will need to-JS conversion too.
1419 for d
in getDependentDictionariesFromDictionary(dictionary
):
1423 def getDictionariesConvertedFromJS(descriptors
, dictionaries
, callbacks
):
1424 for desc
in descriptors
:
1425 if desc
.interface
.isExternal():
1428 if desc
.interface
.isJSImplemented():
1429 # For a JS-implemented interface, we need from-JS conversions for
1430 # all the types involved.
1431 for t
in getTypesFromDescriptor(desc
):
1432 for d
in getDictionariesFromType(t
):
1434 elif desc
.interface
.isCallback():
1435 # For callbacks we only want to include the return value, since
1436 # that's where teh from-JS conversion happens.
1437 for t
in getTypesFromDescriptor(desc
, includeArgs
=False):
1438 for d
in getDictionariesFromType(t
):
1441 # For normal interfaces, we only want to include arguments values,
1442 # since that's where from-JS conversion happens.
1443 for t
in getTypesFromDescriptor(desc
, includeReturns
=False):
1444 for d
in getDictionariesFromType(t
):
1447 for callback
in callbacks
:
1448 # We only want to look at the return value
1449 sig
= callback
.signatures()[0]
1450 for d
in getDictionariesFromType(sig
[0]):
1453 for dictionary
in dictionaries
:
1454 if dictionary
.needsConversionFromJS
:
1455 # It's explicitly flagged as needing from-JS conversion, and all its
1456 # dependent dictionaries will need from-JS conversion too.
1457 for d
in getDependentDictionariesFromDictionary(dictionary
):