Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(662)

Side by Side Diff: bindings/dart/scripts/dart_interface.py

Issue 540533002: Roll IDL to Dartium37 (r181268) (Closed) Base URL: https://dart.googlecode.com/svn/third_party/WebCore
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « bindings/dart/scripts/dart_compiler.py ('k') | bindings/dart/scripts/dart_methods.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (C) 2013 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 """Generate template values for an interface.
30
31 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
32 """
33
34 from collections import defaultdict
35
36 import idl_types
37 from idl_types import IdlType, inherits_interface
38 import dart_attributes
39 import dart_methods
40 import dart_types
41 from dart_utilities import DartUtilities
42 from v8_globals import includes
43
44
45 INTERFACE_H_INCLUDES = frozenset([
46 'bindings/dart/DartDOMWrapper.h',
47 'platform/heap/Handle.h',
48 ])
49
50 INTERFACE_CPP_INCLUDES = frozenset([
51 'DartUtilities.h',
52 'wtf/GetPtr.h',
53 'wtf/RefPtr.h',
54 ])
55
56
57 # TODO(terry): Temporary to not generate a method, getter/setter. Format is:
58 #
59 # interface_name.method_name
60 # interface_name.get:attribute_name
61 # interface_name.set:attribute_name
62 #
63 # Ultimate solution add a special attribute flag to IDL to signal
64 # don't generate IDL entry in Dart (e.g., DartNoGenerate)?
65 IGNORE_MEMBERS = frozenset([
66 'AudioBufferSourceNode.looping', # TODO(vsm): Use deprecated IDL annotation
67 'CSSStyleDeclaration.getPropertyCSSValue',
68 'CanvasRenderingContext2D.clearShadow',
69 'CanvasRenderingContext2D.drawImageFromRect',
70 'CanvasRenderingContext2D.setAlpha',
71 'CanvasRenderingContext2D.setCompositeOperation',
72 'CanvasRenderingContext2D.setFillColor',
73 'CanvasRenderingContext2D.setLineCap',
74 'CanvasRenderingContext2D.setLineJoin',
75 'CanvasRenderingContext2D.setLineWidth',
76 'CanvasRenderingContext2D.setMiterLimit',
77 'CanvasRenderingContext2D.setShadow',
78 'CanvasRenderingContext2D.setStrokeColor',
79 'CharacterData.remove',
80 'Window.call:blur',
81 'Window.call:focus',
82 'Window.clientInformation',
83 'Window.createImageBitmap',
84 'Window.get:frames',
85 'Window.get:length',
86 'Window.on:beforeUnload',
87 'Window.on:webkitTransitionEnd',
88 'Window.pagePopupController',
89 'Window.prompt',
90 'Window.webkitCancelAnimationFrame',
91 'Window.webkitCancelRequestAnimationFrame',
92 'Window.webkitIndexedDB',
93 'Window.webkitRequestAnimationFrame',
94 'Document.alinkColor',
95 'HTMLDocument.all',
96 'Document.applets',
97 'Document.bgColor',
98 'Document.clear',
99 'Document.createAttribute',
100 'Document.createAttributeNS',
101 'Document.createComment',
102 'Document.createExpression',
103 'Document.createNSResolver',
104 'Document.createProcessingInstruction',
105 'Document.designMode',
106 'Document.dir',
107 'Document.evaluate',
108 'Document.fgColor',
109 'Document.get:URL',
110 'Document.get:anchors',
111 'Document.get:characterSet',
112 'Document.get:compatMode',
113 'Document.get:defaultCharset',
114 'Document.get:doctype',
115 'Document.get:documentURI',
116 'Document.get:embeds',
117 'Document.get:forms',
118 'Document.get:inputEncoding',
119 'Document.get:links',
120 'Document.get:plugins',
121 'Document.get:scripts',
122 'Document.get:xmlEncoding',
123 'Document.getElementsByTagNameNS',
124 'Document.getOverrideStyle',
125 'Document.getSelection',
126 'Document.images',
127 'Document.linkColor',
128 'Document.location',
129 'Document.on:wheel',
130 'Document.open',
131 'Document.register',
132 'Document.set:domain',
133 'Document.vlinkColor',
134 'Document.webkitCurrentFullScreenElement',
135 'Document.webkitFullScreenKeyboardInputAllowed',
136 'Document.write',
137 'Document.writeln',
138 'Document.xmlStandalone',
139 'Document.xmlVersion',
140 'DocumentFragment.children',
141 'DocumentType.*',
142 'DOMException.code',
143 'DOMException.ABORT_ERR',
144 'DOMException.DATA_CLONE_ERR',
145 'DOMException.DOMSTRING_SIZE_ERR',
146 'DOMException.HIERARCHY_REQUEST_ERR',
147 'DOMException.INDEX_SIZE_ERR',
148 'DOMException.INUSE_ATTRIBUTE_ERR',
149 'DOMException.INVALID_ACCESS_ERR',
150 'DOMException.INVALID_CHARACTER_ERR',
151 'DOMException.INVALID_MODIFICATION_ERR',
152 'DOMException.INVALID_NODE_TYPE_ERR',
153 'DOMException.INVALID_STATE_ERR',
154 'DOMException.NAMESPACE_ERR',
155 'DOMException.NETWORK_ERR',
156 'DOMException.NOT_FOUND_ERR',
157 'DOMException.NOT_SUPPORTED_ERR',
158 'DOMException.NO_DATA_ALLOWED_ERR',
159 'DOMException.NO_MODIFICATION_ALLOWED_ERR',
160 'DOMException.QUOTA_EXCEEDED_ERR',
161 'DOMException.SECURITY_ERR',
162 'DOMException.SYNTAX_ERR',
163 'DOMException.TIMEOUT_ERR',
164 'DOMException.TYPE_MISMATCH_ERR',
165 'DOMException.URL_MISMATCH_ERR',
166 'DOMException.VALIDATION_ERR',
167 'DOMException.WRONG_DOCUMENT_ERR',
168 'Element.accessKey',
169 'Element.dataset',
170 'Element.get:classList',
171 'Element.getAttributeNode',
172 'Element.getAttributeNodeNS',
173 'Element.getElementsByTagNameNS',
174 'Element.innerText',
175 'Element.on:wheel',
176 'Element.outerText',
177 'Element.removeAttributeNode',
178 'Element.set:outerHTML',
179 'Element.setAttributeNode',
180 'Element.setAttributeNodeNS',
181 'Element.webkitCreateShadowRoot',
182 'Element.webkitMatchesSelector',
183 'Element.webkitPseudo',
184 'Element.webkitShadowRoot',
185 '=Event.returnValue', # Only suppress on Event, allow for BeforeUnloadEvent .
186 'Event.srcElement',
187 'EventSource.URL',
188 'FontFace.ready',
189 'FontFaceSet.load',
190 'FontFaceSet.ready',
191 'HTMLAnchorElement.charset',
192 'HTMLAnchorElement.coords',
193 'HTMLAnchorElement.rev',
194 'HTMLAnchorElement.shape',
195 'HTMLAnchorElement.text',
196 'HTMLAppletElement.*',
197 'HTMLAreaElement.noHref',
198 'HTMLBRElement.clear',
199 'HTMLBaseFontElement.*',
200 'HTMLBodyElement.aLink',
201 'HTMLBodyElement.background',
202 'HTMLBodyElement.bgColor',
203 'HTMLBodyElement.link',
204 'HTMLBodyElement.on:beforeUnload',
205 'HTMLBodyElement.text',
206 'HTMLBodyElement.vLink',
207 'HTMLDListElement.compact',
208 'HTMLDirectoryElement.*',
209 'HTMLDivElement.align',
210 'HTMLFontElement.*',
211 'HTMLFormControlsCollection.__getter__',
212 'HTMLFormElement.get:elements',
213 'HTMLFrameElement.*',
214 'HTMLFrameSetElement.*',
215 'HTMLHRElement.align',
216 'HTMLHRElement.noShade',
217 'HTMLHRElement.size',
218 'HTMLHRElement.width',
219 'HTMLHeadElement.profile',
220 'HTMLHeadingElement.align',
221 'HTMLHtmlElement.manifest',
222 'HTMLHtmlElement.version',
223 'HTMLIFrameElement.align',
224 'HTMLIFrameElement.frameBorder',
225 'HTMLIFrameElement.longDesc',
226 'HTMLIFrameElement.marginHeight',
227 'HTMLIFrameElement.marginWidth',
228 'HTMLIFrameElement.scrolling',
229 'HTMLImageElement.align',
230 'HTMLImageElement.hspace',
231 'HTMLImageElement.longDesc',
232 'HTMLImageElement.name',
233 'HTMLImageElement.vspace',
234 'HTMLInputElement.align',
235 'HTMLLegendElement.align',
236 'HTMLLinkElement.charset',
237 'HTMLLinkElement.rev',
238 'HTMLLinkElement.target',
239 'HTMLMarqueeElement.*',
240 'HTMLMenuElement.compact',
241 'HTMLMetaElement.scheme',
242 'HTMLOListElement.compact',
243 'HTMLObjectElement.align',
244 'HTMLObjectElement.archive',
245 'HTMLObjectElement.border',
246 'HTMLObjectElement.codeBase',
247 'HTMLObjectElement.codeType',
248 'HTMLObjectElement.declare',
249 'HTMLObjectElement.hspace',
250 'HTMLObjectElement.standby',
251 'HTMLObjectElement.vspace',
252 'HTMLOptionElement.text',
253 'HTMLOptionsCollection.*',
254 'HTMLParagraphElement.align',
255 'HTMLParamElement.type',
256 'HTMLParamElement.valueType',
257 'HTMLPreElement.width',
258 'HTMLScriptElement.text',
259 'HTMLSelectElement.options',
260 'HTMLSelectElement.selectedOptions',
261 'HTMLTableCaptionElement.align',
262 'HTMLTableCellElement.abbr',
263 'HTMLTableCellElement.align',
264 'HTMLTableCellElement.axis',
265 'HTMLTableCellElement.bgColor',
266 'HTMLTableCellElement.ch',
267 'HTMLTableCellElement.chOff',
268 'HTMLTableCellElement.height',
269 'HTMLTableCellElement.noWrap',
270 'HTMLTableCellElement.scope',
271 'HTMLTableCellElement.vAlign',
272 'HTMLTableCellElement.width',
273 'HTMLTableColElement.align',
274 'HTMLTableColElement.ch',
275 'HTMLTableColElement.chOff',
276 'HTMLTableColElement.vAlign',
277 'HTMLTableColElement.width',
278 'HTMLTableElement.align',
279 'HTMLTableElement.bgColor',
280 'HTMLTableElement.cellPadding',
281 'HTMLTableElement.cellSpacing',
282 'HTMLTableElement.frame',
283 'HTMLTableElement.rules',
284 'HTMLTableElement.summary',
285 'HTMLTableElement.width',
286 'HTMLTableRowElement.align',
287 'HTMLTableRowElement.bgColor',
288 'HTMLTableRowElement.ch',
289 'HTMLTableRowElement.chOff',
290 'HTMLTableRowElement.vAlign',
291 'HTMLTableSectionElement.align',
292 'HTMLTableSectionElement.ch',
293 'HTMLTableSectionElement.chOff',
294 'HTMLTableSectionElement.vAlign',
295 'HTMLTitleElement.text',
296 'HTMLUListElement.compact',
297 'HTMLUListElement.type',
298 'Location.valueOf',
299 'MessageEvent.ports',
300 'MessageEvent.webkitInitMessageEvent',
301 'MouseEvent.x',
302 'MouseEvent.y',
303 'Navigator.registerServiceWorker',
304 'Navigator.unregisterServiceWorker',
305 'Node.compareDocumentPosition',
306 'Node.get:DOCUMENT_POSITION_CONTAINED_BY',
307 'Node.get:DOCUMENT_POSITION_CONTAINS',
308 'Node.get:DOCUMENT_POSITION_DISCONNECTED',
309 'Node.get:DOCUMENT_POSITION_FOLLOWING',
310 'Node.get:DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC',
311 'Node.get:DOCUMENT_POSITION_PRECEDING',
312 'Node.get:prefix',
313 'Node.hasAttributes',
314 'Node.isDefaultNamespace',
315 'Node.isEqualNode',
316 'Node.isSameNode',
317 'Node.isSupported',
318 'Node.lookupNamespaceURI',
319 'Node.lookupPrefix',
320 'Node.normalize',
321 'Node.set:nodeValue',
322 'NodeFilter.acceptNode',
323 'NodeIterator.expandEntityReferences',
324 'NodeIterator.filter',
325 'Performance.webkitClearMarks',
326 'Performance.webkitClearMeasures',
327 'Performance.webkitGetEntries',
328 'Performance.webkitGetEntriesByName',
329 'Performance.webkitGetEntriesByType',
330 'Performance.webkitMark',
331 'Performance.webkitMeasure',
332 'ShadowRoot.getElementsByTagNameNS',
333 'SVGElement.getPresentationAttribute',
334 'SVGElementInstance.on:wheel',
335 'WheelEvent.wheelDelta',
336 'Window.on:wheel',
337 'WindowEventHandlers.on:beforeUnload',
338 'WorkerGlobalScope.webkitIndexedDB',
339 # TODO(jacobr): should these be removed?
340 'Document.close',
341 'Document.hasFocus',
342 ])
343
344
345 def _suppress_method(interface_name, name):
346 name_to_find = '%s.%s' % (interface_name, name)
347 wildcard_name_to_find = '%s.*' % interface_name
348 return name_to_find in IGNORE_MEMBERS or wildcard_name_to_find in IGNORE_MEM BERS
349
350
351 # Both getter and setter are to be suppressed then the attribute is completely
352 # disappear.
353 def _suppress_attribute(interface_name, name):
354 return (suppress_getter(interface_name, name) and suppress_setter(interface_ name, name))
355
356
357 def suppress_getter(interface_name, name):
358 name_to_find = '%s.get:%s' % (interface_name, name)
359 wildcard_getter_to_find = '%s.get:*' % interface_name
360 return (name_to_find in IGNORE_MEMBERS or
361 _suppress_method(interface_name, name) or
362 wildcard_getter_to_find in IGNORE_MEMBERS)
363
364
365 def suppress_setter(interface_name, name):
366 name_to_find = '%s.set:%s' % (interface_name, name)
367 wildcard_setter_to_find = '%s.set:*' % interface_name
368 return (name_to_find in IGNORE_MEMBERS or
369 _suppress_method(interface_name, name) or
370 wildcard_setter_to_find in IGNORE_MEMBERS)
371
372
373 # To suppress an IDL method or attribute with a particular Extended Attribute
374 # w/o a value e.g, StrictTypeChecking would be an empty set
375 # 'StrictTypeChecking': frozenset([]),
376 IGNORE_EXTENDED_ATTRIBUTES = {
377 # 'RuntimeEnabled': frozenset(['ExperimentalCanvasFeatures']),
378 }
379
380
381 # Return True if the method / attribute should be suppressed.
382 def _suppress_extended_attributes(extended_attributes):
383 if 'DartSuppress' in extended_attributes and extended_attributes.get('DartSu ppress') == None:
384 return True
385
386 # TODO(terry): Eliminate this using DartSuppress extended attribute in the
387 # IDL files instead of the IGNORE_EXTENDED_ATTRIBUTES list.
388 for extended_attribute_name in extended_attributes:
389 ignore_extended_values = IGNORE_EXTENDED_ATTRIBUTES.get(extended_attribu te_name)
390 if ignore_extended_values != None:
391 extended_attribute_value = extended_attributes.get(extended_attribut e_name)
392 if ((not ignore_extended_values and extended_attribute_value == None ) or
393 extended_attribute_value in ignore_extended_values):
394 return True
395 return False
396
397
398 def generate_interface(interface):
399 includes.clear()
400 includes.update(INTERFACE_CPP_INCLUDES)
401 header_includes = set(INTERFACE_H_INCLUDES)
402
403 parent_interface = interface.parent
404 if parent_interface:
405 header_includes.update(dart_types.includes_for_interface(parent_interfac e))
406 extended_attributes = interface.extended_attributes
407
408 is_audio_buffer = inherits_interface(interface.name, 'AudioBuffer')
409 if is_audio_buffer:
410 includes.add('modules/webaudio/AudioBuffer.h')
411
412 is_document = inherits_interface(interface.name, 'Document')
413 if is_document:
414 # FIXME(vsm): We probably need bindings/dart/DartController and
415 # core/frame/LocalFrame.h here.
416 includes.update(['DartDocument.h'])
417
418 if inherits_interface(interface.name, 'DataTransferItemList'):
419 # FIXME(jacobr): this is a hack.
420 includes.update(['core/html/HTMLCollection.h'])
421
422
423 if inherits_interface(interface.name, 'EventTarget'):
424 includes.update(['DartEventListener.h'])
425
426 # [ActiveDOMObject]
427 is_active_dom_object = 'ActiveDOMObject' in extended_attributes
428
429 # [CheckSecurity]
430 is_check_security = 'CheckSecurity' in extended_attributes
431 if is_check_security:
432 includes.add('bindings/common/BindingSecurity.h')
433
434 # [DependentLifetime]
435 is_dependent_lifetime = 'DependentLifetime' in extended_attributes
436
437 # [MeasureAs]
438 # TODO(terry): Remove Me?
439 # is_measure_as = 'MeasureAs' in extended_attributes
440 # if is_measure_as:
441 # includes.add('core/frame/UseCounter.h')
442
443 # [SetWrapperReferenceFrom]
444 reachable_node_function = extended_attributes.get('SetWrapperReferenceFrom')
445 if reachable_node_function:
446 # FIXME(vsm): We may need bindings/dart/DartGCController.h instead.
447 includes.update(['bindings/v8/V8GCController.h',
448 'core/dom/Element.h'])
449
450 # [SetWrapperReferenceTo]
451 set_wrapper_reference_to_list = [{
452 'name': argument.name,
453 # FIXME: properly should be:
454 # 'cpp_type': argument.idl_type.cpp_type_args(used_as_argument=True),
455 # (if type is non-wrapper type like NodeFilter, normally RefPtr)
456 # Raw pointers faster though, and NodeFilter hacky anyway.
457 'cpp_type': argument.idl_type.implemented_as + '*',
458 'idl_type': argument.idl_type,
459 'v8_type': dart_types.v8_type(argument.idl_type.name),
460 } for argument in extended_attributes.get('SetWrapperReferenceTo', [])]
461 for set_wrapper_reference_to in set_wrapper_reference_to_list:
462 set_wrapper_reference_to['idl_type'].add_includes_for_type()
463
464 # [SpecialWrapFor]
465 if 'SpecialWrapFor' in extended_attributes:
466 special_wrap_for = extended_attributes['SpecialWrapFor'].split('|')
467 else:
468 special_wrap_for = []
469 for special_wrap_interface in special_wrap_for:
470 dart_types.add_includes_for_interface(special_wrap_interface)
471
472 # [Custom=Wrap], [SetWrapperReferenceFrom]
473 has_visit_dom_wrapper = (
474 DartUtilities.has_extended_attribute_value(interface, 'Custom', 'VisitDO MWrapper') or
475 reachable_node_function or
476 set_wrapper_reference_to_list)
477
478 this_gc_type = DartUtilities.gc_type(interface)
479
480 template_contents = {
481 'conditional_string': DartUtilities.conditional_string(interface), # [C onditional]
482 'cpp_class': DartUtilities.cpp_name(interface),
483 'gc_type': this_gc_type,
484 'has_custom_legacy_call_as_function': DartUtilities.has_extended_attribu te_value(interface, 'Custom', 'LegacyCallAsFunction'), # [Custom=LegacyCallAsFu nction]
485 'has_custom_to_v8': DartUtilities.has_extended_attribute_value(interface , 'Custom', 'ToV8'), # [Custom=ToV8]
486 'has_custom_wrap': DartUtilities.has_extended_attribute_value(interface, 'Custom', 'Wrap'), # [Custom=Wrap]
487 'has_visit_dom_wrapper': has_visit_dom_wrapper,
488 'header_includes': header_includes,
489 'interface_name': interface.name,
490 'is_active_dom_object': is_active_dom_object,
491 'is_audio_buffer': is_audio_buffer,
492 'is_check_security': is_check_security,
493 'is_dependent_lifetime': is_dependent_lifetime,
494 'is_document': is_document,
495 'is_event_target': inherits_interface(interface.name, 'EventTarget'),
496 'is_exception': interface.is_exception,
497 'is_garbage_collected': this_gc_type == 'GarbageCollectedObject',
498 'is_will_be_garbage_collected': this_gc_type == 'WillBeGarbageCollectedO bject',
499 'is_node': inherits_interface(interface.name, 'Node'),
500 'measure_as': DartUtilities.measure_as(interface), # [MeasureAs]
501 'parent_interface': parent_interface,
502 'pass_cpp_type': dart_types.cpp_template_type(
503 dart_types.cpp_ptr_type('PassRefPtr', 'RawPtr', this_gc_type),
504 DartUtilities.cpp_name(interface)),
505 'reachable_node_function': reachable_node_function,
506 'runtime_enabled_function': DartUtilities.runtime_enabled_function_name( interface), # [RuntimeEnabled]
507 'set_wrapper_reference_to_list': set_wrapper_reference_to_list,
508 'special_wrap_for': special_wrap_for,
509 'dart_class': dart_types.dart_type(interface.name),
510 'v8_class': DartUtilities.v8_class_name(interface),
511 'wrapper_configuration': 'WrapperConfiguration::Dependent'
512 if (has_visit_dom_wrapper or
513 is_active_dom_object or
514 is_dependent_lifetime)
515 else 'WrapperConfiguration::Independent',
516 }
517
518 # Constructors
519 constructors = [generate_constructor(interface, constructor)
520 for constructor in interface.constructors
521 # FIXME: shouldn't put named constructors with constructors
522 # (currently needed for Perl compatibility)
523 # Handle named constructors separately
524 if constructor.name == 'Constructor']
525 generate_constructor_overloads(constructors)
526
527 # [CustomConstructor]
528 custom_constructors = [generate_custom_constructor(interface, constructor)
529 for constructor in interface.custom_constructors]
530
531 # [EventConstructor]
532 has_event_constructor = 'EventConstructor' in extended_attributes
533 any_type_attributes = [attribute for attribute in interface.attributes
534 if attribute.idl_type.name == 'Any']
535 if has_event_constructor:
536 includes.add('bindings/common/Dictionary.h')
537 if any_type_attributes:
538 includes.add('bindings/v8/SerializedScriptValue.h')
539
540 # [NamedConstructor]
541 named_constructor = generate_named_constructor(interface)
542
543 if (constructors or custom_constructors or has_event_constructor or
544 named_constructor):
545 includes.add('core/frame/LocalDOMWindow.h')
546
547 template_contents.update({
548 'any_type_attributes': any_type_attributes,
549 'constructors': constructors,
550 'custom_constructors': custom_constructors,
551 'has_custom_constructor': bool(custom_constructors),
552 'has_event_constructor': has_event_constructor,
553 'interface_length':
554 interface_length(interface, constructors + custom_constructors),
555 'is_constructor_call_with_document': DartUtilities.has_extended_attribut e_value(
556 interface, 'ConstructorCallWith', 'Document'), # [ConstructorCallWi th=Document]
557 'is_constructor_call_with_execution_context': DartUtilities.has_extended _attribute_value(
558 interface, 'ConstructorCallWith', 'ExecutionContext'), # [Construct orCallWith=ExeuctionContext]
559 'is_constructor_raises_exception': extended_attributes.get('RaisesExcept ion') == 'Constructor', # [RaisesException=Constructor]
560 'named_constructor': named_constructor,
561 })
562
563 # Constants
564 template_contents.update({
565 'constants': [generate_constant(constant) for constant in interface.cons tants],
566 'do_not_check_constants': 'DoNotCheckConstants' in extended_attributes,
567 })
568
569 # Attributes
570 attributes = [dart_attributes.generate_attribute(interface, attribute)
571 for attribute in interface.attributes
572 # Skip attributes in the IGNORE_MEMBERS list or if an
573 # extended attribute is in the IGNORE_EXTENDED_ATTRIBUTES.
574 if (not _suppress_attribute(interface.name, attribute.name ) and
575 not dart_attributes.is_constructor_attribute(attribute ) and
576 not _suppress_extended_attributes(attribute.extended_a ttributes) and
577 not ('DartSuppress' in attribute.extended_attributes a nd
578 attribute.extended_attributes.get('DartSuppress') == None))]
579 template_contents.update({
580 'attributes': attributes,
581 'has_accessors': any(attribute['is_expose_js_accessors'] for attribute i n attributes),
582 'has_attribute_configuration': any(
583 not (attribute['is_expose_js_accessors'] or
584 attribute['is_static'] or
585 attribute['runtime_enabled_function'] or
586 attribute['per_context_enabled_function'])
587 for attribute in attributes),
588 'has_constructor_attributes': any(attribute['constructor_type'] for attr ibute in attributes),
589 'has_per_context_enabled_attributes': any(attribute['per_context_enabled _function'] for attribute in attributes),
590 'has_replaceable_attributes': any(attribute['is_replaceable'] for attrib ute in attributes),
591 })
592
593 # Methods
594 methods = [dart_methods.generate_method(interface, method)
595 for method in interface.operations
596 # Skip anonymous special operations (methods name empty).
597 # Skip methods in our IGNORE_MEMBERS list.
598 # Skip methods w/ extended attributes in IGNORE_EXTENDED_ATTRIBUT ES list.
599 if (method.name and
600 # TODO(terry): Eventual eliminate the IGNORE_MEMBERS in favor of DartSupress.
601 not _suppress_method(interface.name, method.name) and
602 not _suppress_extended_attributes(method.extended_attributes) and
603 not 'DartSuppress' in method.extended_attributes)]
604 generate_overloads(methods)
605 for method in methods:
606 method['do_generate_method_configuration'] = (
607 method['do_not_check_signature'] and
608 not method['per_context_enabled_function'] and
609 # For overloaded methods, only generate one accessor
610 ('overload_index' not in method or method['overload_index'] == 1))
611
612 template_contents.update({
613 'has_origin_safe_method_setter': any(
614 method['is_check_security_for_frame'] and not method['is_read_only']
615 for method in methods),
616 'has_method_configuration': any(method['do_generate_method_configuration '] for method in methods),
617 'has_per_context_enabled_methods': any(method['per_context_enabled_funct ion'] for method in methods),
618 'methods': methods,
619 })
620
621 template_contents.update({
622 'indexed_property_getter': indexed_property_getter(interface),
623 'indexed_property_setter': indexed_property_setter(interface),
624 'indexed_property_deleter': indexed_property_deleter(interface),
625 'is_override_builtins': 'OverrideBuiltins' in extended_attributes,
626 'named_property_getter': named_property_getter(interface),
627 'named_property_setter': named_property_setter(interface),
628 'named_property_deleter': named_property_deleter(interface),
629 })
630
631 return template_contents
632
633
634 # [DeprecateAs], [Reflect], [RuntimeEnabled]
635 def generate_constant(constant):
636 # (Blink-only) string literals are unquoted in tokenizer, must be re-quoted
637 # in C++.
638 if constant.idl_type.name == 'String':
639 value = '"%s"' % constant.value
640 else:
641 value = constant.value
642
643 extended_attributes = constant.extended_attributes
644 return {
645 'cpp_class': extended_attributes.get('PartialInterfaceImplementedAs'),
646 'name': constant.name,
647 # FIXME: use 'reflected_name' as correct 'name'
648 'reflected_name': extended_attributes.get('Reflect', constant.name),
649 'runtime_enabled_function': DartUtilities.runtime_enabled_function_name( constant),
650 'value': value,
651 }
652
653
654 ################################################################################
655 # Overloads
656 ################################################################################
657
658 def generate_overloads(methods):
659 generate_overloads_by_type(methods, is_static=False) # Regular methods
660 generate_overloads_by_type(methods, is_static=True)
661
662
663 def generate_overloads_by_type(methods, is_static):
664 # Generates |overloads| template values and modifies |methods| in place;
665 # |is_static| flag used (instead of partitioning list in 2) because need to
666 # iterate over original list of methods to modify in place
667 method_counts = defaultdict(lambda: 0)
668 for method in methods:
669 if method['is_static'] != is_static:
670 continue
671 name = method['name']
672 # FIXME(vsm): We don't seem to capture optional param
673 # overloads here.
674 method_counts[name] += 1
675
676 # Filter to only methods that are actually overloaded
677 overloaded_method_counts = dict((name, count)
678 for name, count in method_counts.iteritems()
679 if count > 1)
680
681 # Add overload information only to overloaded methods, so template code can
682 # easily verify if a function is overloaded
683 method_overloads = defaultdict(list)
684 for method in methods:
685 name = method['name']
686 if (method['is_static'] != is_static or
687 name not in overloaded_method_counts):
688 continue
689 # Overload index includes self, so first append, then compute index
690 method_overloads[name].append(method)
691 method.update({
692 'overload_index': len(method_overloads[name]),
693 'overload_resolution_expression': overload_resolution_expression(met hod),
694 })
695 # FIXME(vsm): Looks like we only handle optional parameters if
696 # the method is already overloaded. For a non-overloaded method
697 # with optional parameters, we never get here.
698
699 # Resolution function is generated after last overloaded function;
700 # package necessary information into |method.overloads| for that method.
701 for method in methods:
702 if (method['is_static'] != is_static or
703 'overload_index' not in method):
704 continue
705 name = method['name']
706 if method['overload_index'] != overloaded_method_counts[name]:
707 continue
708 overloads = method_overloads[name]
709 minimum_number_of_required_arguments = min(
710 overload['number_of_required_arguments']
711 for overload in overloads)
712 method['overloads'] = {
713 'has_exception_state': bool(minimum_number_of_required_arguments),
714 'methods': overloads,
715 'minimum_number_of_required_arguments': minimum_number_of_required_a rguments,
716 'name': name,
717 }
718
719
720 def overload_resolution_expression(method):
721 # Expression is an OR of ANDs: each term in the OR corresponds to a
722 # possible argument count for a given method, with type checks.
723 # FIXME: Blink's overload resolution algorithm is incorrect, per:
724 # Implement WebIDL overload resolution algorithm.
725 # https://code.google.com/p/chromium/issues/detail?id=293561
726 #
727 # Currently if distinguishing non-primitive type from primitive type,
728 # (e.g., sequence<DOMString> from DOMString or Dictionary from double)
729 # the method with a non-primitive type argument must appear *first* in the
730 # IDL file, since we're not adding a check to primitive types.
731 # FIXME: Once fixed, check IDLs, as usually want methods with primitive
732 # types to appear first (style-wise).
733 #
734 # Properly:
735 # 1. Compute effective overload set.
736 # 2. First check type list length.
737 # 3. If multiple entries for given length, compute distinguishing argument
738 # index and have check for that type.
739 arguments = method['arguments']
740 overload_checks = [overload_check_expression(method, index)
741 # check *omitting* optional arguments at |index| and up:
742 # index 0 => argument_count 0 (no arguments)
743 # index 1 => argument_count 1 (index 0 argument only)
744 for index, argument in enumerate(arguments)
745 if argument['is_optional']]
746 # FIXME: this is wrong if a method has optional arguments and a variadic
747 # one, though there are not yet any examples of this
748 if not method['is_variadic']:
749 # Includes all optional arguments (len = last index + 1)
750 overload_checks.append(overload_check_expression(method, len(arguments)) )
751 return ' || '.join('(%s)' % check for check in overload_checks)
752
753
754 def overload_check_expression(method, argument_count):
755 overload_checks = ['info.Length() == %s' % argument_count]
756 arguments = method['arguments'][:argument_count]
757 overload_checks.extend(overload_check_argument(index, argument)
758 for index, argument in
759 enumerate(arguments))
760 return ' && '.join('(%s)' % check for check in overload_checks if check)
761
762
763 def overload_check_argument(index, argument):
764 def null_or_optional_check():
765 # If undefined is passed for an optional argument, the argument should
766 # be treated as missing; otherwise undefined is not allowed.
767
768 # FIXME(vsm): We need Dart specific checks here.
769 if idl_type.is_nullable:
770 if argument['is_optional']:
771 return 'isUndefinedOrNull(%s)'
772 return '%s->IsNull()'
773 if argument['is_optional']:
774 return '%s->IsUndefined()'
775 return None
776
777 cpp_value = 'info[%s]' % index
778 idl_type = argument['idl_type_object']
779 # FIXME(vsm): We need Dart specific checks for the rest of this method.
780 # FIXME: proper type checking, sharing code with attributes and methods
781 # FIXME(terry): StrictTypeChecking no longer supported; TypeChecking is
782 # new extended attribute.
783 if idl_type.name == 'String' and argument['is_strict_type_checking']:
784 return ' || '.join(['isUndefinedOrNull(%s)' % cpp_value,
785 '%s->IsString()' % cpp_value,
786 '%s->IsObject()' % cpp_value])
787 if idl_type.array_or_sequence_type:
788 return '%s->IsArray()' % cpp_value
789 if idl_type.is_callback_interface:
790 return ' || '.join(['%s->IsNull()' % cpp_value,
791 '%s->IsFunction()' % cpp_value])
792 if idl_type.is_wrapper_type:
793 type_check = 'V8{idl_type}::hasInstance({cpp_value}, info.GetIsolate())' .format(idl_type=idl_type.base_type, cpp_value=cpp_value)
794 if idl_type.is_nullable:
795 type_check = ' || '.join(['%s->IsNull()' % cpp_value, type_check])
796 return type_check
797 if idl_type.is_interface_type:
798 # Non-wrapper types are just objects: we don't distinguish type
799 # We only allow undefined for non-wrapper types (notably Dictionary),
800 # as we need it for optional Dictionary arguments, but we don't want to
801 # change behavior of existing bindings for other types.
802 type_check = '%s->IsObject()' % cpp_value
803 added_check_template = null_or_optional_check()
804 if added_check_template:
805 type_check = ' || '.join([added_check_template % cpp_value,
806 type_check])
807 return type_check
808 return None
809
810
811 ################################################################################
812 # Constructors
813 ################################################################################
814
815 # [Constructor]
816 def generate_custom_constructor(interface, constructor):
817 return {
818 'arguments': [custom_constructor_argument(argument, index)
819 for index, argument in enumerate(constructor.arguments)],
820 'auto_scope': 'true',
821 'is_auto_scope': True,
822 'number_of_arguments': len(constructor.arguments),
823 'number_of_required_arguments':
824 number_of_required_arguments(constructor),
825 }
826
827
828 # We don't need much from this - just the idl_type_objects and preproceed_type
829 # to use in generating the resolver strings.
830 def custom_constructor_argument(argument, index):
831 return {
832 'idl_type_object': argument.idl_type,
833 'preprocessed_type': argument.idl_type.preprocessed_type,
834 }
835
836
837 # [Constructor]
838 def generate_constructor(interface, constructor):
839 return {
840 'argument_list': constructor_argument_list(interface, constructor),
841 # TODO(terry): Use dart_methods.generate_argument instead constructor_ar gument.
842 'arguments': [constructor_argument(interface, argument, index)
843 for index, argument in enumerate(constructor.arguments)],
844 'has_exception_state':
845 # [RaisesException=Constructor]
846 interface.extended_attributes.get('RaisesException') == 'Constructor ' or
847 any(argument for argument in constructor.arguments
848 if argument.idl_type.name == 'SerializedScriptValue' or
849 argument.idl_type.is_integer_type),
850 'is_constructor': True,
851 'auto_scope': 'true',
852 'is_auto_scope': True,
853 'is_variadic': False, # Required for overload resolution
854 'number_of_required_arguments':
855 number_of_required_arguments(constructor),
856 'number_of_arguments': len(constructor.arguments),
857 }
858
859
860 def constructor_argument_list(interface, constructor):
861 # FIXME: unify with dart_methods.cpp_argument.
862
863 def cpp_argument(argument):
864 argument_name = dart_types.check_reserved_name(argument.name)
865 idl_type = argument.idl_type
866 if idl_type.is_typed_array_type:
867 return '%s.get()' % argument_name
868
869 return argument_name
870
871 arguments = []
872 # [ConstructorCallWith=ExecutionContext]
873 if DartUtilities.has_extended_attribute_value(interface, 'ConstructorCallWit h', 'ExecutionContext'):
874 arguments.append('context')
875 # [ConstructorCallWith=Document]
876 if DartUtilities.has_extended_attribute_value(interface, 'ConstructorCallWit h', 'Document'):
877 arguments.append('document')
878
879 arguments.extend([cpp_argument(argument) for argument in constructor.argumen ts])
880
881 # [RaisesException=Constructor]
882 if interface.extended_attributes.get('RaisesException') == 'Constructor':
883 arguments.append('es')
884
885 return arguments
886
887
888 # TODO(terry): Eliminate this function use dart_methods.generate_argument instea d
889 # for all constructor arguments.
890 def constructor_argument(interface, argument, index):
891 idl_type = argument.idl_type
892 default_value = str(argument.default_value) if argument.default_value else N one
893
894 argument_content = {
895 'cpp_type': idl_type.cpp_type_args(),
896 'local_cpp_type': idl_type.cpp_type_args(argument.extended_attributes, u sed_as_argument=True),
897 # FIXME: check that the default value's type is compatible with the argu ment's
898 'default_value': default_value,
899 # FIXME: remove once [Default] removed and just use argument.default_val ue
900 'has_default': 'Default' in argument.extended_attributes or default_valu e,
901 'idl_type_object': idl_type,
902 'preprocessed_type': idl_type.preprocessed_type,
903 # Dictionary is special-cased, but arrays and sequences shouldn't be
904 'idl_type': not idl_type.array_or_sequence_type and idl_type.base_type,
905 'index': index,
906 'is_array_or_sequence_type': not not idl_type.array_or_sequence_type,
907 'is_optional': argument.is_optional,
908 'is_strict_type_checking': False, # Required for overload resolution
909 'name': argument.name,
910 'dart_value_to_local_cpp_value': dart_methods.dart_value_to_local_cpp_va lue(interface, argument, index),
911 }
912 return argument_content
913
914
915 def generate_constructor_overloads(constructors):
916 if len(constructors) <= 1:
917 return
918 for overload_index, constructor in enumerate(constructors):
919 constructor.update({
920 'overload_index': overload_index + 1,
921 'overload_resolution_expression':
922 overload_resolution_expression(constructor),
923 })
924
925
926 # [NamedConstructor]
927 def generate_named_constructor(interface):
928 extended_attributes = interface.extended_attributes
929 if 'NamedConstructor' not in extended_attributes:
930 return None
931 # FIXME: parser should return named constructor separately;
932 # included in constructors (and only name stored in extended attribute)
933 # for Perl compatibility
934 idl_constructor = interface.constructors[0]
935 constructor = generate_constructor(interface, idl_constructor)
936 # FIXME(vsm): We drop the name. We don't use this in Dart APIs right now.
937 # We probably need to encode this somehow to deal with conflicts.
938 # constructor['name'] = extended_attributes['NamedConstructor']
939 return constructor
940
941
942 def number_of_required_arguments(constructor):
943 return len([argument for argument in constructor.arguments
944 if not (argument.is_optional and not (('Default' in argument.extended_at tributes) or argument.default_value))])
945
946
947 def interface_length(interface, constructors):
948 # Docs: http://heycam.github.io/webidl/#es-interface-call
949 if 'EventConstructor' in interface.extended_attributes:
950 return 1
951 if not constructors:
952 return 0
953 return min(constructor['number_of_required_arguments']
954 for constructor in constructors)
955
956
957 ################################################################################
958 # Special operations (methods)
959 # http://heycam.github.io/webidl/#idl-special-operations
960 ################################################################################
961
962 def property_getter(getter, cpp_arguments):
963 def is_null_expression(idl_type):
964 if idl_type.is_union_type:
965 return ' && '.join('!result%sEnabled' % i
966 for i, _ in enumerate(idl_type.member_types))
967 if idl_type.name == 'String':
968 # FIXME(vsm): This looks V8 specific.
969 return 'result.isNull()'
970 if idl_type.is_interface_type:
971 return '!result'
972 return ''
973
974 idl_type = getter.idl_type
975 extended_attributes = getter.extended_attributes
976 is_raises_exception = 'RaisesException' in extended_attributes
977
978 # FIXME: make more generic, so can use dart_methods.cpp_value
979 cpp_method_name = 'receiver->%s' % DartUtilities.cpp_name(getter)
980
981 if is_raises_exception:
982 cpp_arguments.append('es')
983 union_arguments = idl_type.union_arguments
984 if union_arguments:
985 cpp_arguments.extend(union_arguments)
986
987 cpp_value = '%s(%s)' % (cpp_method_name, ', '.join(cpp_arguments))
988
989 return {
990 'cpp_type': idl_type.cpp_type,
991 'cpp_value': cpp_value,
992 'is_custom':
993 'Custom' in extended_attributes and
994 (not extended_attributes['Custom'] or
995 DartUtilities.has_extended_attribute_value(getter, 'Custom', 'Prope rtyGetter')),
996 'is_custom_property_enumerator': DartUtilities.has_extended_attribute_va lue(
997 getter, 'Custom', 'PropertyEnumerator'),
998 'is_custom_property_query': DartUtilities.has_extended_attribute_value(
999 getter, 'Custom', 'PropertyQuery'),
1000 'is_enumerable': 'NotEnumerable' not in extended_attributes,
1001 'is_null_expression': is_null_expression(idl_type),
1002 'is_raises_exception': is_raises_exception,
1003 'name': DartUtilities.cpp_name(getter),
1004 'union_arguments': union_arguments,
1005 'dart_set_return_value': idl_type.dart_set_return_value('result',
1006 extended_attribu tes=extended_attributes,
1007 script_wrappable ='receiver',
1008 release=idl_type .release)}
1009
1010
1011 def property_setter(interface, setter):
1012 idl_type = setter.arguments[1].idl_type
1013 extended_attributes = setter.extended_attributes
1014 interface_extended_attributes = interface.extended_attributes
1015 is_raises_exception = 'RaisesException' in extended_attributes
1016 return {
1017 'has_strict_type_checking':
1018 'StrictTypeChecking' in extended_attributes and
1019 idl_type.is_wrapper_type,
1020 'idl_type': idl_type.base_type,
1021 'is_custom': 'Custom' in extended_attributes,
1022 'has_exception_state': is_raises_exception or
1023 idl_type.is_integer_type,
1024 'is_raises_exception': is_raises_exception,
1025 'name': DartUtilities.cpp_name(setter),
1026 'dart_value_to_local_cpp_value': idl_type.dart_value_to_local_cpp_value(
1027 interface_extended_attributes, extended_attributes, 'propertyValue', False),
1028 }
1029
1030
1031 def property_deleter(deleter):
1032 idl_type = deleter.idl_type
1033 if str(idl_type) != 'boolean':
1034 raise Exception(
1035 'Only deleters with boolean type are allowed, but type is "%s"' %
1036 idl_type)
1037 extended_attributes = deleter.extended_attributes
1038 return {
1039 'is_custom': 'Custom' in extended_attributes,
1040 'is_raises_exception': 'RaisesException' in extended_attributes,
1041 'name': DartUtilities.cpp_name(deleter),
1042 }
1043
1044
1045 ################################################################################
1046 # Indexed properties
1047 # http://heycam.github.io/webidl/#idl-indexed-properties
1048 ################################################################################
1049
1050 def indexed_property_getter(interface):
1051 try:
1052 # Find indexed property getter, if present; has form:
1053 # getter TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG1)
1054 getter = next(
1055 method
1056 for method in interface.operations
1057 if ('getter' in method.specials and
1058 len(method.arguments) == 1 and
1059 str(method.arguments[0].idl_type) == 'unsigned long'))
1060 except StopIteration:
1061 return None
1062
1063 getter.name = getter.name or 'anonymousIndexedGetter'
1064
1065 return property_getter(getter, ['index'])
1066
1067
1068 def indexed_property_setter(interface):
1069 try:
1070 # Find indexed property setter, if present; has form:
1071 # setter RETURN_TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG1, ARG_TYPE ARG2)
1072 setter = next(
1073 method
1074 for method in interface.operations
1075 if ('setter' in method.specials and
1076 len(method.arguments) == 2 and
1077 str(method.arguments[0].idl_type) == 'unsigned long'))
1078 except StopIteration:
1079 return None
1080
1081 return property_setter(interface, setter)
1082
1083
1084 def indexed_property_deleter(interface):
1085 try:
1086 # Find indexed property deleter, if present; has form:
1087 # deleter TYPE [OPTIONAL_IDENTIFIER](unsigned long ARG)
1088 deleter = next(
1089 method
1090 for method in interface.operations
1091 if ('deleter' in method.specials and
1092 len(method.arguments) == 1 and
1093 str(method.arguments[0].idl_type) == 'unsigned long'))
1094 except StopIteration:
1095 return None
1096
1097 return property_deleter(deleter)
1098
1099
1100 ################################################################################
1101 # Named properties
1102 # http://heycam.github.io/webidl/#idl-named-properties
1103 ################################################################################
1104
1105 def named_property_getter(interface):
1106 try:
1107 # Find named property getter, if present; has form:
1108 # getter TYPE [OPTIONAL_IDENTIFIER](DOMString ARG1)
1109 getter = next(
1110 method
1111 for method in interface.operations
1112 if ('getter' in method.specials and
1113 len(method.arguments) == 1 and
1114 str(method.arguments[0].idl_type) == 'DOMString'))
1115 except StopIteration:
1116 return None
1117
1118 getter.name = getter.name or 'anonymousNamedGetter'
1119 return property_getter(getter, ['propertyName'])
1120
1121
1122 def named_property_setter(interface):
1123 try:
1124 # Find named property setter, if present; has form:
1125 # setter RETURN_TYPE [OPTIONAL_IDENTIFIER](DOMString ARG1, ARG_TYPE ARG2 )
1126 setter = next(
1127 method
1128 for method in interface.operations
1129 if ('setter' in method.specials and
1130 len(method.arguments) == 2 and
1131 str(method.arguments[0].idl_type) == 'DOMString'))
1132 except StopIteration:
1133 return None
1134
1135 return property_setter(interface, setter)
1136
1137
1138 def named_property_deleter(interface):
1139 try:
1140 # Find named property deleter, if present; has form:
1141 # deleter TYPE [OPTIONAL_IDENTIFIER](DOMString ARG)
1142 deleter = next(
1143 method
1144 for method in interface.operations
1145 if ('deleter' in method.specials and
1146 len(method.arguments) == 1 and
1147 str(method.arguments[0].idl_type) == 'DOMString'))
1148 except StopIteration:
1149 return None
1150
1151 return property_deleter(deleter)
OLDNEW
« no previous file with comments | « bindings/dart/scripts/dart_compiler.py ('k') | bindings/dart/scripts/dart_methods.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698