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
27 def isChildPath(path
, basePath
):
28 path
= os
.path
.normpath(path
)
29 return os
.path
.commonprefix((path
, basePath
)) == basePath
32 class Configuration(DescriptorProvider
):
34 Represents global configuration state based on IDL parse data and
35 the configuration file.
38 def __init__(self
, filename
, webRoots
, parseData
, generatedEvents
=[]):
39 DescriptorProvider
.__init
__(self
)
41 # Read the configuration file.
43 exec(io
.open(filename
, encoding
="utf-8").read(), glbl
)
44 config
= glbl
["DOMInterfaces"]
46 webRoots
= tuple(map(os
.path
.normpath
, webRoots
))
48 def isInWebIDLRoot(path
):
49 return any(isChildPath(path
, root
) for root
in webRoots
)
51 # Build descriptors for all the interfaces we have in the parse data.
52 # This allows callers to specify a subset of interfaces by filtering
56 self
.descriptorsByName
= {}
57 self
.dictionariesByName
= {}
58 self
.generatedEvents
= generatedEvents
59 self
.maxProtoChainLength
= 0
60 for thing
in parseData
:
61 if isinstance(thing
, IDLIncludesStatement
):
62 # Our build system doesn't support dep build involving
63 # addition/removal of "includes" statements that appear in a
64 # different .webidl file than their LHS interface. Make sure we
65 # don't have any of those. See similar block below for partial
67 if thing
.interface
.filename() != thing
.filename():
69 "The binding build system doesn't really support "
70 "'includes' statements which don't appear in the "
71 "file in which the left-hand side of the statement is "
74 "%s" % (thing
.location
, thing
.interface
.location
)
77 assert not thing
.isType()
80 not thing
.isInterface()
81 and not thing
.isNamespace()
82 and not thing
.isInterfaceMixin()
85 # Our build system doesn't support dep builds involving
86 # addition/removal of partial interfaces/namespaces/mixins that
87 # appear in a different .webidl file than the
88 # interface/namespace/mixin they are extending. Make sure we don't
89 # have any of those. See similar block above for "includes"
91 if not thing
.isExternal():
92 for partial
in thing
.getPartials():
93 if partial
.filename() != thing
.filename():
95 "The binding build system doesn't really support "
96 "partial interfaces/namespaces/mixins which don't "
97 "appear in the file in which the "
98 "interface/namespace/mixin they are extending is "
99 "defined. Don't do this.\n"
101 "%s" % (partial
.location
, thing
.location
)
104 # The rest of the logic doesn't apply to mixins.
105 if thing
.isInterfaceMixin():
109 if not iface
.isExternal():
111 iface
.getExtendedAttribute("ChromeOnly")
112 or iface
.getExtendedAttribute("Func")
113 == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"]
114 or not iface
.hasInterfaceObject()
115 or isInWebIDLRoot(iface
.filename())
118 "Interfaces which are exposed to the web may only be "
119 "defined in a DOM WebIDL root %r. Consider marking "
120 "the interface [ChromeOnly] or "
121 "[Func='nsContentUtils::IsCallerChromeOrFuzzingEnabled'] "
122 "if you do not want it exposed to the web.\n"
123 "%s" % (webRoots
, iface
.location
)
126 self
.interfaces
[iface
.identifier
.name
] = iface
128 entry
= config
.get(iface
.identifier
.name
, {})
129 assert not isinstance(entry
, list)
131 desc
= Descriptor(self
, iface
, entry
)
132 self
.descriptors
.append(desc
)
133 # Setting up descriptorsByName while iterating through interfaces
134 # means we can get the nativeType of iterable interfaces without
135 # having to do multiple loops.
136 assert desc
.interface
.identifier
.name
not in self
.descriptorsByName
137 self
.descriptorsByName
[desc
.interface
.identifier
.name
] = desc
139 # Keep the descriptor list sorted for determinism.
140 self
.descriptors
.sort(key
=lambda x
: x
.name
)
142 self
.descriptorsByFile
= {}
143 for d
in self
.descriptors
:
144 self
.descriptorsByFile
.setdefault(d
.interface
.filename(), []).append(d
)
146 self
.enums
= [e
for e
in parseData
if e
.isEnum()]
148 self
.dictionaries
= [d
for d
in parseData
if d
.isDictionary()]
149 self
.dictionariesByName
= {d
.identifier
.name
: d
for d
in self
.dictionaries
}
152 c
for c
in parseData
if c
.isCallback() and not c
.isInterface()
155 # Dictionary mapping from a union type name to a set of filenames where
156 # union types with that name are used.
157 self
.filenamesPerUnion
= defaultdict(set)
159 # Dictionary mapping from a filename to a list of types for
160 # the union types used in that file. If a union type is used
161 # in multiple files then it will be added to the list for the
162 # None key. Note that the list contains a type for every use
163 # of a union type, so there can be multiple entries with union
164 # types that have the same name.
165 self
.unionsPerFilename
= defaultdict(list)
167 for (t
, _
) in getAllTypes(self
.descriptors
, self
.dictionaries
, self
.callbacks
):
168 t
= findInnermostType(t
)
170 filenamesForUnion
= self
.filenamesPerUnion
[t
.name
]
171 if t
.filename() not in filenamesForUnion
:
172 # We have a to be a bit careful: some of our built-in
173 # typedefs are for unions, and those unions end up with
174 # "<unknown>" as the filename. If that happens, we don't
175 # want to try associating this union with one particular
176 # filename, since there isn't one to associate it with,
178 if t
.filename() == "<unknown>":
179 uniqueFilenameForUnion
= None
180 elif len(filenamesForUnion
) == 0:
181 # This is the first file that we found a union with this
182 # name in, record the union as part of the file.
183 uniqueFilenameForUnion
= t
.filename()
185 # We already found a file that contains a union with
187 if len(filenamesForUnion
) == 1:
188 # This is the first time we found a union with this
189 # name in another file.
190 for f
in filenamesForUnion
:
191 # Filter out unions with this name from the
192 # unions for the file where we previously found
194 unionsForFilename
= [
196 for u
in self
.unionsPerFilename
[f
]
199 if len(unionsForFilename
) == 0:
200 del self
.unionsPerFilename
[f
]
202 self
.unionsPerFilename
[f
] = unionsForFilename
203 # Unions with this name appear in multiple files, record
204 # the filename as None, so that we can detect that.
205 uniqueFilenameForUnion
= None
206 self
.unionsPerFilename
[uniqueFilenameForUnion
].append(t
)
207 filenamesForUnion
.add(t
.filename())
209 for d
in getDictionariesConvertedToJS(
210 self
.descriptors
, self
.dictionaries
, self
.callbacks
212 d
.needsConversionToJS
= True
214 for d
in getDictionariesConvertedFromJS(
215 self
.descriptors
, self
.dictionaries
, self
.callbacks
217 d
.needsConversionFromJS
= True
219 def getInterface(self
, ifname
):
220 return self
.interfaces
[ifname
]
222 def getDescriptors(self
, **filters
):
223 """Gets the descriptors that match the given filters."""
224 curr
= self
.descriptors
225 # Collect up our filters, because we may have a webIDLFile filter that
226 # we always want to apply first.
227 tofilter
= [(lambda x
: x
.interface
.isExternal(), False)]
228 for key
, val
in six
.iteritems(filters
):
229 if key
== "webIDLFile":
230 # Special-case this part to make it fast, since most of our
231 # getDescriptors calls are conditioned on a webIDLFile. We may
232 # not have this key, in which case we have no descriptors
234 curr
= self
.descriptorsByFile
.get(val
, [])
236 elif key
== "hasInterfaceObject":
237 getter
= lambda x
: x
.interface
.hasInterfaceObject()
238 elif key
== "hasInterfacePrototypeObject":
239 getter
= lambda x
: x
.interface
.hasInterfacePrototypeObject()
240 elif key
== "hasInterfaceOrInterfacePrototypeObject":
241 getter
= lambda x
: x
.hasInterfaceOrInterfacePrototypeObject()
242 elif key
== "isCallback":
243 getter
= lambda x
: x
.interface
.isCallback()
244 elif key
== "isJSImplemented":
245 getter
= lambda x
: x
.interface
.isJSImplemented()
246 elif key
== "isExposedInAnyWorker":
247 getter
= lambda x
: x
.interface
.isExposedInAnyWorker()
248 elif key
== "isExposedInWorkerDebugger":
249 getter
= lambda x
: x
.interface
.isExposedInWorkerDebugger()
250 elif key
== "isExposedInAnyWorklet":
251 getter
= lambda x
: x
.interface
.isExposedInAnyWorklet()
252 elif key
== "isExposedInWindow":
253 getter
= lambda x
: x
.interface
.isExposedInWindow()
254 elif key
== "isSerializable":
255 getter
= lambda x
: x
.interface
.isSerializable()
257 # Have to watch out: just closing over "key" is not enough,
258 # since we're about to mutate its value
259 getter
= (lambda attrName
: lambda x
: getattr(x
, attrName
))(key
)
260 tofilter
.append((getter
, val
))
262 curr
= [x
for x
in curr
if f
[0](x
) == f
[1]]
265 def getEnums(self
, webIDLFile
):
266 return [e
for e
in self
.enums
if e
.filename() == webIDLFile
]
268 def getDictionaries(self
, webIDLFile
):
269 return [d
for d
in self
.dictionaries
if d
.filename() == webIDLFile
]
271 def getCallbacks(self
, webIDLFile
):
272 return [c
for c
in self
.callbacks
if c
.filename() == webIDLFile
]
274 def getDescriptor(self
, interfaceName
):
276 Gets the appropriate descriptor for the given interface name.
278 # We may have optimized out this descriptor, but the chances of anyone
279 # asking about it are then slim. Put the check for that _after_ we've
280 # done our normal lookup. But that means we have to do our normal
281 # lookup in a way that will not throw if it fails.
282 d
= self
.descriptorsByName
.get(interfaceName
, None)
286 raise NoSuchDescriptorError("For " + interfaceName
+ " found no matches")
291 def getDictionariesConvertibleToJS(self
):
292 return [d
for d
in self
.dictionaries
if d
.needsConversionToJS
]
294 def getDictionariesConvertibleFromJS(self
):
295 return [d
for d
in self
.dictionaries
if d
.needsConversionFromJS
]
297 def getDictionaryIfExists(self
, dictionaryName
):
298 return self
.dictionariesByName
.get(dictionaryName
, None)
301 class NoSuchDescriptorError(TypeError):
302 def __init__(self
, str):
303 TypeError.__init
__(self
, str)
306 def methodReturnsJSObject(method
):
307 assert method
.isMethod()
309 for signature
in method
.signatures():
310 returnType
= signature
[0]
311 if returnType
.isObject() or returnType
.isSpiderMonkeyInterface():
317 def MemberIsLegacyUnforgeable(member
, descriptor
):
318 # Note: "or" and "and" return either their LHS or RHS, not
319 # necessarily booleans. Make sure to return a boolean from this
320 # method, because callers will compare its return value to
323 (member
.isAttr() or member
.isMethod())
324 and not member
.isStatic()
326 member
.isLegacyUnforgeable()
327 or descriptor
.interface
.getExtendedAttribute("LegacyUnforgeable")
332 class Descriptor(DescriptorProvider
):
334 Represents a single descriptor for an interface. See Bindings.conf.
337 def __init__(self
, config
, interface
, desc
):
338 DescriptorProvider
.__init
__(self
)
340 self
.interface
= interface
342 self
.wantsXrays
= not interface
.isExternal() and interface
.isExposedInWindow()
345 # We could try to restrict self.wantsXrayExpandoClass further. For
346 # example, we could set it to false if all of our slots store
347 # Gecko-interface-typed things, because we don't use Xray expando
348 # slots for those. But note that we would need to check the types
349 # of not only the members of "interface" but also of all its
350 # ancestors, because those can have members living in our slots too.
351 # For now, do the simple thing.
352 self
.wantsXrayExpandoClass
= interface
.totalMembersInSlots
!= 0
354 # Read the desc, and fill in the relevant defaults.
355 ifaceName
= self
.interface
.identifier
.name
356 # For generated iterator interfaces for other iterable interfaces, we
357 # just use IterableIterator as the native type, templated on the
358 # nativeType of the iterable interface. That way we can have a
359 # templated implementation for all the duplicated iterator
361 if self
.interface
.isIteratorInterface():
362 itrName
= self
.interface
.iterableInterface
.identifier
.name
363 itrDesc
= self
.getDescriptor(itrName
)
364 nativeTypeDefault
= iteratorNativeType(itrDesc
)
366 elif self
.interface
.isExternal():
367 nativeTypeDefault
= "nsIDOM" + ifaceName
369 nativeTypeDefault
= "mozilla::dom::" + ifaceName
371 self
.nativeType
= desc
.get("nativeType", nativeTypeDefault
)
372 # Now create a version of nativeType that doesn't have extra
373 # mozilla::dom:: at the beginning.
374 prettyNativeType
= self
.nativeType
.split("::")
375 if prettyNativeType
[0] == "mozilla":
376 prettyNativeType
.pop(0)
377 if prettyNativeType
[0] == "dom":
378 prettyNativeType
.pop(0)
379 self
.prettyNativeType
= "::".join(prettyNativeType
)
381 self
.jsImplParent
= desc
.get("jsImplParent", self
.nativeType
)
383 # Do something sane for JSObject
384 if self
.nativeType
== "JSObject":
385 headerDefault
= "js/TypeDecls.h"
386 elif self
.interface
.isCallback() or self
.interface
.isJSImplemented():
387 # A copy of CGHeaders.getDeclarationFilename; we can't
388 # import it here, sadly.
389 # Use our local version of the header, not the exported one, so that
390 # test bindings, which don't export, will work correctly.
391 basename
= os
.path
.basename(self
.interface
.filename())
392 headerDefault
= basename
.replace(".webidl", "Binding.h")
394 if not self
.interface
.isExternal() and self
.interface
.getExtendedAttribute(
397 headerDefault
= self
.interface
.getExtendedAttribute("HeaderFile")[0]
398 elif self
.interface
.isIteratorInterface():
399 headerDefault
= "mozilla/dom/IterableIterator.h"
401 headerDefault
= self
.nativeType
402 headerDefault
= headerDefault
.replace("::", "/") + ".h"
403 self
.headerFile
= desc
.get("headerFile", headerDefault
)
404 self
.headerIsDefault
= self
.headerFile
== headerDefault
405 if self
.jsImplParent
== self
.nativeType
:
406 self
.jsImplParentHeader
= self
.headerFile
408 self
.jsImplParentHeader
= self
.jsImplParent
.replace("::", "/") + ".h"
410 self
.notflattened
= desc
.get("notflattened", False)
411 self
.register
= desc
.get("register", True)
413 # If we're concrete, we need to crawl our ancestor interfaces and mark
414 # them as having a concrete descendant.
416 not self
.interface
.isExternal()
417 and not self
.interface
.isCallback()
418 and not self
.interface
.isNamespace()
420 # We're going to assume that leaf interfaces are
421 # concrete; otherwise what's the point? Also
422 # interfaces with constructors had better be
423 # concrete; otherwise how can you construct them?
425 not self
.interface
.hasChildInterfaces()
426 or self
.interface
.ctor() is not None
430 self
.concrete
= desc
.get("concrete", concreteDefault
)
431 self
.hasLegacyUnforgeableMembers
= self
.concrete
and any(
432 MemberIsLegacyUnforgeable(m
, self
) for m
in self
.interface
.members
435 "IndexedGetter": None,
436 "IndexedSetter": None,
437 "IndexedDeleter": None,
440 "NamedDeleter": None,
442 "LegacyCaller": None,
445 self
.hasDefaultToJSON
= False
447 # Stringifiers need to be set up whether an interface is
448 # concrete or not, because they're actually prototype methods and hence
449 # can apply to instances of descendant interfaces. Legacy callers and
450 # named/indexed operations only need to be set up on concrete
451 # interfaces, since they affect the JSClass we end up using, not the
453 def addOperation(operation
, m
):
454 if not self
.operations
[operation
]:
455 self
.operations
[operation
] = m
457 # Since stringifiers go on the prototype, we only need to worry
458 # about our own stringifier, not those of our ancestor interfaces.
459 if not self
.interface
.isExternal():
460 for m
in self
.interface
.members
:
461 if m
.isMethod() and m
.isStringifier():
462 addOperation("Stringifier", m
)
463 if m
.isMethod() and m
.isDefaultToJSON():
464 self
.hasDefaultToJSON
= True
466 # We keep track of instrumente props for all non-external interfaces.
467 self
.instrumentedProps
= []
468 instrumentedProps
= self
.interface
.getExtendedAttribute("InstrumentedProps")
469 if instrumentedProps
:
470 # It's actually a one-element list, with the list
471 # we want as the only element.
472 self
.instrumentedProps
= instrumentedProps
[0]
474 # Check that we don't have duplicated instrumented props.
475 uniqueInstrumentedProps
= set(self
.instrumentedProps
)
476 if len(uniqueInstrumentedProps
) != len(self
.instrumentedProps
):
479 for p
in uniqueInstrumentedProps
480 if self
.instrumentedProps
.count(p
) > 1
483 "Duplicated instrumented properties: %s.\n%s"
484 % (duplicates
, self
.interface
.location
)
489 iface
= self
.interface
490 for m
in iface
.members
:
491 # Don't worry about inheriting legacycallers either: in
492 # practice these are on most-derived prototypes.
493 if m
.isMethod() and m
.isLegacycaller():
494 if not m
.isIdentifierLess():
496 "We don't support legacycaller with "
497 "identifier.\n%s" % m
.location
499 if len(m
.signatures()) != 1:
501 "We don't support overloaded "
502 "legacycaller.\n%s" % m
.location
504 addOperation("LegacyCaller", m
)
507 for m
in iface
.members
:
511 def addIndexedOrNamedOperation(operation
, m
):
513 operation
= "Indexed" + operation
516 operation
= "Named" + operation
517 addOperation(operation
, m
)
520 addIndexedOrNamedOperation("Getter", m
)
522 addIndexedOrNamedOperation("Setter", m
)
524 addIndexedOrNamedOperation("Deleter", m
)
525 if m
.isLegacycaller() and iface
!= self
.interface
:
527 "We don't support legacycaller on "
528 "non-leaf interface %s.\n%s" % (iface
, iface
.location
)
531 iface
.setUserData("hasConcreteDescendant", True)
535 self
.supportsIndexedProperties()
537 self
.supportsNamedProperties() and not self
.hasNamedPropertiesObject
539 or self
.isMaybeCrossOriginObject()
543 if self
.isMaybeCrossOriginObject() and (
544 self
.supportsIndexedProperties() or self
.supportsNamedProperties()
547 "We don't support named or indexed "
548 "properties on maybe-cross-origin objects. "
549 "This lets us assume that their proxy "
550 "hooks are never called via Xrays. "
551 "Fix %s.\n%s" % (self
.interface
, self
.interface
.location
)
554 if not self
.operations
["IndexedGetter"] and (
555 self
.operations
["IndexedSetter"]
556 or self
.operations
["IndexedDeleter"]
559 "%s supports indexed properties but does "
560 "not have an indexed getter.\n%s"
561 % (self
.interface
, self
.interface
.location
)
563 if not self
.operations
["NamedGetter"] and (
564 self
.operations
["NamedSetter"] or self
.operations
["NamedDeleter"]
567 "%s supports named properties but does "
568 "not have a named getter.\n%s"
569 % (self
.interface
, self
.interface
.location
)
571 iface
= self
.interface
573 iface
.setUserData("hasProxyDescendant", True)
576 if desc
.get("wantsQI", None) is not None:
577 self
._wantsQI
= desc
.get("wantsQI", None)
578 self
.wrapperCache
= (
579 not self
.interface
.isCallback()
580 and not self
.interface
.isIteratorInterface()
581 and desc
.get("wrapperCache", True)
584 self
.name
= interface
.identifier
.name
586 # self.implicitJSContext is a list of names of methods and attributes
587 # that need a JSContext.
588 if self
.interface
.isJSImplemented():
589 self
.implicitJSContext
= ["constructor"]
591 self
.implicitJSContext
= desc
.get("implicitJSContext", [])
592 assert isinstance(self
.implicitJSContext
, list)
594 self
._binaryNames
= {}
596 if not self
.interface
.isExternal():
598 def maybeAddBinaryName(member
):
599 binaryName
= member
.getExtendedAttribute("BinaryName")
601 assert isinstance(binaryName
, list)
602 assert len(binaryName
) == 1
603 self
._binaryNames
.setdefault(member
.identifier
.name
, binaryName
[0])
605 for member
in self
.interface
.members
:
606 if not member
.isAttr() and not member
.isMethod():
608 maybeAddBinaryName(member
)
610 ctor
= self
.interface
.ctor()
612 maybeAddBinaryName(ctor
)
614 # Some default binary names for cases when nothing else got set.
615 self
._binaryNames
.setdefault("__legacycaller", "LegacyCall")
616 self
._binaryNames
.setdefault("__stringifier", "Stringify")
618 # Build the prototype chain.
619 self
.prototypeChain
= []
620 self
.needsMissingPropUseCounters
= False
623 self
.needsMissingPropUseCounters
= (
624 self
.needsMissingPropUseCounters
625 or parent
.getExtendedAttribute("InstrumentedProps")
627 self
.prototypeChain
.insert(0, parent
.identifier
.name
)
628 parent
= parent
.parent
629 config
.maxProtoChainLength
= max(
630 config
.maxProtoChainLength
, len(self
.prototypeChain
)
633 def binaryNameFor(self
, name
):
634 return self
._binaryNames
.get(name
, name
)
637 def prototypeNameChain(self
):
638 return [self
.getDescriptor(p
).name
for p
in self
.prototypeChain
]
641 def parentPrototypeName(self
):
642 if len(self
.prototypeChain
) == 1:
644 return self
.getDescriptor(self
.prototypeChain
[-2]).name
646 def hasInterfaceOrInterfacePrototypeObject(self
):
648 self
.interface
.hasInterfaceObject()
649 or self
.interface
.hasInterfacePrototypeObject()
653 def hasNamedPropertiesObject(self
):
654 return self
.isGlobal() and self
.supportsNamedProperties()
656 def getExtendedAttributes(self
, member
, getter
=False, setter
=False):
657 def ensureValidBoolExtendedAttribute(attr
, name
):
658 if attr
is not None and attr
is not True:
659 raise TypeError("Unknown value for '%s': %s" % (name
, attr
[0]))
661 def ensureValidThrowsExtendedAttribute(attr
):
662 ensureValidBoolExtendedAttribute(attr
, "Throws")
664 def ensureValidCanOOMExtendedAttribute(attr
):
665 ensureValidBoolExtendedAttribute(attr
, "CanOOM")
667 def maybeAppendNeedsErrorResultToAttrs(attrs
, throws
):
668 ensureValidThrowsExtendedAttribute(throws
)
669 if throws
is not None:
670 attrs
.append("needsErrorResult")
672 def maybeAppendCanOOMToAttrs(attrs
, canOOM
):
673 ensureValidCanOOMExtendedAttribute(canOOM
)
674 if canOOM
is not None:
675 attrs
.append("canOOM")
677 def maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
):
679 needsSubjectPrincipal
is not None
680 and needsSubjectPrincipal
is not True
681 and needsSubjectPrincipal
!= ["NonSystem"]
684 "Unknown value for 'NeedsSubjectPrincipal': %s"
685 % needsSubjectPrincipal
[0]
688 if needsSubjectPrincipal
is not None:
689 attrs
.append("needsSubjectPrincipal")
690 if needsSubjectPrincipal
== ["NonSystem"]:
691 attrs
.append("needsNonSystemSubjectPrincipal")
693 name
= member
.identifier
.name
694 throws
= self
.interface
.isJSImplemented() or member
.getExtendedAttribute(
697 canOOM
= member
.getExtendedAttribute("CanOOM")
698 needsSubjectPrincipal
= member
.getExtendedAttribute("NeedsSubjectPrincipal")
700 if name
in self
.implicitJSContext
:
701 attrs
.append("implicitJSContext")
702 if member
.isMethod():
703 # JSObject-returning [NewObject] methods must be fallible,
704 # since they have to (fallibly) allocate the new JSObject.
705 if member
.getExtendedAttribute("NewObject"):
706 if member
.returnsPromise():
708 elif methodReturnsJSObject(member
):
710 maybeAppendNeedsErrorResultToAttrs(attrs
, throws
)
711 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
712 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
)
715 assert member
.isAttr()
716 assert bool(getter
) != bool(setter
)
718 throwsAttr
= "GetterThrows" if getter
else "SetterThrows"
719 throws
= member
.getExtendedAttribute(throwsAttr
)
720 maybeAppendNeedsErrorResultToAttrs(attrs
, throws
)
722 canOOMAttr
= "GetterCanOOM" if getter
else "SetterCanOOM"
723 canOOM
= member
.getExtendedAttribute(canOOMAttr
)
724 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
725 if needsSubjectPrincipal
is None:
726 needsSubjectPrincipalAttr
= (
727 "GetterNeedsSubjectPrincipal"
729 else "SetterNeedsSubjectPrincipal"
731 needsSubjectPrincipal
= member
.getExtendedAttribute(
732 needsSubjectPrincipalAttr
734 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
)
737 def supportsIndexedProperties(self
):
738 return self
.operations
["IndexedGetter"] is not None
740 def lengthNeedsCallerType(self
):
742 Determine whether our length getter needs a caller type; this is needed
743 in some indexed-getter proxy algorithms. The idea is that if our
744 indexed getter needs a caller type, our automatically-generated Length()
747 assert self
.supportsIndexedProperties()
748 indexedGetter
= self
.operations
["IndexedGetter"]
749 return indexedGetter
.getExtendedAttribute("NeedsCallerType")
751 def supportsNamedProperties(self
):
752 return self
.operations
["NamedGetter"] is not None
754 def supportedNamesNeedCallerType(self
):
756 Determine whether our GetSupportedNames call needs a caller type. The
757 idea is that if your named getter needs a caller type, then so does
760 assert self
.supportsNamedProperties()
761 namedGetter
= self
.operations
["NamedGetter"]
762 return namedGetter
.getExtendedAttribute("NeedsCallerType")
764 def isMaybeCrossOriginObject(self
):
765 # If we're isGlobal and have cross-origin members, we're a Window, and
766 # that's not a cross-origin object. The WindowProxy is.
769 and self
.interface
.hasCrossOriginMembers
770 and not self
.isGlobal()
773 def needsHeaderInclude(self
):
775 An interface doesn't need a header file if it is not concrete, not
776 pref-controlled, has no prototype object, has no static methods or
777 attributes and has no parent. The parent matters because we assert
778 things about refcounting that depend on the actual underlying type if we
783 self
.interface
.isExternal()
785 or self
.interface
.hasInterfacePrototypeObject()
787 (m
.isAttr() or m
.isMethod()) and m
.isStatic()
788 for m
in self
.interface
.members
790 or self
.interface
.parent
793 def hasThreadChecks(self
):
794 # isExposedConditionally does not necessarily imply thread checks
795 # (since at least [SecureContext] is independent of them), but we're
796 # only used to decide whether to include nsThreadUtils.h, so we don't
799 self
.isExposedConditionally() and not self
.interface
.isExposedInWindow()
800 ) or self
.interface
.isExposedInSomeButNotAllWorkers()
802 def hasCEReactions(self
):
804 m
.getExtendedAttribute("CEReactions") for m
in self
.interface
.members
807 def isExposedConditionally(self
):
809 self
.interface
.isExposedConditionally()
810 or self
.interface
.isExposedInSomeButNotAllWorkers()
813 def needsXrayResolveHooks(self
):
815 Generally, any interface with NeedResolve needs Xray
816 resolveOwnProperty and enumerateOwnProperties hooks. But for
817 the special case of plugin-loading elements, we do NOT want
818 those, because we don't want to instantiate plug-ins simply
819 due to chrome touching them and that's all those hooks do on
820 those elements. So we special-case those here.
822 return self
.interface
.getExtendedAttribute(
824 ) and self
.interface
.identifier
.name
not in [
829 def needsXrayNamedDeleterHook(self
):
830 return self
.operations
["NamedDeleter"] is not None
834 Returns true if this is the primary interface for a global object
837 return self
.interface
.getExtendedAttribute("Global")
840 def namedPropertiesEnumerable(self
):
842 Returns whether this interface should have enumerable named properties
845 assert self
.supportsNamedProperties()
846 iface
= self
.interface
848 if iface
.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
854 def registersGlobalNamesOnWindow(self
):
856 self
.interface
.hasInterfaceObject()
857 and self
.interface
.isExposedInWindow()
861 def getDescriptor(self
, interfaceName
):
863 Gets the appropriate descriptor for the given interface name.
865 return self
.config
.getDescriptor(interfaceName
)
871 # Some utility methods
872 def getTypesFromDescriptor(descriptor
, includeArgs
=True, includeReturns
=True):
874 Get argument and/or return types for all members of the descriptor. By
875 default returns all argument types (which includes types of writable
876 attributes) and all return types (which includes types of all attributes).
878 assert includeArgs
or includeReturns
# Must want _something_.
879 members
= [m
for m
in descriptor
.interface
.members
]
880 if descriptor
.interface
.ctor():
881 members
.append(descriptor
.interface
.ctor())
882 members
.extend(descriptor
.interface
.legacyFactoryFunctions
)
883 signatures
= [s
for m
in members
if m
.isMethod() for s
in m
.signatures()]
887 (returnType
, arguments
) = s
889 types
.append(returnType
)
891 types
.extend(a
.type for a
in arguments
)
896 if (a
.isAttr() and (includeReturns
or (includeArgs
and not a
.readonly
)))
899 if descriptor
.interface
.maplikeOrSetlikeOrIterable
:
900 maplikeOrSetlikeOrIterable
= descriptor
.interface
.maplikeOrSetlikeOrIterable
901 if maplikeOrSetlikeOrIterable
.isMaplike():
902 # The things we expand into may or may not correctly indicate in
903 # their formal IDL types what things we have as return values. For
904 # example, "keys" returns the moral equivalent of sequence<keyType>
905 # but just claims to return "object". Similarly, "values" returns
906 # the moral equivalent of sequence<valueType> but claims to return
907 # "object". And due to bug 1155340, "get" claims to return "any"
908 # instead of the right type. So let's just manually work around
909 # that lack of specificity. For our arguments, we already enforce
910 # the right types at the IDL level, so those will get picked up
912 assert maplikeOrSetlikeOrIterable
.hasKeyType()
913 assert maplikeOrSetlikeOrIterable
.hasValueType()
915 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
916 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
917 elif maplikeOrSetlikeOrIterable
.isSetlike():
918 assert maplikeOrSetlikeOrIterable
.hasKeyType()
919 assert maplikeOrSetlikeOrIterable
.hasValueType()
921 maplikeOrSetlikeOrIterable
.keyType
922 == maplikeOrSetlikeOrIterable
.valueType
924 # As in the maplike case, we don't always declare our return values
927 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
929 assert maplikeOrSetlikeOrIterable
.isIterable()
930 # As in the maplike/setlike cases we don't do a good job of
931 # declaring our actual return types, while our argument types, if
932 # any, are declared fine.
934 if maplikeOrSetlikeOrIterable
.hasKeyType():
935 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
936 if maplikeOrSetlikeOrIterable
.hasValueType():
937 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
942 def getTypesFromDictionary(dictionary
):
944 Get all member types for this dictionary
949 types
.extend([m
.type for m
in curDict
.members
])
950 curDict
= curDict
.parent
954 def getTypesFromCallback(callback
):
956 Get the types this callback depends on: its return type and the
957 types of its arguments.
959 sig
= callback
.signatures()[0]
960 types
= [sig
[0]] # Return type
961 types
.extend(arg
.type for arg
in sig
[1]) # Arguments
965 def getAllTypes(descriptors
, dictionaries
, callbacks
):
967 Generate all the types we're dealing with. For each type, a tuple
968 containing type, dictionary is yielded. The dictionary can be None if the
969 type does not come from a dictionary.
971 for d
in descriptors
:
972 if d
.interface
.isExternal():
974 for t
in getTypesFromDescriptor(d
):
976 for dictionary
in dictionaries
:
977 for t
in getTypesFromDictionary(dictionary
):
978 yield (t
, dictionary
)
979 for callback
in callbacks
:
980 for t
in getTypesFromCallback(callback
):
984 def iteratorNativeType(descriptor
):
985 assert descriptor
.interface
.isIterable()
986 iterableDecl
= descriptor
.interface
.maplikeOrSetlikeOrIterable
987 assert iterableDecl
.isPairIterator()
988 return "mozilla::dom::IterableIterator<%s>" % descriptor
.nativeType
991 def findInnermostType(t
):
993 Find the innermost type of the given type, unwrapping Promise and Record
994 types, as well as everything that unroll() unwraps.
999 elif t
.unroll() != t
:
1002 t
= t
.promiseInnerType()
1007 def getDependentDictionariesFromDictionary(d
):
1009 Find all the dictionaries contained in the given dictionary, as ancestors or
1010 members. This returns a generator.
1014 for member
in d
.members
:
1015 for next
in getDictionariesFromType(member
.type):
1020 def getDictionariesFromType(type):
1022 Find all the dictionaries contained in type. This can be used to find
1023 dictionaries that need conversion to JS (by looking at types that get
1024 converted to JS) or dictionaries that need conversion from JS (by looking at
1025 types that get converted from JS).
1027 This returns a generator.
1029 type = findInnermostType(type)
1031 # Look for dictionaries in all the member types
1032 for t
in type.flatMemberTypes
:
1033 for next
in getDictionariesFromType(t
):
1035 elif type.isDictionary():
1036 # Find the dictionaries that are itself, any of its ancestors, or
1037 # contained in any of its member types.
1038 for d
in getDependentDictionariesFromDictionary(type.inner
):
1042 def getDictionariesConvertedToJS(descriptors
, dictionaries
, callbacks
):
1043 for desc
in descriptors
:
1044 if desc
.interface
.isExternal():
1047 if desc
.interface
.isJSImplemented():
1048 # For a JS-implemented interface, we need to-JS
1049 # conversions for all the types involved.
1050 for t
in getTypesFromDescriptor(desc
):
1051 for d
in getDictionariesFromType(t
):
1053 elif desc
.interface
.isCallback():
1054 # For callbacks we only want to include the arguments, since that's
1055 # where the to-JS conversion happens.
1056 for t
in getTypesFromDescriptor(desc
, includeReturns
=False):
1057 for d
in getDictionariesFromType(t
):
1060 # For normal interfaces, we only want to include return values,
1061 # since that's where to-JS conversion happens.
1062 for t
in getTypesFromDescriptor(desc
, includeArgs
=False):
1063 for d
in getDictionariesFromType(t
):
1066 for callback
in callbacks
:
1067 # We only want to look at the arguments
1068 sig
= callback
.signatures()[0]
1070 for d
in getDictionariesFromType(arg
.type):
1073 for dictionary
in dictionaries
:
1074 if dictionary
.needsConversionToJS
:
1075 # It's explicitly flagged as needing to-JS conversion, and all its
1076 # dependent dictionaries will need to-JS conversion too.
1077 for d
in getDependentDictionariesFromDictionary(dictionary
):
1081 def getDictionariesConvertedFromJS(descriptors
, dictionaries
, callbacks
):
1082 for desc
in descriptors
:
1083 if desc
.interface
.isExternal():
1086 if desc
.interface
.isJSImplemented():
1087 # For a JS-implemented interface, we need from-JS conversions for
1088 # all the types involved.
1089 for t
in getTypesFromDescriptor(desc
):
1090 for d
in getDictionariesFromType(t
):
1092 elif desc
.interface
.isCallback():
1093 # For callbacks we only want to include the return value, since
1094 # that's where teh from-JS conversion happens.
1095 for t
in getTypesFromDescriptor(desc
, includeArgs
=False):
1096 for d
in getDictionariesFromType(t
):
1099 # For normal interfaces, we only want to include arguments values,
1100 # since that's where from-JS conversion happens.
1101 for t
in getTypesFromDescriptor(desc
, includeReturns
=False):
1102 for d
in getDictionariesFromType(t
):
1105 for callback
in callbacks
:
1106 # We only want to look at the return value
1107 sig
= callback
.signatures()[0]
1108 for d
in getDictionariesFromType(sig
[0]):
1111 for dictionary
in dictionaries
:
1112 if dictionary
.needsConversionFromJS
:
1113 # It's explicitly flagged as needing from-JS conversion, and all its
1114 # dependent dictionaries will need from-JS conversion too.
1115 for d
in getDependentDictionariesFromDictionary(dictionary
):