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
9 from collections
import defaultdict
11 autogenerated_comment
= "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
14 class DescriptorProvider
:
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
26 def isChildPath(path
, basePath
):
27 path
= os
.path
.normpath(path
)
28 return os
.path
.commonprefix((path
, basePath
)) == basePath
31 class Configuration(DescriptorProvider
):
33 Represents global configuration state based on IDL parse data and
34 the configuration file.
36 def __init__(self
, filename
, webRoots
, parseData
, generatedEvents
=[]):
37 DescriptorProvider
.__init
__(self
)
39 # Read the configuration file.
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
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
64 if (thing
.interface
.filename() != thing
.filename()):
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 "
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()):
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"
85 if not thing
.isExternal():
86 for partial
in thing
.getPartials():
87 if partial
.filename() != thing
.filename():
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"
96 (partial
.location
, thing
.location
))
98 # The rest of the logic doesn't apply to mixins.
99 if thing
.isInterfaceMixin():
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())):
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"
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(),
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
)
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,
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()
177 # We already found a file that contains a union with
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
186 unionsForFilename
= [
187 u
for u
in self
.unionsPerFilename
[f
]
190 if len(unionsForFilename
) == 0:
191 del self
.unionsPerFilename
[f
]
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
,
202 d
.needsConversionToJS
= True
204 for d
in getDictionariesConvertedFromJS(self
.descriptors
, self
.dictionaries
,
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
223 curr
= self
.descriptorsByFile
.get(val
, [])
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()
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
))
251 curr
= [x
for x
in curr
if f
[0](x
) == f
[1]]
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)
275 raise NoSuchDescriptorError("For " + interfaceName
+ " found no matches")
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():
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
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
)
324 self
.interface
= interface
326 self
.wantsXrays
= (not interface
.isExternal() and
327 interface
.isExposedInWindow())
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
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
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')
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"
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
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
))
413 'IndexedGetter': None,
414 'IndexedSetter': None,
415 'IndexedDeleter': None,
418 'NamedDeleter': 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
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
))
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
)
476 for m
in iface
.members
:
480 def addIndexedOrNamedOperation(operation
, m
):
482 operation
= 'Indexed' + operation
485 operation
= 'Named' + operation
486 addOperation(operation
, m
)
489 addIndexedOrNamedOperation('Getter', m
)
491 addIndexedOrNamedOperation('Setter', m
)
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)
502 self
.proxy
= (self
.supportsIndexedProperties() or
503 (self
.supportsNamedProperties() and
504 not self
.hasNamedPropertiesObject
) or
505 self
.isMaybeCrossOriginObject())
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. "
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
532 iface
.setUserData('hasProxyDescendant', True)
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']
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")
557 assert isinstance(binaryName
, list)
558 assert len(binaryName
) == 1
559 self
._binaryNames
.setdefault(member
.identifier
.name
,
561 for member
in self
.interface
.members
:
562 if not member
.isAttr() and not member
.isMethod():
564 maybeAddBinaryName(member
);
566 ctor
= self
.interface
.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
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
)
591 def prototypeNameChain(self
):
592 return [self
.getDescriptor(p
).name
for p
in self
.prototypeChain
]
595 def parentPrototypeName(self
):
596 if len(self
.prototypeChain
) == 1:
598 return self
.getDescriptor(self
.prototypeChain
[-2]).name
600 def hasInterfaceOrInterfacePrototypeObject(self
):
601 return (self
.interface
.hasInterfaceObject() or
602 self
.interface
.hasInterfacePrototypeObject())
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
)
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")
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():
657 elif methodReturnsJSObject(member
):
659 maybeAppendInfallibleToAttrs(attrs
, throws
)
660 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
661 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
,
662 needsSubjectPrincipal
)
665 assert member
.isAttr()
666 assert bool(getter
) != bool(setter
)
668 throwsAttr
= "GetterThrows" if getter
else "SetterThrows"
669 throws
= member
.getExtendedAttribute(throwsAttr
)
670 maybeAppendInfallibleToAttrs(attrs
, throws
)
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
)
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()
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
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
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
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",
757 def needsXrayNamedDeleterHook(self
):
758 return self
.operations
["NamedDeleter"] is not None
762 Returns true if this is the primary interface for a global object
765 return self
.interface
.getExtendedAttribute("Global")
768 def namedPropertiesEnumerable(self
):
770 Returns whether this interface should have enumerable named properties
773 assert self
.supportsNamedProperties()
774 iface
= self
.interface
776 if iface
.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
782 def registersGlobalNamesOnWindow(self
):
783 return (self
.interface
.hasInterfaceObject() and
784 self
.interface
.isExposedInWindow() and
787 def getDescriptor(self
, interfaceName
):
789 Gets the appropriate descriptor for the given interface name.
791 return self
.config
.getDescriptor(interfaceName
)
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()]
813 (returnType
, arguments
) = s
815 types
.append(returnType
)
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
836 assert maplikeOrSetlikeOrIterable
.hasKeyType()
837 assert maplikeOrSetlikeOrIterable
.hasValueType()
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
848 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
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.
855 if maplikeOrSetlikeOrIterable
.hasKeyType():
856 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
857 if maplikeOrSetlikeOrIterable
.hasValueType():
858 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
863 def getTypesFromDictionary(dictionary
):
865 Get all member types for this dictionary
870 types
.extend([m
.type for m
in curDict
.members
])
871 curDict
= curDict
.parent
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
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():
895 for t
in getTypesFromDescriptor(d
):
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
):
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.
919 elif t
.unroll() != t
:
922 t
= t
.promiseInnerType()
927 def getDependentDictionariesFromDictionary(d
):
929 Find all the dictionaries contained in the given dictionary, as ancestors or
930 members. This returns a generator.
934 for member
in d
.members
:
935 for next
in getDictionariesFromType(member
.type):
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)
951 # Look for dictionaries in all the member types
952 for t
in type.flatMemberTypes
:
953 for next
in getDictionariesFromType(t
):
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
):
962 def getDictionariesConvertedToJS(descriptors
, dictionaries
, callbacks
):
963 for desc
in descriptors
:
964 if desc
.interface
.isExternal():
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
):
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
):
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
):
986 for callback
in callbacks
:
987 # We only want to look at the arguments
988 sig
= callback
.signatures()[0]
990 for d
in getDictionariesFromType(arg
.type):
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
):
1001 def getDictionariesConvertedFromJS(descriptors
, dictionaries
, callbacks
):
1002 for desc
in descriptors
:
1003 if desc
.interface
.isExternal():
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
):
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
):
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
):
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]):
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
):