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 "config.h" 33#import "EventSendingController.h" 34 35#import "DumpRenderTree.h" 36#import "DumpRenderTreeDraggingInfo.h" 37#import "DumpRenderTreeFileDraggingSource.h" 38 39#import <Carbon/Carbon.h> // for GetCurrentEventTime() 40#import <WebKit/DOMPrivate.h> 41#import <WebKit/WebKit.h> 42#import <WebKit/WebViewPrivate.h> 43 44extern "C" void _NSNewKillRingSequence(); 45 46enum MouseAction { 47 MouseDown, 48 MouseUp, 49 MouseDragged 50}; 51 52// Match the DOM spec (sadly the DOM spec does not provide an enum) 53enum MouseButton { 54 LeftMouseButton = 0, 55 MiddleMouseButton = 1, 56 RightMouseButton = 2, 57 NoMouseButton = -1 58}; 59 60NSPoint lastMousePosition; 61NSPoint lastClickPosition; 62int lastClickButton = NoMouseButton; 63NSArray *webkitDomEventNames; 64NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once. 65BOOL replayingSavedEvents; 66 67@implementation EventSendingController 68 69+ (void)initialize 70{ 71 webkitDomEventNames = [[NSArray alloc] initWithObjects: 72 @"abort", 73 @"beforecopy", 74 @"beforecut", 75 @"beforepaste", 76 @"blur", 77 @"change", 78 @"click", 79 @"contextmenu", 80 @"copy", 81 @"cut", 82 @"dblclick", 83 @"drag", 84 @"dragend", 85 @"dragenter", 86 @"dragleave", 87 @"dragover", 88 @"dragstart", 89 @"drop", 90 @"error", 91 @"focus", 92 @"input", 93 @"keydown", 94 @"keypress", 95 @"keyup", 96 @"load", 97 @"mousedown", 98 @"mousemove", 99 @"mouseout", 100 @"mouseover", 101 @"mouseup", 102 @"mousewheel", 103 @"beforeunload", 104 @"paste", 105 @"readystatechange", 106 @"reset", 107 @"resize", 108 @"scroll", 109 @"search", 110 @"select", 111 @"selectstart", 112 @"submit", 113 @"textInput", 114 @"textzoomin", 115 @"textzoomout", 116 @"unload", 117 @"zoom", 118 nil]; 119} 120 121+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector 122{ 123 if (aSelector == @selector(beginDragWithFiles:) 124 || aSelector == @selector(clearKillRing) 125 || aSelector == @selector(contextClick) 126 || aSelector == @selector(enableDOMUIEventLogging:) 127 || aSelector == @selector(fireKeyboardEventsToElement:) 128 || aSelector == @selector(keyDown:withModifiers:) 129 || aSelector == @selector(leapForward:) 130 || aSelector == @selector(mouseDown:) 131 || aSelector == @selector(mouseMoveToX:Y:) 132 || aSelector == @selector(mouseUp:) 133 || aSelector == @selector(scheduleAsynchronousClick) 134 || aSelector == @selector(textZoomIn) 135 || aSelector == @selector(textZoomOut) 136 || aSelector == @selector(zoomPageIn) 137 || aSelector == @selector(zoomPageOut)) 138 return NO; 139 return YES; 140} 141 142+ (BOOL)isKeyExcludedFromWebScript:(const char*)name 143{ 144 if (strcmp(name, "dragMode") == 0) 145 return NO; 146 return YES; 147} 148 149+ (NSString *)webScriptNameForSelector:(SEL)aSelector 150{ 151 if (aSelector == @selector(beginDragWithFiles:)) 152 return @"beginDragWithFiles"; 153 if (aSelector == @selector(enableDOMUIEventLogging:)) 154 return @"enableDOMUIEventLogging"; 155 if (aSelector == @selector(fireKeyboardEventsToElement:)) 156 return @"fireKeyboardEventsToElement"; 157 if (aSelector == @selector(keyDown:withModifiers:)) 158 return @"keyDown"; 159 if (aSelector == @selector(leapForward:)) 160 return @"leapForward"; 161 if (aSelector == @selector(mouseDown:)) 162 return @"mouseDown"; 163 if (aSelector == @selector(mouseUp:)) 164 return @"mouseUp"; 165 if (aSelector == @selector(mouseMoveToX:Y:)) 166 return @"mouseMoveTo"; 167 if (aSelector == @selector(setDragMode:)) 168 return @"setDragMode"; 169 return nil; 170} 171 172- (id)init 173{ 174 self = [super init]; 175 if (self) 176 dragMode = YES; 177 return self; 178} 179 180- (void)dealloc 181{ 182 [super dealloc]; 183} 184 185- (double)currentEventTime 186{ 187 return GetCurrentEventTime() + timeOffset; 188} 189 190- (void)leapForward:(int)milliseconds 191{ 192 if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { 193 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]]; 194 [invocation setTarget:self]; 195 [invocation setSelector:@selector(leapForward:)]; 196 [invocation setArgument:&milliseconds atIndex:2]; 197 198 [EventSendingController saveEvent:invocation]; 199 200 return; 201 } 202 203 timeOffset += milliseconds / 1000.0; 204} 205 206- (void)clearKillRing 207{ 208 _NSNewKillRingSequence(); 209} 210 211static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action) 212{ 213 switch (button) { 214 case LeftMouseButton: 215 switch (action) { 216 case MouseDown: 217 return NSLeftMouseDown; 218 case MouseUp: 219 return NSLeftMouseUp; 220 case MouseDragged: 221 return NSLeftMouseDragged; 222 } 223 case RightMouseButton: 224 switch (action) { 225 case MouseDown: 226 return NSRightMouseDown; 227 case MouseUp: 228 return NSRightMouseUp; 229 case MouseDragged: 230 return NSRightMouseDragged; 231 } 232 default: 233 switch (action) { 234 case MouseDown: 235 return NSOtherMouseDown; 236 case MouseUp: 237 return NSOtherMouseUp; 238 case MouseDragged: 239 return NSOtherMouseDragged; 240 } 241 } 242 assert(0); 243 return static_cast<NSEventType>(0); 244} 245 246- (void)beginDragWithFiles:(WebScriptObject*)jsFilePaths 247{ 248 assert(!draggingInfo); 249 assert([jsFilePaths isKindOfClass:[WebScriptObject class]]); 250 251 NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName]; 252 [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; 253 254 NSURL *currentTestURL = [NSURL URLWithString:[[mainFrame webView] mainFrameURL]]; 255 256 NSMutableArray *filePaths = [NSMutableArray array]; 257 for (unsigned i = 0; [[jsFilePaths webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { 258 NSString *filePath = (NSString *)[jsFilePaths webScriptValueAtIndex:i]; 259 // Have NSURL encode the name so that we handle '?' in file names correctly. 260 NSURL *fileURL = [NSURL fileURLWithPath:filePath]; 261 NSURL *absoluteFileURL = [NSURL URLWithString:[fileURL relativeString] relativeToURL:currentTestURL]; 262 [filePaths addObject:[absoluteFileURL path]]; 263 } 264 265 [pboard setPropertyList:filePaths forType:NSFilenamesPboardType]; 266 assert([pboard propertyListForType:NSFilenamesPboardType]); // setPropertyList will silently fail on error, assert that it didn't fail 267 268 // Provide a source, otherwise [DumpRenderTreeDraggingInfo draggingSourceOperationMask] defaults to NSDragOperationNone 269 DumpRenderTreeFileDraggingSource *source = [[[DumpRenderTreeFileDraggingSource alloc] init] autorelease]; 270 draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pboard source:source]; 271 [[mainFrame webView] draggingEntered:draggingInfo]; 272 273 dragMode = NO; // dragMode saves events and then replays them later. We don't need/want that. 274 leftMouseButtonDown = YES; // Make the rest of eventSender think a drag is in progress 275} 276 277- (void)updateClickCountForButton:(int)buttonNumber 278{ 279 if (([self currentEventTime] - lastClick >= 1) || 280 !NSEqualPoints(lastMousePosition, lastClickPosition) || 281 lastClickButton != buttonNumber) { 282 clickCount = 1; 283 lastClickButton = buttonNumber; 284 } else 285 clickCount++; 286} 287 288- (void)mouseDown:(int)buttonNumber 289{ 290 [[[mainFrame frameView] documentView] layout]; 291 [self updateClickCountForButton:buttonNumber]; 292 293 NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown); 294 NSEvent *event = [NSEvent mouseEventWithType:eventType 295 location:lastMousePosition 296 modifierFlags:0 297 timestamp:[self currentEventTime] 298 windowNumber:[[[mainFrame webView] window] windowNumber] 299 context:[NSGraphicsContext currentContext] 300 eventNumber:++eventNumber 301 clickCount:clickCount 302 pressure:0.0]; 303 304 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; 305 if (subView) { 306 [subView mouseDown:event]; 307 if (buttonNumber == LeftMouseButton) 308 leftMouseButtonDown = YES; 309 } 310} 311 312- (void)textZoomIn 313{ 314 [[mainFrame webView] makeTextLarger:self]; 315} 316 317- (void)textZoomOut 318{ 319 [[mainFrame webView] makeTextSmaller:self]; 320} 321 322- (void)zoomPageIn 323{ 324 [[mainFrame webView] zoomPageIn:self]; 325} 326 327- (void)zoomPageOut 328{ 329 [[mainFrame webView] zoomPageOut:self]; 330} 331 332- (void)mouseUp:(int)buttonNumber 333{ 334 if (dragMode && !replayingSavedEvents) { 335 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:)]]; 336 [invocation setTarget:self]; 337 [invocation setSelector:@selector(mouseUp:)]; 338 [invocation setArgument:&buttonNumber atIndex:2]; 339 340 [EventSendingController saveEvent:invocation]; 341 [EventSendingController replaySavedEvents]; 342 343 return; 344 } 345 346 [[[mainFrame frameView] documentView] layout]; 347 NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp); 348 NSEvent *event = [NSEvent mouseEventWithType:eventType 349 location:lastMousePosition 350 modifierFlags:0 351 timestamp:[self currentEventTime] 352 windowNumber:[[[mainFrame webView] window] windowNumber] 353 context:[NSGraphicsContext currentContext] 354 eventNumber:++eventNumber 355 clickCount:clickCount 356 pressure:0.0]; 357 358 NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]]; 359 // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView. 360 // The right solution is just to use NSApplication's built-in event sending methods, 361 // instead of rolling our own algorithm for selecting an event target. 362 targetView = targetView ? targetView : [[mainFrame frameView] documentView]; 363 assert(targetView); 364 [targetView mouseUp:event]; 365 if (buttonNumber == LeftMouseButton) 366 leftMouseButtonDown = NO; 367 lastClick = [event timestamp]; 368 lastClickPosition = lastMousePosition; 369 if (draggingInfo) { 370 WebView *webView = [mainFrame webView]; 371 372 NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo]; 373 374 if (dragOperation != NSDragOperationNone) 375 [webView performDragOperation:draggingInfo]; 376 else 377 [webView draggingExited:draggingInfo]; 378 // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation: 379 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)]) 380 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation]; 381 [draggingInfo release]; 382 draggingInfo = nil; 383 } 384} 385 386- (void)mouseMoveToX:(int)x Y:(int)y 387{ 388 if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { 389 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]]; 390 [invocation setTarget:self]; 391 [invocation setSelector:@selector(mouseMoveToX:Y:)]; 392 [invocation setArgument:&x atIndex:2]; 393 [invocation setArgument:&y atIndex:3]; 394 395 [EventSendingController saveEvent:invocation]; 396 return; 397 } 398 399 NSView *view = [mainFrame webView]; 400 lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil]; 401 NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved) 402 location:lastMousePosition 403 modifierFlags:0 404 timestamp:[self currentEventTime] 405 windowNumber:[[view window] windowNumber] 406 context:[NSGraphicsContext currentContext] 407 eventNumber:++eventNumber 408 clickCount:(leftMouseButtonDown ? clickCount : 0) 409 pressure:0.0]; 410 411 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; 412 if (subView) { 413 if (leftMouseButtonDown) { 414 [subView mouseDragged:event]; 415 if (draggingInfo) { 416 // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo: 417 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)]) 418 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition]; 419 [[mainFrame webView] draggingUpdated:draggingInfo]; 420 } 421 } else 422 [subView mouseMoved:event]; 423 } 424} 425 426- (void)contextClick 427{ 428 [[[mainFrame frameView] documentView] layout]; 429 [self updateClickCountForButton:RightMouseButton]; 430 431 NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown 432 location:lastMousePosition 433 modifierFlags:0 434 timestamp:[self currentEventTime] 435 windowNumber:[[[mainFrame webView] window] windowNumber] 436 context:[NSGraphicsContext currentContext] 437 eventNumber:++eventNumber 438 clickCount:clickCount 439 pressure:0.0]; 440 441 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; 442 if (subView) 443 [subView menuForEvent:event]; 444} 445 446- (void)scheduleAsynchronousClick 447{ 448 [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0]; 449 [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0]; 450} 451 452+ (void)saveEvent:(NSInvocation *)event 453{ 454 if (!savedMouseEvents) 455 savedMouseEvents = [[NSMutableArray alloc] init]; 456 [savedMouseEvents addObject:event]; 457} 458 459+ (void)replaySavedEvents 460{ 461 replayingSavedEvents = YES; 462 while ([savedMouseEvents count]) { 463 // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate 464 NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease]; 465 [savedMouseEvents removeObjectAtIndex:0]; 466 [invocation invoke]; 467 } 468 replayingSavedEvents = NO; 469} 470 471+ (void)clearSavedEvents 472{ 473 [savedMouseEvents release]; 474 savedMouseEvents = nil; 475} 476 477- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers 478{ 479 NSString *eventCharacter = character; 480 if ([character isEqualToString:@"leftArrow"]) { 481 const unichar ch = NSLeftArrowFunctionKey; 482 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 483 } else if ([character isEqualToString:@"rightArrow"]) { 484 const unichar ch = NSRightArrowFunctionKey; 485 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 486 } else if ([character isEqualToString:@"upArrow"]) { 487 const unichar ch = NSUpArrowFunctionKey; 488 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 489 } else if ([character isEqualToString:@"downArrow"]) { 490 const unichar ch = NSDownArrowFunctionKey; 491 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 492 } else if ([character isEqualToString:@"pageUp"]) { 493 const unichar ch = NSPageUpFunctionKey; 494 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 495 } else if ([character isEqualToString:@"pageDown"]) { 496 const unichar ch = NSPageDownFunctionKey; 497 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 498 } else if ([character isEqualToString:@"home"]) { 499 const unichar ch = NSHomeFunctionKey; 500 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 501 } else if ([character isEqualToString:@"end"]) { 502 const unichar ch = NSEndFunctionKey; 503 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 504 } else if ([character isEqualToString:@"delete"]) { 505 const unichar ch = 0x7f; 506 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 507 } 508 509 NSString *charactersIgnoringModifiers = eventCharacter; 510 511 int modifierFlags = 0; 512 513 if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') { 514 modifierFlags |= NSShiftKeyMask; 515 charactersIgnoringModifiers = [character lowercaseString]; 516 } 517 518 if ([modifiers isKindOfClass:[WebScriptObject class]]) { 519 for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { 520 NSString *modifier = (NSString *)[modifiers webScriptValueAtIndex:i]; 521 if ([modifier isEqual:@"ctrlKey"]) 522 modifierFlags |= NSControlKeyMask; 523 else if ([modifier isEqual:@"shiftKey"]) 524 modifierFlags |= NSShiftKeyMask; 525 else if ([modifier isEqual:@"altKey"]) 526 modifierFlags |= NSAlternateKeyMask; 527 else if ([modifier isEqual:@"metaKey"]) 528 modifierFlags |= NSCommandKeyMask; 529 } 530 } 531 532 [[[mainFrame frameView] documentView] layout]; 533 534 NSEvent *event = [NSEvent keyEventWithType:NSKeyDown 535 location:NSMakePoint(5, 5) 536 modifierFlags:modifierFlags 537 timestamp:[self currentEventTime] 538 windowNumber:[[[mainFrame webView] window] windowNumber] 539 context:[NSGraphicsContext currentContext] 540 characters:eventCharacter 541 charactersIgnoringModifiers:charactersIgnoringModifiers 542 isARepeat:NO 543 keyCode:0]; 544 545 [[[[mainFrame webView] window] firstResponder] keyDown:event]; 546 547 event = [NSEvent keyEventWithType:NSKeyUp 548 location:NSMakePoint(5, 5) 549 modifierFlags:modifierFlags 550 timestamp:[self currentEventTime] 551 windowNumber:[[[mainFrame webView] window] windowNumber] 552 context:[NSGraphicsContext currentContext] 553 characters:eventCharacter 554 charactersIgnoringModifiers:charactersIgnoringModifiers 555 isARepeat:NO 556 keyCode:0]; 557 558 [[[[mainFrame webView] window] firstResponder] keyUp:event]; 559} 560 561- (void)enableDOMUIEventLogging:(WebScriptObject *)node 562{ 563 NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator]; 564 id eventName; 565 while ((eventName = [eventEnumerator nextObject])) { 566 [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO]; 567 } 568} 569 570- (void)handleEvent:(DOMEvent *)event 571{ 572 DOMNode *target = [event target]; 573 574 printf("event type: %s\n", [[event type] UTF8String]); 575 printf(" target: <%s>\n", [[[target nodeName] lowercaseString] UTF8String]); 576 577 if ([event isKindOfClass:[DOMEvent class]]) { 578 printf(" eventPhase: %d\n", [event eventPhase]); 579 printf(" bubbles: %d\n", [event bubbles] ? 1 : 0); 580 printf(" cancelable: %d\n", [event cancelable] ? 1 : 0); 581 } 582 583 if ([event isKindOfClass:[DOMUIEvent class]]) { 584 printf(" detail: %d\n", [(DOMUIEvent*)event detail]); 585 586 DOMAbstractView *view = [(DOMUIEvent*)event view]; 587 if (view) { 588 printf(" view: OK"); 589 if ([view document]) 590 printf(" (document: OK)"); 591 printf("\n"); 592 } 593 } 594 595 if ([event isKindOfClass:[DOMKeyboardEvent class]]) { 596 printf(" keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]); 597 printf(" keyLocation: %d\n", [(DOMKeyboardEvent*)event keyLocation]); 598 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", 599 [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, 600 [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, 601 [(DOMKeyboardEvent*)event altKey] ? 1 : 0, 602 [(DOMKeyboardEvent*)event metaKey] ? 1 : 0); 603 printf(" keyCode: %d\n", [(DOMKeyboardEvent*)event keyCode]); 604 printf(" charCode: %d\n", [(DOMKeyboardEvent*)event charCode]); 605 } 606 607 if ([event isKindOfClass:[DOMMouseEvent class]]) { 608 printf(" button: %d\n", [(DOMMouseEvent*)event button]); 609 printf(" clientX: %d\n", [(DOMMouseEvent*)event clientX]); 610 printf(" clientY: %d\n", [(DOMMouseEvent*)event clientY]); 611 printf(" screenX: %d\n", [(DOMMouseEvent*)event screenX]); 612 printf(" screenY: %d\n", [(DOMMouseEvent*)event screenY]); 613 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", 614 [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, 615 [(DOMMouseEvent*)event shiftKey] ? 1 : 0, 616 [(DOMMouseEvent*)event altKey] ? 1 : 0, 617 [(DOMMouseEvent*)event metaKey] ? 1 : 0); 618 id relatedTarget = [(DOMMouseEvent*)event relatedTarget]; 619 if (relatedTarget) { 620 printf(" relatedTarget: %s", [[[relatedTarget class] description] UTF8String]); 621 if ([relatedTarget isKindOfClass:[DOMNode class]]) 622 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]); 623 printf("\n"); 624 } 625 } 626 627 if ([event isKindOfClass:[DOMMutationEvent class]]) { 628 printf(" prevValue: %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]); 629 printf(" newValue: %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]); 630 printf(" attrName: %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]); 631 printf(" attrChange: %d\n", [(DOMMutationEvent*)event attrChange]); 632 DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode]; 633 if (relatedNode) { 634 printf(" relatedNode: %s (nodeName: %s)\n", 635 [[[relatedNode class] description] UTF8String], 636 [[relatedNode nodeName] UTF8String]); 637 } 638 } 639 640 if ([event isKindOfClass:[DOMWheelEvent class]]) { 641 printf(" clientX: %d\n", [(DOMWheelEvent*)event clientX]); 642 printf(" clientY: %d\n", [(DOMWheelEvent*)event clientY]); 643 printf(" screenX: %d\n", [(DOMWheelEvent*)event screenX]); 644 printf(" screenY: %d\n", [(DOMWheelEvent*)event screenY]); 645 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", 646 [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, 647 [(DOMWheelEvent*)event shiftKey] ? 1 : 0, 648 [(DOMWheelEvent*)event altKey] ? 1 : 0, 649 [(DOMWheelEvent*)event metaKey] ? 1 : 0); 650 printf(" isHorizontal: %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0); 651 printf(" wheelDelta: %d\n", [(DOMWheelEvent*)event wheelDelta]); 652 } 653} 654 655// FIXME: It's not good to have a test hard-wired into this controller like this. 656// Instead we need to get testing framework based on the Objective-C bindings 657// to work well enough that we can test that way instead. 658- (void)fireKeyboardEventsToElement:(WebScriptObject *)element { 659 660 if (![element isKindOfClass:[DOMHTMLElement class]]) 661 return; 662 663 DOMHTMLElement *target = (DOMHTMLElement*)element; 664 DOMDocument *document = [target ownerDocument]; 665 666 // Keyboard Event 1 667 668 DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"]; 669 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" 670 canBubble:YES 671 cancelable:YES 672 view:[document defaultView] 673 keyIdentifier:@"U+000041" 674 keyLocation:0 675 ctrlKey:YES 676 altKey:NO 677 shiftKey:NO 678 metaKey:NO]; 679 [target dispatchEvent:domEvent]; 680 681 // Keyboard Event 2 682 683 domEvent = [document createEvent:@"KeyboardEvent"]; 684 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" 685 canBubble:YES 686 cancelable:YES 687 view:[document defaultView] 688 keyIdentifier:@"U+000045" 689 keyLocation:1 690 ctrlKey:NO 691 altKey:YES 692 shiftKey:NO 693 metaKey:NO]; 694 [target dispatchEvent:domEvent]; 695 696 // Keyboard Event 3 697 698 domEvent = [document createEvent:@"KeyboardEvent"]; 699 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" 700 canBubble:YES 701 cancelable:YES 702 view:[document defaultView] 703 keyIdentifier:@"U+000056" 704 keyLocation:0 705 ctrlKey:NO 706 altKey:NO 707 shiftKey:NO 708 metaKey:NO]; 709 [target dispatchEvent:domEvent]; 710 711} 712 713@end 714