Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
htmleventgenerator.py
Go to the documentation of this file.
1#!/usr/bin/env python3
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"""This module provides functionality to generate dart:html event classes."""
6
7import logging
8import monitored
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 = monitored.Dict(
17 'htmleventgenerator._html_manual_events', {
18 'Element': ['ontouchleave', 'ontouchenter', 'ontransitionend'],
19 'HTMLCanvasElement': ['onwebglcontextlost', 'onwebglcontextrestored'],
20 'Window': ['onDOMContentLoaded']
21 })
22
23# These event names must be camel case when attaching event listeners
24# using addEventListener even though the onEventName properties in the DOM for
25# them are not camel case.
26_on_attribute_to_event_name_mapping = monitored.Dict(
27 'htmleventgenerator._on_attribute_to_event_name_mapping', {
28 'webkitanimationend': 'webkitAnimationEnd',
29 'webkitanimationiteration': 'webkitAnimationIteration',
30 'webkitanimationstart': 'webkitAnimationStart',
31 'webkitspeechchange': 'webkitSpeechChange',
32 })
33
34_html_event_types = monitored.Dict(
35 'htmleventgenerator._html_event_types',
36 {
37 '*.abort': ('abort', 'Event'),
38 '*.beforecopy': ('beforeCopy', 'Event'),
39 '*.beforecut': ('beforeCut', 'Event'),
40 '*.beforepaste': ('beforePaste', 'Event'),
41 '*.beforeunload': ('beforeUnload', 'Event'),
42 '*.blur': ('blur', 'Event'),
43 '*.canplay': ('canPlay', 'Event'),
44 '*.canplaythrough': ('canPlayThrough', 'Event'),
45 '*.change': ('change', 'Event'),
46 '*.click': ('click', 'MouseEvent'),
47 '*.contextmenu': ('contextMenu', 'MouseEvent'),
48 '*.copy': ('copy', 'ClipboardEvent'),
49 '*.cut': ('cut', 'ClipboardEvent'),
50 '*.dblclick': ('doubleClick', 'Event'),
51 '*.drag': ('drag', 'MouseEvent'),
52 '*.dragend': ('dragEnd', 'MouseEvent'),
53 '*.dragenter': ('dragEnter', 'MouseEvent'),
54 '*.dragleave': ('dragLeave', 'MouseEvent'),
55 '*.dragover': ('dragOver', 'MouseEvent'),
56 '*.dragstart': ('dragStart', 'MouseEvent'),
57 '*.drop': ('drop', 'MouseEvent'),
58 '*.durationchange': ('durationChange', 'Event'),
59 '*.emptied': ('emptied', 'Event'),
60 '*.ended': ('ended', 'Event'),
61 '*.error': ('error', 'Event'),
62 '*.focus': ('focus', 'Event'),
63 # Should be HashChangeEvent, but IE does not support it.
64 '*.hashchange': ('hashChange', 'Event'),
65 '*.input': ('input', 'Event'),
66 '*.invalid': ('invalid', 'Event'),
67 '*.keydown': ('keyDown', 'KeyboardEvent'),
68 '*.keypress': ('keyPress', 'KeyboardEvent'),
69 '*.keyup': ('keyUp', 'KeyboardEvent'),
70 '*.load': ('load', 'Event'),
71 '*.loadeddata': ('loadedData', 'Event'),
72 '*.loadedmetadata': ('loadedMetadata', 'Event'),
73 '*.message': ('message', 'MessageEvent'),
74 '*.mousedown': ('mouseDown', 'MouseEvent'),
75 '*.mouseenter': ('mouseEnter', 'MouseEvent'),
76 '*.mouseleave': ('mouseLeave', 'MouseEvent'),
77 '*.mousemove': ('mouseMove', 'MouseEvent'),
78 '*.mouseout': ('mouseOut', 'MouseEvent'),
79 '*.mouseover': ('mouseOver', 'MouseEvent'),
80 '*.mouseup': ('mouseUp', 'MouseEvent'),
81 '*.mousewheel': ('mouseWheel', 'WheelEvent'),
82 '*.offline': ('offline', 'Event'),
83 '*.online': ('online', 'Event'),
84 '*.paste': ('paste', 'ClipboardEvent'),
85 '*.pause': ('pause', 'Event'),
86 '*.play': ('play', 'Event'),
87 '*.playing': ('playing', 'Event'),
88 '*.popstate': ('popState', 'PopStateEvent'),
89 '*.ratechange': ('rateChange', 'Event'),
90 '*.reset': ('reset', 'Event'),
91 '*.resize': ('resize', 'Event'),
92 '*.scroll': ('scroll', 'Event'),
93 '*.search': ('search', 'Event'),
94 '*.seeked': ('seeked', 'Event'),
95 '*.seeking': ('seeking', 'Event'),
96 '*.select': ('select', 'Event'),
97 '*.selectstart': ('selectStart', 'Event'),
98 '*.stalled': ('stalled', 'Event'),
99 '*.storage': ('storage', 'StorageEvent'),
100 '*.submit': ('submit', 'Event'),
101 '*.suspend': ('suspend', 'Event'),
102 '*.timeupdate': ('timeUpdate', 'Event'),
103 '*.touchcancel': ('touchCancel', 'TouchEvent'),
104 '*.touchend': ('touchEnd', 'TouchEvent'),
105 '*.touchenter': ('touchEnter', 'TouchEvent'),
106 '*.touchleave': ('touchLeave', 'TouchEvent'),
107 '*.touchmove': ('touchMove', 'TouchEvent'),
108 '*.touchstart': ('touchStart', 'TouchEvent'),
109 '*.unload': ('unload', 'Event'),
110 '*.volumechange': ('volumeChange', 'Event'),
111 '*.waiting': ('waiting', 'Event'),
112 '*.webkitAnimationEnd': ('animationEnd', 'AnimationEvent'),
113 '*.webkitAnimationIteration': ('animationIteration', 'AnimationEvent'),
114 '*.webkitAnimationStart': ('animationStart', 'AnimationEvent'),
115 '*.transitionend': ('transitionEnd', 'TransitionEvent'),
116 '*.webkitfullscreenchange': ('fullscreenChange', 'Event'),
117 '*.webkitfullscreenerror': ('fullscreenError', 'Event'),
118 '*.wheel': ('wheel', 'WheelEvent'),
119 'AbstractWorker.error': ('error', 'Event'),
120 'AccessibleNode.accessibleclick': ('accessibleClick', 'Event'),
121 'AccessibleNode.accessiblecontextmenu':
122 ('accessibleContextMenu', 'Event'),
123 'AccessibleNode.accessibledecrement': ('accessibleDecrement', 'Event'),
124 'AccessibleNode.accessiblefocus': ('accessibleFocus', 'Event'),
125 'AccessibleNode.accessibleincrement': ('accessibleIncrement', 'Event'),
126 'AccessibleNode.accessiblescrollintoview':
127 ('accessibleScrollIntoView', 'Event'),
128 'Animation.finish': ('finish', 'Event'),
129 'Animation.cancel': ('cancel', 'Event'),
130 'AudioContext.complete': ('complete', 'Event'),
131 'ApplicationCache.cached': ('cached', 'Event'),
132 'ApplicationCache.checking': ('checking', 'Event'),
133 'ApplicationCache.downloading': ('downloading', 'Event'),
134 'ApplicationCache.noupdate': ('noUpdate', 'Event'),
135 'ApplicationCache.obsolete': ('obsolete', 'Event'),
136 'ApplicationCache.progress': ('progress', 'ProgressEvent'),
137 'ApplicationCache.updateready': ('updateReady', 'Event'),
138 'CompositorWorker.error': ('error', 'Event'),
139 'Document.readystatechange': ('readyStateChange', 'Event'),
140 'Document.securitypolicyviolation':
141 ('securityPolicyViolation', 'SecurityPolicyViolationEvent'),
142 'Document.selectionchange': ('selectionChange', 'Event'),
143 'Document.pointerlockchange': ('pointerLockChange', 'Event'),
144 'Document.pointerlockerror': ('pointerLockError', 'Event'),
145 'EventSource.open': ('open', 'Event'),
146 'FileReader.abort': ('abort', 'ProgressEvent'),
147 'FileReader.error': ('error', 'ProgressEvent'),
148 'FileReader.load': ('load', 'ProgressEvent'),
149 'FileReader.loadend': ('loadEnd', 'ProgressEvent'),
150 'FileReader.loadstart': ('loadStart', 'ProgressEvent'),
151 'FileReader.progress': ('progress', 'ProgressEvent'),
152 'FileWriter.abort': ('abort', 'ProgressEvent'),
153 'FileWriter.progress': ('progress', 'ProgressEvent'),
154 'FileWriter.write': ('write', 'ProgressEvent'),
155 'FileWriter.writeend': ('writeEnd', 'ProgressEvent'),
156 'FileWriter.writestart': ('writeStart', 'ProgressEvent'),
157 'FontFaceSet.loading': ('loading', 'FontFaceSetLoadEvent'),
158 'FontFaceSet.loadingdone': ('loadingDone', 'FontFaceSetLoadEvent'),
159 'FontFaceSet.loadingerror': ('loadingError', 'FontFaceSetLoadEvent'),
160 'HTMLBodyElement.storage': ('storage', 'StorageEvent'),
161 'HTMLCanvasElement.webglcontextlost':
162 ('webGlContextLost', 'gl.ContextEvent'),
163 'HTMLCanvasElement.webglcontextrestored':
164 ('webGlContextRestored', 'gl.ContextEvent'),
165 'HTMLInputElement.webkitSpeechChange': ('speechChange', 'Event'),
166 'HTMLFormElement.autocomplete': ('autocomplete', 'Event'),
167 'HTMLFormElement.autocompleteerror':
168 ('autocompleteError', 'AutocompleteErrorEvent'),
169 'HTMLMediaElement.loadstart': ('loadStart', 'Event'),
170 'HTMLMediaElement.progress': ('progress', 'Event'),
171 'HTMLMediaElement.show': ('show', 'Event'),
172 'HTMLMediaElement.webkitkeyadded': ('keyAdded', 'MediaKeyEvent'),
173 'HTMLMediaElement.webkitkeyerror': ('keyError', 'MediaKeyEvent'),
174 'HTMLMediaElement.webkitkeymessage': ('keyMessage', 'MediaKeyEvent'),
175 'HTMLMediaElement.webkitneedkey': ('needKey', 'MediaKeyEvent'),
176 'IDBDatabase.close': ('close', 'Event'),
177 'IDBDatabase.versionchange': ('versionChange', 'VersionChangeEvent'),
178 'IDBOpenDBRequest.blocked': ('blocked', 'Event'),
179 'IDBOpenDBRequest.upgradeneeded':
180 ('upgradeNeeded', 'VersionChangeEvent'),
181 'IDBRequest.success': ('success', 'Event'),
182 'IDBTransaction.complete': ('complete', 'Event'),
183 'MediaKeySession.webkitkeyadded': ('keyAdded', 'MediaKeyEvent'),
184 'MediaKeySession.webkitkeyerror': ('keyError', 'MediaKeyEvent'),
185 'MediaKeySession.webkitkeymessage': ('keyMessage', 'MediaKeyEvent'),
186 'MediaStream.addtrack': ('addTrack', 'Event'),
187 'MediaStream.removetrack': ('removeTrack', 'Event'),
188 'MediaStreamTrack.mute': ('mute', 'Event'),
189 'MediaStreamTrack.unmute': ('unmute', 'Event'),
190 'MIDIAccess.connect': ('connect', 'MidiConnectionEvent'),
191 'MIDIAccess.disconnect': ('disconnect', 'MidiConnectionEvent'),
192 'MIDIInput.midimessage': ('midiMessage', 'MidiMessageEvent'),
193 'MIDIPort.disconnect': ('disconnect', 'MidiConnectionEvent'),
194 'Notification.click': ('click', 'Event'),
195 'Notification.close': ('close', 'Event'),
196 'Notification.display': ('display', 'Event'),
197 'Notification.show': ('show', 'Event'),
198 'Performance.webkitresourcetimingbufferfull':
199 ('resourceTimingBufferFull', 'Event'),
200 'RTCDTMFSender.tonechange': ('toneChange', 'RtcDtmfToneChangeEvent'),
201 'RTCDataChannel.close': ('close', 'Event'),
202 'RTCDataChannel.open': ('open', 'Event'),
203 'RTCPeerConnection.addstream': ('addStream', 'MediaStreamEvent'),
204 'RTCPeerConnection.datachannel': ('dataChannel', 'RtcDataChannelEvent'),
205 'RTCPeerConnection.connectionstatechange':
206 ('connectionStateChange', 'Event'),
207 'RTCPeerConnection.icecandidate':
208 ('iceCandidate', 'RtcPeerConnectionIceEvent'),
209 'RTCPeerConnection.iceconnectionstatechange':
210 ('iceConnectionStateChange', 'Event'),
211 'RTCPeerConnection.negotiationneeded': ('negotiationNeeded', 'Event'),
212 'RTCPeerConnection.removestream': ('removeStream', 'MediaStreamEvent'),
213 'RTCPeerConnection.signalingstatechange':
214 ('signalingStateChange', 'Event'),
215 'RTCPeerConnection.track': ('track', 'RtcTrackEvent'),
216 'ScriptProcessorNode.audioprocess':
217 ('audioProcess', 'AudioProcessingEvent'),
218 'ServiceWorkerGlobalScope.activate': ('activate', 'Event'),
219 'ServiceWorkerGlobalScope.fetch': ('fetch', 'Event'),
220 'ServiceWorkerGlobalScope.install': ('install', 'Event'),
221 'ServiceWorkerGlobalScope.foreignfetch':
222 ('foreignfetch', 'ForeignFetchEvent'),
223 'SharedWorker.error': ('error', 'Event'),
224 'SharedWorkerGlobalScope.connect': ('connect', 'Event'),
225 'SpeechRecognition.audioend': ('audioEnd', 'Event'),
226 'SpeechRecognition.audiostart': ('audioStart', 'Event'),
227 'SpeechRecognition.end': ('end', 'Event'),
228 'SpeechRecognition.error': ('error', 'SpeechRecognitionError'),
229 'SpeechRecognition.nomatch': ('noMatch', 'SpeechRecognitionEvent'),
230 'SpeechRecognition.result': ('result', 'SpeechRecognitionEvent'),
231 'SpeechRecognition.soundend': ('soundEnd', 'Event'),
232 'SpeechRecognition.soundstart': ('soundStart', 'Event'),
233 'SpeechRecognition.speechend': ('speechEnd', 'Event'),
234 'SpeechRecognition.speechstart': ('speechStart', 'Event'),
235 'SpeechRecognition.start': ('start', 'Event'),
236 'SpeechSynthesisUtterance.boundary':
237 ('boundary', 'SpeechSynthesisEvent'),
238 'SpeechSynthesisUtterance.end': ('end', 'SpeechSynthesisEvent'),
239 'SpeechSynthesisUtterance.mark': ('mark', 'SpeechSynthesisEvent'),
240 'SpeechSynthesisUtterance.resume': ('resume', 'SpeechSynthesisEvent'),
241 'SpeechSynthesisUtterance.start': ('start', 'SpeechSynthesisEvent'),
242 'TextTrack.cuechange': ('cueChange', 'Event'),
243 'TextTrackCue.enter': ('enter', 'Event'),
244 'TextTrackCue.exit': ('exit', 'Event'),
245 'TextTrackList.addtrack': ('addTrack', 'TrackEvent'),
246 'WebSocket.close': ('close', 'CloseEvent'),
247 'WebSocket.open':
248 ('open', 'Event'), # should be OpenEvent, but not exposed.
249 'Window.DOMContentLoaded': ('contentLoaded', 'Event'),
250 'Window.devicemotion': ('deviceMotion', 'DeviceMotionEvent'),
251 'Window.deviceorientation':
252 ('deviceOrientation', 'DeviceOrientationEvent'),
253 'Window.loadstart': ('loadStart', 'Event'),
254 'Window.pagehide': ('pageHide', 'Event'),
255 'Window.pageshow': ('pageShow', 'Event'),
256 'Window.progress': ('progress', 'Event'),
257 'Window.webkittransitionend':
258 ('webkitTransitionEnd', 'TransitionEvent'),
259 'Window.wheel': ('wheel', 'WheelEvent'),
260 'Worker.error': ('error', 'Event'),
261 'XMLHttpRequestEventTarget.abort': ('abort', 'ProgressEvent'),
262 'XMLHttpRequestEventTarget.error': ('error', 'ProgressEvent'),
263 'XMLHttpRequestEventTarget.load': ('load', 'ProgressEvent'),
264 'XMLHttpRequestEventTarget.loadend': ('loadEnd', 'ProgressEvent'),
265 'XMLHttpRequestEventTarget.loadstart': ('loadStart', 'ProgressEvent'),
266 'XMLHttpRequestEventTarget.progress': ('progress', 'ProgressEvent'),
267 'XMLHttpRequestEventTarget.timeout': ('timeout', 'ProgressEvent'),
268 'XMLHttpRequest.readystatechange': ('readyStateChange', 'Event'),
269 })
270
271# These classes require an explicit declaration for the "on" method even though
272# they don't declare any unique events, because the concrete class hierarchy
273# doesn't match the interface hierarchy.
274_html_explicit_event_classes = set(['DocumentFragment'])
275
276
277class HtmlEventGenerator(object):
278
279 def __init__(self, database, renamer, metadata, template_loader):
280 self._event_classes = set()
281 self._database = database
282 self._renamer = renamer
283 self._metadata = metadata
284 self._template_loader = template_loader
285 self._media_events = None
286
287 def EmitStreamProviders(self, interface, custom_events, members_emitter,
288 library_name):
289
290 events = self.GetEvents(interface, custom_events)
291 if not events:
292 return
293
294 for event_info in events:
295 (dom_name, html_name, event_type) = event_info
296 annotation_name = dom_name + 'Event'
297
298 # If we're using a different provider, then don't declare one.
299 if self._GetEventRedirection(interface, html_name, event_type):
300 continue
301
302 annotations = self._metadata.FormatMetadata(
303 self._metadata.GetMetadata(library_name, interface,
304 annotation_name, 'on' + dom_name),
305 ' ')
306
307 members_emitter.Emit(
308 "\n"
309 " $(ANNOTATIONS)static const EventStreamProvider<$TYPE> "
310 "$(NAME)Event = const EventStreamProvider<$TYPE>('$DOM_NAME');\n",
311 ANNOTATIONS=annotations,
312 NAME=html_name,
313 DOM_NAME=dom_name,
314 TYPE=event_type)
315
317 interface,
318 custom_events,
319 members_emitter,
320 library_name,
321 stream_getter_signatures_emitter=None,
322 element_stream_getters_emitter=None):
323
324 events = self.GetEvents(interface, custom_events)
325 if not events:
326 return
327
328 for event_info in events:
329 (dom_name, html_name, event_type) = event_info
330 getter_name = 'on%s%s' % (html_name[:1].upper(), html_name[1:])
331 annotation_name = 'on' + dom_name
332
333 # If the provider is declared elsewhere, point to that.
334 redirection = self._GetEventRedirection(interface, html_name,
335 event_type)
336 if redirection:
337 provider = '%s.%sEvent' % (redirection, html_name)
338 else:
339 provider = html_name + 'Event'
340
341 annotations = self._metadata.GetFormattedMetadata(
342 library_name, interface, annotation_name, ' ')
343
344 isElement = False
345 for parent in self._database.Hierarchy(interface):
346 if parent.id == 'Element':
347 isElement = True
348 # We add the same event stream getters Element, ElementList, and
349 # _FrozenElementList. So, in impl_Element.darttemplate, we have two
350 # additional emitters to add the correct variation of the stream getter
351 # for that context.
352 for emitter in [
353 members_emitter, stream_getter_signatures_emitter,
354 element_stream_getters_emitter
355 ]:
356 if emitter == None:
357 continue
358 elem_type = 'Element' if isElement else ''
359 call_name = 'forElement' if isElement else 'forTarget'
360 if emitter == element_stream_getters_emitter:
361 call_name = '_forElementList'
362 emitter.Emit(
363 "\n"
364 " $(ANNOTATIONS)$(ELEM_TYPE)Stream<$TYPE> get $(NAME)$BODY;\n",
365 ANNOTATIONS=annotations,
366 ELEM_TYPE=elem_type,
367 NAME=getter_name,
368 BODY=('' if emitter == stream_getter_signatures_emitter else
369 ' => %s.%s(this)' % ((
370 ('' if emitter != element_stream_getters_emitter
371 else 'Element.') + provider), call_name)),
372 TYPE=event_type)
373
374 def _GetRawEvents(self, interface):
375 all_interfaces = (
376 [interface] + self._database.TransitiveSecondaryParents(
377 interface, False))
378 events = set([])
379 for super_interface in all_interfaces:
380 events = events.union(
381 set([
382 attr.id
383 for attr in super_interface.attributes
384 if (attr.type.id == 'EventHandler' or
385 attr.type.id == 'EventListener')
386 ]))
387 return events
388
389 def GetEvents(self, interface, custom_events):
390 """ Gets a list of all of the events for the specified interface.
391 """
392 html_interface_name = interface.doc_js_name
393
394 events = self._GetRawEvents(interface)
395
396 if html_interface_name in _html_manual_events:
397 events.update(_html_manual_events[html_interface_name])
398
399 if not events and interface.id not in _html_explicit_event_classes:
400 return None
401
402 dom_event_names = set()
403 for event in events:
404 dom_name = event[2:]
405 dom_name = _on_attribute_to_event_name_mapping.get(
406 dom_name, dom_name)
407 dom_event_names.add(dom_name)
408
409 events = []
410 for dom_name in sorted(dom_event_names):
411 event_info = self._FindEventInfo(html_interface_name, dom_name)
412 if not event_info:
413 continue
414
415 (html_name, event_type) = event_info
416 if self._IsEventSuppressed(interface, html_name, event_type):
417 continue
418
419 full_event_name = '%sEvents.%s' % (html_interface_name, html_name)
420 if not full_event_name in custom_events:
421 events.append((dom_name, html_name, event_type))
422 return events
423
424 def _HasEvent(self, events, event_name, event_type):
425 """ Checks if the event is declared in the list of events (from GetEvents),
426 with the same event type.
427 """
428 for (dom_name, html_name, found_type) in events:
429 if html_name == event_name and event_type == found_type:
430 return True
431 return False
432
433 def _IsEventSuppressed(self, interface, event_name, event_type):
434 """ Checks if the event should not be emitted.
435 """
436 if self._renamer.ShouldSuppressMember(interface, event_name, 'on:'):
437 return True
438
439 if (interface.doc_js_name == 'Window' or
440 interface.doc_js_name == 'Element' or
441 interface.doc_js_name == 'Document' or
442 interface.doc_js_name == 'GlobalEventHandlers'):
443 media_interface = self._database.GetInterface('HTMLMediaElement')
444 if not self._media_events:
445 self._media_events = self.GetEvents(media_interface, [])
446 if self._HasEvent(self._media_events, event_name, event_type):
447 return True
448
449 def _GetEventRedirection(self, interface, event_name, event_type):
450 """ For events which are declared in one place, but exposed elsewhere,
451 this gets the source of the event (where the provider is declared)
452 """
453 if interface.doc_js_name == 'Window' or interface.doc_js_name == 'Document':
454 element_interface = self._database.GetInterface('Element')
455 element_events = self.GetEvents(element_interface, [])
456 if self._HasEvent(element_events, event_name, event_type):
457 return 'Element'
458 return None
459
460 def _FindEventInfo(self, html_interface_name, dom_event_name):
461 """ Finds the event info (event name and type).
462 """
463 key = '%s.%s' % (html_interface_name, dom_event_name)
464 if key in _html_event_types:
465 return _html_event_types[key]
466 key = '*.%s' % dom_event_name
467 if key in _html_event_types:
468 return _html_event_types[key]
469 _logger.warn('Cannot resolve event type for %s.%s' %
470 (html_interface_name, dom_event_name))
471 return None
_IsEventSuppressed(self, interface, event_name, event_type)
EmitStreamGetters(self, interface, custom_events, members_emitter, library_name, stream_getter_signatures_emitter=None, element_stream_getters_emitter=None)
EmitStreamProviders(self, interface, custom_events, members_emitter, library_name)
__init__(self, database, renamer, metadata, template_loader)
_FindEventInfo(self, html_interface_name, dom_event_name)
_GetEventRedirection(self, interface, event_name, event_type)
_HasEvent(self, events, event_name, event_type)