Bug 1707290 [wpt PR 28671] - Auto-expand details elements for find-in-page, a=testonly
[gecko.git] / dom / bindings / Configuration.py
blob3817db2c18e71cd34fe7f34f6d42dbccd9404ca6
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 # You can obtain one at http://mozilla.org/MPL/2.0/.
5 from WebIDL import IDLIncludesStatement
6 import io
7 import os
8 import six
9 from collections import defaultdict
11 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
14 class DescriptorProvider:
15 """
16 A way of getting descriptors for interface names. Subclasses must
17 have a getDescriptor method callable with the interface name only.
19 Subclasses must also have a getConfig() method that returns a
20 Configuration.
21 """
23 def __init__(self):
24 pass
27 def isChildPath(path, basePath):
28 path = os.path.normpath(path)
29 return os.path.commonprefix((path, basePath)) == basePath
32 class Configuration(DescriptorProvider):
33 """
34 Represents global configuration state based on IDL parse data and
35 the configuration file.
36 """
38 def __init__(self, filename, webRoots, parseData, generatedEvents=[]):
39 DescriptorProvider.__init__(self)
41 # Read the configuration file.
42 glbl = {}
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
53 # |parseData|.
54 self.descriptors = []
55 self.interfaces = {}
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
66 # interfaces!
67 if thing.interface.filename() != thing.filename():
68 raise TypeError(
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 "
72 "defined.\n"
73 "%s\n"
74 "%s" % (thing.location, thing.interface.location)
77 assert not thing.isType()
79 if (
80 not thing.isInterface()
81 and not thing.isNamespace()
82 and not thing.isInterfaceMixin()
84 continue
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"
90 # statements!
91 if not thing.isExternal():
92 for partial in thing.getPartials():
93 if partial.filename() != thing.filename():
94 raise TypeError(
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"
100 "%s\n"
101 "%s" % (partial.location, thing.location)
104 # The rest of the logic doesn't apply to mixins.
105 if thing.isInterfaceMixin():
106 continue
108 iface = thing
109 if not iface.isExternal():
110 if not (
111 iface.getExtendedAttribute("ChromeOnly")
112 or iface.getExtendedAttribute("Func")
113 == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"]
114 or not iface.hasInterfaceObject()
115 or isInWebIDLRoot(iface.filename())
117 raise TypeError(
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}
151 self.callbacks = [
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)
169 if t.isUnion():
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,
177 # really.
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()
184 else:
185 # We already found a file that contains a union with
186 # this name.
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
193 # them.
194 unionsForFilename = [
196 for u in self.unionsPerFilename[f]
197 if u.name != t.name
199 if len(unionsForFilename) == 0:
200 del self.unionsPerFilename[f]
201 else:
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
233 # either.
234 curr = self.descriptorsByFile.get(val, [])
235 continue
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()
256 else:
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))
261 for f in tofilter:
262 curr = [x for x in curr if f[0](x) == f[1]]
263 return curr
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)
283 if d:
284 return d
286 raise NoSuchDescriptorError("For " + interfaceName + " found no matches")
288 def getConfig(self):
289 return self
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():
312 return True
314 return False
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
321 # booleans.
322 return bool(
323 (member.isAttr() or member.isMethod())
324 and not member.isStatic()
325 and (
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)
339 self.config = config
340 self.interface = interface
342 self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow()
344 if self.wantsXrays:
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
360 # functionality.
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
368 else:
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")
393 else:
394 if not self.interface.isExternal() and self.interface.getExtendedAttribute(
395 "HeaderFile"
397 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
398 elif self.interface.isIteratorInterface():
399 headerDefault = "mozilla/dom/IterableIterator.h"
400 else:
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
407 else:
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.
415 concreteDefault = (
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
434 self.operations = {
435 "IndexedGetter": None,
436 "IndexedSetter": None,
437 "IndexedDeleter": None,
438 "NamedGetter": None,
439 "NamedSetter": None,
440 "NamedDeleter": None,
441 "Stringifier": 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
452 # prototype object.
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):
477 duplicates = [
479 for p in uniqueInstrumentedProps
480 if self.instrumentedProps.count(p) > 1
482 raise TypeError(
483 "Duplicated instrumented properties: %s.\n%s"
484 % (duplicates, self.interface.location)
487 if self.concrete:
488 self.proxy = False
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():
495 raise TypeError(
496 "We don't support legacycaller with "
497 "identifier.\n%s" % m.location
499 if len(m.signatures()) != 1:
500 raise TypeError(
501 "We don't support overloaded "
502 "legacycaller.\n%s" % m.location
504 addOperation("LegacyCaller", m)
506 while iface:
507 for m in iface.members:
508 if not m.isMethod():
509 continue
511 def addIndexedOrNamedOperation(operation, m):
512 if m.isIndexed():
513 operation = "Indexed" + operation
514 else:
515 assert m.isNamed()
516 operation = "Named" + operation
517 addOperation(operation, m)
519 if m.isGetter():
520 addIndexedOrNamedOperation("Getter", m)
521 if m.isSetter():
522 addIndexedOrNamedOperation("Setter", m)
523 if m.isDeleter():
524 addIndexedOrNamedOperation("Deleter", m)
525 if m.isLegacycaller() and iface != self.interface:
526 raise TypeError(
527 "We don't support legacycaller on "
528 "non-leaf interface %s.\n%s" % (iface, iface.location)
531 iface.setUserData("hasConcreteDescendant", True)
532 iface = iface.parent
534 self.proxy = (
535 self.supportsIndexedProperties()
536 or (
537 self.supportsNamedProperties() and not self.hasNamedPropertiesObject
539 or self.isMaybeCrossOriginObject()
542 if self.proxy:
543 if self.isMaybeCrossOriginObject() and (
544 self.supportsIndexedProperties() or self.supportsNamedProperties()
546 raise TypeError(
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"]
558 raise SyntaxError(
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"]
566 raise SyntaxError(
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
572 while iface:
573 iface.setUserData("hasProxyDescendant", True)
574 iface = iface.parent
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"]
590 else:
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")
600 if 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():
607 continue
608 maybeAddBinaryName(member)
610 ctor = self.interface.ctor()
611 if 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
621 parent = interface
622 while parent:
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)
636 @property
637 def prototypeNameChain(self):
638 return [self.getDescriptor(p).name for p in self.prototypeChain]
640 @property
641 def parentPrototypeName(self):
642 if len(self.prototypeChain) == 1:
643 return None
644 return self.getDescriptor(self.prototypeChain[-2]).name
646 def hasInterfaceOrInterfacePrototypeObject(self):
647 return (
648 self.interface.hasInterfaceObject()
649 or self.interface.hasInterfacePrototypeObject()
652 @property
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):
678 if (
679 needsSubjectPrincipal is not None
680 and needsSubjectPrincipal is not True
681 and needsSubjectPrincipal != ["NonSystem"]
683 raise TypeError(
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(
695 "Throws"
697 canOOM = member.getExtendedAttribute("CanOOM")
698 needsSubjectPrincipal = member.getExtendedAttribute("NeedsSubjectPrincipal")
699 attrs = []
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():
707 throws = True
708 elif methodReturnsJSObject(member):
709 canOOM = True
710 maybeAppendNeedsErrorResultToAttrs(attrs, throws)
711 maybeAppendCanOOMToAttrs(attrs, canOOM)
712 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
713 return attrs
715 assert member.isAttr()
716 assert bool(getter) != bool(setter)
717 if throws is None:
718 throwsAttr = "GetterThrows" if getter else "SetterThrows"
719 throws = member.getExtendedAttribute(throwsAttr)
720 maybeAppendNeedsErrorResultToAttrs(attrs, throws)
721 if canOOM is None:
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"
728 if getter
729 else "SetterNeedsSubjectPrincipal"
731 needsSubjectPrincipal = member.getExtendedAttribute(
732 needsSubjectPrincipalAttr
734 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
735 return attrs
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()
745 calls need one too.
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
758 GetSupportedNames.
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.
767 return (
768 self.concrete
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
779 have a parent.
782 return (
783 self.interface.isExternal()
784 or self.concrete
785 or self.interface.hasInterfacePrototypeObject()
786 or any(
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
797 # worry about that.
798 return (
799 self.isExposedConditionally() and not self.interface.isExposedInWindow()
800 ) or self.interface.isExposedInSomeButNotAllWorkers()
802 def hasCEReactions(self):
803 return any(
804 m.getExtendedAttribute("CEReactions") for m in self.interface.members
807 def isExposedConditionally(self):
808 return (
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(
823 "NeedResolve"
824 ) and self.interface.identifier.name not in [
825 "HTMLObjectElement",
826 "HTMLEmbedElement",
829 def needsXrayNamedDeleterHook(self):
830 return self.operations["NamedDeleter"] is not None
832 def isGlobal(self):
834 Returns true if this is the primary interface for a global object
835 of some sort.
837 return self.interface.getExtendedAttribute("Global")
839 @property
840 def namedPropertiesEnumerable(self):
842 Returns whether this interface should have enumerable named properties
844 assert self.proxy
845 assert self.supportsNamedProperties()
846 iface = self.interface
847 while iface:
848 if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
849 return False
850 iface = iface.parent
851 return True
853 @property
854 def registersGlobalNamesOnWindow(self):
855 return (
856 self.interface.hasInterfaceObject()
857 and self.interface.isExposedInWindow()
858 and self.register
861 def getDescriptor(self, interfaceName):
863 Gets the appropriate descriptor for the given interface name.
865 return self.config.getDescriptor(interfaceName)
867 def getConfig(self):
868 return self.config
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()]
884 types = []
885 for s in signatures:
886 assert len(s) == 2
887 (returnType, arguments) = s
888 if includeReturns:
889 types.append(returnType)
890 if includeArgs:
891 types.extend(a.type for a in arguments)
893 types.extend(
894 a.type
895 for a in members
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
911 # correctly.
912 assert maplikeOrSetlikeOrIterable.hasKeyType()
913 assert maplikeOrSetlikeOrIterable.hasValueType()
914 if includeReturns:
915 types.append(maplikeOrSetlikeOrIterable.keyType)
916 types.append(maplikeOrSetlikeOrIterable.valueType)
917 elif maplikeOrSetlikeOrIterable.isSetlike():
918 assert maplikeOrSetlikeOrIterable.hasKeyType()
919 assert maplikeOrSetlikeOrIterable.hasValueType()
920 assert (
921 maplikeOrSetlikeOrIterable.keyType
922 == maplikeOrSetlikeOrIterable.valueType
924 # As in the maplike case, we don't always declare our return values
925 # quite correctly.
926 if includeReturns:
927 types.append(maplikeOrSetlikeOrIterable.keyType)
928 else:
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.
933 if includeReturns:
934 if maplikeOrSetlikeOrIterable.hasKeyType():
935 types.append(maplikeOrSetlikeOrIterable.keyType)
936 if maplikeOrSetlikeOrIterable.hasValueType():
937 types.append(maplikeOrSetlikeOrIterable.valueType)
939 return types
942 def getTypesFromDictionary(dictionary):
944 Get all member types for this dictionary
946 types = []
947 curDict = dictionary
948 while curDict:
949 types.extend([m.type for m in curDict.members])
950 curDict = curDict.parent
951 return types
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
962 return types
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():
973 continue
974 for t in getTypesFromDescriptor(d):
975 yield (t, None)
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):
981 yield (t, None)
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.
996 while True:
997 if t.isRecord():
998 t = t.inner
999 elif t.unroll() != t:
1000 t = t.unroll()
1001 elif t.isPromise():
1002 t = t.promiseInnerType()
1003 else:
1004 return t
1007 def getDependentDictionariesFromDictionary(d):
1009 Find all the dictionaries contained in the given dictionary, as ancestors or
1010 members. This returns a generator.
1012 while d:
1013 yield d
1014 for member in d.members:
1015 for next in getDictionariesFromType(member.type):
1016 yield next
1017 d = d.parent
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)
1030 if type.isUnion():
1031 # Look for dictionaries in all the member types
1032 for t in type.flatMemberTypes:
1033 for next in getDictionariesFromType(t):
1034 yield next
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):
1039 yield d
1042 def getDictionariesConvertedToJS(descriptors, dictionaries, callbacks):
1043 for desc in descriptors:
1044 if desc.interface.isExternal():
1045 continue
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):
1052 yield d
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):
1058 yield d
1059 else:
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):
1064 yield d
1066 for callback in callbacks:
1067 # We only want to look at the arguments
1068 sig = callback.signatures()[0]
1069 for arg in sig[1]:
1070 for d in getDictionariesFromType(arg.type):
1071 yield d
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):
1078 yield d
1081 def getDictionariesConvertedFromJS(descriptors, dictionaries, callbacks):
1082 for desc in descriptors:
1083 if desc.interface.isExternal():
1084 continue
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):
1091 yield d
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):
1097 yield d
1098 else:
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):
1103 yield d
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]):
1109 yield d
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):
1116 yield d