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

Side by Side Diff: lib/dom/scripts/systemhtml.py

Issue 10913125: Remove lib/dom directory! (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 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
OLDNEW
(Empty)
1 #!/usr/bin/python
2 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
3 # for details. All rights reserved. Use of this source code is governed by a
4 # BSD-style license that can be found in the LICENSE file.
5
6 """This module provides shared functionality for the system to generate
7 Dart:html APIs from the IDL database."""
8
9 import emitter
10
11 from systemdart2js import *
12 from systeminterface import *
13
14 _js_custom_members = set([
15 'Element.insertAdjacentElement',
16 'Element.insertAdjacentHTML',
17 'Element.insertAdjacentText',
18 'IDBDatabase.transaction',
19 'IFrameElement.contentWindow',
20 'MouseEvent.offsetX',
21 'MouseEvent.offsetY',
22 'Window.document',
23 'Window.top',
24 'Window.location',
25 'Window.open',
26 ])
27
28 # This map controls merging of interfaces in dart:html library.
29 # All constants, attributes, and operations of merged interface (key) are
30 # added to target interface (value). All references to the merged interface
31 # (e.g. parameter types, return types, parent interfaces) are replaced with
32 # target interface. There are two important restrictions:
33 # 1) Merged and target interfaces shouldn't have common members, otherwise there
34 # would be duplicated declarations in generated Dart code.
35 # 2) Merged interface should be direct child of target interface, so the
36 # children of merged interface are not affected by the merge.
37 # As a consequence, target interface implementation and its direct children
38 # interface implementations should implement merged attribute accessors and
39 # operations. For example, SVGElement and Element implementation classes should
40 # implement HTMLElement.insertAdjacentElement(), HTMLElement.innerHTML, etc.
41 _merged_html_interfaces = {
42 'HTMLDocument': 'Document',
43 'HTMLElement': 'Element'
44 }
45
46 # Events without onEventName attributes in the IDL we want to support.
47 # We can automatically extract most event event names by checking for
48 # onEventName methods in the IDL but some events aren't listed so we need
49 # to manually add them here so that they are easy for users to find.
50 _html_manual_events = {
51 'Element': ['touchleave', 'touchenter', 'webkitTransitionEnd'],
52 'Window': ['DOMContentLoaded']
53 }
54
55 # These event names must be camel case when attaching event listeners
56 # using addEventListener even though the onEventName properties in the DOM for
57 # them are not camel case.
58 _on_attribute_to_event_name_mapping = {
59 'webkitanimationend': 'webkitAnimationEnd',
60 'webkitanimationiteration': 'webkitAnimationIteration',
61 'webkitanimationstart': 'webkitAnimationStart',
62 'webkitspeechchange': 'webkitSpeechChange',
63 'webkittransitionend': 'webkitTransitionEnd',
64 }
65
66 # Mapping from raw event names to the pretty camelCase event names exposed as
67 # properties in dart:html. If the DOM exposes a new event name, you will need
68 # to add the lower case to camel case conversion for that event name here.
69 _html_event_names = {
70 'DOMContentLoaded': 'contentLoaded',
71 'abort': 'abort',
72 'addstream': 'addStream',
73 'addtrack': 'addTrack',
74 'audioend': 'audioEnd',
75 'audioprocess': 'audioProcess',
76 'audiostart': 'audioStart',
77 'beforecopy': 'beforeCopy',
78 'beforecut': 'beforeCut',
79 'beforepaste': 'beforePaste',
80 'beforeunload': 'beforeUnload',
81 'blocked': 'blocked',
82 'blur': 'blur',
83 'cached': 'cached',
84 'canplay': 'canPlay',
85 'canplaythrough': 'canPlayThrough',
86 'change': 'change',
87 'chargingchange': 'chargingChange',
88 'chargingtimechange': 'chargingTimeChange',
89 'checking': 'checking',
90 'click': 'click',
91 'close': 'close',
92 'complete': 'complete',
93 'connect': 'connect',
94 'connecting': 'connecting',
95 'contextmenu': 'contextMenu',
96 'copy': 'copy',
97 'cuechange': 'cueChange',
98 'cut': 'cut',
99 'dblclick': 'doubleClick',
100 'devicemotion': 'deviceMotion',
101 'deviceorientation': 'deviceOrientation',
102 'dischargingtimechange': 'dischargingTimeChange',
103 'display': 'display',
104 'downloading': 'downloading',
105 'drag': 'drag',
106 'dragend': 'dragEnd',
107 'dragenter': 'dragEnter',
108 'dragleave': 'dragLeave',
109 'dragover': 'dragOver',
110 'dragstart': 'dragStart',
111 'drop': 'drop',
112 'durationchange': 'durationChange',
113 'emptied': 'emptied',
114 'end': 'end',
115 'ended': 'ended',
116 'enter': 'enter',
117 'error': 'error',
118 'exit': 'exit',
119 'focus': 'focus',
120 'hashchange': 'hashChange',
121 'icecandidate': 'iceCandidate',
122 'icechange': 'iceChange',
123 'input': 'input',
124 'invalid': 'invalid',
125 'keydown': 'keyDown',
126 'keypress': 'keyPress',
127 'keyup': 'keyUp',
128 'levelchange': 'levelChange',
129 'load': 'load',
130 'loadeddata': 'loadedData',
131 'loadedmetadata': 'loadedMetadata',
132 'loadend': 'loadEnd',
133 'loadstart': 'loadStart',
134 'message': 'message',
135 'mousedown': 'mouseDown',
136 'mousemove': 'mouseMove',
137 'mouseout': 'mouseOut',
138 'mouseover': 'mouseOver',
139 'mouseup': 'mouseUp',
140 'mousewheel': 'mouseWheel',
141 'mute': 'mute',
142 'nomatch': 'noMatch',
143 'noupdate': 'noUpdate',
144 'obsolete': 'obsolete',
145 'offline': 'offline',
146 'online': 'online',
147 'open': 'open',
148 'pagehide': 'pageHide',
149 'pageshow': 'pageShow',
150 'paste': 'paste',
151 'pause': 'pause',
152 'play': 'play',
153 'playing': 'playing',
154 'popstate': 'popState',
155 'progress': 'progress',
156 'ratechange': 'rateChange',
157 'readystatechange': 'readyStateChange',
158 'removestream': 'removeStream',
159 'removetrack': 'removeTrack',
160 'reset': 'reset',
161 'resize': 'resize',
162 'result': 'result',
163 'resultdeleted': 'resultDeleted',
164 'scroll': 'scroll',
165 'search': 'search',
166 'seeked': 'seeked',
167 'seeking': 'seeking',
168 'select': 'select',
169 'selectionchange': 'selectionChange',
170 'selectstart': 'selectStart',
171 'show': 'show',
172 'soundend': 'soundEnd',
173 'soundstart': 'soundStart',
174 'speechend': 'speechEnd',
175 'speechstart': 'speechStart',
176 'stalled': 'stalled',
177 'start': 'start',
178 'statechange': 'stateChange',
179 'storage': 'storage',
180 'submit': 'submit',
181 'success': 'success',
182 'suspend': 'suspend',
183 'timeupdate': 'timeUpdate',
184 'touchcancel': 'touchCancel',
185 'touchend': 'touchEnd',
186 'touchenter': 'touchEnter',
187 'touchleave': 'touchLeave',
188 'touchmove': 'touchMove',
189 'touchstart': 'touchStart',
190 'unload': 'unload',
191 'upgradeneeded': 'upgradeNeeded',
192 'unmute': 'unmute',
193 'updateready': 'updateReady',
194 'versionchange': 'versionChange',
195 'volumechange': 'volumeChange',
196 'waiting': 'waiting',
197 'webkitAnimationEnd': 'animationEnd',
198 'webkitAnimationIteration': 'animationIteration',
199 'webkitAnimationStart': 'animationStart',
200 'webkitfullscreenchange': 'fullscreenChange',
201 'webkitfullscreenerror': 'fullscreenError',
202 'webkitkeyadded': 'keyAdded',
203 'webkitkeyerror': 'keyError',
204 'webkitkeymessage': 'keyMessage',
205 'webkitneedkey': 'needKey',
206 'webkitpointerlockchange': 'pointerLockChange',
207 'webkitpointerlockerror': 'pointerLockError',
208 'webkitSpeechChange': 'speechChange',
209 'webkitsourceclose': 'sourceClose',
210 'webkitsourceended': 'sourceEnded',
211 'webkitsourceopen': 'sourceOpen',
212 'webkitTransitionEnd': 'transitionEnd',
213 'write': 'write',
214 'writeend': 'writeEnd',
215 'writestart': 'writeStart'
216 }
217
218
219
220 # Information for generating element constructors.
221 #
222 # TODO(sra): maybe remove all the argument complexity and use cascades.
223 #
224 # var c = new CanvasElement(width: 100, height: 70);
225 # var c = new CanvasElement()..width = 100..height = 70;
226 #
227 class ElementConstructorInfo(object):
228 def __init__(self, name=None, tag=None,
229 params=[], opt_params=[],
230 factory_provider_name='_Elements'):
231 self.name = name # The constructor name 'h1' in 'HeadingElement.h1'
232 self.tag = tag or name # The HTML tag
233 self.params = params
234 self.opt_params = opt_params
235 self.factory_provider_name = factory_provider_name
236
237 def ConstructorInfo(self, interface_name):
238 info = OperationInfo()
239 info.overloads = None
240 info.declared_name = interface_name
241 info.name = interface_name
242 info.constructor_name = self.name
243 info.js_name = None
244 info.type_name = interface_name
245 info.param_infos = map(lambda tXn: ParamInfo(tXn[1], None, tXn[0], 'null'),
246 self.opt_params)
247 return info
248
249 _html_element_constructors = {
250 'AnchorElement' :
251 ElementConstructorInfo(tag='a', opt_params=[('String', 'href')]),
252 'AreaElement': 'area',
253 'ButtonElement': 'button',
254 'BRElement': 'br',
255 'BaseElement': 'base',
256 'BodyElement': 'body',
257 'ButtonElement': 'button',
258 'CanvasElement':
259 ElementConstructorInfo(tag='canvas',
260 opt_params=[('int', 'width'), ('int', 'height')]),
261 'DataListElement': 'datalist',
262 'DListElement': 'dl',
263 'DetailsElement': 'details',
264 'DivElement': 'div',
265 'EmbedElement': 'embed',
266 'FieldSetElement': 'fieldset',
267 'FormElement': 'form',
268 'HRElement': 'hr',
269 'HeadElement': 'head',
270 'HeadingElement': [ElementConstructorInfo('h1'),
271 ElementConstructorInfo('h2'),
272 ElementConstructorInfo('h3'),
273 ElementConstructorInfo('h4'),
274 ElementConstructorInfo('h5'),
275 ElementConstructorInfo('h6')],
276 'HtmlElement': 'html',
277 'IFrameElement': 'iframe',
278 'ImageElement':
279 ElementConstructorInfo(tag='img',
280 opt_params=[('String', 'src'),
281 ('int', 'width'), ('int', 'height')]),
282 'InputElement':
283 ElementConstructorInfo(tag='input', opt_params=[('String', 'type')]),
284 'KeygenElement': 'keygen',
285 'LIElement': 'li',
286 'LabelElement': 'label',
287 'LegendElement': 'legend',
288 'LinkElement': 'link',
289 'MapElement': 'map',
290 'MenuElement': 'menu',
291 'MeterElement': 'meter',
292 'OListElement': 'ol',
293 'ObjectElement': 'object',
294 'OptGroupElement': 'optgroup',
295 'OutputElement': 'output',
296 'ParagraphElement': 'p',
297 'ParamElement': 'param',
298 'PreElement': 'pre',
299 'ProgressElement': 'progress',
300 'ScriptElement': 'script',
301 'SelectElement': 'select',
302 'SourceElement': 'source',
303 'SpanElement': 'span',
304 'StyleElement': 'style',
305 'TableCaptionElement': 'caption',
306 'TableCellElement': 'td',
307 'TableColElement': 'col',
308 'TableElement': 'table',
309 'TableRowElement': 'tr',
310 #'TableSectionElement' <thead> <tbody> <tfoot>
311 'TextAreaElement': 'textarea',
312 'TitleElement': 'title',
313 'TrackElement': 'track',
314 'UListElement': 'ul',
315 'VideoElement': 'video'
316 }
317
318 def HtmlElementConstructorInfos(typename):
319 """Returns list of ElementConstructorInfos about the convenience constructors
320 for an Element."""
321 # TODO(sra): Handle multiple and named constructors.
322 if typename not in _html_element_constructors:
323 return []
324 infos = _html_element_constructors[typename]
325 if isinstance(infos, str):
326 infos = ElementConstructorInfo(tag=infos)
327 if not isinstance(infos, list):
328 infos = [infos]
329 return infos
330
331 def EmitHtmlElementFactoryConstructors(emitter, infos, typename, class_name):
332 for info in infos:
333 constructor_info = info.ConstructorInfo(typename)
334 inits = emitter.Emit(
335 '\n'
336 ' factory $CONSTRUCTOR($PARAMS) {\n'
337 ' $CLASS _e = _document.$dom_createElement("$TAG");\n'
338 '$!INITS'
339 ' return _e;\n'
340 ' }\n',
341 CONSTRUCTOR=constructor_info.ConstructorFullName(),
342 CLASS=class_name,
343 TAG=info.tag,
344 PARAMS=constructor_info.ParametersInterfaceDeclaration(DartType))
345 for param in constructor_info.param_infos:
346 inits.Emit(' if ($E != null) _e.$E = $E;\n', E=param.name)
347
348
349 # These classes require an explicit declaration for the "on" method even though
350 # they don't declare any unique events, because the concrete class hierarchy
351 # doesn't match the interface hierarchy.
352 _html_explicit_event_classes = set(['DocumentFragment'])
353
354 def _OnAttributeToEventName(on_method):
355 event_name = on_method.id[2:]
356 if event_name in _on_attribute_to_event_name_mapping:
357 return _on_attribute_to_event_name_mapping[event_name]
358 else:
359 return event_name
360
361 def DomToHtmlEvents(interface_id, events):
362 event_names = set(map(_OnAttributeToEventName, events))
363 if interface_id in _html_manual_events:
364 for manual_event_name in _html_manual_events[interface_id]:
365 event_names.add(manual_event_name)
366
367 return sorted(event_names, key=lambda name: _html_event_names[name])
368
369 def DomToHtmlEvent(event_name):
370 assert event_name in _html_event_names, \
371 'No known html event name for event: ' + event_name
372 return _html_event_names[event_name]
373
374 # ------------------------------------------------------------------------------
375 class HtmlSystemShared(object):
376
377 def __init__(self, context):
378 self._event_classes = set()
379 self._seen_event_names = {}
380 self._database = context.database
381
382 # TODO(jacobr): this already exists
383 def _TraverseParents(self, interface, callback):
384 for parent in interface.parents:
385 parent_id = parent.type.id
386 if self._database.HasInterface(parent_id):
387 parent_interface = self._database.GetInterface(parent_id)
388 callback(parent_interface)
389 self._TraverseParents(parent_interface, callback)
390
391 # TODO(jacobr): this isn't quite right....
392 def GetParentsEventsClasses(self, interface):
393 # Ugly hack as we don't specify that Document and DocumentFragment inherit
394 # from Element in our IDL.
395 if interface.id == 'Document' or interface.id == 'DocumentFragment':
396 return ['ElementEvents']
397
398 interfaces_with_events = set()
399 def visit(parent):
400 if parent.id in self._event_classes:
401 interfaces_with_events.add(parent)
402
403 self._TraverseParents(interface, visit)
404 if len(interfaces_with_events) == 0:
405 return ['Events']
406 else:
407 names = []
408 for interface in interfaces_with_events:
409 names.append(interface.id + 'Events')
410 return names
411
412 def GetParentEventsClass(self, interface):
413 parent_event_classes = self.GetParentsEventsClasses(interface)
414 if len(parent_event_classes) != 1:
415 raise Exception('Only one parent event class allowed ' + interface.id)
416 return parent_event_classes[0]
417
418 # This returns two values: the first is whether or not an "on" property should
419 # be generated for the interface, and the second is the event attributes to
420 # generate if it should.
421 def GetEventAttributes(self, interface):
422 events = set([attr for attr in interface.attributes
423 if attr.type.id == 'EventListener'])
424
425 if events or interface.id in _html_explicit_event_classes:
426 return True, events
427 else:
428 return False, None
429
430 def IsPrivate(self, name):
431 return name.startswith('_')
432
433
434 class HtmlInterfacesSystem(System):
435 def __init__(self, options, backend):
436 super(HtmlInterfacesSystem, self).__init__(options)
437 self._backend = backend
438 self._shared = HtmlSystemShared(options)
439 self._dart_interface_file_paths = []
440 self._elements_factory_emitter = None
441
442 def ProcessInterface(self, interface):
443 HtmlDartInterfaceGenerator(self, interface).Generate()
444
445 def ProcessCallback(self, interface, info):
446 """Generates a typedef for the callback interface."""
447 interface_name = interface.id
448 file_path = self._FilePathForDartInterface(interface_name)
449 self._ProcessCallback(interface, info, file_path)
450 self._backend.ProcessCallback(interface, info)
451
452 def GenerateLibraries(self):
453 self._backend.GenerateLibraries(self._dart_interface_file_paths)
454
455 def _FilePathForDartInterface(self, interface_name):
456 """Returns the file path of the Dart interface definition."""
457 # TODO(jmesserly): is this the right path
458 return os.path.join(self._output_dir, 'html', 'interface',
459 '%s.dart' % interface_name)
460
461 # ------------------------------------------------------------------------------
462
463 class HtmlDartInterfaceGenerator(BaseGenerator):
464 """Generates dart interface and implementation for the DOM IDL interface."""
465
466 def __init__(self, system, interface):
467 super(HtmlDartInterfaceGenerator, self).__init__(
468 system._database, interface)
469 self._system = system
470 self._shared = system._shared
471 self._html_interface_name = system._renamer.RenameInterface(self._interface)
472 self._backend = system._backend.ImplementationGenerator(self._interface)
473
474 def StartInterface(self):
475 if not self._interface.id in _merged_html_interfaces:
476 path = self._system._FilePathForDartInterface(self._html_interface_name)
477 self._system._dart_interface_file_paths.append(path)
478 self._interface_emitter = self._system._emitters.FileEmitter(path)
479 else:
480 self._interface_emitter = emitter.Emitter()
481
482 template_file = 'interface_%s.darttemplate' % self._html_interface_name
483 interface_template = (self._system._templates.TryLoad(template_file) or
484 self._system._templates.Load('interface.darttemplate') )
485
486 typename = self._html_interface_name
487
488 extends = []
489 suppressed_extends = []
490
491 for parent in self._interface.parents:
492 # TODO(vsm): Remove source_filter.
493 if MatchSourceFilter(parent):
494 # Parent is a DOM type.
495 extends.append(self._DartType(parent.type.id))
496 elif '<' in parent.type.id:
497 # Parent is a Dart collection type.
498 # TODO(vsm): Make this check more robust.
499 extends.append(self._DartType(parent.type.id))
500 else:
501 suppressed_extends.append('%s.%s' %
502 (self._common_prefix, self._DartType(parent.type.id)))
503
504 comment = ' extends'
505 extends_str = ''
506 if extends:
507 extends_str += ' extends ' + ', '.join(extends)
508 comment = ','
509 if suppressed_extends:
510 extends_str += ' /*%s %s */' % (comment, ', '.join(suppressed_extends))
511
512 factory_provider = None
513 if typename in interface_factories:
514 factory_provider = interface_factories[typename]
515
516 constructors = []
517 constructor_info = AnalyzeConstructor(self._interface)
518 if constructor_info:
519 constructors.append(constructor_info)
520 factory_provider = '_' + typename + 'FactoryProvider'
521 path = self._backend.FilePathForDartFactoryProviderImplementation()
522 self._system._dart_interface_file_paths.append(path)
523 factory_provider_emitter = self._system._emitters.FileEmitter(path)
524 self._backend.EmitFactoryProvider(
525 constructor_info, factory_provider, factory_provider_emitter)
526
527 infos = HtmlElementConstructorInfos(typename)
528 if infos:
529 if not self._system._elements_factory_emitter:
530 path = self._backend.FilePathForDartElementsFactoryProviderImplementatio n()
531 self._system._dart_interface_file_paths.append(path)
532 file_emitter = self._system._emitters.FileEmitter(path)
533 template = self._system._templates.Load(
534 'factoryprovider_Elements.darttemplate')
535 self._system._elements_factory_emitter = file_emitter.Emit(template)
536 EmitHtmlElementFactoryConstructors(
537 self._system._elements_factory_emitter,
538 infos,
539 self._html_interface_name,
540 self._backend.ImplementationClassName())
541
542 for info in infos:
543 constructors.append(info.ConstructorInfo(typename))
544 if factory_provider:
545 assert factory_provider == info.factory_provider_name
546 else:
547 factory_provider = info.factory_provider_name
548
549 if factory_provider:
550 extends_str += ' default ' + factory_provider
551
552 # TODO(vsm): Add appropriate package / namespace syntax.
553 (self._type_comment_emitter,
554 self._members_emitter,
555 self._top_level_emitter) = self._interface_emitter.Emit(
556 interface_template + '$!TOP_LEVEL',
557 ID=typename,
558 EXTENDS=extends_str)
559
560 self._type_comment_emitter.Emit("/// @domName $DOMNAME",
561 DOMNAME=self._interface.doc_js_name)
562
563 if self._backend.HasImplementation():
564 path = self._backend.FilePathForDartImplementation()
565 self._system._dart_interface_file_paths.append(path)
566 self._implementation_emitter = self._system._emitters.FileEmitter(path)
567 else:
568 self._implementation_emitter = emitter.Emitter()
569 self._backend.SetImplementationEmitter(self._implementation_emitter)
570 self._implementation_members_emitter = self._backend.StartInterface()
571
572 for constructor_info in constructors:
573 self._members_emitter.Emit(
574 '\n'
575 ' $CTOR($PARAMS);\n',
576 CTOR=self._DartType(constructor_info.ConstructorFullName()),
577 PARAMS=constructor_info.ParametersInterfaceDeclaration(self._DartType) )
578
579 element_type = MaybeTypedArrayElementTypeInHierarchy(
580 self._interface, self._system._database)
581 if element_type:
582 self._members_emitter.Emit(
583 '\n'
584 ' $CTOR(int length);\n'
585 '\n'
586 ' $CTOR.fromList(List<$TYPE> list);\n'
587 '\n'
588 ' $CTOR.fromBuffer(ArrayBuffer buffer,'
589 ' [int byteOffset, int length]);\n',
590 CTOR=self._interface.id,
591 TYPE=self._DartType(element_type))
592
593 self._GenerateEvents()
594
595 old_backend = self._backend
596 if not self._backend.ImplementsMergedMembers():
597 self._backend = HtmlGeneratorDummyBackend()
598 for merged_interface in _merged_html_interfaces:
599 if _merged_html_interfaces[merged_interface] == self._interface.id:
600 merged_interface = self._database.GetInterface(merged_interface)
601 self.AddMembers(merged_interface)
602 self._backend = old_backend
603
604 def AddIndexer(self, element_type):
605 self._backend.AddIndexer(element_type)
606
607 def AmendIndexer(self, element_type):
608 self._backend.AmendIndexer(element_type)
609
610 def AddAttribute(self, attribute, is_secondary=False):
611 dom_name = DartDomNameOfAttribute(attribute)
612 html_name = self._system._renamer.RenameMember(
613 self._interface.id, dom_name, 'get:')
614 if not html_name or self._shared.IsPrivate(html_name):
615 return
616
617
618 html_setter_name = self._system._renamer.RenameMember(
619 self._interface.id, dom_name, 'set:')
620 read_only = IsReadOnly(attribute) or not html_setter_name
621
622 # We don't yet handle inconsistent renames of the getter and setter yet.
623 assert(not html_setter_name or html_name == html_setter_name)
624
625 if not is_secondary:
626 self._members_emitter.Emit('\n /** @domName $DOMINTERFACE.$DOMNAME */',
627 DOMINTERFACE=attribute.doc_js_interface_name,
628 DOMNAME=dom_name)
629 modifier = 'final ' if read_only else ''
630 self._members_emitter.Emit('\n $MODIFIER$TYPE $NAME;\n',
631 MODIFIER=modifier,
632 NAME=html_name,
633 TYPE=self._DartType(attribute.type.id))
634 self._backend.AddAttribute(attribute, html_name, read_only)
635
636 def AddSecondaryAttribute(self, interface, attribute):
637 self._backend.SecondaryContext(interface)
638 self.AddAttribute(attribute, True)
639
640 def AddOperation(self, info, skip_declaration=False):
641 """
642 Arguments:
643 operations - contains the overloads, one or more operations with the same
644 name.
645 """
646 html_name = self._system._renamer.RenameMember(self._interface.id, info.name )
647 if not html_name:
648 if info.name == 'item':
649 # FIXME: item should be renamed to operator[], not removed.
650 self._backend.AddOperation(info, '_item')
651 return
652
653 if not self._shared.IsPrivate(html_name) and not skip_declaration:
654 self._members_emitter.Emit('\n /** @domName $DOMINTERFACE.$DOMNAME */',
655 DOMINTERFACE=info.overloads[0].doc_js_interface_name,
656 DOMNAME=info.name)
657
658 self._members_emitter.Emit('\n'
659 ' $TYPE $NAME($PARAMS);\n',
660 TYPE=self._DartType(info.type_name),
661 NAME=html_name,
662 PARAMS=info.ParametersInterfaceDeclaration(self ._DartType))
663 self._backend.AddOperation(info, html_name)
664
665 def AddStaticOperation(self, info):
666 self.AddOperation(info, True)
667
668 def AddSecondaryOperation(self, interface, info):
669 self._backend.SecondaryContext(interface)
670 self.AddOperation(info, True)
671
672 def FinishInterface(self):
673 self._backend.FinishInterface()
674
675 def AddConstant(self, constant):
676 type = TypeOrNothing(self._DartType(constant.type.id), constant.type.id)
677 self._members_emitter.Emit('\n static const $TYPE$NAME = $VALUE;\n',
678 NAME=constant.id,
679 TYPE=type,
680 VALUE=constant.value)
681 self._backend.AddConstant(constant)
682
683 def _GenerateEvents(self):
684 emit_events, event_attrs = self._shared.GetEventAttributes(self._interface)
685 if not emit_events:
686 return
687
688 self._shared._event_classes.add(self._interface.id)
689 events_interface = self._html_interface_name + 'Events'
690 events_class = '_%sImpl' % events_interface
691 parent_events_interface = self._shared.GetParentEventsClass(self._interface)
692 parent_events_class = '_%sImpl' % parent_events_interface
693
694 if not event_attrs:
695 self._EmitEventGetter(parent_events_interface, parent_events_class)
696 return
697
698 self._EmitEventGetter(events_interface, events_class)
699
700 events_members = self._interface_emitter.Emit(
701 '\ninterface $INTERFACE extends $PARENTS {\n$!MEMBERS}\n',
702 INTERFACE=events_interface,
703 PARENTS=', '.join(
704 self._shared.GetParentsEventsClasses(self._interface)))
705
706 # TODO(jacobr): specify the type of _ptr as EventTarget
707 implementation_events_members = self._implementation_emitter.Emit(
708 '\n'
709 'class $CLASSNAME extends $SUPER implements $INTERFACE {\n'
710 ' $CLASSNAME(_ptr) : super(_ptr);\n'
711 '$!MEMBERS}\n',
712 CLASSNAME=events_class,
713 INTERFACE=events_interface,
714 SUPER=parent_events_class)
715
716 event_attrs = DomToHtmlEvents(self._html_interface_name, event_attrs)
717 for event_name in event_attrs:
718 if event_name in _html_event_names:
719 events_members.Emit('\n EventListenerList get $NAME();\n',
720 NAME=_html_event_names[event_name])
721 implementation_events_members.Emit(
722 "\n"
723 " EventListenerList get $NAME() => this['$DOM_NAME'];\n",
724 NAME=_html_event_names[event_name],
725 DOM_NAME=event_name)
726 else:
727 raise Exception('No known html even name for event: ' + event_name)
728
729 def _EmitEventGetter(self, events_interface, events_class):
730 self._members_emitter.Emit(
731 '\n /**'
732 '\n * @domName EventTarget.addEventListener, '
733 'EventTarget.removeEventListener, EventTarget.dispatchEvent'
734 '\n */'
735 '\n $TYPE get on();\n',
736 TYPE=events_interface)
737
738 self._implementation_members_emitter.Emit(
739 '\n $TYPE get on() =>\n new $TYPE(this);\n',
740 TYPE=events_class)
741
742
743 class HtmlGeneratorDummyBackend(object):
744 def AddAttribute(self, attribute, html_name, read_only):
745 pass
746
747 def AddOperation(self, info, html_name):
748 pass
749
750
751 # ------------------------------------------------------------------------------
752
753 # TODO(jmesserly): inheritance is probably not the right way to factor this long
754 # term, but it makes merging better for now.
755 class HtmlDart2JSClassGenerator(Dart2JSInterfaceGenerator):
756 """Generates a dart2js class for the dart:html library from a DOM IDL
757 interface.
758 """
759
760 def __init__(self, system, interface):
761 super(HtmlDart2JSClassGenerator, self).__init__(
762 system, interface, None, None)
763 self._html_interface_name = system._renamer.RenameInterface(self._interface)
764
765 def HasImplementation(self):
766 return not (IsPureInterface(self._interface.id) or
767 self._interface.id in _merged_html_interfaces)
768
769 def ImplementationClassName(self):
770 return self._ImplClassName(self._html_interface_name)
771
772 def FilePathForDartImplementation(self):
773 return os.path.join(self._system._output_dir, 'html', 'dart2js',
774 '%s.dart' % self._html_interface_name)
775
776 def FilePathForDartFactoryProviderImplementation(self):
777 return os.path.join(self._system._output_dir, 'html', 'dart2js',
778 '_%sFactoryProvider.dart' % self._html_interface_name)
779
780 def FilePathForDartElementsFactoryProviderImplementation(self):
781 return os.path.join(self._system._output_dir, 'html', 'dart2js',
782 '_Elements.dart')
783
784 def SetImplementationEmitter(self, implementation_emitter):
785 self._dart_code = implementation_emitter
786
787 def ImplementsMergedMembers(self):
788 return True
789
790 def _ImplClassName(self, type_name):
791 return '_%sImpl' % type_name
792
793 def StartInterface(self):
794 interface = self._interface
795 interface_name = interface.id
796
797 self._class_name = self._ImplClassName(self._html_interface_name)
798
799 base = None
800 if interface.parents:
801 supertype = interface.parents[0].type.id
802 if IsDartCollectionType(supertype):
803 # List methods are injected in AddIndexer.
804 pass
805 elif IsPureInterface(supertype):
806 pass
807 else:
808 base = self._ImplClassName(self._DartType(supertype))
809
810 native_spec = MakeNativeSpec(interface.javascript_binding_name)
811
812 extends = ' extends ' + base if base else ''
813
814 # TODO: Include all implemented interfaces, including other Lists.
815 implements = [self._html_interface_name]
816 element_type = MaybeTypedArrayElementType(self._interface)
817 if element_type:
818 implements.append('List<%s>' % self._DartType(element_type))
819
820 if self._HasJavaScriptIndexingBehaviour():
821 implements.append('JavaScriptIndexingBehavior')
822
823 template_file = 'impl_%s.darttemplate' % self._html_interface_name
824 template = (self._system._templates.TryLoad(template_file) or
825 self._system._templates.Load('dart2js_impl.darttemplate'))
826 self._members_emitter = self._dart_code.Emit(
827 template,
828 #class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
829 #$!MEMBERS
830 #}
831 CLASSNAME=self._class_name,
832 EXTENDS=extends,
833 IMPLEMENTS=' implements ' + ', '.join(implements),
834 NATIVESPEC=' native "' + native_spec + '"')
835 if self._members_emitter == None:
836 raise Exception("Class %s doesn't use the $!MEMBERS variable" %
837 self._class_name)
838
839 return self._members_emitter
840
841 def EmitFactoryProvider(self, constructor_info, factory_provider, emitter):
842 template_file = ('factoryprovider_%s.darttemplate' %
843 self._html_interface_name)
844 template = self._system._templates.TryLoad(template_file)
845 if not template:
846 template = self._system._templates.Load('factoryprovider.darttemplate')
847
848 emitter.Emit(
849 template,
850 FACTORYPROVIDER=factory_provider,
851 CONSTRUCTOR=self._html_interface_name,
852 PARAMETERS=constructor_info.ParametersImplementationDeclaration(self._Da rtType),
853 NAMED_CONSTRUCTOR=constructor_info.name or self._html_interface_name,
854 ARGUMENTS=constructor_info.ParametersAsArgumentList())
855
856 def AddIndexer(self, element_type):
857 """Adds all the methods required to complete implementation of List."""
858 # We would like to simply inherit the implementation of everything except
859 # get length(), [], and maybe []=. It is possible to extend from a base
860 # array implementation class only when there is no other implementation
861 # inheritance. There might be no implementation inheritance other than
862 # DOMBaseWrapper for many classes, but there might be some where the
863 # array-ness is introduced by a non-root interface:
864 #
865 # interface Y extends X, List<T> ...
866 #
867 # In the non-root case we have to choose between:
868 #
869 # class YImpl extends XImpl { add List<T> methods; }
870 #
871 # and
872 #
873 # class YImpl extends ListBase<T> { copies of transitive XImpl methods; }
874 #
875 self._members_emitter.Emit(
876 '\n'
877 ' $TYPE operator[](int index) native "return this[index];";\n',
878 TYPE=self._NarrowOutputType(element_type))
879
880 if 'CustomIndexedSetter' in self._interface.ext_attrs:
881 self._members_emitter.Emit(
882 '\n'
883 ' void operator[]=(int index, $TYPE value) native "this[index] = valu e";\n',
884 TYPE=self._NarrowInputType(element_type))
885 else:
886 # The HTML library implementation of NodeList has a custom indexed setter
887 # implementation that uses the parent node the NodeList is associated
888 # with if one is available.
889 if self._interface.id != 'NodeList':
890 self._members_emitter.Emit(
891 '\n'
892 ' void operator[]=(int index, $TYPE value) {\n'
893 ' throw new UnsupportedOperationException("Cannot assign element of immutable List.");\n'
894 ' }\n',
895 TYPE=self._NarrowInputType(element_type))
896
897 # TODO(sra): Use separate mixins for mutable implementations of List<T>.
898 # TODO(sra): Use separate mixins for typed array implementations of List<T>.
899 if self._interface.id != 'NodeList':
900 template_file = 'immutable_list_mixin.darttemplate'
901 template = self._system._templates.Load(template_file)
902 self._members_emitter.Emit(template, E=self._DartType(element_type))
903
904 def AddAttribute(self, attribute, html_name, read_only):
905 if self._HasCustomImplementation(attribute.id):
906 return
907
908 if attribute.id != html_name:
909 self._AddAttributeUsingProperties(attribute, html_name, read_only)
910 return
911
912 # If the attribute is shadowing, we can't generate a shadowing
913 # field (Issue 1633).
914 # TODO(sra): _FindShadowedAttribute does not take into account the html
915 # renaming. we should be looking for another attribute that has the same
916 # html_name. Two attributes with the same IDL name might not match if one
917 # is renamed.
918 (super_attribute, super_attribute_interface) = self._FindShadowedAttribute(
919 attribute, _merged_html_interfaces)
920 if super_attribute:
921 if read_only:
922 if attribute.type.id == super_attribute.type.id:
923 # Compatible attribute, use the superclass property. This works
924 # because JavaScript will do its own dynamic dispatch.
925 self._members_emitter.Emit(
926 '\n'
927 ' // Use implementation from $SUPER.\n'
928 ' // final $TYPE $NAME;\n',
929 SUPER=super_attribute_interface,
930 NAME=DartDomNameOfAttribute(attribute),
931 TYPE=self._NarrowOutputType(attribute.type.id))
932 return
933 self._members_emitter.Emit('\n // Shadowing definition.')
934 self._AddAttributeUsingProperties(attribute, html_name, read_only)
935 return
936
937 # If the type has a conversion we need a getter or setter to contain the
938 # conversion code.
939 if (self._OutputConversion(attribute.type.id, attribute.id) or
940 self._InputConversion(attribute.type.id, attribute.id)):
941 self._AddAttributeUsingProperties(attribute, html_name, read_only)
942 return
943
944 output_type = self._NarrowOutputType(attribute.type.id)
945 input_type = self._NarrowInputType(attribute.type.id)
946 if not read_only:
947 self._members_emitter.Emit(
948 '\n $TYPE $NAME;\n',
949 NAME=DartDomNameOfAttribute(attribute),
950 TYPE=output_type)
951 else:
952 self._members_emitter.Emit(
953 '\n final $TYPE $NAME;\n',
954 NAME=DartDomNameOfAttribute(attribute),
955 TYPE=output_type)
956
957 def _AddAttributeUsingProperties(self, attribute, html_name, read_only):
958 self._AddRenamingGetter(attribute, html_name)
959 if not read_only:
960 self._AddRenamingSetter(attribute, html_name)
961
962 def _AddRenamingGetter(self, attr, html_name):
963 conversion = self._OutputConversion(attr.type.id, attr.id)
964 if conversion:
965 return self._AddConvertingGetter(attr, html_name, conversion)
966 return_type = self._NarrowOutputType(attr.type.id)
967 self._members_emitter.Emit(
968 '\n $TYPE get $HTML_NAME() native "return this.$NAME;";\n',
969 HTML_NAME=html_name,
970 NAME=attr.id,
971 TYPE=return_type)
972
973 def _AddRenamingSetter(self, attr, html_name):
974 conversion = self._InputConversion(attr.type.id, attr.id)
975 if conversion:
976 return self._AddConvertingSetter(attr, html_name, conversion)
977 self._members_emitter.Emit(
978 '\n void set $HTML_NAME($TYPE value)'
979 ' native "this.$NAME = value;";\n',
980 HTML_NAME=html_name,
981 NAME=attr.id,
982 TYPE=self._NarrowInputType(attr.type.id))
983
984 def _AddConvertingGetter(self, attr, html_name, conversion):
985 self._members_emitter.Emit(
986 '\n $RETURN_TYPE get $HTML_NAME() => $CONVERT(this._$(HTML_NAME));'
987 '\n $NATIVE_TYPE get _$HTML_NAME() native "return this.$NAME;";'
988 '\n',
989 CONVERT=conversion.function_name,
990 HTML_NAME=html_name,
991 NAME=attr.id,
992 RETURN_TYPE=conversion.output_type,
993 NATIVE_TYPE=conversion.input_type)
994
995 def _AddConvertingSetter(self, attr, html_name, conversion):
996 self._members_emitter.Emit(
997 '\n void set $HTML_NAME($INPUT_TYPE value) {'
998 ' this._$HTML_NAME = $CONVERT(value); }'
999 '\n void set _$HTML_NAME(/*$NATIVE_TYPE*/ value)'
1000 ' native "this.$NAME = value;";'
1001 '\n',
1002 CONVERT=conversion.function_name,
1003 HTML_NAME=html_name,
1004 NAME=attr.id,
1005 INPUT_TYPE=conversion.input_type,
1006 NATIVE_TYPE=conversion.output_type)
1007
1008
1009 def AddOperation(self, info, html_name):
1010 """
1011 Arguments:
1012 info: An OperationInfo object.
1013 """
1014 if self._HasCustomImplementation(info.name):
1015 return
1016
1017 # FIXME: support static operations.
1018 if info.IsStatic():
1019 return
1020
1021 # Any conversions needed?
1022 if any(self._OperationRequiresConversions(op) for op in info.overloads):
1023 self._AddOperationWithConversions(info, html_name)
1024 else:
1025 self._AddDirectNativeOperation(info, html_name)
1026
1027 def _AddDirectNativeOperation(self, info, html_name):
1028 # Do we need a native body?
1029 if html_name != info.declared_name:
1030 return_type = self._NarrowOutputType(info.type_name)
1031
1032 operation_emitter = self._members_emitter.Emit('$!SCOPE',
1033 TYPE=return_type,
1034 HTML_NAME=html_name,
1035 NAME=info.declared_name,
1036 PARAMS=info.ParametersImplementationDeclaration(
1037 lambda type_name: self._NarrowInputType(type_name)))
1038
1039 operation_emitter.Emit(
1040 '\n'
1041 #' // @native("$NAME")\n;'
1042 ' $TYPE $(HTML_NAME)($PARAMS) native "$NAME";\n')
1043 else:
1044 self._members_emitter.Emit(
1045 '\n'
1046 ' $TYPE $NAME($PARAMS) native;\n',
1047 TYPE=self._NarrowOutputType(info.type_name),
1048 NAME=info.name,
1049 PARAMS=info.ParametersImplementationDeclaration(
1050 lambda type_name: self._NarrowInputType(type_name)))
1051
1052 def _AddOperationWithConversions(self, info, html_name):
1053 # Assert all operations have same return type.
1054 assert len(set([op.type.id for op in info.operations])) == 1
1055 info = info.CopyAndWidenDefaultParameters()
1056 output_conversion = self._OutputConversion(info.type_name,
1057 info.declared_name)
1058 if output_conversion:
1059 return_type = output_conversion.output_type
1060 native_return_type = output_conversion.input_type
1061 else:
1062 return_type = self._NarrowInputType(info.type_name)
1063 native_return_type = return_type
1064
1065 def InputType(type_name):
1066 conversion = self._InputConversion(type_name, info.declared_name)
1067 if conversion:
1068 return conversion.input_type
1069 else:
1070 return self._NarrowInputType(type_name)
1071
1072 body = self._members_emitter.Emit(
1073 '\n'
1074 ' $TYPE $(HTML_NAME)($PARAMS) {\n'
1075 '$!BODY'
1076 ' }\n',
1077 TYPE=return_type,
1078 HTML_NAME=html_name,
1079 PARAMS=info.ParametersImplementationDeclaration(InputType, '_default'))
1080
1081 parameter_names = [param_info.name for param_info in info.param_infos]
1082 parameter_types = [InputType(param_info.dart_type)
1083 for param_info in info.param_infos]
1084 operations = info.operations
1085
1086 method_version = [0]
1087 temp_version = [0]
1088
1089 def GenerateCall(operation, argument_count, checks):
1090 checks = filter(lambda e: e != 'true', checks)
1091 if checks:
1092 (stmts_emitter, call_emitter) = body.Emit(
1093 ' if ($CHECKS) {\n$!STMTS$!CALL }\n',
1094 INDENT=' ',
1095 CHECKS=' &&\n '.join(checks))
1096 else:
1097 (stmts_emitter, call_emitter) = body.Emit('$!A$!B', INDENT=' ');
1098
1099 method_version[0] += 1
1100 target = '_%s_%d' % (html_name, method_version[0])
1101 arguments = []
1102 target_parameters = []
1103 for position, arg in enumerate(operation.arguments[:argument_count]):
1104 conversion = self._InputConversion(arg.type.id, operation.id)
1105 param_name = operation.arguments[position].id
1106 if conversion:
1107 temp_version[0] += 1
1108 temp_name = '%s_%s' % (param_name, temp_version[0])
1109 temp_type = conversion.output_type
1110 stmts_emitter.Emit(
1111 '$(INDENT)$TYPE $NAME = $CONVERT($ARG);\n',
1112 TYPE=TypeOrVar(temp_type),
1113 NAME=temp_name,
1114 CONVERT=conversion.function_name,
1115 ARG=parameter_names[position])
1116 arguments.append(temp_name)
1117 param_type = temp_type
1118 verified_type = temp_type # verified by assignment in checked mode.
1119 else:
1120 arguments.append(parameter_names[position])
1121 param_type = self._NarrowInputType(DartType(arg.type.id))
1122 # Verified by argument checking on entry to the dispatcher.
1123 verified_type = InputType(info.param_infos[position].dart_type)
1124
1125 # The native method does not need an argument type if we know the type.
1126 # But we do need the native methods to have correct function types, so
1127 # be conservative.
1128 if param_type == verified_type:
1129 if param_type in ['String', 'num', 'int', 'double', 'bool', 'Object']:
1130 param_type = 'Dynamic'
1131 target_parameters.append(
1132 '%s%s' % (TypeOrNothing(param_type), param_name))
1133
1134 argument_list = ', '.join(arguments)
1135 # TODO(sra): If the native method has zero type checks, we can 'inline' is
1136 # and call it directly with a JS-expression.
1137 call = '%s(%s)' % (target, argument_list)
1138
1139 if output_conversion:
1140 call = '%s(%s)' % (output_conversion.function_name, call)
1141
1142 if operation.type.id == 'void':
1143 call_emitter.Emit('$(INDENT)$CALL;\n$(INDENT)return;\n',
1144 CALL=call)
1145 else:
1146 call_emitter.Emit('$(INDENT)return $CALL;\n', CALL=call)
1147
1148 self._members_emitter.Emit(
1149 ' $TYPE$TARGET($PARAMS) native "$NATIVE";\n',
1150 TYPE=TypeOrNothing(native_return_type),
1151 TARGET=target,
1152 PARAMS=', '.join(target_parameters),
1153 NATIVE=info.declared_name)
1154
1155 def GenerateChecksAndCall(operation, argument_count):
1156 checks = ['_default == %s' % name for name in parameter_names]
1157 for i in range(0, argument_count):
1158 argument = operation.arguments[i]
1159 parameter_name = parameter_names[i]
1160 test_type = self._DartType(argument.type.id)
1161 if test_type in ['Dynamic', 'Object']:
1162 checks[i] = '_default != %s' % parameter_name
1163 elif test_type == parameter_types[i]:
1164 checks[i] = 'true'
1165 else:
1166 checks[i] = '(%s is %s || %s == null)' % (
1167 parameter_name, test_type, parameter_name)
1168 # There can be multiple _default checks. We need them all since a later
1169 # optional argument could have been passed by name, leaving 'holes'.
1170 GenerateCall(operation, argument_count, checks)
1171
1172 # TODO: Optimize the dispatch to avoid repeated checks.
1173 if len(operations) > 1:
1174 for operation in operations:
1175 for position, argument in enumerate(operation.arguments):
1176 if self._IsOptional(operation, argument):
1177 GenerateChecksAndCall(operation, position)
1178 GenerateChecksAndCall(operation, len(operation.arguments))
1179 body.Emit(
1180 ' throw const Exception("Incorrect number or type of arguments");'
1181 '\n');
1182 else:
1183 operation = operations[0]
1184 argument_count = len(operation.arguments)
1185 for position, argument in list(enumerate(operation.arguments))[::-1]:
1186 if self._IsOptional(operation, argument):
1187 check = '_default != %s' % parameter_names[position]
1188 GenerateCall(operation, position + 1, [check])
1189 argument_count = position
1190 GenerateCall(operation, argument_count, [])
1191
1192
1193 def _IsOptional(self, operation, argument):
1194 return IsOptional(argument)
1195
1196
1197 def _OperationRequiresConversions(self, operation):
1198 return (self._OperationRequiresOutputConversion(operation) or
1199 self._OperationRequiresInputConversions(operation))
1200
1201 def _OperationRequiresOutputConversion(self, operation):
1202 return self._OutputConversion(operation.type.id, operation.id)
1203
1204 def _OperationRequiresInputConversions(self, operation):
1205 return any(self._InputConversion(arg.type.id, operation.id)
1206 for arg in operation.arguments)
1207
1208 def _OutputConversion(self, idl_type, member):
1209 return FindConversion(idl_type, 'get', self._interface.id, member)
1210
1211 def _InputConversion(self, idl_type, member):
1212 return FindConversion(idl_type, 'set', self._interface.id, member)
1213
1214 def _HasCustomImplementation(self, member_name):
1215 member_name = '%s.%s' % (self._html_interface_name, member_name)
1216 return member_name in _js_custom_members
1217
1218 def _HasJavaScriptIndexingBehaviour(self):
1219 """Returns True if the native object has an indexer and length property."""
1220 (element_type, requires_indexer) = ListImplementationInfo(
1221 self._interface, self._database)
1222 if element_type and requires_indexer: return True
1223 return False
1224
1225 # ------------------------------------------------------------------------------
1226
1227 class HtmlDart2JSSystem(System):
1228
1229 def __init__(self, options):
1230 super(HtmlDart2JSSystem, self).__init__(options)
1231
1232 def ImplementationGenerator(self, interface):
1233 return HtmlDart2JSClassGenerator(self, interface)
1234
1235 def GenerateLibraries(self, dart_files):
1236 self._GenerateLibFile(
1237 'html_dart2js.darttemplate',
1238 os.path.join(self._output_dir, 'html_dart2js.dart'),
1239 dart_files)
1240
1241 def Finish(self):
1242 pass
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698