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