WebKitTools:
[WebKit-https.git] / WebKitTools / DumpRenderTree / mac / EventSendingController.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2006 Jonas Witt <jonas.witt@gmail.com>
4  * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
5  * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer. 
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution. 
16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission. 
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #import "EventSendingController.h"
33
34 #import "DumpRenderTree.h"
35 #import "DumpRenderTreeDraggingInfo.h"
36
37 #import <Carbon/Carbon.h>                           // for GetCurrentEventTime()
38 #import <WebKit/WebKit.h>
39 #import <WebKit/DOMPrivate.h>
40
41 extern "C" void _NSNewKillRingSequence();
42
43 NSPoint lastMousePosition;
44 NSPoint lastClickPosition;
45 NSArray *webkitDomEventNames;
46 NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once.
47 BOOL replayingSavedEvents;
48
49 @implementation EventSendingController
50
51 + (void)initialize
52 {
53     webkitDomEventNames = [[NSArray alloc] initWithObjects:
54         @"abort",
55         @"beforecopy",
56         @"beforecut",
57         @"beforepaste",
58         @"blur",
59         @"change",
60         @"click",
61         @"contextmenu",
62         @"copy",
63         @"cut",
64         @"dblclick",
65         @"drag",
66         @"dragend",
67         @"dragenter",
68         @"dragleave",
69         @"dragover",
70         @"dragstart",
71         @"drop",
72         @"error",
73         @"focus",
74         @"input",
75         @"keydown",
76         @"keypress",
77         @"keyup",
78         @"load",
79         @"mousedown",
80         @"mousemove",
81         @"mouseout",
82         @"mouseover",
83         @"mouseup",
84         @"mousewheel",
85         @"beforeunload",
86         @"paste",
87         @"readystatechange",
88         @"reset",
89         @"resize", 
90         @"scroll", 
91         @"search",
92         @"select",
93         @"selectstart",
94         @"submit", 
95         @"textInput", 
96         @"textzoomin",
97         @"textzoomout",
98         @"unload",
99         @"zoom",
100         nil];
101 }
102
103 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
104 {
105     if (aSelector == @selector(mouseDown)
106             || aSelector == @selector(mouseUp)
107             || aSelector == @selector(contextClick)
108             || aSelector == @selector(mouseMoveToX:Y:)
109             || aSelector == @selector(leapForward:)
110             || aSelector == @selector(keyDown:withModifiers:)
111             || aSelector == @selector(enableDOMUIEventLogging:)
112             || aSelector == @selector(fireKeyboardEventsToElement:)
113             || aSelector == @selector(clearKillRing)
114             || aSelector == @selector(textZoomIn)
115             || aSelector == @selector(textZoomOut))
116         return NO;
117     return YES;
118 }
119
120 + (BOOL)isKeyExcludedFromWebScript:(const char*)name
121 {
122     if (strcmp(name, "dragMode") == 0)
123         return NO;
124     return YES;
125 }
126
127 + (NSString *)webScriptNameForSelector:(SEL)aSelector
128 {
129     if (aSelector == @selector(mouseMoveToX:Y:))
130         return @"mouseMoveTo";
131     if (aSelector == @selector(leapForward:))
132         return @"leapForward";
133     if (aSelector == @selector(keyDown:withModifiers:))
134         return @"keyDown";
135     if (aSelector == @selector(enableDOMUIEventLogging:))
136         return @"enableDOMUIEventLogging";
137     if (aSelector == @selector(fireKeyboardEventsToElement:))
138         return @"fireKeyboardEventsToElement";
139     if (aSelector == @selector(setDragMode:))
140         return @"setDragMode";
141     return nil;
142 }
143
144 - (id)init
145 {
146     self = [super init];
147     if (self)
148         dragMode = YES;
149     return self;
150 }
151
152 - (void)dealloc
153 {
154     [super dealloc];
155 }
156
157 - (double)currentEventTime
158 {
159     return GetCurrentEventTime() + timeOffset;
160 }
161
162 - (void)leapForward:(int)milliseconds
163 {
164     if (dragMode && down && !replayingSavedEvents) {
165         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]];
166         [invocation setTarget:self];
167         [invocation setSelector:@selector(leapForward:)];
168         [invocation setArgument:&milliseconds atIndex:2];
169         
170         [EventSendingController saveEvent:invocation];
171         
172         return;
173     }
174
175     timeOffset += milliseconds / 1000.0;
176 }
177
178 - (void)clearKillRing
179 {
180     _NSNewKillRingSequence();
181 }
182
183 - (void)mouseDown
184 {
185     [[[mainFrame frameView] documentView] layout];
186     if (([self currentEventTime] - lastClick >= 1) ||
187         !NSEqualPoints(lastMousePosition, lastClickPosition))
188         clickCount = 1;
189     else
190         clickCount++;
191     NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseDown 
192                                         location:lastMousePosition 
193                                    modifierFlags:0 
194                                        timestamp:[self currentEventTime]
195                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
196                                          context:[NSGraphicsContext currentContext] 
197                                      eventNumber:++eventNumber 
198                                       clickCount:clickCount 
199                                         pressure:0.0];
200
201     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
202     if (subView) {
203         [subView mouseDown:event];
204         down = YES;
205     }
206 }
207
208 - (void)textZoomIn
209 {
210     [[mainFrame webView] makeTextLarger:self];
211 }
212
213 - (void)textZoomOut
214 {
215     [[mainFrame webView] makeTextSmaller:self];
216 }
217
218 - (void)mouseUp
219 {
220     if (dragMode && !replayingSavedEvents) {
221         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp)]];
222         [invocation setTarget:self];
223         [invocation setSelector:@selector(mouseUp)];
224         
225         [EventSendingController saveEvent:invocation];
226         [EventSendingController replaySavedEvents];
227
228         return;
229     }
230
231     [[[mainFrame frameView] documentView] layout];
232     NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseUp 
233                                         location:lastMousePosition 
234                                    modifierFlags:0 
235                                        timestamp:[self currentEventTime]
236                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
237                                          context:[NSGraphicsContext currentContext] 
238                                      eventNumber:++eventNumber 
239                                       clickCount:clickCount 
240                                         pressure:0.0];
241
242     NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]];
243     // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView.
244     // The right solution is just to use NSApplication's built-in event sending methods, 
245     // instead of rolling our own algorithm for selecting an event target.
246     targetView = targetView ? targetView : [[mainFrame frameView] documentView];
247     assert(targetView);
248     [targetView mouseUp:event];
249     down = NO;
250     lastClick = [event timestamp];
251     lastClickPosition = lastMousePosition;
252     if (draggingInfo) {
253         WebView *webView = [mainFrame webView];
254         
255         NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo];
256         
257         if (dragOperation != NSDragOperationNone)
258             [webView performDragOperation:draggingInfo];
259         else
260             [webView draggingExited:draggingInfo];
261         [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation];
262         [draggingInfo release];
263         draggingInfo = nil;
264     }
265 }
266
267 - (void)mouseMoveToX:(int)x Y:(int)y
268 {
269     if (dragMode && down && !replayingSavedEvents) {
270         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]];
271         [invocation setTarget:self];
272         [invocation setSelector:@selector(mouseMoveToX:Y:)];
273         [invocation setArgument:&x atIndex:2];
274         [invocation setArgument:&y atIndex:3];
275         
276         [EventSendingController saveEvent:invocation];
277         
278         return;
279     }
280
281     NSView *view = [mainFrame webView];
282     lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil];
283     NSEvent *event = [NSEvent mouseEventWithType:(down ? NSLeftMouseDragged : NSMouseMoved) 
284                                         location:lastMousePosition 
285                                    modifierFlags:0 
286                                        timestamp:[self currentEventTime]
287                                     windowNumber:[[view window] windowNumber] 
288                                          context:[NSGraphicsContext currentContext] 
289                                      eventNumber:++eventNumber 
290                                       clickCount:(down ? clickCount : 0) 
291                                         pressure:0.0];
292
293     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
294     if (subView) {
295         if (down) {
296             [subView mouseDragged:event];
297             if (draggingInfo) {
298                 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition];
299                 [[mainFrame webView] draggingUpdated:draggingInfo];
300             }
301         } else
302             [subView mouseMoved:event];
303     }
304 }
305
306 - (void)contextClick
307 {
308     [[[mainFrame frameView] documentView] layout];
309     if ([self currentEventTime] - lastClick >= 1)
310         clickCount = 1;
311     else
312         clickCount++;
313     NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown 
314                                         location:lastMousePosition 
315                                    modifierFlags:0 
316                                        timestamp:[self currentEventTime]
317                                     windowNumber:[[[mainFrame webView] window] windowNumber] 
318                                          context:[NSGraphicsContext currentContext] 
319                                      eventNumber:++eventNumber 
320                                       clickCount:clickCount 
321                                         pressure:0.0];
322
323     NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
324     if (subView)
325         [subView menuForEvent:event];
326 }
327
328 + (void)saveEvent:(NSInvocation *)event
329 {
330     if (!savedMouseEvents)
331         savedMouseEvents = [[NSMutableArray alloc] init];
332     [savedMouseEvents addObject:event];
333 }
334
335 + (void)replaySavedEvents
336 {
337     replayingSavedEvents = YES;
338     while ([savedMouseEvents count]) {
339         // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate
340         NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease];
341         [savedMouseEvents removeObjectAtIndex:0];
342         [invocation invoke];
343     }
344     replayingSavedEvents = NO;
345 }
346
347 + (void)clearSavedEvents
348 {
349     [savedMouseEvents release];
350     savedMouseEvents = nil;
351 }
352
353 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers
354 {
355     NSString *eventCharacter = character;
356     if ([character isEqualToString:@"leftArrow"]) {
357         const unichar ch = NSLeftArrowFunctionKey;
358         eventCharacter = [NSString stringWithCharacters:&ch length:1];
359     } else if ([character isEqualToString:@"rightArrow"]) {
360         const unichar ch = NSRightArrowFunctionKey;
361         eventCharacter = [NSString stringWithCharacters:&ch length:1];
362     } else if ([character isEqualToString:@"upArrow"]) {
363         const unichar ch = NSUpArrowFunctionKey;
364         eventCharacter = [NSString stringWithCharacters:&ch length:1];
365     } else if ([character isEqualToString:@"downArrow"]) {
366         const unichar ch = NSDownArrowFunctionKey;
367         eventCharacter = [NSString stringWithCharacters:&ch length:1];
368     } else if ([character isEqualToString:@"pageUp"]) {
369         const unichar ch = NSPageUpFunctionKey;
370         eventCharacter = [NSString stringWithCharacters:&ch length:1];
371     } else if ([character isEqualToString:@"pageDown"]) {
372         const unichar ch = NSPageDownFunctionKey;
373         eventCharacter = [NSString stringWithCharacters:&ch length:1];
374     } else if ([character isEqualToString:@"home"]) {
375         const unichar ch = NSHomeFunctionKey;
376         eventCharacter = [NSString stringWithCharacters:&ch length:1];
377     } else if ([character isEqualToString:@"end"]) {
378         const unichar ch = NSEndFunctionKey;
379         eventCharacter = [NSString stringWithCharacters:&ch length:1];
380     } else if ([character isEqualToString:@"delete"]) {
381         const unichar ch = 0x7f;
382         eventCharacter = [NSString stringWithCharacters:&ch length:1];
383     }
384
385     NSString *charactersIgnoringModifiers = eventCharacter;
386
387     int modifierFlags = 0;
388
389     if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
390         modifierFlags |= NSShiftKeyMask;
391         charactersIgnoringModifiers = [character lowercaseString];
392     }
393
394     if ([modifiers isKindOfClass:[WebScriptObject class]])
395         for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
396             NSString *modifier = (NSString *)[modifiers webScriptValueAtIndex:i];
397             if ([modifier isEqual:@"ctrlKey"])
398                 modifierFlags |= NSControlKeyMask;
399             else if ([modifier isEqual:@"shiftKey"])
400                 modifierFlags |= NSShiftKeyMask;
401             else if ([modifier isEqual:@"altKey"])
402                 modifierFlags |= NSAlternateKeyMask;
403             else if ([modifier isEqual:@"metaKey"])
404                 modifierFlags |= NSCommandKeyMask;
405         }
406
407     [[[mainFrame frameView] documentView] layout];
408
409     NSEvent *event = [NSEvent keyEventWithType:NSKeyDown
410                         location:NSMakePoint(5, 5)
411                         modifierFlags:modifierFlags
412                         timestamp:[self currentEventTime]
413                         windowNumber:[[[mainFrame webView] window] windowNumber]
414                         context:[NSGraphicsContext currentContext]
415                         characters:eventCharacter
416                         charactersIgnoringModifiers:charactersIgnoringModifiers
417                         isARepeat:NO
418                         keyCode:0];
419
420     [[[[mainFrame webView] window] firstResponder] keyDown:event];
421
422     event = [NSEvent keyEventWithType:NSKeyUp
423                         location:NSMakePoint(5, 5)
424                         modifierFlags:modifierFlags
425                         timestamp:[self currentEventTime]
426                         windowNumber:[[[mainFrame webView] window] windowNumber]
427                         context:[NSGraphicsContext currentContext]
428                         characters:eventCharacter
429                         charactersIgnoringModifiers:charactersIgnoringModifiers
430                         isARepeat:NO
431                         keyCode:0];
432
433     [[[[mainFrame webView] window] firstResponder] keyUp:event];
434 }
435
436 - (void)enableDOMUIEventLogging:(WebScriptObject *)node
437 {
438     NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator];
439     id eventName;
440     while ((eventName = [eventEnumerator nextObject])) {
441         [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO];
442     }
443 }
444
445 - (void)handleEvent:(DOMEvent *)event
446 {
447     DOMNode *target = [event target];
448
449     printf("event type:      %s\n", [[event type] UTF8String]);
450     printf("  target:        <%s>\n", [[[target nodeName] lowercaseString] UTF8String]);
451     
452     if ([event isKindOfClass:[DOMEvent class]]) {
453         printf("  eventPhase:    %d\n", [event eventPhase]);
454         printf("  bubbles:       %d\n", [event bubbles] ? 1 : 0);
455         printf("  cancelable:    %d\n", [event cancelable] ? 1 : 0);
456     }
457     
458     if ([event isKindOfClass:[DOMUIEvent class]]) {
459         printf("  detail:        %d\n", [(DOMUIEvent*)event detail]);
460         
461         DOMAbstractView *view = [(DOMUIEvent*)event view];
462         if (view) {
463             printf("  view:          OK");            
464             if ([view document])
465                 printf(" (document: OK)");
466             printf("\n");
467         }
468     }
469     
470     if ([event isKindOfClass:[DOMKeyboardEvent class]]) {
471         printf("  keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]);
472         printf("  keyLocation:   %d\n", [(DOMKeyboardEvent*)event keyLocation]);
473         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
474                [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, 
475                [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, 
476                [(DOMKeyboardEvent*)event altKey] ? 1 : 0, 
477                [(DOMKeyboardEvent*)event metaKey] ? 1 : 0);
478         printf("  keyCode:       %d\n", [(DOMKeyboardEvent*)event keyCode]);
479         printf("  charCode:      %d\n", [(DOMKeyboardEvent*)event charCode]);
480     }
481     
482     if ([event isKindOfClass:[DOMMouseEvent class]]) {
483         printf("  button:        %d\n", [(DOMMouseEvent*)event button]);
484         printf("  clientX:       %d\n", [(DOMMouseEvent*)event clientX]);
485         printf("  clientY:       %d\n", [(DOMMouseEvent*)event clientY]);
486         printf("  screenX:       %d\n", [(DOMMouseEvent*)event screenX]);
487         printf("  screenY:       %d\n", [(DOMMouseEvent*)event screenY]);
488         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
489                [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, 
490                [(DOMMouseEvent*)event shiftKey] ? 1 : 0, 
491                [(DOMMouseEvent*)event altKey] ? 1 : 0, 
492                [(DOMMouseEvent*)event metaKey] ? 1 : 0);
493         id relatedTarget = [(DOMMouseEvent*)event relatedTarget];
494         if (relatedTarget) {
495             printf("  relatedTarget: %s", [[[relatedTarget class] description] UTF8String]);
496             if ([relatedTarget isKindOfClass:[DOMNode class]])
497                 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]);
498             printf("\n");
499         }
500     }
501     
502     if ([event isKindOfClass:[DOMMutationEvent class]]) {
503         printf("  prevValue:     %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]);
504         printf("  newValue:      %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]);
505         printf("  attrName:      %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]);
506         printf("  attrChange:    %d\n", [(DOMMutationEvent*)event attrChange]);
507         DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode];
508         if (relatedNode) {
509             printf("  relatedNode:   %s (nodeName: %s)\n", 
510                    [[[relatedNode class] description] UTF8String],
511                    [[relatedNode nodeName] UTF8String]);
512         }
513     }
514     
515     if ([event isKindOfClass:[DOMWheelEvent class]]) {
516         printf("  clientX:       %d\n", [(DOMWheelEvent*)event clientX]);
517         printf("  clientY:       %d\n", [(DOMWheelEvent*)event clientY]);
518         printf("  screenX:       %d\n", [(DOMWheelEvent*)event screenX]);
519         printf("  screenY:       %d\n", [(DOMWheelEvent*)event screenY]);
520         printf("  modifier keys: c:%d s:%d a:%d m:%d\n", 
521                [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, 
522                [(DOMWheelEvent*)event shiftKey] ? 1 : 0, 
523                [(DOMWheelEvent*)event altKey] ? 1 : 0, 
524                [(DOMWheelEvent*)event metaKey] ? 1 : 0);
525         printf("  isHorizontal:  %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0);
526         printf("  wheelDelta:    %d\n", [(DOMWheelEvent*)event wheelDelta]);
527     }
528 }
529
530 // FIXME: It's not good to have a test hard-wired into this controller like this.
531 // Instead we need to get testing framework based on the Objective-C bindings
532 // to work well enough that we can test that way instead.
533 - (void)fireKeyboardEventsToElement:(WebScriptObject *)element {
534     
535     if (![element isKindOfClass:[DOMHTMLElement class]]) {
536         return;
537     }
538     
539     DOMHTMLElement *target = (DOMHTMLElement*)element;
540     DOMDocument *document = [target ownerDocument];
541     
542     // Keyboard Event 1
543     
544     DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"];
545     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" 
546                                          canBubble:YES
547                                         cancelable:YES
548                                               view:[document defaultView]
549                                      keyIdentifier:@"U+000041" 
550                                        keyLocation:0
551                                            ctrlKey:YES
552                                             altKey:NO
553                                           shiftKey:NO
554                                            metaKey:NO];
555     [target dispatchEvent:domEvent];  
556         
557     // Keyboard Event 2
558     
559     domEvent = [document createEvent:@"KeyboardEvent"];
560     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" 
561                                          canBubble:YES
562                                         cancelable:YES
563                                               view:[document defaultView]
564                                      keyIdentifier:@"U+000045" 
565                                        keyLocation:1
566                                            ctrlKey:NO
567                                             altKey:YES
568                                           shiftKey:NO
569                                            metaKey:NO];
570     [target dispatchEvent:domEvent];    
571     
572     // Keyboard Event 3
573     
574     domEvent = [document createEvent:@"KeyboardEvent"];
575     [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" 
576                                          canBubble:YES
577                                         cancelable:YES
578                                               view:[document defaultView]
579                                      keyIdentifier:@"U+000056" 
580                                        keyLocation:0
581                                            ctrlKey:NO
582                                             altKey:NO
583                                           shiftKey:NO
584                                            metaKey:NO];
585     [target dispatchEvent:domEvent];   
586     
587 }
588
589 @end