Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | # You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | from WebIDL import IDLInterface, IDLExternalInterface |
michael@0 | 6 | import os |
michael@0 | 7 | |
michael@0 | 8 | autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" |
michael@0 | 9 | |
michael@0 | 10 | class Configuration: |
michael@0 | 11 | """ |
michael@0 | 12 | Represents global configuration state based on IDL parse data and |
michael@0 | 13 | the configuration file. |
michael@0 | 14 | """ |
michael@0 | 15 | def __init__(self, filename, parseData): |
michael@0 | 16 | |
michael@0 | 17 | # Read the configuration file. |
michael@0 | 18 | glbl = {} |
michael@0 | 19 | execfile(filename, glbl) |
michael@0 | 20 | config = glbl['DOMInterfaces'] |
michael@0 | 21 | |
michael@0 | 22 | # Build descriptors for all the interfaces we have in the parse data. |
michael@0 | 23 | # This allows callers to specify a subset of interfaces by filtering |
michael@0 | 24 | # |parseData|. |
michael@0 | 25 | self.descriptors = [] |
michael@0 | 26 | self.interfaces = {} |
michael@0 | 27 | self.maxProtoChainLength = 0; |
michael@0 | 28 | for thing in parseData: |
michael@0 | 29 | # Some toplevel things are sadly types, and those have an |
michael@0 | 30 | # isInterface that doesn't mean the same thing as IDLObject's |
michael@0 | 31 | # isInterface()... |
michael@0 | 32 | if (not isinstance(thing, IDLInterface) and |
michael@0 | 33 | not isinstance(thing, IDLExternalInterface)): |
michael@0 | 34 | continue |
michael@0 | 35 | iface = thing |
michael@0 | 36 | self.interfaces[iface.identifier.name] = iface |
michael@0 | 37 | if iface.identifier.name not in config: |
michael@0 | 38 | # Completely skip consequential interfaces with no descriptor |
michael@0 | 39 | # if they have no interface object because chances are we |
michael@0 | 40 | # don't need to do anything interesting with them. |
michael@0 | 41 | if iface.isConsequential() and not iface.hasInterfaceObject(): |
michael@0 | 42 | continue |
michael@0 | 43 | entry = {} |
michael@0 | 44 | else: |
michael@0 | 45 | entry = config[iface.identifier.name] |
michael@0 | 46 | if not isinstance(entry, list): |
michael@0 | 47 | assert isinstance(entry, dict) |
michael@0 | 48 | entry = [entry] |
michael@0 | 49 | elif len(entry) == 1: |
michael@0 | 50 | if entry[0].get("workers", False): |
michael@0 | 51 | # List with only a workers descriptor means we should |
michael@0 | 52 | # infer a mainthread descriptor. If you want only |
michael@0 | 53 | # workers bindings, don't use a list here. |
michael@0 | 54 | entry.append({}) |
michael@0 | 55 | else: |
michael@0 | 56 | raise TypeError("Don't use a single-element list for " |
michael@0 | 57 | "non-worker-only interface " + iface.identifier.name + |
michael@0 | 58 | " in Bindings.conf") |
michael@0 | 59 | elif len(entry) == 2: |
michael@0 | 60 | if entry[0].get("workers", False) == entry[1].get("workers", False): |
michael@0 | 61 | raise TypeError("The two entries for interface " + iface.identifier.name + |
michael@0 | 62 | " in Bindings.conf should not have the same value for 'workers'") |
michael@0 | 63 | else: |
michael@0 | 64 | raise TypeError("Interface " + iface.identifier.name + |
michael@0 | 65 | " should have no more than two entries in Bindings.conf") |
michael@0 | 66 | self.descriptors.extend([Descriptor(self, iface, x) for x in entry]) |
michael@0 | 67 | |
michael@0 | 68 | # Keep the descriptor list sorted for determinism. |
michael@0 | 69 | self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) |
michael@0 | 70 | |
michael@0 | 71 | self.descriptorsByName = {} |
michael@0 | 72 | for d in self.descriptors: |
michael@0 | 73 | self.descriptorsByName.setdefault(d.interface.identifier.name, |
michael@0 | 74 | []).append(d) |
michael@0 | 75 | |
michael@0 | 76 | self.descriptorsByFile = {} |
michael@0 | 77 | for d in self.descriptors: |
michael@0 | 78 | self.descriptorsByFile.setdefault(d.interface.filename(), |
michael@0 | 79 | []).append(d) |
michael@0 | 80 | |
michael@0 | 81 | self.enums = [e for e in parseData if e.isEnum()] |
michael@0 | 82 | |
michael@0 | 83 | # Figure out what our main-thread and worker dictionaries and callbacks |
michael@0 | 84 | # are. |
michael@0 | 85 | mainTypes = set() |
michael@0 | 86 | for descriptor in ([self.getDescriptor("DummyInterface", workers=False)] + |
michael@0 | 87 | self.getDescriptors(workers=False, isExternal=False, skipGen=False)): |
michael@0 | 88 | mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor))) |
michael@0 | 89 | (mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes) |
michael@0 | 90 | |
michael@0 | 91 | workerTypes = set(); |
michael@0 | 92 | for descriptor in ([self.getDescriptor("DummyInterfaceWorkers", workers=True)] + |
michael@0 | 93 | self.getDescriptors(workers=True, isExternal=False, skipGen=False)): |
michael@0 | 94 | workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor))) |
michael@0 | 95 | (workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes) |
michael@0 | 96 | |
michael@0 | 97 | self.dictionaries = [d for d in parseData if d.isDictionary()] |
michael@0 | 98 | self.callbacks = [c for c in parseData if |
michael@0 | 99 | c.isCallback() and not c.isInterface()] |
michael@0 | 100 | |
michael@0 | 101 | def flagWorkerOrMainThread(items, main, worker): |
michael@0 | 102 | for item in items: |
michael@0 | 103 | if item in main: |
michael@0 | 104 | item.setUserData("mainThread", True) |
michael@0 | 105 | if item in worker: |
michael@0 | 106 | item.setUserData("workers", True) |
michael@0 | 107 | flagWorkerOrMainThread(self.callbacks, mainCallbacks, workerCallbacks) |
michael@0 | 108 | |
michael@0 | 109 | def getInterface(self, ifname): |
michael@0 | 110 | return self.interfaces[ifname] |
michael@0 | 111 | def getDescriptors(self, **filters): |
michael@0 | 112 | """Gets the descriptors that match the given filters.""" |
michael@0 | 113 | curr = self.descriptors |
michael@0 | 114 | # Collect up our filters, because we may have a webIDLFile filter that |
michael@0 | 115 | # we always want to apply first. |
michael@0 | 116 | tofilter = [] |
michael@0 | 117 | for key, val in filters.iteritems(): |
michael@0 | 118 | if key == 'webIDLFile': |
michael@0 | 119 | # Special-case this part to make it fast, since most of our |
michael@0 | 120 | # getDescriptors calls are conditioned on a webIDLFile. We may |
michael@0 | 121 | # not have this key, in which case we have no descriptors |
michael@0 | 122 | # either. |
michael@0 | 123 | curr = self.descriptorsByFile.get(val, []) |
michael@0 | 124 | continue |
michael@0 | 125 | elif key == 'hasInterfaceObject': |
michael@0 | 126 | getter = lambda x: (not x.interface.isExternal() and |
michael@0 | 127 | x.interface.hasInterfaceObject()) |
michael@0 | 128 | elif key == 'hasInterfacePrototypeObject': |
michael@0 | 129 | getter = lambda x: (not x.interface.isExternal() and |
michael@0 | 130 | x.interface.hasInterfacePrototypeObject()) |
michael@0 | 131 | elif key == 'hasInterfaceOrInterfacePrototypeObject': |
michael@0 | 132 | getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject() |
michael@0 | 133 | elif key == 'isCallback': |
michael@0 | 134 | getter = lambda x: x.interface.isCallback() |
michael@0 | 135 | elif key == 'isExternal': |
michael@0 | 136 | getter = lambda x: x.interface.isExternal() |
michael@0 | 137 | elif key == 'isJSImplemented': |
michael@0 | 138 | getter = lambda x: x.interface.isJSImplemented() |
michael@0 | 139 | elif key == 'isNavigatorProperty': |
michael@0 | 140 | getter = lambda x: x.interface.getNavigatorProperty() != None |
michael@0 | 141 | else: |
michael@0 | 142 | # Have to watch out: just closing over "key" is not enough, |
michael@0 | 143 | # since we're about to mutate its value |
michael@0 | 144 | getter = (lambda attrName: lambda x: getattr(x, attrName))(key) |
michael@0 | 145 | tofilter.append((getter, val)) |
michael@0 | 146 | for f in tofilter: |
michael@0 | 147 | curr = filter(lambda x: f[0](x) == f[1], curr) |
michael@0 | 148 | return curr |
michael@0 | 149 | def getEnums(self, webIDLFile): |
michael@0 | 150 | return filter(lambda e: e.filename() == webIDLFile, self.enums) |
michael@0 | 151 | |
michael@0 | 152 | @staticmethod |
michael@0 | 153 | def _filterForFileAndWorkers(items, filters): |
michael@0 | 154 | """Gets the items that match the given filters.""" |
michael@0 | 155 | for key, val in filters.iteritems(): |
michael@0 | 156 | if key == 'webIDLFile': |
michael@0 | 157 | items = filter(lambda x: x.filename() == val, items) |
michael@0 | 158 | elif key == 'workers': |
michael@0 | 159 | if val: |
michael@0 | 160 | items = filter(lambda x: x.getUserData("workers", False), items) |
michael@0 | 161 | else: |
michael@0 | 162 | items = filter(lambda x: x.getUserData("mainThread", False), items) |
michael@0 | 163 | else: |
michael@0 | 164 | assert(0) # Unknown key |
michael@0 | 165 | return items |
michael@0 | 166 | def getDictionaries(self, **filters): |
michael@0 | 167 | return self._filterForFileAndWorkers(self.dictionaries, filters) |
michael@0 | 168 | def getCallbacks(self, **filters): |
michael@0 | 169 | return self._filterForFileAndWorkers(self.callbacks, filters) |
michael@0 | 170 | |
michael@0 | 171 | def getDescriptor(self, interfaceName, workers): |
michael@0 | 172 | """ |
michael@0 | 173 | Gets the appropriate descriptor for the given interface name |
michael@0 | 174 | and the given workers boolean. |
michael@0 | 175 | """ |
michael@0 | 176 | for d in self.descriptorsByName[interfaceName]: |
michael@0 | 177 | if d.workers == workers: |
michael@0 | 178 | return d |
michael@0 | 179 | |
michael@0 | 180 | if workers: |
michael@0 | 181 | for d in self.descriptorsByName[interfaceName]: |
michael@0 | 182 | return d |
michael@0 | 183 | |
michael@0 | 184 | raise NoSuchDescriptorError("For " + interfaceName + " found no matches"); |
michael@0 | 185 | def getDescriptorProvider(self, workers): |
michael@0 | 186 | """ |
michael@0 | 187 | Gets a descriptor provider that can provide descriptors as needed, |
michael@0 | 188 | for the given workers boolean |
michael@0 | 189 | """ |
michael@0 | 190 | return DescriptorProvider(self, workers) |
michael@0 | 191 | |
michael@0 | 192 | class NoSuchDescriptorError(TypeError): |
michael@0 | 193 | def __init__(self, str): |
michael@0 | 194 | TypeError.__init__(self, str) |
michael@0 | 195 | |
michael@0 | 196 | class DescriptorProvider: |
michael@0 | 197 | """ |
michael@0 | 198 | A way of getting descriptors for interface names |
michael@0 | 199 | """ |
michael@0 | 200 | def __init__(self, config, workers): |
michael@0 | 201 | self.config = config |
michael@0 | 202 | self.workers = workers |
michael@0 | 203 | |
michael@0 | 204 | def getDescriptor(self, interfaceName): |
michael@0 | 205 | """ |
michael@0 | 206 | Gets the appropriate descriptor for the given interface name given the |
michael@0 | 207 | context of the current descriptor. This selects the appropriate |
michael@0 | 208 | implementation for cases like workers. |
michael@0 | 209 | """ |
michael@0 | 210 | return self.config.getDescriptor(interfaceName, self.workers) |
michael@0 | 211 | |
michael@0 | 212 | class Descriptor(DescriptorProvider): |
michael@0 | 213 | """ |
michael@0 | 214 | Represents a single descriptor for an interface. See Bindings.conf. |
michael@0 | 215 | """ |
michael@0 | 216 | def __init__(self, config, interface, desc): |
michael@0 | 217 | DescriptorProvider.__init__(self, config, desc.get('workers', False)) |
michael@0 | 218 | self.interface = interface |
michael@0 | 219 | |
michael@0 | 220 | # Read the desc, and fill in the relevant defaults. |
michael@0 | 221 | ifaceName = self.interface.identifier.name |
michael@0 | 222 | if self.interface.isExternal(): |
michael@0 | 223 | if self.workers: |
michael@0 | 224 | nativeTypeDefault = "JSObject" |
michael@0 | 225 | else: |
michael@0 | 226 | nativeTypeDefault = "nsIDOM" + ifaceName |
michael@0 | 227 | elif self.interface.isCallback(): |
michael@0 | 228 | nativeTypeDefault = "mozilla::dom::" + ifaceName |
michael@0 | 229 | else: |
michael@0 | 230 | if self.workers: |
michael@0 | 231 | nativeTypeDefault = "mozilla::dom::workers::" + ifaceName |
michael@0 | 232 | else: |
michael@0 | 233 | nativeTypeDefault = "mozilla::dom::" + ifaceName |
michael@0 | 234 | |
michael@0 | 235 | self.nativeType = desc.get('nativeType', nativeTypeDefault) |
michael@0 | 236 | # Now create a version of nativeType that doesn't have extra |
michael@0 | 237 | # mozilla::dom:: at the beginning. |
michael@0 | 238 | prettyNativeType = self.nativeType.split("::") |
michael@0 | 239 | if prettyNativeType[0] == "mozilla": |
michael@0 | 240 | prettyNativeType.pop(0) |
michael@0 | 241 | if prettyNativeType[0] == "dom": |
michael@0 | 242 | prettyNativeType.pop(0) |
michael@0 | 243 | self.prettyNativeType = "::".join(prettyNativeType) |
michael@0 | 244 | |
michael@0 | 245 | self.jsImplParent = desc.get('jsImplParent', self.nativeType) |
michael@0 | 246 | |
michael@0 | 247 | # Do something sane for JSObject |
michael@0 | 248 | if self.nativeType == "JSObject": |
michael@0 | 249 | headerDefault = "js/TypeDecls.h" |
michael@0 | 250 | elif self.interface.isCallback() or self.interface.isJSImplemented(): |
michael@0 | 251 | # A copy of CGHeaders.getDeclarationFilename; we can't |
michael@0 | 252 | # import it here, sadly. |
michael@0 | 253 | # Use our local version of the header, not the exported one, so that |
michael@0 | 254 | # test bindings, which don't export, will work correctly. |
michael@0 | 255 | basename = os.path.basename(self.interface.filename()) |
michael@0 | 256 | headerDefault = basename.replace('.webidl', 'Binding.h') |
michael@0 | 257 | else: |
michael@0 | 258 | if self.workers: |
michael@0 | 259 | headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName |
michael@0 | 260 | elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"): |
michael@0 | 261 | headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0] |
michael@0 | 262 | else: |
michael@0 | 263 | headerDefault = self.nativeType |
michael@0 | 264 | headerDefault = headerDefault.replace("::", "/") + ".h" |
michael@0 | 265 | self.headerFile = desc.get('headerFile', headerDefault) |
michael@0 | 266 | self.headerIsDefault = self.headerFile == headerDefault |
michael@0 | 267 | if self.jsImplParent == self.nativeType: |
michael@0 | 268 | self.jsImplParentHeader = self.headerFile |
michael@0 | 269 | else: |
michael@0 | 270 | self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h" |
michael@0 | 271 | |
michael@0 | 272 | self.skipGen = desc.get('skipGen', False) |
michael@0 | 273 | |
michael@0 | 274 | self.notflattened = desc.get('notflattened', False) |
michael@0 | 275 | self.register = desc.get('register', True) |
michael@0 | 276 | |
michael@0 | 277 | self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False) |
michael@0 | 278 | |
michael@0 | 279 | # If we're concrete, we need to crawl our ancestor interfaces and mark |
michael@0 | 280 | # them as having a concrete descendant. |
michael@0 | 281 | self.concrete = (not self.interface.isExternal() and |
michael@0 | 282 | not self.interface.isCallback() and |
michael@0 | 283 | desc.get('concrete', True)) |
michael@0 | 284 | operations = { |
michael@0 | 285 | 'IndexedGetter': None, |
michael@0 | 286 | 'IndexedSetter': None, |
michael@0 | 287 | 'IndexedCreator': None, |
michael@0 | 288 | 'IndexedDeleter': None, |
michael@0 | 289 | 'NamedGetter': None, |
michael@0 | 290 | 'NamedSetter': None, |
michael@0 | 291 | 'NamedCreator': None, |
michael@0 | 292 | 'NamedDeleter': None, |
michael@0 | 293 | 'Stringifier': None, |
michael@0 | 294 | 'LegacyCaller': None, |
michael@0 | 295 | 'Jsonifier': None |
michael@0 | 296 | } |
michael@0 | 297 | if self.concrete: |
michael@0 | 298 | self.proxy = False |
michael@0 | 299 | iface = self.interface |
michael@0 | 300 | def addOperation(operation, m): |
michael@0 | 301 | if not operations[operation]: |
michael@0 | 302 | operations[operation] = m |
michael@0 | 303 | # Since stringifiers go on the prototype, we only need to worry |
michael@0 | 304 | # about our own stringifier, not those of our ancestor interfaces. |
michael@0 | 305 | for m in iface.members: |
michael@0 | 306 | if m.isMethod() and m.isStringifier(): |
michael@0 | 307 | addOperation('Stringifier', m) |
michael@0 | 308 | if m.isMethod() and m.isJsonifier(): |
michael@0 | 309 | addOperation('Jsonifier', m) |
michael@0 | 310 | # Don't worry about inheriting legacycallers either: in |
michael@0 | 311 | # practice these are on most-derived prototypes. |
michael@0 | 312 | if m.isMethod() and m.isLegacycaller(): |
michael@0 | 313 | if not m.isIdentifierLess(): |
michael@0 | 314 | raise TypeError("We don't support legacycaller with " |
michael@0 | 315 | "identifier.\n%s" % m.location); |
michael@0 | 316 | if len(m.signatures()) != 1: |
michael@0 | 317 | raise TypeError("We don't support overloaded " |
michael@0 | 318 | "legacycaller.\n%s" % m.location) |
michael@0 | 319 | addOperation('LegacyCaller', m) |
michael@0 | 320 | while iface: |
michael@0 | 321 | for m in iface.members: |
michael@0 | 322 | if not m.isMethod(): |
michael@0 | 323 | continue |
michael@0 | 324 | |
michael@0 | 325 | def addIndexedOrNamedOperation(operation, m): |
michael@0 | 326 | self.proxy = True |
michael@0 | 327 | if m.isIndexed(): |
michael@0 | 328 | operation = 'Indexed' + operation |
michael@0 | 329 | else: |
michael@0 | 330 | assert m.isNamed() |
michael@0 | 331 | operation = 'Named' + operation |
michael@0 | 332 | addOperation(operation, m) |
michael@0 | 333 | |
michael@0 | 334 | if m.isGetter(): |
michael@0 | 335 | addIndexedOrNamedOperation('Getter', m) |
michael@0 | 336 | if m.isSetter(): |
michael@0 | 337 | addIndexedOrNamedOperation('Setter', m) |
michael@0 | 338 | if m.isCreator(): |
michael@0 | 339 | addIndexedOrNamedOperation('Creator', m) |
michael@0 | 340 | if m.isDeleter(): |
michael@0 | 341 | addIndexedOrNamedOperation('Deleter', m) |
michael@0 | 342 | if m.isLegacycaller() and iface != self.interface: |
michael@0 | 343 | raise TypeError("We don't support legacycaller on " |
michael@0 | 344 | "non-leaf interface %s.\n%s" % |
michael@0 | 345 | (iface, iface.location)) |
michael@0 | 346 | |
michael@0 | 347 | iface.setUserData('hasConcreteDescendant', True) |
michael@0 | 348 | iface = iface.parent |
michael@0 | 349 | |
michael@0 | 350 | if self.proxy: |
michael@0 | 351 | if (not operations['IndexedGetter'] and |
michael@0 | 352 | (operations['IndexedSetter'] or |
michael@0 | 353 | operations['IndexedDeleter'] or |
michael@0 | 354 | operations['IndexedCreator'])): |
michael@0 | 355 | raise SyntaxError("%s supports indexed properties but does " |
michael@0 | 356 | "not have an indexed getter.\n%s" % |
michael@0 | 357 | (self.interface, self.interface.location)) |
michael@0 | 358 | if (not operations['NamedGetter'] and |
michael@0 | 359 | (operations['NamedSetter'] or |
michael@0 | 360 | operations['NamedDeleter'] or |
michael@0 | 361 | operations['NamedCreator'])): |
michael@0 | 362 | raise SyntaxError("%s supports named properties but does " |
michael@0 | 363 | "not have a named getter.\n%s" % |
michael@0 | 364 | (self.interface, self.interface.location)) |
michael@0 | 365 | iface = self.interface |
michael@0 | 366 | while iface: |
michael@0 | 367 | iface.setUserData('hasProxyDescendant', True) |
michael@0 | 368 | iface = iface.parent |
michael@0 | 369 | self.operations = operations |
michael@0 | 370 | |
michael@0 | 371 | self.nativeOwnership = desc.get('nativeOwnership', 'refcounted') |
michael@0 | 372 | if not self.nativeOwnership in ('owned', 'refcounted'): |
michael@0 | 373 | raise TypeError("Descriptor for %s has unrecognized value (%s) " |
michael@0 | 374 | "for nativeOwnership" % |
michael@0 | 375 | (self.interface.identifier.name, self.nativeOwnership)) |
michael@0 | 376 | if desc.get('wantsQI', None) != None: |
michael@0 | 377 | self._wantsQI = desc.get('wantsQI', None) |
michael@0 | 378 | self.wrapperCache = (not self.interface.isCallback() and |
michael@0 | 379 | (self.nativeOwnership != 'owned' and |
michael@0 | 380 | desc.get('wrapperCache', True))) |
michael@0 | 381 | |
michael@0 | 382 | def make_name(name): |
michael@0 | 383 | return name + "_workers" if self.workers else name |
michael@0 | 384 | self.name = make_name(interface.identifier.name) |
michael@0 | 385 | |
michael@0 | 386 | # self.extendedAttributes is a dict of dicts, keyed on |
michael@0 | 387 | # all/getterOnly/setterOnly and then on member name. Values are an |
michael@0 | 388 | # array of extended attributes. |
michael@0 | 389 | self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} } |
michael@0 | 390 | |
michael@0 | 391 | def addExtendedAttribute(attribute, config): |
michael@0 | 392 | def add(key, members, attribute): |
michael@0 | 393 | for member in members: |
michael@0 | 394 | self.extendedAttributes[key].setdefault(member, []).append(attribute) |
michael@0 | 395 | |
michael@0 | 396 | if isinstance(config, dict): |
michael@0 | 397 | for key in ['all', 'getterOnly', 'setterOnly']: |
michael@0 | 398 | add(key, config.get(key, []), attribute) |
michael@0 | 399 | elif isinstance(config, list): |
michael@0 | 400 | add('all', config, attribute) |
michael@0 | 401 | else: |
michael@0 | 402 | assert isinstance(config, str) |
michael@0 | 403 | if config == '*': |
michael@0 | 404 | iface = self.interface |
michael@0 | 405 | while iface: |
michael@0 | 406 | add('all', map(lambda m: m.name, iface.members), attribute) |
michael@0 | 407 | iface = iface.parent |
michael@0 | 408 | else: |
michael@0 | 409 | add('all', [config], attribute) |
michael@0 | 410 | |
michael@0 | 411 | if self.interface.isJSImplemented(): |
michael@0 | 412 | addExtendedAttribute('implicitJSContext', ['constructor']) |
michael@0 | 413 | else: |
michael@0 | 414 | for attribute in ['implicitJSContext', 'resultNotAddRefed']: |
michael@0 | 415 | addExtendedAttribute(attribute, desc.get(attribute, {})) |
michael@0 | 416 | |
michael@0 | 417 | self.binaryNames = desc.get('binaryNames', {}) |
michael@0 | 418 | if '__legacycaller' not in self.binaryNames: |
michael@0 | 419 | self.binaryNames["__legacycaller"] = "LegacyCall" |
michael@0 | 420 | if '__stringifier' not in self.binaryNames: |
michael@0 | 421 | self.binaryNames["__stringifier"] = "Stringify" |
michael@0 | 422 | |
michael@0 | 423 | # Build the prototype chain. |
michael@0 | 424 | self.prototypeChain = [] |
michael@0 | 425 | parent = interface |
michael@0 | 426 | while parent: |
michael@0 | 427 | self.prototypeChain.insert(0, parent.identifier.name) |
michael@0 | 428 | parent = parent.parent |
michael@0 | 429 | config.maxProtoChainLength = max(config.maxProtoChainLength, |
michael@0 | 430 | len(self.prototypeChain)) |
michael@0 | 431 | |
michael@0 | 432 | def hasInterfaceOrInterfacePrototypeObject(self): |
michael@0 | 433 | |
michael@0 | 434 | # Forward-declared interfaces don't need either interface object or |
michael@0 | 435 | # interface prototype object as they're going to use QI (on main thread) |
michael@0 | 436 | # or be passed as a JSObject (on worker threads). |
michael@0 | 437 | if self.interface.isExternal(): |
michael@0 | 438 | return False |
michael@0 | 439 | |
michael@0 | 440 | return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject() |
michael@0 | 441 | |
michael@0 | 442 | def getExtendedAttributes(self, member, getter=False, setter=False): |
michael@0 | 443 | def ensureValidThrowsExtendedAttribute(attr): |
michael@0 | 444 | assert(attr is None or attr is True or len(attr) == 1) |
michael@0 | 445 | if (attr is not None and attr is not True and |
michael@0 | 446 | 'Workers' not in attr and 'MainThread' not in attr): |
michael@0 | 447 | raise TypeError("Unknown value for 'Throws': " + attr[0]) |
michael@0 | 448 | |
michael@0 | 449 | def maybeAppendInfallibleToAttrs(attrs, throws): |
michael@0 | 450 | ensureValidThrowsExtendedAttribute(throws) |
michael@0 | 451 | if (throws is None or |
michael@0 | 452 | (throws is not True and |
michael@0 | 453 | ('Workers' not in throws or not self.workers) and |
michael@0 | 454 | ('MainThread' not in throws or self.workers))): |
michael@0 | 455 | attrs.append("infallible") |
michael@0 | 456 | |
michael@0 | 457 | name = member.identifier.name |
michael@0 | 458 | throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws") |
michael@0 | 459 | if member.isMethod(): |
michael@0 | 460 | attrs = self.extendedAttributes['all'].get(name, []) |
michael@0 | 461 | maybeAppendInfallibleToAttrs(attrs, throws) |
michael@0 | 462 | return attrs |
michael@0 | 463 | |
michael@0 | 464 | assert member.isAttr() |
michael@0 | 465 | assert bool(getter) != bool(setter) |
michael@0 | 466 | key = 'getterOnly' if getter else 'setterOnly' |
michael@0 | 467 | attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, []) |
michael@0 | 468 | if throws is None: |
michael@0 | 469 | throwsAttr = "GetterThrows" if getter else "SetterThrows" |
michael@0 | 470 | throws = member.getExtendedAttribute(throwsAttr) |
michael@0 | 471 | maybeAppendInfallibleToAttrs(attrs, throws) |
michael@0 | 472 | return attrs |
michael@0 | 473 | |
michael@0 | 474 | def supportsIndexedProperties(self): |
michael@0 | 475 | return self.operations['IndexedGetter'] is not None |
michael@0 | 476 | |
michael@0 | 477 | def supportsNamedProperties(self): |
michael@0 | 478 | return self.operations['NamedGetter'] is not None |
michael@0 | 479 | |
michael@0 | 480 | def needsConstructHookHolder(self): |
michael@0 | 481 | assert self.interface.hasInterfaceObject() |
michael@0 | 482 | return False |
michael@0 | 483 | |
michael@0 | 484 | def needsHeaderInclude(self): |
michael@0 | 485 | """ |
michael@0 | 486 | An interface doesn't need a header file if it is not concrete, |
michael@0 | 487 | not pref-controlled, has no prototype object, and has no |
michael@0 | 488 | static methods or attributes. |
michael@0 | 489 | """ |
michael@0 | 490 | return (self.interface.isExternal() or self.concrete or |
michael@0 | 491 | self.interface.hasInterfacePrototypeObject() or |
michael@0 | 492 | any((m.isAttr() or m.isMethod()) and m.isStatic() for m |
michael@0 | 493 | in self.interface.members)) |
michael@0 | 494 | |
michael@0 | 495 | def isExposedConditionally(self): |
michael@0 | 496 | return (self.interface.getExtendedAttribute("Pref") or |
michael@0 | 497 | self.interface.getExtendedAttribute("ChromeOnly") or |
michael@0 | 498 | self.interface.getExtendedAttribute("Func") or |
michael@0 | 499 | self.interface.getExtendedAttribute("AvailableIn")) |
michael@0 | 500 | |
michael@0 | 501 | def needsXrayResolveHooks(self): |
michael@0 | 502 | """ |
michael@0 | 503 | Generally, any interface with NeedNewResolve needs Xray |
michael@0 | 504 | resolveOwnProperty and enumerateOwnProperties hooks. But for |
michael@0 | 505 | the special case of plugin-loading elements, we do NOT want |
michael@0 | 506 | those, because we don't want to instantiate plug-ins simply |
michael@0 | 507 | due to chrome touching them and that's all those hooks do on |
michael@0 | 508 | those elements. So we special-case those here. |
michael@0 | 509 | """ |
michael@0 | 510 | return (self.interface.getExtendedAttribute("NeedNewResolve") and |
michael@0 | 511 | self.interface.identifier.name not in ["HTMLObjectElement", |
michael@0 | 512 | "HTMLEmbedElement", |
michael@0 | 513 | "HTMLAppletElement"]) |
michael@0 | 514 | |
michael@0 | 515 | def needsSpecialGenericOps(self): |
michael@0 | 516 | """ |
michael@0 | 517 | Returns true if this descriptor requires generic ops other than |
michael@0 | 518 | GenericBindingMethod/GenericBindingGetter/GenericBindingSetter. |
michael@0 | 519 | |
michael@0 | 520 | In practice we need to do this if our this value might be an XPConnect |
michael@0 | 521 | object or if we need to coerce null/undefined to the global. |
michael@0 | 522 | """ |
michael@0 | 523 | return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain() |
michael@0 | 524 | |
michael@0 | 525 | # Some utility methods |
michael@0 | 526 | def getTypesFromDescriptor(descriptor): |
michael@0 | 527 | """ |
michael@0 | 528 | Get all argument and return types for all members of the descriptor |
michael@0 | 529 | """ |
michael@0 | 530 | members = [m for m in descriptor.interface.members] |
michael@0 | 531 | if descriptor.interface.ctor(): |
michael@0 | 532 | members.append(descriptor.interface.ctor()) |
michael@0 | 533 | members.extend(descriptor.interface.namedConstructors) |
michael@0 | 534 | signatures = [s for m in members if m.isMethod() for s in m.signatures()] |
michael@0 | 535 | types = [] |
michael@0 | 536 | for s in signatures: |
michael@0 | 537 | assert len(s) == 2 |
michael@0 | 538 | (returnType, arguments) = s |
michael@0 | 539 | types.append(returnType) |
michael@0 | 540 | types.extend(a.type for a in arguments) |
michael@0 | 541 | |
michael@0 | 542 | types.extend(a.type for a in members if a.isAttr()) |
michael@0 | 543 | return types |
michael@0 | 544 | |
michael@0 | 545 | def getFlatTypes(types): |
michael@0 | 546 | retval = set() |
michael@0 | 547 | for type in types: |
michael@0 | 548 | type = type.unroll() |
michael@0 | 549 | if type.isUnion(): |
michael@0 | 550 | retval |= set(type.flatMemberTypes) |
michael@0 | 551 | else: |
michael@0 | 552 | retval.add(type) |
michael@0 | 553 | return retval |
michael@0 | 554 | |
michael@0 | 555 | def getTypesFromDictionary(dictionary): |
michael@0 | 556 | """ |
michael@0 | 557 | Get all member types for this dictionary |
michael@0 | 558 | """ |
michael@0 | 559 | types = [] |
michael@0 | 560 | curDict = dictionary |
michael@0 | 561 | while curDict: |
michael@0 | 562 | types.extend([m.type for m in curDict.members]) |
michael@0 | 563 | curDict = curDict.parent |
michael@0 | 564 | return types |
michael@0 | 565 | |
michael@0 | 566 | def getTypesFromCallback(callback): |
michael@0 | 567 | """ |
michael@0 | 568 | Get the types this callback depends on: its return type and the |
michael@0 | 569 | types of its arguments. |
michael@0 | 570 | """ |
michael@0 | 571 | sig = callback.signatures()[0] |
michael@0 | 572 | types = [sig[0]] # Return type |
michael@0 | 573 | types.extend(arg.type for arg in sig[1]) # Arguments |
michael@0 | 574 | return types |
michael@0 | 575 | |
michael@0 | 576 | def findCallbacksAndDictionaries(inputTypes): |
michael@0 | 577 | """ |
michael@0 | 578 | Ensure that all callbacks and dictionaries reachable from types end up in |
michael@0 | 579 | the returned callbacks and dictionaries sets. |
michael@0 | 580 | |
michael@0 | 581 | Note that we assume that our initial invocation already includes all types |
michael@0 | 582 | reachable via descriptors in "types", so we only have to deal with things |
michael@0 | 583 | that are themeselves reachable via callbacks and dictionaries. |
michael@0 | 584 | """ |
michael@0 | 585 | def doFindCallbacksAndDictionaries(types, callbacks, dictionaries): |
michael@0 | 586 | unhandledTypes = set() |
michael@0 | 587 | for type in types: |
michael@0 | 588 | if type.isCallback() and type not in callbacks: |
michael@0 | 589 | unhandledTypes |= getFlatTypes(getTypesFromCallback(type)) |
michael@0 | 590 | callbacks.add(type) |
michael@0 | 591 | elif type.isDictionary() and type.inner not in dictionaries: |
michael@0 | 592 | d = type.inner |
michael@0 | 593 | unhandledTypes |= getFlatTypes(getTypesFromDictionary(d)) |
michael@0 | 594 | while d: |
michael@0 | 595 | dictionaries.add(d) |
michael@0 | 596 | d = d.parent |
michael@0 | 597 | if len(unhandledTypes) != 0: |
michael@0 | 598 | doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries) |
michael@0 | 599 | |
michael@0 | 600 | retCallbacks = set() |
michael@0 | 601 | retDictionaries = set() |
michael@0 | 602 | doFindCallbacksAndDictionaries(inputTypes, retCallbacks, retDictionaries) |
michael@0 | 603 | return (retCallbacks, retDictionaries) |