tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Configuration.py (60393B)


      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/.
      4 
      5 import itertools
      6 import os
      7 from collections import defaultdict
      8 
      9 from WebIDL import IDLIncludesStatement, IDLInterface, IDLTypedef
     10 
     11 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
     12 
     13 
     14 def toStringBool(arg):
     15    """
     16    Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
     17    """
     18    return str(not not arg).lower()
     19 
     20 
     21 class DescriptorProvider:
     22    """
     23    A way of getting descriptors for interface names.  Subclasses must
     24    have a getDescriptor method callable with the interface name only.
     25 
     26    Subclasses must also have a getConfig() method that returns a
     27    Configuration.
     28    """
     29 
     30    def __init__(self):
     31        pass
     32 
     33 
     34 def isChildPath(path, basePath):
     35    path = os.path.normpath(path)
     36    return os.path.commonprefix((path, basePath)) == basePath
     37 
     38 
     39 class Configuration(DescriptorProvider):
     40    """
     41    Represents global configuration state based on IDL parse data and
     42    the configuration file.
     43    """
     44 
     45    unionTypedefs: list[IDLTypedef]
     46 
     47    class IDLAttrGetterOrSetterTemplate:
     48        class TemplateAdditionalArg:
     49            def __init__(self, type, name, value=None):
     50                self.type = type
     51                self.name = name
     52                self.value = value
     53 
     54        def __init__(self, template, getter, setter, argument, attrName):
     55            self.descriptor = None
     56            self.usedInOtherInterfaces = False
     57            self.getter = getter
     58            self.setter = setter
     59            self.argument = (
     60                Configuration.IDLAttrGetterOrSetterTemplate.TemplateAdditionalArg(
     61                    *argument
     62                )
     63            )
     64            self.attrNameString = attrName
     65            self.attr = None
     66 
     67    class TemplateIDLAttribute:
     68        def __init__(self, attr):
     69            assert attr.isAttr()
     70            assert not attr.isMaplikeOrSetlikeAttr()
     71            assert not attr.slotIndices
     72 
     73            self.identifier = attr.identifier
     74            self.type = attr.type
     75            self.extendedAttributes = attr.getExtendedAttributes()
     76            self.slotIndices = None
     77 
     78        def getExtendedAttribute(self, name):
     79            return self.extendedAttributes.get(name)
     80 
     81        def isAttr(self):
     82            return True
     83 
     84        def isMaplikeOrSetlikeAttr(self):
     85            return False
     86 
     87        def isMethod(self):
     88            return False
     89 
     90        def isStatic(self):
     91            return False
     92 
     93    def __init__(self, filename, webRoots, parseData, generatedEvents=[]):
     94        DescriptorProvider.__init__(self)
     95 
     96        # Read the configuration file.
     97        glbl = {}
     98        exec(open(filename, encoding="utf-8").read(), glbl)
     99        config = glbl["DOMInterfaces"]
    100 
    101        self.attributeTemplates = dict()
    102        attributeTemplatesByInterface = dict()
    103        for interface, templates in glbl["TemplatedAttributes"].items():
    104            for template in templates:
    105                name = template.get("template")
    106                t = Configuration.IDLAttrGetterOrSetterTemplate(**template)
    107                self.attributeTemplates[name] = t
    108                attributeTemplatesByInterface.setdefault(interface, list()).append(t)
    109 
    110        webRoots = tuple(map(os.path.normpath, webRoots))
    111 
    112        def isInWebIDLRoot(path):
    113            return any(isChildPath(path, root) for root in webRoots)
    114 
    115        # Build descriptors for all the interfaces we have in the parse data.
    116        # This allows callers to specify a subset of interfaces by filtering
    117        # |parseData|.
    118        self.descriptors = []
    119        self.interfaces = {}
    120        self.descriptorsByName = {}
    121        self.dictionariesByName = {}
    122        self.generatedEvents = generatedEvents
    123        self.maxProtoChainLength = 0
    124        for thing in parseData:
    125            if isinstance(thing, IDLIncludesStatement):
    126                # Our build system doesn't support dep build involving
    127                # addition/removal of "includes" statements that appear in a
    128                # different .webidl file than their LHS interface.  Make sure we
    129                # don't have any of those.  See similar block below for partial
    130                # interfaces!
    131                if thing.interface.filename != thing.filename:
    132                    raise TypeError(
    133                        "The binding build system doesn't really support "
    134                        "'includes' statements which don't appear in the "
    135                        "file in which the left-hand side of the statement is "
    136                        "defined.\n"
    137                        "%s\n"
    138                        "%s" % (thing.location, thing.interface.location)
    139                    )
    140 
    141            assert not thing.isType()
    142 
    143            if (
    144                not thing.isInterface()
    145                and not thing.isNamespace()
    146                and not thing.isInterfaceMixin()
    147            ):
    148                continue
    149            # Our build system doesn't support dep builds involving
    150            # addition/removal of partial interfaces/namespaces/mixins that
    151            # appear in a different .webidl file than the
    152            # interface/namespace/mixin they are extending.  Make sure we don't
    153            # have any of those.  See similar block above for "includes"
    154            # statements!
    155            if not thing.isExternal():
    156                for partial in thing.getPartials():
    157                    if partial.filename != thing.filename:
    158                        raise TypeError(
    159                            "The binding build system doesn't really support "
    160                            "partial interfaces/namespaces/mixins which don't "
    161                            "appear in the file in which the "
    162                            "interface/namespace/mixin they are extending is "
    163                            "defined.  Don't do this.\n"
    164                            "%s\n"
    165                            "%s" % (partial.location, thing.location)
    166                        )
    167 
    168            # The rest of the logic doesn't apply to mixins.
    169            if thing.isInterfaceMixin():
    170                continue
    171 
    172            iface = thing
    173            if not iface.isExternal():
    174                if not (
    175                    iface.getExtendedAttribute("ChromeOnly")
    176                    or iface.getExtendedAttribute("Func")
    177                    == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"]
    178                    or not iface.hasInterfaceObject()
    179                    or isInWebIDLRoot(iface.filename)
    180                ):
    181                    raise TypeError(
    182                        "Interfaces which are exposed to the web may only be "
    183                        "defined in a DOM WebIDL root %r. Consider marking "
    184                        "the interface [ChromeOnly] or "
    185                        "[Func='nsContentUtils::IsCallerChromeOrFuzzingEnabled'] "
    186                        "if you do not want it exposed to the web.\n"
    187                        "%s" % (webRoots, iface.location)
    188                    )
    189 
    190            self.interfaces[iface.identifier.name] = iface
    191 
    192            entry = config.pop(iface.identifier.name, {})
    193            assert not isinstance(entry, list)
    194 
    195            desc = Descriptor(
    196                self,
    197                iface,
    198                entry,
    199                attributeTemplatesByInterface.get(iface.identifier.name),
    200            )
    201            self.descriptors.append(desc)
    202            # Setting up descriptorsByName while iterating through interfaces
    203            # means we can get the nativeType of iterable interfaces without
    204            # having to do multiple loops.
    205            assert desc.interface.identifier.name not in self.descriptorsByName
    206            self.descriptorsByName[desc.interface.identifier.name] = desc
    207 
    208        if len(config) > 0:
    209            raise NoSuchDescriptorError(
    210                "Bindings.conf contains entries for "
    211                + str(list(config))
    212                + " that aren't declared as interfaces in the .webidl files."
    213            )
    214 
    215        # Keep the descriptor list sorted for determinism.
    216        self.descriptors.sort(key=lambda x: x.name)
    217 
    218        self.descriptorsByFile = {}
    219        for d in self.descriptors:
    220            self.descriptorsByFile.setdefault(d.interface.filename, []).append(d)
    221 
    222        self.enums = [e for e in parseData if e.isEnum()]
    223 
    224        self.dictionaries = [d for d in parseData if d.isDictionary()]
    225        self.dictionariesByName = {d.identifier.name: d for d in self.dictionaries}
    226 
    227        self.callbacks = [
    228            c for c in parseData if c.isCallback() and not c.isInterface()
    229        ]
    230 
    231        self.unionTypedefs = [
    232            t for t in parseData if t.isTypedef() and t.innerType.isUnion()
    233        ]
    234 
    235        # Dictionary mapping from a union type name to a set of filenames where
    236        # union types with that name are used.
    237        self.filenamesPerUnion = defaultdict(set)
    238 
    239        # Dictionary mapping from a filename to a list of types for
    240        # the union types used in that file. If a union type is used
    241        # in multiple files then it will be added to the list for the
    242        # None key. Note that the list contains a type for every use
    243        # of a union type, so there can be multiple entries with union
    244        # types that have the same name.
    245        self.unionsPerFilename = defaultdict(list)
    246 
    247        def addUnion(t):
    248            filenamesForUnion = self.filenamesPerUnion[t.name]
    249            if t.filename not in filenamesForUnion:
    250                # We have a to be a bit careful: some of our built-in
    251                # typedefs are for unions, and those unions end up with
    252                # "<unknown>" as the filename.  If that happens, we don't
    253                # want to try associating this union with one particular
    254                # filename, since there isn't one to associate it with,
    255                # really.
    256                if t.filename == "<unknown>":
    257                    uniqueFilenameForUnion = None
    258                elif len(filenamesForUnion) == 0:
    259                    # This is the first file that we found a union with this
    260                    # name in, record the union as part of the file.
    261                    uniqueFilenameForUnion = t.filename
    262                else:
    263                    # We already found a file that contains a union with
    264                    # this name.
    265                    if len(filenamesForUnion) == 1:
    266                        # This is the first time we found a union with this
    267                        # name in another file.
    268                        for f in filenamesForUnion:
    269                            # Filter out unions with this name from the
    270                            # unions for the file where we previously found
    271                            # them.
    272                            unionsForFilename = [
    273                                u for u in self.unionsPerFilename[f] if u.name != t.name
    274                            ]
    275                            if len(unionsForFilename) == 0:
    276                                del self.unionsPerFilename[f]
    277                            else:
    278                                self.unionsPerFilename[f] = unionsForFilename
    279                    # Unions with this name appear in multiple files, record
    280                    # the filename as None, so that we can detect that.
    281                    uniqueFilenameForUnion = None
    282                self.unionsPerFilename[uniqueFilenameForUnion].append(t)
    283                filenamesForUnion.add(t.filename)
    284 
    285        def addUnions(t):
    286            t = findInnermostType(t)
    287            if t.isUnion():
    288                addUnion(t)
    289                for m in t.flatMemberTypes:
    290                    addUnions(m)
    291 
    292        for t, _ in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
    293            addUnions(t)
    294 
    295        for d in getDictionariesConvertedToJS(
    296            self.descriptors, self.dictionaries, self.callbacks
    297        ):
    298            d.needsConversionToJS = True
    299 
    300        for d in getDictionariesConvertedFromJS(
    301            self.descriptors, self.dictionaries, self.callbacks
    302        ):
    303            d.needsConversionFromJS = True
    304 
    305        # Collect all the global names exposed on a Window object (to implement
    306        # the hash for looking up these names when resolving a property).
    307        self.windowGlobalNames = []
    308        for desc in self.getDescriptors(registersGlobalNamesOnWindow=True):
    309            self.windowGlobalNames.append((desc.name, desc))
    310            self.windowGlobalNames.extend(
    311                (n.identifier.name, desc) for n in desc.interface.legacyFactoryFunctions
    312            )
    313            self.windowGlobalNames.extend(
    314                (n, desc) for n in desc.interface.legacyWindowAliases
    315            )
    316 
    317        # Collect a sorted list of strings that we want to concatenate into
    318        # one big string and a dict mapping each string to its offset in the
    319        # concatenated string.
    320 
    321        # We want the names of all the interfaces with a prototype (for
    322        # implementing @@toStringTag).
    323        names = set(
    324            d.interface.getClassName()
    325            for d in self.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True)
    326        )
    327        names.update(
    328            d.interface.getClassName()
    329            for d in self.getDescriptors(hasOrdinaryObjectPrototype=True)
    330        )
    331 
    332        # Now also add the names from windowGlobalNames, we need them for the
    333        # perfect hash that we build for these.
    334        names.update(n[0] for n in self.windowGlobalNames)
    335 
    336        # Sorting is not strictly necessary, but makes the generated code a bit
    337        # more readable.
    338        names = sorted(names)
    339 
    340        # We can't rely on being able to pass initial=0 to itertools.accumulate
    341        # because it was only added in version 3.8, so define an accumulate
    342        # function that chains the initial value into the iterator.
    343        def accumulate(iterable, initial):
    344            return itertools.accumulate(itertools.chain([initial], iterable))
    345 
    346        # Calculate the offset of each name in the concatenated string. Note that
    347        # we need to add 1 to the length to account for the null terminating each
    348        # name.
    349        offsets = accumulate(map(lambda n: len(n) + 1, names), initial=0)
    350        self.namesStringOffsets = list(zip(names, offsets))
    351 
    352        allTemplatedAttributes = (
    353            (m, d)
    354            for d in self.descriptors
    355            if not d.interface.isExternal()
    356            for m in d.interface.members
    357            if m.isAttr() and m.getExtendedAttribute("BindingTemplate") is not None
    358        )
    359        # attributesPerTemplate will have the template names as keys, and a
    360        # list of tuples as values. Every tuple contains an IDLAttribute and a
    361        # descriptor.
    362        attributesPerTemplate = dict()
    363        for m, d in allTemplatedAttributes:
    364            t = m.getExtendedAttribute("BindingTemplate")
    365            if isinstance(t[0], list):
    366                t = t[0]
    367            l = attributesPerTemplate.setdefault(t[0], list())
    368            # We want the readonly attributes last, because we use the first
    369            # attribute in the list as the canonical attribute for the
    370            # template, and if there are any writable attributes the
    371            # template should have support for that.
    372            if not m.readonly:
    373                l.insert(0, (m, d))
    374            else:
    375                l.append((m, d))
    376 
    377        for name, attributes in attributesPerTemplate.items():
    378            # We use the first attribute to generate a canonical implementation
    379            # of getter and setter.
    380            firstAttribute, firstDescriptor = attributes[0]
    381            template = self.attributeTemplates.get(name)
    382            if template is None:
    383                raise TypeError(
    384                    "Unknown BindingTemplate with name %s for %s on %s"
    385                    % (
    386                        name,
    387                        firstAttribute.identifier.name,
    388                        firstDescriptor.interface.identifier.name,
    389                    )
    390                )
    391 
    392            # This mimics a real IDL attribute for templated bindings.
    393 
    394            template.attr = Configuration.TemplateIDLAttribute(firstAttribute)
    395 
    396            def filterExtendedAttributes(extendedAttributes):
    397                # These are the extended attributes that we allow to have
    398                # different values among all attributes that use the same
    399                # template.
    400                ignoredAttributes = {
    401                    "BindingTemplate",
    402                    "BindingAlias",
    403                    "ChromeOnly",
    404                    "Pure",
    405                    "Pref",
    406                    "Func",
    407                    "Throws",
    408                    "GetterThrows",
    409                    "SetterThrows",
    410                }
    411                return dict(
    412                    filter(
    413                        lambda i: i[0] not in ignoredAttributes,
    414                        extendedAttributes.items(),
    415                    )
    416                )
    417 
    418            firstExtAttrs = filterExtendedAttributes(
    419                firstAttribute.getExtendedAttributes()
    420            )
    421 
    422            for a, d in attributes:
    423                # We want to make sure all getters or setters grouped by a
    424                # template have the same WebIDL signatures, so make sure
    425                # their types are the same.
    426                if template.attr.type != a.type:
    427                    raise TypeError(
    428                        "%s on %s and %s on %s have different type, but they're using the same template %s."
    429                        % (
    430                            firstAttribute.identifier.name,
    431                            firstDescriptor.interface.identifier.name,
    432                            a.identifier.name,
    433                            d.interface.identifier.name,
    434                            name,
    435                        )
    436                    )
    437 
    438                extAttrs = filterExtendedAttributes(a.getExtendedAttributes())
    439                if template.attr.extendedAttributes != extAttrs:
    440                    for k in extAttrs.keys() - firstExtAttrs.keys():
    441                        raise TypeError(
    442                            "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
    443                            % (
    444                                a.identifier.name,
    445                                d.interface.identifier.name,
    446                                k,
    447                                firstAttribute.identifier.name,
    448                                firstDescriptor.interface.identifier.name,
    449                                name,
    450                            )
    451                        )
    452                    for k in firstExtAttrs.keys() - extAttrs.keys():
    453                        raise TypeError(
    454                            "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
    455                            % (
    456                                firstAttribute.identifier.name,
    457                                firstDescriptor.interface.identifier.name,
    458                                k,
    459                                a.identifier.name,
    460                                d.interface.identifier.name,
    461                                name,
    462                            )
    463                        )
    464                    for k, v in firstExtAttrs.items():
    465                        if extAttrs[k] != v:
    466                            raise TypeError(
    467                                "%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s."
    468                                % (
    469                                    firstAttribute.identifier.name,
    470                                    firstDescriptor.interface.identifier.name,
    471                                    a.identifier.name,
    472                                    d.interface.identifier.name,
    473                                    k,
    474                                    name,
    475                                )
    476                            )
    477 
    478                def sameThrows(getter=False, setter=False):
    479                    extAttrs1 = firstDescriptor.getExtendedAttributes(
    480                        firstAttribute, getter=getter, setter=setter
    481                    )
    482                    extAttrs2 = d.getExtendedAttributes(a, getter=getter, setter=setter)
    483                    return ("needsErrorResult" in extAttrs1) == (
    484                        "needsErrorResult" in extAttrs2
    485                    )
    486 
    487                if not sameThrows(getter=True) or (
    488                    not a.readonly and not sameThrows(setter=True)
    489                ):
    490                    raise TypeError(
    491                        "%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s."
    492                        % (
    493                            firstAttribute.identifier.name,
    494                            firstDescriptor.interface.identifier.name,
    495                            a.identifier.name,
    496                            d.interface.identifier.name,
    497                            name,
    498                        )
    499                    )
    500 
    501        for name, template in self.attributeTemplates.items():
    502            if template.attr is None:
    503                print("Template %s is unused, please remove it." % name)
    504 
    505    def getInterface(self, ifname):
    506        return self.interfaces[ifname]
    507 
    508    def getDescriptors(self, **filters):
    509        """Gets the descriptors that match the given filters."""
    510        curr = self.descriptors
    511        # Collect up our filters, because we may have a webIDLFile filter that
    512        # we always want to apply first.
    513        tofilter = [(lambda x: x.interface.isExternal(), False)]
    514        for key, val in filters.items():
    515            if key == "webIDLFile":
    516                # Special-case this part to make it fast, since most of our
    517                # getDescriptors calls are conditioned on a webIDLFile.  We may
    518                # not have this key, in which case we have no descriptors
    519                # either.
    520                curr = self.descriptorsByFile.get(val, [])
    521                continue
    522            elif key == "hasInterfaceObject":
    523 
    524                def getter(x):
    525                    return x.interface.hasInterfaceObject()
    526 
    527            elif key == "hasInterfacePrototypeObject":
    528 
    529                def getter(x):
    530                    return x.interface.hasInterfacePrototypeObject()
    531 
    532            elif key == "hasInterfaceOrInterfacePrototypeObject":
    533 
    534                def getter(x):
    535                    return x.hasInterfaceOrInterfacePrototypeObject()
    536 
    537            elif key == "hasOrdinaryObjectPrototype":
    538 
    539                def getter(x):
    540                    return x.hasOrdinaryObjectPrototype()
    541 
    542            elif key == "isCallback":
    543 
    544                def getter(x):
    545                    return x.interface.isCallback()
    546 
    547            elif key == "isJSImplemented":
    548 
    549                def getter(x):
    550                    return x.interface.isJSImplemented()
    551 
    552            elif key == "isExposedInAnyWorker":
    553 
    554                def getter(x):
    555                    return x.interface.isExposedInAnyWorker()
    556 
    557            elif key == "isExposedInWorkerDebugger":
    558 
    559                def getter(x):
    560                    return x.interface.isExposedInWorkerDebugger()
    561 
    562            elif key == "isExposedInAnyWorklet":
    563 
    564                def getter(x):
    565                    return x.interface.isExposedInAnyWorklet()
    566 
    567            elif key == "isExposedInWindow":
    568 
    569                def getter(x):
    570                    return x.interface.isExposedInWindow()
    571 
    572            elif key == "isExposedInShadowRealms":
    573 
    574                def getter(x):
    575                    return x.interface.isExposedInShadowRealms()
    576 
    577            elif key == "isSerializable":
    578 
    579                def getter(x):
    580                    return x.interface.isSerializable()
    581 
    582            else:
    583                # Have to watch out: just closing over "key" is not enough,
    584                # since we're about to mutate its value
    585                getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
    586            tofilter.append((getter, val))
    587        for f in tofilter:
    588            curr = [x for x in curr if f[0](x) == f[1]]
    589        return curr
    590 
    591    def getEnums(self, webIDLFile):
    592        return [e for e in self.enums if e.filename == webIDLFile]
    593 
    594    def getDictionaries(self, webIDLFile):
    595        return [d for d in self.dictionaries if d.filename == webIDLFile]
    596 
    597    def getCallbacks(self, webIDLFile):
    598        return [c for c in self.callbacks if c.filename == webIDLFile]
    599 
    600    def getUnionTypedefs(self, webIDLFile):
    601        return [t for t in self.unionTypedefs if t.filename == webIDLFile]
    602 
    603    def getDescriptor(self, interfaceName):
    604        """
    605        Gets the appropriate descriptor for the given interface name.
    606        """
    607        # We may have optimized out this descriptor, but the chances of anyone
    608        # asking about it are then slim.  Put the check for that _after_ we've
    609        # done our normal lookup.  But that means we have to do our normal
    610        # lookup in a way that will not throw if it fails.
    611        d = self.descriptorsByName.get(interfaceName, None)
    612        if d:
    613            return d
    614 
    615        raise NoSuchDescriptorError("For " + interfaceName + " found no matches")
    616 
    617    def getConfig(self):
    618        return self
    619 
    620    def getDictionariesConvertibleToJS(self):
    621        return [d for d in self.dictionaries if d.needsConversionToJS]
    622 
    623    def getDictionariesConvertibleFromJS(self):
    624        return [d for d in self.dictionaries if d.needsConversionFromJS]
    625 
    626    def getDictionaryIfExists(self, dictionaryName):
    627        return self.dictionariesByName.get(dictionaryName, None)
    628 
    629 
    630 class NoSuchDescriptorError(TypeError):
    631    def __init__(self, str):
    632        TypeError.__init__(self, str)
    633 
    634 
    635 def methodReturnsJSObject(method):
    636    assert method.isMethod()
    637 
    638    for signature in method.signatures():
    639        returnType = signature[0]
    640        if returnType.isObject() or returnType.isSpiderMonkeyInterface():
    641            return True
    642 
    643    return False
    644 
    645 
    646 def MemberIsLegacyUnforgeable(member, descriptor):
    647    # Note: "or" and "and" return either their LHS or RHS, not
    648    # necessarily booleans.  Make sure to return a boolean from this
    649    # method, because callers will compare its return value to
    650    # booleans.
    651    return bool(
    652        (member.isAttr() or member.isMethod())
    653        and not member.isStatic()
    654        and (
    655            member.isLegacyUnforgeable()
    656            or descriptor.interface.getExtendedAttribute("LegacyUnforgeable")
    657        )
    658    )
    659 
    660 
    661 class Descriptor(DescriptorProvider):
    662    """
    663    Represents a single descriptor for an interface. See Bindings.conf.
    664    """
    665 
    666    def __init__(self, config, interface: IDLInterface, desc, attributeTemplates):
    667        DescriptorProvider.__init__(self)
    668        self.config = config
    669        self.interface = interface
    670        self.attributeTemplates = attributeTemplates
    671        if self.attributeTemplates is not None:
    672            for t in self.attributeTemplates:
    673                t.descriptor = self
    674 
    675        self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow()
    676 
    677        if self.wantsXrays:
    678            # We could try to restrict self.wantsXrayExpandoClass further.  For
    679            # example, we could set it to false if all of our slots store
    680            # Gecko-interface-typed things, because we don't use Xray expando
    681            # slots for those.  But note that we would need to check the types
    682            # of not only the members of "interface" but also of all its
    683            # ancestors, because those can have members living in our slots too.
    684            # For now, do the simple thing.
    685            self.wantsXrayExpandoClass = interface.totalMembersInSlots != 0
    686 
    687        # Read the desc, and fill in the relevant defaults.
    688        ifaceName = self.interface.identifier.name
    689        # For generated iterator interfaces for other iterable interfaces, we
    690        # just use IterableIterator as the native type, templated on the
    691        # nativeType of the iterable interface. That way we can have a
    692        # templated implementation for all the duplicated iterator
    693        # functionality.
    694        if self.interface.isIteratorInterface():
    695            itrName = self.interface.iterableInterface.identifier.name
    696            itrDesc = self.getDescriptor(itrName)
    697            nativeTypeDefault = iteratorNativeType(itrDesc)
    698        elif self.interface.isAsyncIteratorInterface():
    699            itrName = self.interface.asyncIterableInterface.identifier.name
    700            itrDesc = self.getDescriptor(itrName)
    701            nativeTypeDefault = iteratorNativeType(itrDesc)
    702 
    703        elif self.interface.isExternal():
    704            nativeTypeDefault = "nsIDOM" + ifaceName
    705        else:
    706            nativeTypeDefault = "mozilla::dom::" + ifaceName
    707 
    708        self.nativeType = desc.get("nativeType", nativeTypeDefault)
    709        # Now create a version of nativeType that doesn't have extra
    710        # mozilla::dom:: at the beginning.
    711        prettyNativeType = self.nativeType.split("::")
    712        if prettyNativeType[0] == "mozilla":
    713            prettyNativeType.pop(0)
    714            if prettyNativeType[0] == "dom":
    715                prettyNativeType.pop(0)
    716        self.prettyNativeType = "::".join(prettyNativeType)
    717 
    718        self.jsImplParent = desc.get("jsImplParent", self.nativeType)
    719 
    720        # Do something sane for JSObject
    721        if self.nativeType == "JSObject":
    722            headerDefault = "js/TypeDecls.h"
    723        elif self.interface.isCallback() or self.interface.isJSImplemented():
    724            # A copy of CGHeaders.getDeclarationFilename; we can't
    725            # import it here, sadly.
    726            # Use our local version of the header, not the exported one, so that
    727            # test bindings, which don't export, will work correctly.
    728            basename = os.path.basename(self.interface.filename)
    729            headerDefault = basename.replace(".webidl", "Binding.h")
    730        elif not self.interface.isExternal() and self.interface.getExtendedAttribute(
    731            "HeaderFile"
    732        ):
    733            headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
    734        elif (
    735            self.interface.isIteratorInterface()
    736            or self.interface.isAsyncIteratorInterface()
    737        ):
    738            headerDefault = "mozilla/dom/IterableIterator.h"
    739        else:
    740            headerDefault = self.nativeType
    741            headerDefault = headerDefault.replace("::", "/") + ".h"
    742        self.headerFile = desc.get("headerFile", headerDefault)
    743        self.headerIsDefault = self.headerFile == headerDefault
    744        if self.jsImplParent == self.nativeType:
    745            self.jsImplParentHeader = self.headerFile
    746        else:
    747            self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
    748 
    749        self.notflattened = desc.get("notflattened", False)
    750        self.register = desc.get("register", True)
    751 
    752        # If we're concrete, we need to crawl our ancestor interfaces and mark
    753        # them as having a concrete descendant.
    754        concreteDefault = (
    755            not self.interface.isExternal()
    756            and not self.interface.isCallback()
    757            and not self.interface.isNamespace()
    758            and
    759            # We're going to assume that leaf interfaces are
    760            # concrete; otherwise what's the point?  Also
    761            # interfaces with constructors had better be
    762            # concrete; otherwise how can you construct them?
    763            (
    764                not self.interface.hasChildInterfaces()
    765                or self.interface.ctor() is not None
    766            )
    767        )
    768 
    769        self.concrete = desc.get("concrete", concreteDefault)
    770        self.hasLegacyUnforgeableMembers = self.concrete and any(
    771            MemberIsLegacyUnforgeable(m, self) for m in self.interface.members
    772        )
    773        self.operations = {
    774            "IndexedGetter": None,
    775            "IndexedSetter": None,
    776            "IndexedDeleter": None,
    777            "NamedGetter": None,
    778            "NamedSetter": None,
    779            "NamedDeleter": None,
    780            "Stringifier": None,
    781            "LegacyCaller": None,
    782        }
    783 
    784        self.hasDefaultToJSON = False
    785 
    786        # Stringifiers need to be set up whether an interface is
    787        # concrete or not, because they're actually prototype methods and hence
    788        # can apply to instances of descendant interfaces.  Legacy callers and
    789        # named/indexed operations only need to be set up on concrete
    790        # interfaces, since they affect the JSClass we end up using, not the
    791        # prototype object.
    792        def addOperation(operation, m):
    793            if not self.operations[operation]:
    794                self.operations[operation] = m
    795 
    796        # Since stringifiers go on the prototype, we only need to worry
    797        # about our own stringifier, not those of our ancestor interfaces.
    798        if not self.interface.isExternal():
    799            for m in self.interface.members:
    800                if m.isMethod() and m.isStringifier():
    801                    addOperation("Stringifier", m)
    802                if m.isMethod() and m.isDefaultToJSON():
    803                    self.hasDefaultToJSON = True
    804 
    805            # We keep track of instrumente props for all non-external interfaces.
    806            self.instrumentedProps = []
    807            instrumentedProps = self.interface.getExtendedAttribute("InstrumentedProps")
    808            if instrumentedProps:
    809                # It's actually a one-element list, with the list
    810                # we want as the only element.
    811                self.instrumentedProps = instrumentedProps[0]
    812 
    813                # Check that we don't have duplicated instrumented props.
    814                uniqueInstrumentedProps = set(self.instrumentedProps)
    815                if len(uniqueInstrumentedProps) != len(self.instrumentedProps):
    816                    duplicates = [
    817                        p
    818                        for p in uniqueInstrumentedProps
    819                        if self.instrumentedProps.count(p) > 1
    820                    ]
    821                    raise TypeError(
    822                        "Duplicated instrumented properties: %s.\n%s"
    823                        % (duplicates, self.interface.location)
    824                    )
    825 
    826        if self.concrete:
    827            self.proxy = False
    828            iface = self.interface
    829            for m in iface.members:
    830                # Don't worry about inheriting legacycallers either: in
    831                # practice these are on most-derived prototypes.
    832                if m.isMethod() and m.isLegacycaller():
    833                    if not m.isIdentifierLess():
    834                        raise TypeError(
    835                            "We don't support legacycaller with "
    836                            "identifier.\n%s" % m.location
    837                        )
    838                    if len(m.signatures()) != 1:
    839                        raise TypeError(
    840                            "We don't support overloaded legacycaller.\n%s" % m.location
    841                        )
    842                    addOperation("LegacyCaller", m)
    843 
    844            if desc.get("hasOrdinaryObjectPrototype", False):
    845                iface.setUserData("hasOrdinaryObjectPrototype", True)
    846 
    847            while iface:
    848                for m in iface.members:
    849                    if not m.isMethod():
    850                        continue
    851 
    852                    def addIndexedOrNamedOperation(operation, m):
    853                        if m.isIndexed():
    854                            operation = "Indexed" + operation
    855                        else:
    856                            assert m.isNamed()
    857                            operation = "Named" + operation
    858                        addOperation(operation, m)
    859 
    860                    if m.isGetter():
    861                        addIndexedOrNamedOperation("Getter", m)
    862                    if m.isSetter():
    863                        addIndexedOrNamedOperation("Setter", m)
    864                    if m.isDeleter():
    865                        addIndexedOrNamedOperation("Deleter", m)
    866                    if m.isLegacycaller() and iface != self.interface:
    867                        raise TypeError(
    868                            "We don't support legacycaller on "
    869                            "non-leaf interface %s.\n%s" % (iface, iface.location)
    870                        )
    871 
    872                iface.setUserData("hasConcreteDescendant", True)
    873                iface = iface.parent
    874 
    875            self.proxy = (
    876                self.supportsIndexedProperties()
    877                or (
    878                    self.supportsNamedProperties() and not self.hasNamedPropertiesObject
    879                )
    880                or self.isMaybeCrossOriginObject()
    881            )
    882 
    883            if self.proxy:
    884                if self.isMaybeCrossOriginObject() and (
    885                    self.supportsIndexedProperties() or self.supportsNamedProperties()
    886                ):
    887                    raise TypeError(
    888                        "We don't support named or indexed "
    889                        "properties on maybe-cross-origin objects. "
    890                        "This lets us assume that their proxy "
    891                        "hooks are never called via Xrays.  "
    892                        "Fix %s.\n%s" % (self.interface, self.interface.location)
    893                    )
    894 
    895                if not self.operations["IndexedGetter"] and (
    896                    self.operations["IndexedSetter"]
    897                    or self.operations["IndexedDeleter"]
    898                ):
    899                    raise SyntaxError(
    900                        "%s supports indexed properties but does "
    901                        "not have an indexed getter.\n%s"
    902                        % (self.interface, self.interface.location)
    903                    )
    904                if not self.operations["NamedGetter"] and (
    905                    self.operations["NamedSetter"] or self.operations["NamedDeleter"]
    906                ):
    907                    raise SyntaxError(
    908                        "%s supports named properties but does "
    909                        "not have a named getter.\n%s"
    910                        % (self.interface, self.interface.location)
    911                    )
    912                iface = self.interface
    913                while iface:
    914                    iface.setUserData("hasProxyDescendant", True)
    915                    iface = iface.parent
    916 
    917        if desc.get("wantsQI", None) is not None:
    918            self._wantsQI = desc.get("wantsQI", None)
    919        self.wrapperCache = (
    920            not self.interface.isCallback()
    921            and not self.interface.isIteratorInterface()
    922            and not self.interface.isAsyncIteratorInterface()
    923            and desc.get("wrapperCache", True)
    924        )
    925 
    926        self.name = interface.identifier.name
    927 
    928        # self.implicitJSContext is a list of names of methods and attributes
    929        # that need a JSContext.
    930        if self.interface.isJSImplemented():
    931            self.implicitJSContext = ["constructor"]
    932        else:
    933            self.implicitJSContext = desc.get("implicitJSContext", [])
    934        assert isinstance(self.implicitJSContext, list)
    935 
    936        self._binaryNames = {}
    937 
    938        if not self.interface.isExternal():
    939 
    940            def maybeAddBinaryName(member):
    941                binaryName = member.getExtendedAttribute("BinaryName")
    942                if binaryName:
    943                    assert isinstance(binaryName, list)
    944                    assert len(binaryName) == 1
    945                    self._binaryNames.setdefault(
    946                        (member.identifier.name, member.isStatic()), binaryName[0]
    947                    )
    948 
    949            for member in self.interface.members:
    950                if not member.isAttr() and not member.isMethod():
    951                    continue
    952                maybeAddBinaryName(member)
    953 
    954            ctor = self.interface.ctor()
    955            if ctor:
    956                maybeAddBinaryName(ctor)
    957 
    958            # Some default binary names for cases when nothing else got set.
    959            self._binaryNames.setdefault(("__legacycaller", False), "LegacyCall")
    960            self._binaryNames.setdefault(("__stringifier", False), "Stringify")
    961 
    962            # Build the prototype chain.
    963            self.prototypeChain = []
    964            self.needsMissingPropUseCounters = False
    965            parent = interface
    966            while parent:
    967                self.needsMissingPropUseCounters = (
    968                    self.needsMissingPropUseCounters
    969                    or parent.getExtendedAttribute("InstrumentedProps")
    970                )
    971                self.prototypeChain.insert(0, parent.identifier.name)
    972                parent = parent.parent
    973            config.maxProtoChainLength = max(
    974                config.maxProtoChainLength, len(self.prototypeChain)
    975            )
    976 
    977    def binaryNameFor(self, name, isStatic):
    978        return self._binaryNames.get((name, isStatic), name)
    979 
    980    @property
    981    def prototypeNameChain(self):
    982        return [self.getDescriptor(p).name for p in self.prototypeChain]
    983 
    984    @property
    985    def parentPrototypeName(self):
    986        if len(self.prototypeChain) == 1:
    987            return None
    988        return self.getDescriptor(self.prototypeChain[-2]).name
    989 
    990    def hasInterfaceOrInterfacePrototypeObject(self):
    991        return (
    992            self.interface.hasInterfaceObject()
    993            or self.interface.hasInterfacePrototypeObject()
    994        )
    995 
    996    def hasOrdinaryObjectPrototype(self):
    997        return self.interface.getUserData("hasOrdinaryObjectPrototype", False)
    998 
    999    @property
   1000    def hasNamedPropertiesObject(self):
   1001        return self.isGlobal() and self.supportsNamedProperties()
   1002 
   1003    def getExtendedAttributes(self, member, getter=False, setter=False):
   1004        def ensureValidBoolExtendedAttribute(attr, name):
   1005            if attr is not None and attr is not True:
   1006                raise TypeError("Unknown value for '%s': %s" % (name, attr[0]))
   1007 
   1008        def ensureValidThrowsExtendedAttribute(attr):
   1009            ensureValidBoolExtendedAttribute(attr, "Throws")
   1010 
   1011        def ensureValidCanOOMExtendedAttribute(attr):
   1012            ensureValidBoolExtendedAttribute(attr, "CanOOM")
   1013 
   1014        def maybeAppendNeedsErrorResultToAttrs(attrs, throws):
   1015            ensureValidThrowsExtendedAttribute(throws)
   1016            if throws is not None:
   1017                attrs.append("needsErrorResult")
   1018 
   1019        def maybeAppendCanOOMToAttrs(attrs, canOOM):
   1020            ensureValidCanOOMExtendedAttribute(canOOM)
   1021            if canOOM is not None:
   1022                attrs.append("canOOM")
   1023 
   1024        def maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal):
   1025            if (
   1026                needsSubjectPrincipal is not None
   1027                and needsSubjectPrincipal is not True
   1028                and needsSubjectPrincipal != ["NonSystem"]
   1029            ):
   1030                raise TypeError(
   1031                    "Unknown value for 'NeedsSubjectPrincipal': %s"
   1032                    % needsSubjectPrincipal[0]
   1033                )
   1034 
   1035            if needsSubjectPrincipal is not None:
   1036                attrs.append("needsSubjectPrincipal")
   1037                if needsSubjectPrincipal == ["NonSystem"]:
   1038                    attrs.append("needsNonSystemSubjectPrincipal")
   1039 
   1040        name = member.identifier.name
   1041        throws = self.interface.isJSImplemented() or member.getExtendedAttribute(
   1042            "Throws"
   1043        )
   1044        canOOM = member.getExtendedAttribute("CanOOM")
   1045        needsSubjectPrincipal = member.getExtendedAttribute("NeedsSubjectPrincipal")
   1046        attrs = []
   1047        if name in self.implicitJSContext:
   1048            attrs.append("implicitJSContext")
   1049        if member.isMethod():
   1050            if self.interface.isAsyncIteratorInterface() and name == "next":
   1051                attrs.append("implicitJSContext")
   1052            # JSObject-returning [NewObject] methods must be fallible,
   1053            # since they have to (fallibly) allocate the new JSObject.
   1054            if member.getExtendedAttribute("NewObject"):
   1055                if member.returnsPromise():
   1056                    throws = True
   1057                elif methodReturnsJSObject(member):
   1058                    canOOM = True
   1059            maybeAppendNeedsErrorResultToAttrs(attrs, throws)
   1060            maybeAppendCanOOMToAttrs(attrs, canOOM)
   1061            maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
   1062            return attrs
   1063 
   1064        assert member.isAttr()
   1065        assert bool(getter) != bool(setter)
   1066        if throws is None:
   1067            throwsAttr = "GetterThrows" if getter else "SetterThrows"
   1068            throws = member.getExtendedAttribute(throwsAttr)
   1069        maybeAppendNeedsErrorResultToAttrs(attrs, throws)
   1070        if canOOM is None:
   1071            canOOMAttr = "GetterCanOOM" if getter else "SetterCanOOM"
   1072            canOOM = member.getExtendedAttribute(canOOMAttr)
   1073        maybeAppendCanOOMToAttrs(attrs, canOOM)
   1074        if needsSubjectPrincipal is None:
   1075            needsSubjectPrincipalAttr = (
   1076                "GetterNeedsSubjectPrincipal"
   1077                if getter
   1078                else "SetterNeedsSubjectPrincipal"
   1079            )
   1080            needsSubjectPrincipal = member.getExtendedAttribute(
   1081                needsSubjectPrincipalAttr
   1082            )
   1083        maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
   1084        return attrs
   1085 
   1086    def supportsIndexedProperties(self):
   1087        return self.operations["IndexedGetter"] is not None
   1088 
   1089    def lengthNeedsCallerType(self):
   1090        """
   1091        Determine whether our length getter needs a caller type; this is needed
   1092        in some indexed-getter proxy algorithms.  The idea is that if our
   1093        indexed getter needs a caller type, our automatically-generated Length()
   1094        calls need one too.
   1095        """
   1096        assert self.supportsIndexedProperties()
   1097        indexedGetter = self.operations["IndexedGetter"]
   1098        return indexedGetter.getExtendedAttribute("NeedsCallerType")
   1099 
   1100    def supportsNamedProperties(self):
   1101        return self.operations["NamedGetter"] is not None
   1102 
   1103    def supportedNamesNeedCallerType(self):
   1104        """
   1105        Determine whether our GetSupportedNames call needs a caller type.  The
   1106        idea is that if your named getter needs a caller type, then so does
   1107        GetSupportedNames.
   1108        """
   1109        assert self.supportsNamedProperties()
   1110        namedGetter = self.operations["NamedGetter"]
   1111        return namedGetter.getExtendedAttribute("NeedsCallerType")
   1112 
   1113    def isMaybeCrossOriginObject(self):
   1114        # If we're isGlobal and have cross-origin members, we're a Window, and
   1115        # that's not a cross-origin object.  The WindowProxy is.
   1116        return (
   1117            self.concrete
   1118            and self.interface.hasCrossOriginMembers
   1119            and not self.isGlobal()
   1120        )
   1121 
   1122    def needsHeaderInclude(self):
   1123        """
   1124        An interface doesn't need a header file if it is not concrete, not
   1125        pref-controlled, has no prototype object, has no static methods or
   1126        attributes and has no parent.  The parent matters because we assert
   1127        things about refcounting that depend on the actual underlying type if we
   1128        have a parent.
   1129 
   1130        """
   1131        return (
   1132            self.interface.isExternal()
   1133            or self.concrete
   1134            or self.interface.hasInterfacePrototypeObject()
   1135            or any(
   1136                (m.isAttr() or m.isMethod()) and m.isStatic()
   1137                for m in self.interface.members
   1138            )
   1139            or self.interface.parent
   1140        )
   1141 
   1142    def hasThreadChecks(self):
   1143        # isExposedConditionally does not necessarily imply thread checks
   1144        # (since at least [SecureContext] is independent of them), but we're
   1145        # only used to decide whether to include nsThreadUtils.h, so we don't
   1146        # worry about that.
   1147        return (
   1148            self.isExposedConditionally() and not self.interface.isExposedInWindow()
   1149        ) or self.interface.isExposedInSomeButNotAllWorkers()
   1150 
   1151    def hasCEReactions(self):
   1152        return any(
   1153            m.getExtendedAttribute("CEReactions") for m in self.interface.members
   1154        )
   1155 
   1156    def isExposedConditionally(self):
   1157        return (
   1158            self.interface.isExposedConditionally()
   1159            or self.interface.isExposedInSomeButNotAllWorkers()
   1160        )
   1161 
   1162    def needsXrayResolveHooks(self):
   1163        """
   1164        Generally, any interface with NeedResolve needs Xray
   1165        resolveOwnProperty and enumerateOwnProperties hooks.  But for
   1166        the special case of plugin-loading elements, we do NOT want
   1167        those, because we don't want to instantiate plug-ins simply
   1168        due to chrome touching them and that's all those hooks do on
   1169        those elements.  So we special-case those here.
   1170        """
   1171        return self.interface.getExtendedAttribute(
   1172            "NeedResolve"
   1173        ) and self.interface.identifier.name not in [
   1174            "HTMLObjectElement",
   1175            "HTMLEmbedElement",
   1176        ]
   1177 
   1178    def needsXrayNamedDeleterHook(self):
   1179        return self.operations["NamedDeleter"] is not None
   1180 
   1181    def isGlobal(self):
   1182        """
   1183        Returns true if this is the primary interface for a global object
   1184        of some sort.
   1185        """
   1186        return self.interface.getExtendedAttribute("Global")
   1187 
   1188    @property
   1189    def namedPropertiesEnumerable(self):
   1190        """
   1191        Returns whether this interface should have enumerable named properties
   1192        """
   1193        assert self.proxy
   1194        assert self.supportsNamedProperties()
   1195        iface = self.interface
   1196        while iface:
   1197            if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
   1198                return False
   1199            iface = iface.parent
   1200        return True
   1201 
   1202    @property
   1203    def registersGlobalNamesOnWindow(self):
   1204        return (
   1205            self.interface.hasInterfaceObject()
   1206            and self.interface.isExposedInWindow()
   1207            and self.register
   1208        )
   1209 
   1210    def getDescriptor(self, interfaceName):
   1211        """
   1212        Gets the appropriate descriptor for the given interface name.
   1213        """
   1214        return self.config.getDescriptor(interfaceName)
   1215 
   1216    def getConfig(self):
   1217        return self.config
   1218 
   1219 
   1220 # Some utility methods
   1221 def getTypesFromDescriptor(descriptor, includeArgs=True, includeReturns=True):
   1222    """
   1223    Get argument and/or return types for all members of the descriptor.  By
   1224    default returns all argument types (which includes types of writable
   1225    attributes) and all return types (which includes types of all attributes).
   1226    """
   1227    assert includeArgs or includeReturns  # Must want _something_.
   1228    members = [m for m in descriptor.interface.members]
   1229    if descriptor.interface.ctor():
   1230        members.append(descriptor.interface.ctor())
   1231    members.extend(descriptor.interface.legacyFactoryFunctions)
   1232    signatures = [s for m in members if m.isMethod() for s in m.signatures()]
   1233    types = []
   1234    for s in signatures:
   1235        assert len(s) == 2
   1236        (returnType, arguments) = s
   1237        if includeReturns:
   1238            types.append(returnType)
   1239        if includeArgs:
   1240            types.extend(a.type for a in arguments)
   1241 
   1242    types.extend(
   1243        a.type
   1244        for a in members
   1245        if (a.isAttr() and (includeReturns or (includeArgs and not a.readonly)))
   1246    )
   1247 
   1248    if descriptor.interface.maplikeOrSetlikeOrIterable:
   1249        maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
   1250        if maplikeOrSetlikeOrIterable.isMaplike():
   1251            # The things we expand into may or may not correctly indicate in
   1252            # their formal IDL types what things we have as return values.  For
   1253            # example, "keys" returns the moral equivalent of sequence<keyType>
   1254            # but just claims to return "object".  Similarly, "values" returns
   1255            # the moral equivalent of sequence<valueType> but claims to return
   1256            # "object".  And due to bug 1155340, "get" claims to return "any"
   1257            # instead of the right type.  So let's just manually work around
   1258            # that lack of specificity.  For our arguments, we already enforce
   1259            # the right types at the IDL level, so those will get picked up
   1260            # correctly.
   1261            assert maplikeOrSetlikeOrIterable.hasKeyType()
   1262            assert maplikeOrSetlikeOrIterable.hasValueType()
   1263            if includeReturns:
   1264                types.append(maplikeOrSetlikeOrIterable.keyType)
   1265                types.append(maplikeOrSetlikeOrIterable.valueType)
   1266        elif maplikeOrSetlikeOrIterable.isSetlike():
   1267            assert maplikeOrSetlikeOrIterable.hasKeyType()
   1268            assert maplikeOrSetlikeOrIterable.hasValueType()
   1269            assert (
   1270                maplikeOrSetlikeOrIterable.keyType
   1271                == maplikeOrSetlikeOrIterable.valueType
   1272            )
   1273            # As in the maplike case, we don't always declare our return values
   1274            # quite correctly.
   1275            if includeReturns:
   1276                types.append(maplikeOrSetlikeOrIterable.keyType)
   1277        else:
   1278            assert (
   1279                maplikeOrSetlikeOrIterable.isIterable()
   1280                or maplikeOrSetlikeOrIterable.isAsyncIterable()
   1281            )
   1282            # As in the maplike/setlike cases we don't do a good job of
   1283            # declaring our actual return types, while our argument types, if
   1284            # any, are declared fine.
   1285            if includeReturns:
   1286                if maplikeOrSetlikeOrIterable.hasKeyType():
   1287                    types.append(maplikeOrSetlikeOrIterable.keyType)
   1288                if maplikeOrSetlikeOrIterable.hasValueType():
   1289                    types.append(maplikeOrSetlikeOrIterable.valueType)
   1290 
   1291    return types
   1292 
   1293 
   1294 def getTypesFromDictionary(dictionary):
   1295    """
   1296    Get all member types for this dictionary
   1297    """
   1298    return [m.type for m in dictionary.members]
   1299 
   1300 
   1301 def getTypesFromCallback(callback):
   1302    """
   1303    Get the types this callback depends on: its return type and the
   1304    types of its arguments.
   1305    """
   1306    sig = callback.signatures()[0]
   1307    types = [sig[0]]  # Return type
   1308    types.extend(arg.type for arg in sig[1])  # Arguments
   1309    return types
   1310 
   1311 
   1312 def getAllTypes(descriptors, dictionaries, callbacks):
   1313    """
   1314    Generate all the types we're dealing with.  For each type, a tuple
   1315    containing type, dictionary is yielded.  The dictionary can be None if the
   1316    type does not come from a dictionary.
   1317    """
   1318    for d in descriptors:
   1319        if d.interface.isExternal():
   1320            continue
   1321        for t in getTypesFromDescriptor(d):
   1322            yield (t, None)
   1323    for dictionary in dictionaries:
   1324        for t in getTypesFromDictionary(dictionary):
   1325            yield (t, dictionary)
   1326    for callback in callbacks:
   1327        for t in getTypesFromCallback(callback):
   1328            yield (t, callback)
   1329 
   1330 
   1331 # For sync value iterators, we use default array implementation, for async
   1332 # iterators and sync pair iterators, we use AsyncIterableIterator or
   1333 # IterableIterator instead.
   1334 def iteratorNativeType(descriptor):
   1335    assert descriptor.interface.isIterable() or descriptor.interface.isAsyncIterable()
   1336    iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
   1337    assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable()
   1338    if descriptor.interface.isIterable():
   1339        return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
   1340    needReturnMethod = toStringBool(
   1341        descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
   1342            "GenerateReturnMethod"
   1343        )
   1344        is not None
   1345    )
   1346    return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
   1347        descriptor.nativeType,
   1348        needReturnMethod,
   1349    )
   1350 
   1351 
   1352 def findInnermostType(t):
   1353    """
   1354    Find the innermost type of the given type, unwrapping Promise and Record
   1355    types, as well as everything that unroll() unwraps.
   1356    """
   1357    while True:
   1358        if t.isRecord():
   1359            t = t.inner
   1360        elif t.unroll() != t:
   1361            t = t.unroll()
   1362        elif t.isPromise():
   1363            t = t.promiseInnerType()
   1364        else:
   1365            return t
   1366 
   1367 
   1368 def getDependentDictionariesFromDictionary(d):
   1369    """
   1370    Find all the dictionaries contained in the given dictionary, as ancestors or
   1371    members.  This returns a generator.
   1372    """
   1373    while d:
   1374        yield d
   1375        for member in d.members:
   1376            yield from getDictionariesFromType(member.type)
   1377        d = d.parent
   1378 
   1379 
   1380 def getDictionariesFromType(type):
   1381    """
   1382    Find all the dictionaries contained in type.  This can be used to find
   1383    dictionaries that need conversion to JS (by looking at types that get
   1384    converted to JS) or dictionaries that need conversion from JS (by looking at
   1385    types that get converted from JS).
   1386 
   1387    This returns a generator.
   1388    """
   1389    type = findInnermostType(type)
   1390    if type.isUnion():
   1391        # Look for dictionaries in all the member types
   1392        for t in type.flatMemberTypes:
   1393            yield from getDictionariesFromType(t)
   1394    elif type.isDictionary():
   1395        # Find the dictionaries that are itself, any of its ancestors, or
   1396        # contained in any of its member types.
   1397        yield from getDependentDictionariesFromDictionary(type.inner)
   1398 
   1399 
   1400 def getDictionariesConvertedToJS(descriptors, dictionaries, callbacks):
   1401    for desc in descriptors:
   1402        if desc.interface.isExternal():
   1403            continue
   1404 
   1405        if desc.interface.isJSImplemented():
   1406            # For a JS-implemented interface, we need to-JS
   1407            # conversions for all the types involved.
   1408            for t in getTypesFromDescriptor(desc):
   1409                for d in getDictionariesFromType(t):
   1410                    yield d
   1411        elif desc.interface.isCallback():
   1412            # For callbacks we only want to include the arguments, since that's
   1413            # where the to-JS conversion happens.
   1414            for t in getTypesFromDescriptor(desc, includeReturns=False):
   1415                for d in getDictionariesFromType(t):
   1416                    yield d
   1417        else:
   1418            # For normal interfaces, we only want to include return values,
   1419            # since that's where to-JS conversion happens.
   1420            for t in getTypesFromDescriptor(desc, includeArgs=False):
   1421                for d in getDictionariesFromType(t):
   1422                    yield d
   1423 
   1424    for callback in callbacks:
   1425        # We only want to look at the arguments
   1426        sig = callback.signatures()[0]
   1427        for arg in sig[1]:
   1428            for d in getDictionariesFromType(arg.type):
   1429                yield d
   1430 
   1431    for dictionary in dictionaries:
   1432        if dictionary.needsConversionToJS:
   1433            # It's explicitly flagged as needing to-JS conversion, and all its
   1434            # dependent dictionaries will need to-JS conversion too.
   1435            for d in getDependentDictionariesFromDictionary(dictionary):
   1436                yield d
   1437 
   1438 
   1439 def getDictionariesConvertedFromJS(descriptors, dictionaries, callbacks):
   1440    for desc in descriptors:
   1441        if desc.interface.isExternal():
   1442            continue
   1443 
   1444        if desc.interface.isJSImplemented():
   1445            # For a JS-implemented interface, we need from-JS conversions for
   1446            # all the types involved.
   1447            for t in getTypesFromDescriptor(desc):
   1448                for d in getDictionariesFromType(t):
   1449                    yield d
   1450        elif desc.interface.isCallback():
   1451            # For callbacks we only want to include the return value, since
   1452            # that's where teh from-JS conversion happens.
   1453            for t in getTypesFromDescriptor(desc, includeArgs=False):
   1454                for d in getDictionariesFromType(t):
   1455                    yield d
   1456        else:
   1457            # For normal interfaces, we only want to include arguments values,
   1458            # since that's where from-JS conversion happens.
   1459            for t in getTypesFromDescriptor(desc, includeReturns=False):
   1460                for d in getDictionariesFromType(t):
   1461                    yield d
   1462 
   1463    for callback in callbacks:
   1464        # We only want to look at the return value
   1465        sig = callback.signatures()[0]
   1466        for d in getDictionariesFromType(sig[0]):
   1467            yield d
   1468 
   1469    for dictionary in dictionaries:
   1470        if dictionary.needsConversionFromJS:
   1471            # It's explicitly flagged as needing from-JS conversion, and all its
   1472            # dependent dictionaries will need from-JS conversion too.
   1473            for d in getDependentDictionariesFromDictionary(dictionary):
   1474                yield d