|
1 /* |
|
2 * Copyright (C) 2005, 2006 Apple Computer, 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 NSArray *webkitDomEventNames; |
|
45 NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once. |
|
46 BOOL replayingSavedEvents; |
|
47 |
|
48 @implementation EventSendingController |
|
49 |
|
50 + (void)initialize |
|
51 { |
|
52 webkitDomEventNames = [[NSArray alloc] initWithObjects: |
|
53 @"abort", |
|
54 @"beforecopy", |
|
55 @"beforecut", |
|
56 @"beforepaste", |
|
57 @"blur", |
|
58 @"change", |
|
59 @"click", |
|
60 @"contextmenu", |
|
61 @"copy", |
|
62 @"cut", |
|
63 @"dblclick", |
|
64 @"drag", |
|
65 @"dragend", |
|
66 @"dragenter", |
|
67 @"dragleave", |
|
68 @"dragover", |
|
69 @"dragstart", |
|
70 @"drop", |
|
71 @"error", |
|
72 @"focus", |
|
73 @"input", |
|
74 @"keydown", |
|
75 @"keypress", |
|
76 @"keyup", |
|
77 @"load", |
|
78 @"mousedown", |
|
79 @"mousemove", |
|
80 @"mouseout", |
|
81 @"mouseover", |
|
82 @"mouseup", |
|
83 @"mousewheel", |
|
84 @"beforeunload", |
|
85 @"paste", |
|
86 @"readystatechange", |
|
87 @"reset", |
|
88 @"resize", |
|
89 @"scroll", |
|
90 @"search", |
|
91 @"select", |
|
92 @"selectstart", |
|
93 @"submit", |
|
94 @"textInput", |
|
95 @"textzoomin", |
|
96 @"textzoomout", |
|
97 @"unload", |
|
98 @"zoom", |
|
99 nil]; |
|
100 } |
|
101 |
|
102 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector |
|
103 { |
|
104 if (aSelector == @selector(mouseDown) |
|
105 || aSelector == @selector(mouseUp) |
|
106 || aSelector == @selector(contextClick) |
|
107 || aSelector == @selector(mouseMoveToX:Y:) |
|
108 || aSelector == @selector(leapForward:) |
|
109 || aSelector == @selector(keyDown:withModifiers:) |
|
110 || aSelector == @selector(enableDOMUIEventLogging:) |
|
111 || aSelector == @selector(fireKeyboardEventsToElement:) |
|
112 || aSelector == @selector(clearKillRing) |
|
113 || aSelector == @selector(textZoomIn) |
|
114 || aSelector == @selector(textZoomOut)) |
|
115 return NO; |
|
116 return YES; |
|
117 } |
|
118 |
|
119 + (BOOL)isKeyExcludedFromWebScript:(const char*)name |
|
120 { |
|
121 if (strcmp(name, "dragMode") == 0) |
|
122 return NO; |
|
123 return YES; |
|
124 } |
|
125 |
|
126 + (NSString *)webScriptNameForSelector:(SEL)aSelector |
|
127 { |
|
128 if (aSelector == @selector(mouseMoveToX:Y:)) |
|
129 return @"mouseMoveTo"; |
|
130 if (aSelector == @selector(leapForward:)) |
|
131 return @"leapForward"; |
|
132 if (aSelector == @selector(keyDown:withModifiers:)) |
|
133 return @"keyDown"; |
|
134 if (aSelector == @selector(enableDOMUIEventLogging:)) |
|
135 return @"enableDOMUIEventLogging"; |
|
136 if (aSelector == @selector(fireKeyboardEventsToElement:)) |
|
137 return @"fireKeyboardEventsToElement"; |
|
138 if (aSelector == @selector(setDragMode:)) |
|
139 return @"setDragMode"; |
|
140 return nil; |
|
141 } |
|
142 |
|
143 - (id)init |
|
144 { |
|
145 self = [super init]; |
|
146 if (self) |
|
147 dragMode = YES; |
|
148 return self; |
|
149 } |
|
150 |
|
151 - (void)dealloc |
|
152 { |
|
153 [super dealloc]; |
|
154 } |
|
155 |
|
156 - (double)currentEventTime |
|
157 { |
|
158 return GetCurrentEventTime() + timeOffset; |
|
159 } |
|
160 |
|
161 - (void)leapForward:(int)milliseconds |
|
162 { |
|
163 if (dragMode && down && !replayingSavedEvents) { |
|
164 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]]; |
|
165 [invocation setTarget:self]; |
|
166 [invocation setSelector:@selector(leapForward:)]; |
|
167 [invocation setArgument:&milliseconds atIndex:2]; |
|
168 |
|
169 [EventSendingController saveEvent:invocation]; |
|
170 |
|
171 return; |
|
172 } |
|
173 |
|
174 timeOffset += milliseconds / 1000.0; |
|
175 } |
|
176 |
|
177 - (void)clearKillRing |
|
178 { |
|
179 _NSNewKillRingSequence(); |
|
180 } |
|
181 |
|
182 - (void)mouseDown |
|
183 { |
|
184 [[[mainFrame frameView] documentView] layout]; |
|
185 if ([self currentEventTime] - lastClick >= 1) |
|
186 clickCount = 1; |
|
187 else |
|
188 clickCount++; |
|
189 NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseDown |
|
190 location:lastMousePosition |
|
191 modifierFlags:0 |
|
192 timestamp:[self currentEventTime] |
|
193 windowNumber:[[[mainFrame webView] window] windowNumber] |
|
194 context:[NSGraphicsContext currentContext] |
|
195 eventNumber:++eventNumber |
|
196 clickCount:clickCount |
|
197 pressure:0.0]; |
|
198 |
|
199 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
|
200 if (subView) { |
|
201 [subView mouseDown:event]; |
|
202 down = YES; |
|
203 } |
|
204 } |
|
205 |
|
206 - (void)textZoomIn |
|
207 { |
|
208 [[mainFrame webView] makeTextLarger:self]; |
|
209 } |
|
210 |
|
211 - (void)textZoomOut |
|
212 { |
|
213 [[mainFrame webView] makeTextSmaller:self]; |
|
214 } |
|
215 |
|
216 - (void)mouseUp |
|
217 { |
|
218 if (dragMode && !replayingSavedEvents) { |
|
219 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp)]]; |
|
220 [invocation setTarget:self]; |
|
221 [invocation setSelector:@selector(mouseUp)]; |
|
222 |
|
223 [EventSendingController saveEvent:invocation]; |
|
224 [EventSendingController replaySavedEvents]; |
|
225 |
|
226 return; |
|
227 } |
|
228 |
|
229 [[[mainFrame frameView] documentView] layout]; |
|
230 NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseUp |
|
231 location:lastMousePosition |
|
232 modifierFlags:0 |
|
233 timestamp:[self currentEventTime] |
|
234 windowNumber:[[[mainFrame webView] window] windowNumber] |
|
235 context:[NSGraphicsContext currentContext] |
|
236 eventNumber:++eventNumber |
|
237 clickCount:clickCount |
|
238 pressure:0.0]; |
|
239 |
|
240 NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
|
241 // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView. |
|
242 // The right solution is just to use NSApplication's built-in event sending methods, |
|
243 // instead of rolling our own algorithm for selecting an event target. |
|
244 targetView = targetView ? targetView : [[mainFrame frameView] documentView]; |
|
245 assert(targetView); |
|
246 [targetView mouseUp:event]; |
|
247 down = NO; |
|
248 lastClick = [event timestamp]; |
|
249 if (draggingInfo) { |
|
250 WebView *webView = [mainFrame webView]; |
|
251 |
|
252 NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo]; |
|
253 |
|
254 if (dragOperation != NSDragOperationNone) |
|
255 [webView performDragOperation:draggingInfo]; |
|
256 else |
|
257 [webView draggingExited:draggingInfo]; |
|
258 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation]; |
|
259 [draggingInfo release]; |
|
260 draggingInfo = nil; |
|
261 } |
|
262 } |
|
263 |
|
264 - (void)mouseMoveToX:(int)x Y:(int)y |
|
265 { |
|
266 if (dragMode && down && !replayingSavedEvents) { |
|
267 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]]; |
|
268 [invocation setTarget:self]; |
|
269 [invocation setSelector:@selector(mouseMoveToX:Y:)]; |
|
270 [invocation setArgument:&x atIndex:2]; |
|
271 [invocation setArgument:&y atIndex:3]; |
|
272 |
|
273 [EventSendingController saveEvent:invocation]; |
|
274 |
|
275 return; |
|
276 } |
|
277 |
|
278 NSView *view = [mainFrame webView]; |
|
279 lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil]; |
|
280 NSEvent *event = [NSEvent mouseEventWithType:(down ? NSLeftMouseDragged : NSMouseMoved) |
|
281 location:lastMousePosition |
|
282 modifierFlags:0 |
|
283 timestamp:[self currentEventTime] |
|
284 windowNumber:[[view window] windowNumber] |
|
285 context:[NSGraphicsContext currentContext] |
|
286 eventNumber:++eventNumber |
|
287 clickCount:(down ? clickCount : 0) |
|
288 pressure:0.0]; |
|
289 |
|
290 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
|
291 if (subView) { |
|
292 if (down) { |
|
293 [subView mouseDragged:event]; |
|
294 if (draggingInfo) { |
|
295 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition]; |
|
296 [[mainFrame webView] draggingUpdated:draggingInfo]; |
|
297 } |
|
298 } else |
|
299 [subView mouseMoved:event]; |
|
300 } |
|
301 } |
|
302 |
|
303 - (void)contextClick |
|
304 { |
|
305 [[[mainFrame frameView] documentView] layout]; |
|
306 if ([self currentEventTime] - lastClick >= 1) |
|
307 clickCount = 1; |
|
308 else |
|
309 clickCount++; |
|
310 NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown |
|
311 location:lastMousePosition |
|
312 modifierFlags:0 |
|
313 timestamp:[self currentEventTime] |
|
314 windowNumber:[[[mainFrame webView] window] windowNumber] |
|
315 context:[NSGraphicsContext currentContext] |
|
316 eventNumber:++eventNumber |
|
317 clickCount:clickCount |
|
318 pressure:0.0]; |
|
319 |
|
320 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
|
321 if (subView) |
|
322 [subView menuForEvent:event]; |
|
323 } |
|
324 |
|
325 + (void)saveEvent:(NSInvocation *)event |
|
326 { |
|
327 if (!savedMouseEvents) |
|
328 savedMouseEvents = [[NSMutableArray alloc] init]; |
|
329 [savedMouseEvents addObject:event]; |
|
330 } |
|
331 |
|
332 + (void)replaySavedEvents |
|
333 { |
|
334 replayingSavedEvents = YES; |
|
335 while ([savedMouseEvents count]) { |
|
336 // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate |
|
337 NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease]; |
|
338 [savedMouseEvents removeObjectAtIndex:0]; |
|
339 [invocation invoke]; |
|
340 } |
|
341 replayingSavedEvents = NO; |
|
342 } |
|
343 |
|
344 + (void)clearSavedEvents |
|
345 { |
|
346 [savedMouseEvents release]; |
|
347 savedMouseEvents = nil; |
|
348 } |
|
349 |
|
350 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers |
|
351 { |
|
352 NSString *modifier = nil; |
|
353 int mask = 0; |
|
354 |
|
355 if ([modifiers isKindOfClass:[WebScriptObject class]]) |
|
356 for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { |
|
357 modifier = (NSString *)[modifiers webScriptValueAtIndex:i]; |
|
358 if ([modifier isEqual:@"ctrlKey"]) |
|
359 mask |= NSControlKeyMask; |
|
360 else if ([modifier isEqual:@"shiftKey"]) |
|
361 mask |= NSShiftKeyMask; |
|
362 else if ([modifier isEqual:@"altKey"]) |
|
363 mask |= NSAlternateKeyMask; |
|
364 else if ([modifier isEqual:@"metaKey"]) |
|
365 mask |= NSCommandKeyMask; |
|
366 else |
|
367 break; |
|
368 } |
|
369 |
|
370 [[[mainFrame frameView] documentView] layout]; |
|
371 |
|
372 NSEvent *event = [NSEvent keyEventWithType:NSKeyDown |
|
373 location:NSMakePoint(5, 5) |
|
374 modifierFlags:mask |
|
375 timestamp:[self currentEventTime] |
|
376 windowNumber:[[[mainFrame webView] window] windowNumber] |
|
377 context:[NSGraphicsContext currentContext] |
|
378 characters:character |
|
379 charactersIgnoringModifiers:character |
|
380 isARepeat:NO |
|
381 keyCode:0]; |
|
382 |
|
383 |
|
384 NSResponder *firstResponder = [[[mainFrame webView] window] firstResponder]; |
|
385 [firstResponder keyDown:event]; |
|
386 } |
|
387 |
|
388 - (void)enableDOMUIEventLogging:(WebScriptObject *)node |
|
389 { |
|
390 NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator]; |
|
391 id eventName; |
|
392 while ((eventName = [eventEnumerator nextObject])) { |
|
393 [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO]; |
|
394 } |
|
395 } |
|
396 |
|
397 - (void)handleEvent:(DOMEvent *)event |
|
398 { |
|
399 DOMNode *target = [event target]; |
|
400 |
|
401 printf("event type: %s\n", [[event type] UTF8String]); |
|
402 printf(" target: <%s>\n", [[[target nodeName] lowercaseString] UTF8String]); |
|
403 |
|
404 if ([event isKindOfClass:[DOMEvent class]]) { |
|
405 printf(" eventPhase: %d\n", [event eventPhase]); |
|
406 printf(" bubbles: %d\n", [event bubbles] ? 1 : 0); |
|
407 printf(" cancelable: %d\n", [event cancelable] ? 1 : 0); |
|
408 } |
|
409 |
|
410 if ([event isKindOfClass:[DOMUIEvent class]]) { |
|
411 printf(" detail: %d\n", [(DOMUIEvent*)event detail]); |
|
412 |
|
413 DOMAbstractView *view = [(DOMUIEvent*)event view]; |
|
414 if (view) { |
|
415 printf(" view: OK"); |
|
416 if ([view document]) |
|
417 printf(" (document: OK)"); |
|
418 printf("\n"); |
|
419 } |
|
420 } |
|
421 |
|
422 if ([event isKindOfClass:[DOMKeyboardEvent class]]) { |
|
423 printf(" keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]); |
|
424 printf(" keyLocation: %d\n", [(DOMKeyboardEvent*)event keyLocation]); |
|
425 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", |
|
426 [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, |
|
427 [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, |
|
428 [(DOMKeyboardEvent*)event altKey] ? 1 : 0, |
|
429 [(DOMKeyboardEvent*)event metaKey] ? 1 : 0); |
|
430 printf(" keyCode: %d\n", [(DOMKeyboardEvent*)event keyCode]); |
|
431 printf(" charCode: %d\n", [(DOMKeyboardEvent*)event charCode]); |
|
432 } |
|
433 |
|
434 if ([event isKindOfClass:[DOMMouseEvent class]]) { |
|
435 printf(" button: %d\n", [(DOMMouseEvent*)event button]); |
|
436 printf(" clientX: %d\n", [(DOMMouseEvent*)event clientX]); |
|
437 printf(" clientY: %d\n", [(DOMMouseEvent*)event clientY]); |
|
438 printf(" screenX: %d\n", [(DOMMouseEvent*)event screenX]); |
|
439 printf(" screenY: %d\n", [(DOMMouseEvent*)event screenY]); |
|
440 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", |
|
441 [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, |
|
442 [(DOMMouseEvent*)event shiftKey] ? 1 : 0, |
|
443 [(DOMMouseEvent*)event altKey] ? 1 : 0, |
|
444 [(DOMMouseEvent*)event metaKey] ? 1 : 0); |
|
445 id relatedTarget = [(DOMMouseEvent*)event relatedTarget]; |
|
446 if (relatedTarget) { |
|
447 printf(" relatedTarget: %s", [[[relatedTarget class] description] UTF8String]); |
|
448 if ([relatedTarget isKindOfClass:[DOMNode class]]) |
|
449 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]); |
|
450 printf("\n"); |
|
451 } |
|
452 } |
|
453 |
|
454 if ([event isKindOfClass:[DOMMutationEvent class]]) { |
|
455 printf(" prevValue: %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]); |
|
456 printf(" newValue: %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]); |
|
457 printf(" attrName: %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]); |
|
458 printf(" attrChange: %d\n", [(DOMMutationEvent*)event attrChange]); |
|
459 DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode]; |
|
460 if (relatedNode) { |
|
461 printf(" relatedNode: %s (nodeName: %s)\n", |
|
462 [[[relatedNode class] description] UTF8String], |
|
463 [[relatedNode nodeName] UTF8String]); |
|
464 } |
|
465 } |
|
466 |
|
467 if ([event isKindOfClass:[DOMWheelEvent class]]) { |
|
468 printf(" clientX: %d\n", [(DOMWheelEvent*)event clientX]); |
|
469 printf(" clientY: %d\n", [(DOMWheelEvent*)event clientY]); |
|
470 printf(" screenX: %d\n", [(DOMWheelEvent*)event screenX]); |
|
471 printf(" screenY: %d\n", [(DOMWheelEvent*)event screenY]); |
|
472 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", |
|
473 [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, |
|
474 [(DOMWheelEvent*)event shiftKey] ? 1 : 0, |
|
475 [(DOMWheelEvent*)event altKey] ? 1 : 0, |
|
476 [(DOMWheelEvent*)event metaKey] ? 1 : 0); |
|
477 printf(" isHorizontal: %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0); |
|
478 printf(" wheelDelta: %d\n", [(DOMWheelEvent*)event wheelDelta]); |
|
479 } |
|
480 } |
|
481 |
|
482 // FIXME: It's not good to have a test hard-wired into this controller like this. |
|
483 // Instead we need to get testing framework based on the Objective-C bindings |
|
484 // to work well enough that we can test that way instead. |
|
485 - (void)fireKeyboardEventsToElement:(WebScriptObject *)element { |
|
486 |
|
487 if (![element isKindOfClass:[DOMHTMLElement class]]) { |
|
488 return; |
|
489 } |
|
490 |
|
491 DOMHTMLElement *target = (DOMHTMLElement*)element; |
|
492 DOMDocument *document = [target ownerDocument]; |
|
493 |
|
494 // Keyboard Event 1 |
|
495 |
|
496 DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"]; |
|
497 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" |
|
498 canBubble:YES |
|
499 cancelable:YES |
|
500 view:[document defaultView] |
|
501 keyIdentifier:@"U+000041" |
|
502 keyLocation:0 |
|
503 ctrlKey:YES |
|
504 altKey:NO |
|
505 shiftKey:NO |
|
506 metaKey:NO]; |
|
507 [target dispatchEvent:domEvent]; |
|
508 |
|
509 // Keyboard Event 2 |
|
510 |
|
511 domEvent = [document createEvent:@"KeyboardEvent"]; |
|
512 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" |
|
513 canBubble:YES |
|
514 cancelable:YES |
|
515 view:[document defaultView] |
|
516 keyIdentifier:@"U+000045" |
|
517 keyLocation:1 |
|
518 ctrlKey:NO |
|
519 altKey:YES |
|
520 shiftKey:NO |
|
521 metaKey:NO]; |
|
522 [target dispatchEvent:domEvent]; |
|
523 |
|
524 // Keyboard Event 3 |
|
525 |
|
526 domEvent = [document createEvent:@"KeyboardEvent"]; |
|
527 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" |
|
528 canBubble:YES |
|
529 cancelable:YES |
|
530 view:[document defaultView] |
|
531 keyIdentifier:@"U+000056" |
|
532 keyLocation:0 |
|
533 ctrlKey:NO |
|
534 altKey:NO |
|
535 shiftKey:NO |
|
536 metaKey:NO]; |
|
537 [target dispatchEvent:domEvent]; |
|
538 |
|
539 } |
|
540 |
|
541 @end |