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:withLocation:) 129 || aSelector == @selector(leapForward:) 130 || aSelector == @selector(mouseDown:withModifiers:) 131 || aSelector == @selector(mouseMoveToX:Y:) 132 || aSelector == @selector(mouseUp:withModifiers:) 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:withLocation:)) 158 return @"keyDown"; 159 if (aSelector == @selector(leapForward:)) 160 return @"leapForward"; 161 if (aSelector == @selector(mouseDown:withModifiers:)) 162 return @"mouseDown"; 163 if (aSelector == @selector(mouseUp:withModifiers:)) 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 288static int buildModifierFlags(const WebScriptObject* modifiers) 289{ 290 int flags = 0; 291 if (![modifiers isKindOfClass:[WebScriptObject class]]) 292 return flags; 293 for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { 294 NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i]; 295 if ([modifierName isEqual:@"ctrlKey"]) 296 flags |= NSControlKeyMask; 297 else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"]) 298 flags |= NSShiftKeyMask; 299 else if ([modifierName isEqual:@"altKey"]) 300 flags |= NSAlternateKeyMask; 301 else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"]) 302 flags |= NSCommandKeyMask; 303 } 304 return flags; 305} 306 307- (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers 308{ 309 [[[mainFrame frameView] documentView] layout]; 310 [self updateClickCountForButton:buttonNumber]; 311 312 NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown); 313 NSEvent *event = [NSEvent mouseEventWithType:eventType 314 location:lastMousePosition 315 modifierFlags:buildModifierFlags(modifiers) 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 mouseDown:event]; 326 if (buttonNumber == LeftMouseButton) 327 leftMouseButtonDown = YES; 328 } 329} 330 331- (void)mouseDown:(int)buttonNumber 332{ 333 [self mouseDown:buttonNumber withModifiers:nil]; 334} 335 336- (void)textZoomIn 337{ 338 [[mainFrame webView] makeTextLarger:self]; 339} 340 341- (void)textZoomOut 342{ 343 [[mainFrame webView] makeTextSmaller:self]; 344} 345 346- (void)zoomPageIn 347{ 348 [[mainFrame webView] zoomPageIn:self]; 349} 350 351- (void)zoomPageOut 352{ 353 [[mainFrame webView] zoomPageOut:self]; 354} 355 356- (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers 357{ 358 if (dragMode && !replayingSavedEvents) { 359 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]]; 360 [invocation setTarget:self]; 361 [invocation setSelector:@selector(mouseUp:withModifiers:)]; 362 [invocation setArgument:&buttonNumber atIndex:2]; 363 [invocation setArgument:&modifiers atIndex:3]; 364 365 [EventSendingController saveEvent:invocation]; 366 [EventSendingController replaySavedEvents]; 367 368 return; 369 } 370 371 [[[mainFrame frameView] documentView] layout]; 372 NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp); 373 NSEvent *event = [NSEvent mouseEventWithType:eventType 374 location:lastMousePosition 375 modifierFlags:buildModifierFlags(modifiers) 376 timestamp:[self currentEventTime] 377 windowNumber:[[[mainFrame webView] window] windowNumber] 378 context:[NSGraphicsContext currentContext] 379 eventNumber:++eventNumber 380 clickCount:clickCount 381 pressure:0.0]; 382 383 NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]]; 384 // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView. 385 // The right solution is just to use NSApplication's built-in event sending methods, 386 // instead of rolling our own algorithm for selecting an event target. 387 targetView = targetView ? targetView : [[mainFrame frameView] documentView]; 388 assert(targetView); 389 [targetView mouseUp:event]; 390 if (buttonNumber == LeftMouseButton) 391 leftMouseButtonDown = NO; 392 lastClick = [event timestamp]; 393 lastClickPosition = lastMousePosition; 394 if (draggingInfo) { 395 WebView *webView = [mainFrame webView]; 396 397 NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo]; 398 399 if (dragOperation != NSDragOperationNone) 400 [webView performDragOperation:draggingInfo]; 401 else 402 [webView draggingExited:draggingInfo]; 403 // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation: 404 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)]) 405 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation]; 406 [draggingInfo release]; 407 draggingInfo = nil; 408 } 409} 410 411- (void)mouseUp:(int)buttonNumber 412{ 413 [self mouseUp:buttonNumber withModifiers:nil]; 414} 415 416- (void)mouseMoveToX:(int)x Y:(int)y 417{ 418 if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { 419 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]]; 420 [invocation setTarget:self]; 421 [invocation setSelector:@selector(mouseMoveToX:Y:)]; 422 [invocation setArgument:&x atIndex:2]; 423 [invocation setArgument:&y atIndex:3]; 424 425 [EventSendingController saveEvent:invocation]; 426 return; 427 } 428 429 NSView *view = [mainFrame webView]; 430 lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil]; 431 NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved) 432 location:lastMousePosition 433 modifierFlags:0 434 timestamp:[self currentEventTime] 435 windowNumber:[[view window] windowNumber] 436 context:[NSGraphicsContext currentContext] 437 eventNumber:++eventNumber 438 clickCount:(leftMouseButtonDown ? clickCount : 0) 439 pressure:0.0]; 440 441 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; 442 if (subView) { 443 if (leftMouseButtonDown) { 444 [subView mouseDragged:event]; 445 if (draggingInfo) { 446 // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo: 447 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)]) 448 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition]; 449 [[mainFrame webView] draggingUpdated:draggingInfo]; 450 } 451 } else 452 [subView mouseMoved:event]; 453 } 454} 455 456- (void)contextClick 457{ 458 [[[mainFrame frameView] documentView] layout]; 459 [self updateClickCountForButton:RightMouseButton]; 460 461 NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown 462 location:lastMousePosition 463 modifierFlags:0 464 timestamp:[self currentEventTime] 465 windowNumber:[[[mainFrame webView] window] windowNumber] 466 context:[NSGraphicsContext currentContext] 467 eventNumber:++eventNumber 468 clickCount:clickCount 469 pressure:0.0]; 470 471 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; 472 if (subView) 473 [subView menuForEvent:event]; 474} 475 476- (void)scheduleAsynchronousClick 477{ 478 [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0]; 479 [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0]; 480} 481 482+ (void)saveEvent:(NSInvocation *)event 483{ 484 if (!savedMouseEvents) 485 savedMouseEvents = [[NSMutableArray alloc] init]; 486 [savedMouseEvents addObject:event]; 487} 488 489+ (void)replaySavedEvents 490{ 491 replayingSavedEvents = YES; 492 while ([savedMouseEvents count]) { 493 // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate 494 NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease]; 495 [savedMouseEvents removeObjectAtIndex:0]; 496 [invocation invoke]; 497 } 498 replayingSavedEvents = NO; 499} 500 501+ (void)clearSavedEvents 502{ 503 [savedMouseEvents release]; 504 savedMouseEvents = nil; 505} 506 507- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)keyLocation 508{ 509 NSString *eventCharacter = character; 510 if ([character isEqualToString:@"leftArrow"]) { 511 const unichar ch = NSLeftArrowFunctionKey; 512 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 513 } else if ([character isEqualToString:@"rightArrow"]) { 514 const unichar ch = NSRightArrowFunctionKey; 515 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 516 } else if ([character isEqualToString:@"upArrow"]) { 517 const unichar ch = NSUpArrowFunctionKey; 518 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 519 } else if ([character isEqualToString:@"downArrow"]) { 520 const unichar ch = NSDownArrowFunctionKey; 521 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 522 } else if ([character isEqualToString:@"pageUp"]) { 523 const unichar ch = NSPageUpFunctionKey; 524 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 525 } else if ([character isEqualToString:@"pageDown"]) { 526 const unichar ch = NSPageDownFunctionKey; 527 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 528 } else if ([character isEqualToString:@"home"]) { 529 const unichar ch = NSHomeFunctionKey; 530 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 531 } else if ([character isEqualToString:@"end"]) { 532 const unichar ch = NSEndFunctionKey; 533 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 534 } else if ([character isEqualToString:@"delete"]) { 535 const unichar ch = 0x7f; 536 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 537 } 538 539 // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24"). 540 // If the input string is a function-key name, set its key code. 541 for (unsigned i = 1; i <= 24; i++) { 542 if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) { 543 const unichar ch = NSF1FunctionKey + (i - 1); 544 eventCharacter = [NSString stringWithCharacters:&ch length:1]; 545 } 546 } 547 548 NSString *charactersIgnoringModifiers = eventCharacter; 549 550 int modifierFlags = 0; 551 552 if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') { 553 modifierFlags |= NSShiftKeyMask; 554 charactersIgnoringModifiers = [character lowercaseString]; 555 } 556 557 modifierFlags |= buildModifierFlags(modifiers); 558 559 if (keyLocation == DOM_KEY_LOCATION_NUMPAD) 560 modifierFlags |= NSNumericPadKeyMask; 561 562 [[[mainFrame frameView] documentView] layout]; 563 564 NSEvent *event = [NSEvent keyEventWithType:NSKeyDown 565 location:NSMakePoint(5, 5) 566 modifierFlags:modifierFlags 567 timestamp:[self currentEventTime] 568 windowNumber:[[[mainFrame webView] window] windowNumber] 569 context:[NSGraphicsContext currentContext] 570 characters:eventCharacter 571 charactersIgnoringModifiers:charactersIgnoringModifiers 572 isARepeat:NO 573 keyCode:0]; 574 575 [[[[mainFrame webView] window] firstResponder] keyDown:event]; 576 577 event = [NSEvent keyEventWithType:NSKeyUp 578 location:NSMakePoint(5, 5) 579 modifierFlags:modifierFlags 580 timestamp:[self currentEventTime] 581 windowNumber:[[[mainFrame webView] window] windowNumber] 582 context:[NSGraphicsContext currentContext] 583 characters:eventCharacter 584 charactersIgnoringModifiers:charactersIgnoringModifiers 585 isARepeat:NO 586 keyCode:0]; 587 588 [[[[mainFrame webView] window] firstResponder] keyUp:event]; 589} 590 591- (void)enableDOMUIEventLogging:(WebScriptObject *)node 592{ 593 NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator]; 594 id eventName; 595 while ((eventName = [eventEnumerator nextObject])) { 596 [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO]; 597 } 598} 599 600- (void)handleEvent:(DOMEvent *)event 601{ 602 DOMNode *target = [event target]; 603 604 printf("event type: %s\n", [[event type] UTF8String]); 605 printf(" target: <%s>\n", [[[target nodeName] lowercaseString] UTF8String]); 606 607 if ([event isKindOfClass:[DOMEvent class]]) { 608 printf(" eventPhase: %d\n", [event eventPhase]); 609 printf(" bubbles: %d\n", [event bubbles] ? 1 : 0); 610 printf(" cancelable: %d\n", [event cancelable] ? 1 : 0); 611 } 612 613 if ([event isKindOfClass:[DOMUIEvent class]]) { 614 printf(" detail: %d\n", [(DOMUIEvent*)event detail]); 615 616 DOMAbstractView *view = [(DOMUIEvent*)event view]; 617 if (view) { 618 printf(" view: OK"); 619 if ([view document]) 620 printf(" (document: OK)"); 621 printf("\n"); 622 } 623 } 624 625 if ([event isKindOfClass:[DOMKeyboardEvent class]]) { 626 printf(" keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]); 627 printf(" keyLocation: %d\n", [(DOMKeyboardEvent*)event keyLocation]); 628 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", 629 [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, 630 [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, 631 [(DOMKeyboardEvent*)event altKey] ? 1 : 0, 632 [(DOMKeyboardEvent*)event metaKey] ? 1 : 0); 633 printf(" keyCode: %d\n", [(DOMKeyboardEvent*)event keyCode]); 634 printf(" charCode: %d\n", [(DOMKeyboardEvent*)event charCode]); 635 } 636 637 if ([event isKindOfClass:[DOMMouseEvent class]]) { 638 printf(" button: %d\n", [(DOMMouseEvent*)event button]); 639 printf(" clientX: %d\n", [(DOMMouseEvent*)event clientX]); 640 printf(" clientY: %d\n", [(DOMMouseEvent*)event clientY]); 641 printf(" screenX: %d\n", [(DOMMouseEvent*)event screenX]); 642 printf(" screenY: %d\n", [(DOMMouseEvent*)event screenY]); 643 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", 644 [(DOMMouseEvent*)event ctrlKey] ? 1 : 0, 645 [(DOMMouseEvent*)event shiftKey] ? 1 : 0, 646 [(DOMMouseEvent*)event altKey] ? 1 : 0, 647 [(DOMMouseEvent*)event metaKey] ? 1 : 0); 648 id relatedTarget = [(DOMMouseEvent*)event relatedTarget]; 649 if (relatedTarget) { 650 printf(" relatedTarget: %s", [[[relatedTarget class] description] UTF8String]); 651 if ([relatedTarget isKindOfClass:[DOMNode class]]) 652 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]); 653 printf("\n"); 654 } 655 } 656 657 if ([event isKindOfClass:[DOMMutationEvent class]]) { 658 printf(" prevValue: %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]); 659 printf(" newValue: %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]); 660 printf(" attrName: %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]); 661 printf(" attrChange: %d\n", [(DOMMutationEvent*)event attrChange]); 662 DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode]; 663 if (relatedNode) { 664 printf(" relatedNode: %s (nodeName: %s)\n", 665 [[[relatedNode class] description] UTF8String], 666 [[relatedNode nodeName] UTF8String]); 667 } 668 } 669 670 if ([event isKindOfClass:[DOMWheelEvent class]]) { 671 printf(" clientX: %d\n", [(DOMWheelEvent*)event clientX]); 672 printf(" clientY: %d\n", [(DOMWheelEvent*)event clientY]); 673 printf(" screenX: %d\n", [(DOMWheelEvent*)event screenX]); 674 printf(" screenY: %d\n", [(DOMWheelEvent*)event screenY]); 675 printf(" modifier keys: c:%d s:%d a:%d m:%d\n", 676 [(DOMWheelEvent*)event ctrlKey] ? 1 : 0, 677 [(DOMWheelEvent*)event shiftKey] ? 1 : 0, 678 [(DOMWheelEvent*)event altKey] ? 1 : 0, 679 [(DOMWheelEvent*)event metaKey] ? 1 : 0); 680 printf(" isHorizontal: %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0); 681 printf(" wheelDelta: %d\n", [(DOMWheelEvent*)event wheelDelta]); 682 } 683} 684 685// FIXME: It's not good to have a test hard-wired into this controller like this. 686// Instead we need to get testing framework based on the Objective-C bindings 687// to work well enough that we can test that way instead. 688- (void)fireKeyboardEventsToElement:(WebScriptObject *)element { 689 690 if (![element isKindOfClass:[DOMHTMLElement class]]) 691 return; 692 693 DOMHTMLElement *target = (DOMHTMLElement*)element; 694 DOMDocument *document = [target ownerDocument]; 695 696 // Keyboard Event 1 697 698 DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"]; 699 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" 700 canBubble:YES 701 cancelable:YES 702 view:[document defaultView] 703 keyIdentifier:@"U+000041" 704 keyLocation:0 705 ctrlKey:YES 706 altKey:NO 707 shiftKey:NO 708 metaKey:NO]; 709 [target dispatchEvent:domEvent]; 710 711 // Keyboard Event 2 712 713 domEvent = [document createEvent:@"KeyboardEvent"]; 714 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" 715 canBubble:YES 716 cancelable:YES 717 view:[document defaultView] 718 keyIdentifier:@"U+000045" 719 keyLocation:1 720 ctrlKey:NO 721 altKey:YES 722 shiftKey:NO 723 metaKey:NO]; 724 [target dispatchEvent:domEvent]; 725 726 // Keyboard Event 3 727 728 domEvent = [document createEvent:@"KeyboardEvent"]; 729 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" 730 canBubble:YES 731 cancelable:YES 732 view:[document defaultView] 733 keyIdentifier:@"U+000056" 734 keyLocation:0 735 ctrlKey:NO 736 altKey:NO 737 shiftKey:NO 738 metaKey:NO]; 739 [target dispatchEvent:domEvent]; 740 741} 742 743@end 744