| OLD | NEW |
| (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 functionality to generate dart:html event classes.""" | |
| 7 | |
| 8 import logging | |
| 9 | |
| 10 _logger = logging.getLogger('dartgenerator') | |
| 11 | |
| 12 # Events without onEventName attributes in the IDL we want to support. | |
| 13 # We can automatically extract most event names by checking for | |
| 14 # onEventName methods in the IDL but some events aren't listed so we need | |
| 15 # to manually add them here so that they are easy for users to find. | |
| 16 _html_manual_events = { | |
| 17 'Element': ['touchleave', 'touchenter', 'webkitTransitionEnd'], | |
| 18 'Window': ['DOMContentLoaded'] | |
| 19 } | |
| 20 | |
| 21 # These event names must be camel case when attaching event listeners | |
| 22 # using addEventListener even though the onEventName properties in the DOM for | |
| 23 # them are not camel case. | |
| 24 _on_attribute_to_event_name_mapping = { | |
| 25 'webkitanimationend': 'webkitAnimationEnd', | |
| 26 'webkitanimationiteration': 'webkitAnimationIteration', | |
| 27 'webkitanimationstart': 'webkitAnimationStart', | |
| 28 'webkitspeechchange': 'webkitSpeechChange', | |
| 29 'webkittransitionend': 'webkitTransitionEnd', | |
| 30 } | |
| 31 | |
| 32 # Mapping from raw event names to the pretty camelCase event names exposed as | |
| 33 # properties in dart:html. If the DOM exposes a new event name, you will need | |
| 34 # to add the lower case to camel case conversion for that event name here. | |
| 35 _html_event_names = { | |
| 36 'DOMContentLoaded': 'contentLoaded', | |
| 37 'abort': 'abort', | |
| 38 'addstream': 'addStream', | |
| 39 'addtrack': 'addTrack', | |
| 40 'audioend': 'audioEnd', | |
| 41 'audioprocess': 'audioProcess', | |
| 42 'audiostart': 'audioStart', | |
| 43 'beforecopy': 'beforeCopy', | |
| 44 'beforecut': 'beforeCut', | |
| 45 'beforepaste': 'beforePaste', | |
| 46 'beforeunload': 'beforeUnload', | |
| 47 'blocked': 'blocked', | |
| 48 'blur': 'blur', | |
| 49 'cached': 'cached', | |
| 50 'canplay': 'canPlay', | |
| 51 'canplaythrough': 'canPlayThrough', | |
| 52 'change': 'change', | |
| 53 'chargingchange': 'chargingChange', | |
| 54 'chargingtimechange': 'chargingTimeChange', | |
| 55 'checking': 'checking', | |
| 56 'click': 'click', | |
| 57 'close': 'close', | |
| 58 'complete': 'complete', | |
| 59 'connect': 'connect', | |
| 60 'connecting': 'connecting', | |
| 61 'contextmenu': 'contextMenu', | |
| 62 'copy': 'copy', | |
| 63 'cuechange': 'cueChange', | |
| 64 'cut': 'cut', | |
| 65 'dblclick': 'doubleClick', | |
| 66 'devicemotion': 'deviceMotion', | |
| 67 'deviceorientation': 'deviceOrientation', | |
| 68 'dischargingtimechange': 'dischargingTimeChange', | |
| 69 'display': 'display', | |
| 70 'downloading': 'downloading', | |
| 71 'drag': 'drag', | |
| 72 'dragend': 'dragEnd', | |
| 73 'dragenter': 'dragEnter', | |
| 74 'dragleave': 'dragLeave', | |
| 75 'dragover': 'dragOver', | |
| 76 'dragstart': 'dragStart', | |
| 77 'drop': 'drop', | |
| 78 'durationchange': 'durationChange', | |
| 79 'emptied': 'emptied', | |
| 80 'end': 'end', | |
| 81 'ended': 'ended', | |
| 82 'enter': 'enter', | |
| 83 'error': 'error', | |
| 84 'exit': 'exit', | |
| 85 'focus': 'focus', | |
| 86 'hashchange': 'hashChange', | |
| 87 'icecandidate': 'iceCandidate', | |
| 88 'icechange': 'iceChange', | |
| 89 'input': 'input', | |
| 90 'invalid': 'invalid', | |
| 91 'keydown': 'keyDown', | |
| 92 'keypress': 'keyPress', | |
| 93 'keyup': 'keyUp', | |
| 94 'levelchange': 'levelChange', | |
| 95 'load': 'load', | |
| 96 'loadeddata': 'loadedData', | |
| 97 'loadedmetadata': 'loadedMetadata', | |
| 98 'loadend': 'loadEnd', | |
| 99 'loadstart': 'loadStart', | |
| 100 'message': 'message', | |
| 101 'mousedown': 'mouseDown', | |
| 102 'mousemove': 'mouseMove', | |
| 103 'mouseout': 'mouseOut', | |
| 104 'mouseover': 'mouseOver', | |
| 105 'mouseup': 'mouseUp', | |
| 106 'mousewheel': 'mouseWheel', | |
| 107 'mute': 'mute', | |
| 108 'negotiationneeded': 'negotiationNeeded', | |
| 109 'nomatch': 'noMatch', | |
| 110 'noupdate': 'noUpdate', | |
| 111 'obsolete': 'obsolete', | |
| 112 'offline': 'offline', | |
| 113 'online': 'online', | |
| 114 'open': 'open', | |
| 115 'pagehide': 'pageHide', | |
| 116 'pageshow': 'pageShow', | |
| 117 'paste': 'paste', | |
| 118 'pause': 'pause', | |
| 119 'play': 'play', | |
| 120 'playing': 'playing', | |
| 121 'popstate': 'popState', | |
| 122 'progress': 'progress', | |
| 123 'ratechange': 'rateChange', | |
| 124 'readystatechange': 'readyStateChange', | |
| 125 'removestream': 'removeStream', | |
| 126 'removetrack': 'removeTrack', | |
| 127 'reset': 'reset', | |
| 128 'resize': 'resize', | |
| 129 'result': 'result', | |
| 130 'resultdeleted': 'resultDeleted', | |
| 131 'scroll': 'scroll', | |
| 132 'search': 'search', | |
| 133 'seeked': 'seeked', | |
| 134 'seeking': 'seeking', | |
| 135 'select': 'select', | |
| 136 'selectionchange': 'selectionChange', | |
| 137 'selectstart': 'selectStart', | |
| 138 'show': 'show', | |
| 139 'soundend': 'soundEnd', | |
| 140 'soundstart': 'soundStart', | |
| 141 'speechend': 'speechEnd', | |
| 142 'speechstart': 'speechStart', | |
| 143 'stalled': 'stalled', | |
| 144 'start': 'start', | |
| 145 'statechange': 'stateChange', | |
| 146 'storage': 'storage', | |
| 147 'submit': 'submit', | |
| 148 'success': 'success', | |
| 149 'suspend': 'suspend', | |
| 150 'timeupdate': 'timeUpdate', | |
| 151 'touchcancel': 'touchCancel', | |
| 152 'touchend': 'touchEnd', | |
| 153 'touchenter': 'touchEnter', | |
| 154 'touchleave': 'touchLeave', | |
| 155 'touchmove': 'touchMove', | |
| 156 'touchstart': 'touchStart', | |
| 157 'unload': 'unload', | |
| 158 'upgradeneeded': 'upgradeNeeded', | |
| 159 'unmute': 'unmute', | |
| 160 'updateready': 'updateReady', | |
| 161 'versionchange': 'versionChange', | |
| 162 'volumechange': 'volumeChange', | |
| 163 'waiting': 'waiting', | |
| 164 'webkitAnimationEnd': 'animationEnd', | |
| 165 'webkitAnimationIteration': 'animationIteration', | |
| 166 'webkitAnimationStart': 'animationStart', | |
| 167 'webkitfullscreenchange': 'fullscreenChange', | |
| 168 'webkitfullscreenerror': 'fullscreenError', | |
| 169 'webkitkeyadded': 'keyAdded', | |
| 170 'webkitkeyerror': 'keyError', | |
| 171 'webkitkeymessage': 'keyMessage', | |
| 172 'webkitneedkey': 'needKey', | |
| 173 'webkitpointerlockchange': 'pointerLockChange', | |
| 174 'webkitpointerlockerror': 'pointerLockError', | |
| 175 'webkitSpeechChange': 'speechChange', | |
| 176 'webkitsourceclose': 'sourceClose', | |
| 177 'webkitsourceended': 'sourceEnded', | |
| 178 'webkitsourceopen': 'sourceOpen', | |
| 179 'webkitTransitionEnd': 'transitionEnd', | |
| 180 'write': 'write', | |
| 181 'writeend': 'writeEnd', | |
| 182 'writestart': 'writeStart' | |
| 183 } | |
| 184 | |
| 185 # These classes require an explicit declaration for the "on" method even though | |
| 186 # they don't declare any unique events, because the concrete class hierarchy | |
| 187 # doesn't match the interface hierarchy. | |
| 188 _html_explicit_event_classes = set(['DocumentFragment']) | |
| 189 | |
| 190 class HtmlEventGenerator(object): | |
| 191 | |
| 192 def __init__(self, database, renamer, template_loader): | |
| 193 self._event_classes = set() | |
| 194 self._database = database | |
| 195 self._renamer = renamer | |
| 196 self._template_loader = template_loader | |
| 197 | |
| 198 def ProcessInterface(self, interface, html_interface_name, custom_events, | |
| 199 events_implementation_emitter): | |
| 200 event_names = set([attr.id[2:] for attr in interface.attributes | |
| 201 if attr.type.id == 'EventListener']) | |
| 202 | |
| 203 # Document and DocumentFragment actually derive from Element, so omit | |
| 204 # any events which are duplicated with that. | |
| 205 if interface.id == 'Document' or interface.id == 'DocumentFragment': | |
| 206 element_interface = self._database.GetInterface('Element') | |
| 207 for attr in element_interface.attributes: | |
| 208 if attr.type.id == 'EventListener' and attr.id[2:] in event_names: | |
| 209 event_names.remove(attr.id[2:]) | |
| 210 | |
| 211 if not event_names and interface.id not in _html_explicit_event_classes: | |
| 212 return None | |
| 213 | |
| 214 self._event_classes.add(interface.id) | |
| 215 events_class_name = html_interface_name + 'Events' | |
| 216 parent_events_class_name = self._GetParentEventsClassName(interface) | |
| 217 | |
| 218 if not event_names: | |
| 219 return parent_events_class_name | |
| 220 | |
| 221 template_file = 'impl_%s.darttemplate' % events_class_name | |
| 222 template = (self._template_loader.TryLoad(template_file) or | |
| 223 '\n' | |
| 224 '/// @docsEditable true\n' | |
| 225 'class $CLASSNAME extends $SUPER {\n' | |
| 226 ' /// @docsEditable true\n' | |
| 227 ' $CLASSNAME(EventTarget _ptr) : super(_ptr);\n' | |
| 228 '$!MEMBERS}\n') | |
| 229 | |
| 230 # TODO(jacobr): specify the type of _ptr as EventTarget | |
| 231 implementation_events_members = events_implementation_emitter.Emit( | |
| 232 template, | |
| 233 CLASSNAME=events_class_name, | |
| 234 SUPER='%s' % parent_events_class_name) | |
| 235 | |
| 236 dom_event_names = set() | |
| 237 for event in event_names: | |
| 238 dom_name = event | |
| 239 dom_name = _on_attribute_to_event_name_mapping.get(dom_name, dom_name) | |
| 240 dom_event_names.add(dom_name) | |
| 241 if html_interface_name in _html_manual_events: | |
| 242 dom_event_names.update(_html_manual_events[html_interface_name]) | |
| 243 for dom_name in sorted(dom_event_names): | |
| 244 if dom_name not in _html_event_names: | |
| 245 _logger.warn('omitting %s event as there is no HTML name for it' % dom_n
ame) | |
| 246 continue | |
| 247 | |
| 248 html_name = _html_event_names[dom_name] | |
| 249 full_event_name = '%sEvents.%s' % (html_interface_name, html_name) | |
| 250 if not full_event_name in custom_events: | |
| 251 implementation_events_members.Emit( | |
| 252 "\n" | |
| 253 " /// @docsEditable true\n" | |
| 254 " EventListenerList get $NAME => this['$DOM_NAME'];\n", | |
| 255 NAME=html_name, | |
| 256 DOM_NAME=dom_name) | |
| 257 | |
| 258 return events_class_name | |
| 259 | |
| 260 # TODO(jacobr): this isn't quite right.... | |
| 261 def _GetParentEventsClassName(self, interface): | |
| 262 # Ugly hack as we don't specify that Document and DocumentFragment inherit | |
| 263 # from Element in our IDL. | |
| 264 if interface.id == 'Document' or interface.id == 'DocumentFragment': | |
| 265 return 'ElementEvents' | |
| 266 | |
| 267 parent_events_class_name = 'Events' | |
| 268 interfaces_with_events = set() | |
| 269 for parent in self._database.Hierarchy(interface): | |
| 270 if parent != interface and parent.id in self._event_classes: | |
| 271 parent_name = self._renamer.RenameInterface(parent) | |
| 272 parent_events_class_name = parent_name + 'Events' | |
| 273 interfaces_with_events.add(parent) | |
| 274 if len(interfaces_with_events) > 1: | |
| 275 raise Exception('Only one parent event class allowed ' + interface.id) | |
| 276 return parent_events_class_name | |
| OLD | NEW |