e44ef72329
That should lead to less frequent build breaks. R=podivilov@chromium.org Review URL: https://codereview.chromium.org//11034022 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@13160 260f80e4-7a28-3924-810f-c04153c831b5
271 lines
9.0 KiB
Python
271 lines
9.0 KiB
Python
#!/usr/bin/python
|
|
# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
|
|
"""This module provides functionality to generate dart:html event classes."""
|
|
|
|
import logging
|
|
|
|
_logger = logging.getLogger('dartgenerator')
|
|
|
|
# Events without onEventName attributes in the IDL we want to support.
|
|
# We can automatically extract most event names by checking for
|
|
# onEventName methods in the IDL but some events aren't listed so we need
|
|
# to manually add them here so that they are easy for users to find.
|
|
_html_manual_events = {
|
|
'Element': ['touchleave', 'touchenter', 'webkitTransitionEnd'],
|
|
'Window': ['DOMContentLoaded']
|
|
}
|
|
|
|
# These event names must be camel case when attaching event listeners
|
|
# using addEventListener even though the onEventName properties in the DOM for
|
|
# them are not camel case.
|
|
_on_attribute_to_event_name_mapping = {
|
|
'webkitanimationend': 'webkitAnimationEnd',
|
|
'webkitanimationiteration': 'webkitAnimationIteration',
|
|
'webkitanimationstart': 'webkitAnimationStart',
|
|
'webkitspeechchange': 'webkitSpeechChange',
|
|
'webkittransitionend': 'webkitTransitionEnd',
|
|
}
|
|
|
|
# Mapping from raw event names to the pretty camelCase event names exposed as
|
|
# properties in dart:html. If the DOM exposes a new event name, you will need
|
|
# to add the lower case to camel case conversion for that event name here.
|
|
_html_event_names = {
|
|
'DOMContentLoaded': 'contentLoaded',
|
|
'abort': 'abort',
|
|
'addstream': 'addStream',
|
|
'addtrack': 'addTrack',
|
|
'audioend': 'audioEnd',
|
|
'audioprocess': 'audioProcess',
|
|
'audiostart': 'audioStart',
|
|
'beforecopy': 'beforeCopy',
|
|
'beforecut': 'beforeCut',
|
|
'beforepaste': 'beforePaste',
|
|
'beforeunload': 'beforeUnload',
|
|
'blocked': 'blocked',
|
|
'blur': 'blur',
|
|
'cached': 'cached',
|
|
'canplay': 'canPlay',
|
|
'canplaythrough': 'canPlayThrough',
|
|
'change': 'change',
|
|
'chargingchange': 'chargingChange',
|
|
'chargingtimechange': 'chargingTimeChange',
|
|
'checking': 'checking',
|
|
'click': 'click',
|
|
'close': 'close',
|
|
'complete': 'complete',
|
|
'connect': 'connect',
|
|
'connecting': 'connecting',
|
|
'contextmenu': 'contextMenu',
|
|
'copy': 'copy',
|
|
'cuechange': 'cueChange',
|
|
'cut': 'cut',
|
|
'dblclick': 'doubleClick',
|
|
'devicemotion': 'deviceMotion',
|
|
'deviceorientation': 'deviceOrientation',
|
|
'dischargingtimechange': 'dischargingTimeChange',
|
|
'display': 'display',
|
|
'downloading': 'downloading',
|
|
'drag': 'drag',
|
|
'dragend': 'dragEnd',
|
|
'dragenter': 'dragEnter',
|
|
'dragleave': 'dragLeave',
|
|
'dragover': 'dragOver',
|
|
'dragstart': 'dragStart',
|
|
'drop': 'drop',
|
|
'durationchange': 'durationChange',
|
|
'emptied': 'emptied',
|
|
'end': 'end',
|
|
'ended': 'ended',
|
|
'enter': 'enter',
|
|
'error': 'error',
|
|
'exit': 'exit',
|
|
'focus': 'focus',
|
|
'hashchange': 'hashChange',
|
|
'icecandidate': 'iceCandidate',
|
|
'icechange': 'iceChange',
|
|
'input': 'input',
|
|
'invalid': 'invalid',
|
|
'keydown': 'keyDown',
|
|
'keypress': 'keyPress',
|
|
'keyup': 'keyUp',
|
|
'levelchange': 'levelChange',
|
|
'load': 'load',
|
|
'loadeddata': 'loadedData',
|
|
'loadedmetadata': 'loadedMetadata',
|
|
'loadend': 'loadEnd',
|
|
'loadstart': 'loadStart',
|
|
'message': 'message',
|
|
'mousedown': 'mouseDown',
|
|
'mousemove': 'mouseMove',
|
|
'mouseout': 'mouseOut',
|
|
'mouseover': 'mouseOver',
|
|
'mouseup': 'mouseUp',
|
|
'mousewheel': 'mouseWheel',
|
|
'mute': 'mute',
|
|
'negotiationneeded': 'negotiationNeeded',
|
|
'nomatch': 'noMatch',
|
|
'noupdate': 'noUpdate',
|
|
'obsolete': 'obsolete',
|
|
'offline': 'offline',
|
|
'online': 'online',
|
|
'open': 'open',
|
|
'pagehide': 'pageHide',
|
|
'pageshow': 'pageShow',
|
|
'paste': 'paste',
|
|
'pause': 'pause',
|
|
'play': 'play',
|
|
'playing': 'playing',
|
|
'popstate': 'popState',
|
|
'progress': 'progress',
|
|
'ratechange': 'rateChange',
|
|
'readystatechange': 'readyStateChange',
|
|
'removestream': 'removeStream',
|
|
'removetrack': 'removeTrack',
|
|
'reset': 'reset',
|
|
'resize': 'resize',
|
|
'result': 'result',
|
|
'resultdeleted': 'resultDeleted',
|
|
'scroll': 'scroll',
|
|
'search': 'search',
|
|
'seeked': 'seeked',
|
|
'seeking': 'seeking',
|
|
'select': 'select',
|
|
'selectionchange': 'selectionChange',
|
|
'selectstart': 'selectStart',
|
|
'show': 'show',
|
|
'soundend': 'soundEnd',
|
|
'soundstart': 'soundStart',
|
|
'speechend': 'speechEnd',
|
|
'speechstart': 'speechStart',
|
|
'stalled': 'stalled',
|
|
'start': 'start',
|
|
'statechange': 'stateChange',
|
|
'storage': 'storage',
|
|
'submit': 'submit',
|
|
'success': 'success',
|
|
'suspend': 'suspend',
|
|
'timeupdate': 'timeUpdate',
|
|
'touchcancel': 'touchCancel',
|
|
'touchend': 'touchEnd',
|
|
'touchenter': 'touchEnter',
|
|
'touchleave': 'touchLeave',
|
|
'touchmove': 'touchMove',
|
|
'touchstart': 'touchStart',
|
|
'unload': 'unload',
|
|
'upgradeneeded': 'upgradeNeeded',
|
|
'unmute': 'unmute',
|
|
'updateready': 'updateReady',
|
|
'versionchange': 'versionChange',
|
|
'volumechange': 'volumeChange',
|
|
'waiting': 'waiting',
|
|
'webkitAnimationEnd': 'animationEnd',
|
|
'webkitAnimationIteration': 'animationIteration',
|
|
'webkitAnimationStart': 'animationStart',
|
|
'webkitfullscreenchange': 'fullscreenChange',
|
|
'webkitfullscreenerror': 'fullscreenError',
|
|
'webkitkeyadded': 'keyAdded',
|
|
'webkitkeyerror': 'keyError',
|
|
'webkitkeymessage': 'keyMessage',
|
|
'webkitneedkey': 'needKey',
|
|
'webkitpointerlockchange': 'pointerLockChange',
|
|
'webkitpointerlockerror': 'pointerLockError',
|
|
'webkitSpeechChange': 'speechChange',
|
|
'webkitsourceclose': 'sourceClose',
|
|
'webkitsourceended': 'sourceEnded',
|
|
'webkitsourceopen': 'sourceOpen',
|
|
'webkitTransitionEnd': 'transitionEnd',
|
|
'write': 'write',
|
|
'writeend': 'writeEnd',
|
|
'writestart': 'writeStart'
|
|
}
|
|
|
|
# These classes require an explicit declaration for the "on" method even though
|
|
# they don't declare any unique events, because the concrete class hierarchy
|
|
# doesn't match the interface hierarchy.
|
|
_html_explicit_event_classes = set(['DocumentFragment'])
|
|
|
|
class HtmlEventGenerator(object):
|
|
|
|
def __init__(self, database, template_loader):
|
|
self._event_classes = set()
|
|
self._database = database
|
|
self._template_loader = template_loader
|
|
|
|
def ProcessInterface(self, interface, html_interface_name, custom_events,
|
|
events_interface_emitter, events_implementation_emitter):
|
|
events = set([attr for attr in interface.attributes
|
|
if attr.type.id == 'EventListener'])
|
|
if not events and interface.id not in _html_explicit_event_classes:
|
|
return None
|
|
|
|
self._event_classes.add(interface.id)
|
|
events_interface = html_interface_name + 'Events'
|
|
parent_events_interface = self._GetParentEventsInterface(interface)
|
|
|
|
if not events:
|
|
return parent_events_interface
|
|
|
|
interface_events_members = events_interface_emitter.Emit(
|
|
'\nabstract class $INTERFACE implements $PARENT {\n$!MEMBERS}\n',
|
|
INTERFACE=events_interface,
|
|
PARENT=parent_events_interface)
|
|
|
|
template_file = 'impl_%s.darttemplate' % events_interface
|
|
template = (self._template_loader.TryLoad(template_file) or
|
|
'\n'
|
|
'class $CLASSNAME extends $SUPER implements $INTERFACE {\n'
|
|
' $CLASSNAME(_ptr) : super(_ptr);\n'
|
|
'$!MEMBERS}\n')
|
|
|
|
# TODO(jacobr): specify the type of _ptr as EventTarget
|
|
implementation_events_members = events_implementation_emitter.Emit(
|
|
template,
|
|
CLASSNAME='_%sImpl' % events_interface,
|
|
INTERFACE=events_interface,
|
|
SUPER='_%sImpl' % parent_events_interface)
|
|
|
|
dom_event_names = set()
|
|
for event in events:
|
|
dom_name = event.id[2:]
|
|
dom_name = _on_attribute_to_event_name_mapping.get(dom_name, dom_name)
|
|
dom_event_names.add(dom_name)
|
|
if html_interface_name in _html_manual_events:
|
|
dom_event_names.update(_html_manual_events[html_interface_name])
|
|
for dom_name in sorted(dom_event_names):
|
|
if dom_name not in _html_event_names:
|
|
_logger.warn('omitting %s event as there is no HTML name for it' % dom_name)
|
|
continue
|
|
|
|
html_name = _html_event_names[dom_name]
|
|
interface_events_members.Emit('\n EventListenerList get $NAME;\n',
|
|
NAME=html_name)
|
|
full_event_name = '%sEvents.%s' % (html_interface_name, html_name)
|
|
if not full_event_name in custom_events:
|
|
implementation_events_members.Emit(
|
|
"\n"
|
|
" EventListenerList get $NAME => this['$DOM_NAME'];\n",
|
|
NAME=html_name,
|
|
DOM_NAME=dom_name)
|
|
|
|
return events_interface
|
|
|
|
# TODO(jacobr): this isn't quite right....
|
|
def _GetParentEventsInterface(self, interface):
|
|
# Ugly hack as we don't specify that Document and DocumentFragment inherit
|
|
# from Element in our IDL.
|
|
if interface.id == 'Document' or interface.id == 'DocumentFragment':
|
|
return 'ElementEvents'
|
|
|
|
parent_events_interface = 'Events'
|
|
interfaces_with_events = set()
|
|
for parent in self._database.Hierarchy(interface):
|
|
if parent != interface and parent.id in self._event_classes:
|
|
parent_events_interface = parent.id + 'Events'
|
|
interfaces_with_events.add(parent)
|
|
if len(interfaces_with_events) > 1:
|
|
raise Exception('Only one parent event class allowed ' + interface.id)
|
|
return parent_events_interface
|