|
Line 0
Tools/WebKitTestRunner/mac/EventSenderMac.mm_sec1
|
|
|
1 |
/* |
| 2 |
* Copyright (C) 2011 Apple Inc. All rights reserved. |
| 3 |
* |
| 4 |
* Redistribution and use in source and binary forms, with or without |
| 5 |
* modification, are permitted provided that the following conditions |
| 6 |
* are met: |
| 7 |
* |
| 8 |
* 1. Redistributions of source code must retain the above copyright |
| 9 |
* notice, this list of conditions and the following disclaimer. |
| 10 |
* 2. Redistributions in binary form must reproduce the above copyright |
| 11 |
* notice, this list of conditions and the following disclaimer in the |
| 12 |
* documentation and/or other materials provided with the distribution. |
| 13 |
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| 14 |
* its contributors may be used to endorse or promote products derived |
| 15 |
* from this software without specific prior written permission. |
| 16 |
* |
| 17 |
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| 18 |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 20 |
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 21 |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 22 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 23 |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 24 |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 26 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 |
*/ |
| 28 |
|
| 29 |
#if 0 |
| 30 |
#import "config.h" |
| 31 |
#import "EventSender.h" |
| 32 |
|
| 33 |
#import "DumpRenderTree.h" |
| 34 |
#import "DumpRenderTreeDraggingInfo.h" |
| 35 |
#import "DumpRenderTreeFileDraggingSource.h" |
| 36 |
|
| 37 |
#import <Carbon/Carbon.h> // for GetCurrentEventTime() |
| 38 |
#import <WebKit/DOMPrivate.h> |
| 39 |
#import <WebKit/WebKit.h> |
| 40 |
#import <WebKit/WebViewPrivate.h> |
| 41 |
|
| 42 |
extern "C" void _NSNewKillRingSequence(); |
| 43 |
|
| 44 |
enum MouseAction { |
| 45 |
MouseDown, |
| 46 |
MouseUp, |
| 47 |
MouseDragged |
| 48 |
}; |
| 49 |
|
| 50 |
// Match the DOM spec (sadly the DOM spec does not provide an enum) |
| 51 |
enum MouseButton { |
| 52 |
LeftMouseButton = 0, |
| 53 |
MiddleMouseButton = 1, |
| 54 |
RightMouseButton = 2, |
| 55 |
NoMouseButton = -1 |
| 56 |
}; |
| 57 |
|
| 58 |
NSPoint lastMousePosition; |
| 59 |
NSPoint lastClickPosition; |
| 60 |
int lastClickButton = NoMouseButton; |
| 61 |
NSArray *webkitDomEventNames; |
| 62 |
NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once. |
| 63 |
BOOL replayingSavedEvents; |
| 64 |
|
| 65 |
@implementation EventSender |
| 66 |
|
| 67 |
+ (void)initialize |
| 68 |
{ |
| 69 |
webkitDomEventNames = [[NSArray alloc] initWithObjects: |
| 70 |
@"abort", |
| 71 |
@"beforecopy", |
| 72 |
@"beforecut", |
| 73 |
@"beforepaste", |
| 74 |
@"blur", |
| 75 |
@"change", |
| 76 |
@"click", |
| 77 |
@"contextmenu", |
| 78 |
@"copy", |
| 79 |
@"cut", |
| 80 |
@"dblclick", |
| 81 |
@"drag", |
| 82 |
@"dragend", |
| 83 |
@"dragenter", |
| 84 |
@"dragleave", |
| 85 |
@"dragover", |
| 86 |
@"dragstart", |
| 87 |
@"drop", |
| 88 |
@"error", |
| 89 |
@"focus", |
| 90 |
@"input", |
| 91 |
@"keydown", |
| 92 |
@"keypress", |
| 93 |
@"keyup", |
| 94 |
@"load", |
| 95 |
@"mousedown", |
| 96 |
@"mousemove", |
| 97 |
@"mouseout", |
| 98 |
@"mouseover", |
| 99 |
@"mouseup", |
| 100 |
@"mousewheel", |
| 101 |
@"beforeunload", |
| 102 |
@"paste", |
| 103 |
@"readystatechange", |
| 104 |
@"reset", |
| 105 |
@"resize", |
| 106 |
@"scroll", |
| 107 |
@"search", |
| 108 |
@"select", |
| 109 |
@"selectstart", |
| 110 |
@"submit", |
| 111 |
@"textInput", |
| 112 |
@"textzoomin", |
| 113 |
@"textzoomout", |
| 114 |
@"unload", |
| 115 |
@"zoom", |
| 116 |
nil]; |
| 117 |
} |
| 118 |
|
| 119 |
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector |
| 120 |
{ |
| 121 |
if (aSelector == @selector(beginDragWithFiles:) |
| 122 |
|| aSelector == @selector(clearKillRing) |
| 123 |
|| aSelector == @selector(contextClick) |
| 124 |
|| aSelector == @selector(enableDOMUIEventLogging:) |
| 125 |
|| aSelector == @selector(fireKeyboardEventsToElement:) |
| 126 |
|| aSelector == @selector(keyDown:withModifiers:withLocation:) |
| 127 |
|| aSelector == @selector(leapForward:) |
| 128 |
|| aSelector == @selector(mouseDown:withModifiers:) |
| 129 |
|| aSelector == @selector(mouseMoveToX:Y:) |
| 130 |
|| aSelector == @selector(mouseUp:withModifiers:) |
| 131 |
|| aSelector == @selector(scheduleAsynchronousClick) |
| 132 |
|| aSelector == @selector(textZoomIn) |
| 133 |
|| aSelector == @selector(textZoomOut) |
| 134 |
|| aSelector == @selector(zoomPageIn) |
| 135 |
|| aSelector == @selector(zoomPageOut) |
| 136 |
|| aSelector == @selector(mouseScrollByX:andY:) |
| 137 |
|| aSelector == @selector(continuousMouseScrollByX:andY:)) |
| 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(contextClick)) |
| 154 |
return @"contextClick"; |
| 155 |
if (aSelector == @selector(enableDOMUIEventLogging:)) |
| 156 |
return @"enableDOMUIEventLogging"; |
| 157 |
if (aSelector == @selector(fireKeyboardEventsToElement:)) |
| 158 |
return @"fireKeyboardEventsToElement"; |
| 159 |
if (aSelector == @selector(keyDown:withModifiers:withLocation:)) |
| 160 |
return @"keyDown"; |
| 161 |
if (aSelector == @selector(leapForward:)) |
| 162 |
return @"leapForward"; |
| 163 |
if (aSelector == @selector(mouseDown:withModifiers:)) |
| 164 |
return @"mouseDown"; |
| 165 |
if (aSelector == @selector(mouseUp:withModifiers:)) |
| 166 |
return @"mouseUp"; |
| 167 |
if (aSelector == @selector(mouseMoveToX:Y:)) |
| 168 |
return @"mouseMoveTo"; |
| 169 |
if (aSelector == @selector(setDragMode:)) |
| 170 |
return @"setDragMode"; |
| 171 |
if (aSelector == @selector(mouseScrollByX:andY:)) |
| 172 |
return @"mouseScrollBy"; |
| 173 |
if (aSelector == @selector(continuousMouseScrollByX:andY:)) |
| 174 |
return @"continuousMouseScrollBy"; |
| 175 |
return nil; |
| 176 |
} |
| 177 |
|
| 178 |
- (id)init |
| 179 |
{ |
| 180 |
self = [super init]; |
| 181 |
if (self) |
| 182 |
dragMode = YES; |
| 183 |
return self; |
| 184 |
} |
| 185 |
|
| 186 |
- (void)dealloc |
| 187 |
{ |
| 188 |
[super dealloc]; |
| 189 |
} |
| 190 |
|
| 191 |
- (double)currentEventTime |
| 192 |
{ |
| 193 |
return GetCurrentEventTime() + timeOffset; |
| 194 |
} |
| 195 |
|
| 196 |
- (void)leapForward:(int)milliseconds |
| 197 |
{ |
| 198 |
if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { |
| 199 |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSender instanceMethodSignatureForSelector:@selector(leapForward:)]]; |
| 200 |
[invocation setTarget:self]; |
| 201 |
[invocation setSelector:@selector(leapForward:)]; |
| 202 |
[invocation setArgument:&milliseconds atIndex:2]; |
| 203 |
|
| 204 |
[EventSender saveEvent:invocation]; |
| 205 |
|
| 206 |
return; |
| 207 |
} |
| 208 |
|
| 209 |
timeOffset += milliseconds / 1000.0; |
| 210 |
} |
| 211 |
|
| 212 |
- (void)clearKillRing |
| 213 |
{ |
| 214 |
_NSNewKillRingSequence(); |
| 215 |
} |
| 216 |
|
| 217 |
static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action) |
| 218 |
{ |
| 219 |
switch (button) { |
| 220 |
case LeftMouseButton: |
| 221 |
switch (action) { |
| 222 |
case MouseDown: |
| 223 |
return NSLeftMouseDown; |
| 224 |
case MouseUp: |
| 225 |
return NSLeftMouseUp; |
| 226 |
case MouseDragged: |
| 227 |
return NSLeftMouseDragged; |
| 228 |
} |
| 229 |
case RightMouseButton: |
| 230 |
switch (action) { |
| 231 |
case MouseDown: |
| 232 |
return NSRightMouseDown; |
| 233 |
case MouseUp: |
| 234 |
return NSRightMouseUp; |
| 235 |
case MouseDragged: |
| 236 |
return NSRightMouseDragged; |
| 237 |
} |
| 238 |
default: |
| 239 |
switch (action) { |
| 240 |
case MouseDown: |
| 241 |
return NSOtherMouseDown; |
| 242 |
case MouseUp: |
| 243 |
return NSOtherMouseUp; |
| 244 |
case MouseDragged: |
| 245 |
return NSOtherMouseDragged; |
| 246 |
} |
| 247 |
} |
| 248 |
assert(0); |
| 249 |
return static_cast<NSEventType>(0); |
| 250 |
} |
| 251 |
|
| 252 |
- (void)beginDragWithFiles:(WebScriptObject*)jsFilePaths |
| 253 |
{ |
| 254 |
assert(!draggingInfo); |
| 255 |
assert([jsFilePaths isKindOfClass:[WebScriptObject class]]); |
| 256 |
|
| 257 |
NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName]; |
| 258 |
[pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; |
| 259 |
|
| 260 |
NSURL *currentTestURL = [NSURL URLWithString:[[mainFrame webView] mainFrameURL]]; |
| 261 |
|
| 262 |
NSMutableArray *filePaths = [NSMutableArray array]; |
| 263 |
for (unsigned i = 0; [[jsFilePaths webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { |
| 264 |
NSString *filePath = (NSString *)[jsFilePaths webScriptValueAtIndex:i]; |
| 265 |
// Have NSURL encode the name so that we handle '?' in file names correctly. |
| 266 |
NSURL *fileURL = [NSURL fileURLWithPath:filePath]; |
| 267 |
NSURL *absoluteFileURL = [NSURL URLWithString:[fileURL relativeString] relativeToURL:currentTestURL]; |
| 268 |
[filePaths addObject:[absoluteFileURL path]]; |
| 269 |
} |
| 270 |
|
| 271 |
[pboard setPropertyList:filePaths forType:NSFilenamesPboardType]; |
| 272 |
assert([pboard propertyListForType:NSFilenamesPboardType]); // setPropertyList will silently fail on error, assert that it didn't fail |
| 273 |
|
| 274 |
// Provide a source, otherwise [DumpRenderTreeDraggingInfo draggingSourceOperationMask] defaults to NSDragOperationNone |
| 275 |
DumpRenderTreeFileDraggingSource *source = [[[DumpRenderTreeFileDraggingSource alloc] init] autorelease]; |
| 276 |
draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pboard source:source]; |
| 277 |
[[mainFrame webView] draggingEntered:draggingInfo]; |
| 278 |
|
| 279 |
dragMode = NO; // dragMode saves events and then replays them later. We don't need/want that. |
| 280 |
leftMouseButtonDown = YES; // Make the rest of eventSender think a drag is in progress |
| 281 |
} |
| 282 |
|
| 283 |
- (void)updateClickCountForButton:(int)buttonNumber |
| 284 |
{ |
| 285 |
if (([self currentEventTime] - lastClick >= 1) || |
| 286 |
!NSEqualPoints(lastMousePosition, lastClickPosition) || |
| 287 |
lastClickButton != buttonNumber) { |
| 288 |
clickCount = 1; |
| 289 |
lastClickButton = buttonNumber; |
| 290 |
} else |
| 291 |
clickCount++; |
| 292 |
} |
| 293 |
|
| 294 |
static int buildModifierFlags(const WebScriptObject* modifiers) |
| 295 |
{ |
| 296 |
int flags = 0; |
| 297 |
if (![modifiers isKindOfClass:[WebScriptObject class]]) |
| 298 |
return flags; |
| 299 |
for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) { |
| 300 |
NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i]; |
| 301 |
if ([modifierName isEqual:@"ctrlKey"]) |
| 302 |
flags |= NSControlKeyMask; |
| 303 |
else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"]) |
| 304 |
flags |= NSShiftKeyMask; |
| 305 |
else if ([modifierName isEqual:@"altKey"]) |
| 306 |
flags |= NSAlternateKeyMask; |
| 307 |
else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"]) |
| 308 |
flags |= NSCommandKeyMask; |
| 309 |
} |
| 310 |
return flags; |
| 311 |
} |
| 312 |
|
| 313 |
- (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers |
| 314 |
{ |
| 315 |
[[[mainFrame frameView] documentView] layout]; |
| 316 |
[self updateClickCountForButton:buttonNumber]; |
| 317 |
|
| 318 |
NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown); |
| 319 |
NSEvent *event = [NSEvent mouseEventWithType:eventType |
| 320 |
location:lastMousePosition |
| 321 |
modifierFlags:buildModifierFlags(modifiers) |
| 322 |
timestamp:[self currentEventTime] |
| 323 |
windowNumber:[[[mainFrame webView] window] windowNumber] |
| 324 |
context:[NSGraphicsContext currentContext] |
| 325 |
eventNumber:++eventNumber |
| 326 |
clickCount:clickCount |
| 327 |
pressure:0.0]; |
| 328 |
|
| 329 |
NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
| 330 |
if (subView) { |
| 331 |
[subView mouseDown:event]; |
| 332 |
if (buttonNumber == LeftMouseButton) |
| 333 |
leftMouseButtonDown = YES; |
| 334 |
} |
| 335 |
} |
| 336 |
|
| 337 |
- (void)mouseDown:(int)buttonNumber |
| 338 |
{ |
| 339 |
[self mouseDown:buttonNumber withModifiers:nil]; |
| 340 |
} |
| 341 |
|
| 342 |
- (void)textZoomIn |
| 343 |
{ |
| 344 |
[[mainFrame webView] makeTextLarger:self]; |
| 345 |
} |
| 346 |
|
| 347 |
- (void)textZoomOut |
| 348 |
{ |
| 349 |
[[mainFrame webView] makeTextSmaller:self]; |
| 350 |
} |
| 351 |
|
| 352 |
- (void)zoomPageIn |
| 353 |
{ |
| 354 |
[[mainFrame webView] zoomPageIn:self]; |
| 355 |
} |
| 356 |
|
| 357 |
- (void)zoomPageOut |
| 358 |
{ |
| 359 |
[[mainFrame webView] zoomPageOut:self]; |
| 360 |
} |
| 361 |
|
| 362 |
- (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers |
| 363 |
{ |
| 364 |
if (dragMode && !replayingSavedEvents) { |
| 365 |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSender instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]]; |
| 366 |
[invocation setTarget:self]; |
| 367 |
[invocation setSelector:@selector(mouseUp:withModifiers:)]; |
| 368 |
[invocation setArgument:&buttonNumber atIndex:2]; |
| 369 |
[invocation setArgument:&modifiers atIndex:3]; |
| 370 |
|
| 371 |
[EventSender saveEvent:invocation]; |
| 372 |
[EventSender replaySavedEvents]; |
| 373 |
|
| 374 |
return; |
| 375 |
} |
| 376 |
|
| 377 |
[[[mainFrame frameView] documentView] layout]; |
| 378 |
NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp); |
| 379 |
NSEvent *event = [NSEvent mouseEventWithType:eventType |
| 380 |
location:lastMousePosition |
| 381 |
modifierFlags:buildModifierFlags(modifiers) |
| 382 |
timestamp:[self currentEventTime] |
| 383 |
windowNumber:[[[mainFrame webView] window] windowNumber] |
| 384 |
context:[NSGraphicsContext currentContext] |
| 385 |
eventNumber:++eventNumber |
| 386 |
clickCount:clickCount |
| 387 |
pressure:0.0]; |
| 388 |
|
| 389 |
NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
| 390 |
// FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView. |
| 391 |
// The right solution is just to use NSApplication's built-in event sending methods, |
| 392 |
// instead of rolling our own algorithm for selecting an event target. |
| 393 |
targetView = targetView ? targetView : [[mainFrame frameView] documentView]; |
| 394 |
assert(targetView); |
| 395 |
[targetView mouseUp:event]; |
| 396 |
if (buttonNumber == LeftMouseButton) |
| 397 |
leftMouseButtonDown = NO; |
| 398 |
lastClick = [event timestamp]; |
| 399 |
lastClickPosition = lastMousePosition; |
| 400 |
if (draggingInfo) { |
| 401 |
WebView *webView = [mainFrame webView]; |
| 402 |
|
| 403 |
NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo]; |
| 404 |
|
| 405 |
if (dragOperation != NSDragOperationNone) |
| 406 |
[webView performDragOperation:draggingInfo]; |
| 407 |
else |
| 408 |
[webView draggingExited:draggingInfo]; |
| 409 |
// Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation: |
| 410 |
if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)]) |
| 411 |
[[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation]; |
| 412 |
[draggingInfo release]; |
| 413 |
draggingInfo = nil; |
| 414 |
} |
| 415 |
} |
| 416 |
|
| 417 |
- (void)mouseUp:(int)buttonNumber |
| 418 |
{ |
| 419 |
[self mouseUp:buttonNumber withModifiers:nil]; |
| 420 |
} |
| 421 |
|
| 422 |
- (void)mouseMoveToX:(int)x Y:(int)y |
| 423 |
{ |
| 424 |
if (dragMode && leftMouseButtonDown && !replayingSavedEvents) { |
| 425 |
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSender instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]]; |
| 426 |
[invocation setTarget:self]; |
| 427 |
[invocation setSelector:@selector(mouseMoveToX:Y:)]; |
| 428 |
[invocation setArgument:&x atIndex:2]; |
| 429 |
[invocation setArgument:&y atIndex:3]; |
| 430 |
|
| 431 |
[EventSender saveEvent:invocation]; |
| 432 |
return; |
| 433 |
} |
| 434 |
|
| 435 |
NSView *view = [mainFrame webView]; |
| 436 |
lastMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil]; |
| 437 |
NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSLeftMouseDragged : NSMouseMoved) |
| 438 |
location:lastMousePosition |
| 439 |
modifierFlags:0 |
| 440 |
timestamp:[self currentEventTime] |
| 441 |
windowNumber:[[view window] windowNumber] |
| 442 |
context:[NSGraphicsContext currentContext] |
| 443 |
eventNumber:++eventNumber |
| 444 |
clickCount:(leftMouseButtonDown ? clickCount : 0) |
| 445 |
pressure:0.0]; |
| 446 |
|
| 447 |
NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
| 448 |
if (subView) { |
| 449 |
if (leftMouseButtonDown) { |
| 450 |
if (draggingInfo) { |
| 451 |
// Per NSDragging.h: draggingSources may not implement draggedImage:movedTo: |
| 452 |
if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)]) |
| 453 |
[[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition]; |
| 454 |
[[mainFrame webView] draggingUpdated:draggingInfo]; |
| 455 |
} else |
| 456 |
[subView mouseDragged:event]; |
| 457 |
} else |
| 458 |
[subView mouseMoved:event]; |
| 459 |
} |
| 460 |
} |
| 461 |
|
| 462 |
- (void)mouseScrollByX:(int)x andY:(int)y continuously:(BOOL)c |
| 463 |
{ |
| 464 |
// CGEventCreateScrollWheelEvent() was introduced in 10.5 |
| 465 |
#if !defined(BUILDING_ON_TIGER) |
| 466 |
CGScrollEventUnit unit = c?kCGScrollEventUnitPixel:kCGScrollEventUnitLine; |
| 467 |
CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, unit, 2, y, x); |
| 468 |
|
| 469 |
// CGEvent locations are in global display coordinates. |
| 470 |
CGPoint lastGlobalMousePosition = { |
| 471 |
lastMousePosition.x, |
| 472 |
[[NSScreen mainScreen] frame].size.height - lastMousePosition.y |
| 473 |
}; |
| 474 |
CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition); |
| 475 |
|
| 476 |
NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent]; |
| 477 |
CFRelease(cgScrollEvent); |
| 478 |
|
| 479 |
NSView *subView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]]; |
| 480 |
if (subView) |
| 481 |
[subView scrollWheel:scrollEvent]; |
| 482 |
#endif |
| 483 |
} |
| 484 |
|
| 485 |
- (void)continuousMouseScrollByX:(int)x andY:(int)y |
| 486 |
{ |
| 487 |
[self mouseScrollByX:x andY:y continuously:YES]; |
| 488 |
} |
| 489 |
|
| 490 |
- (void)mouseScrollByX:(int)x andY:(int)y |
| 491 |
{ |
| 492 |
[self mouseScrollByX:x andY:y continuously:NO]; |
| 493 |
} |
| 494 |
|
| 495 |
- (NSArray *)contextClick |
| 496 |
{ |
| 497 |
[[[mainFrame frameView] documentView] layout]; |
| 498 |
[self updateClickCountForButton:RightMouseButton]; |
| 499 |
|
| 500 |
NSEvent *event = [NSEvent mouseEventWithType:NSRightMouseDown |
| 501 |
location:lastMousePosition |
| 502 |
modifierFlags:0 |
| 503 |
timestamp:[self currentEventTime] |
| 504 |
windowNumber:[[[mainFrame webView] window] windowNumber] |
| 505 |
context:[NSGraphicsContext currentContext] |
| 506 |
eventNumber:++eventNumber |
| 507 |
clickCount:clickCount |
| 508 |
pressure:0.0]; |
| 509 |
|
| 510 |
NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]]; |
| 511 |
NSMutableArray *menuItemStrings = [NSMutableArray array]; |
| 512 |
|
| 513 |
if (subView) { |
| 514 |
NSMenu* menu = [subView menuForEvent:event]; |
| 515 |
|
| 516 |
for (int i = 0; i < [menu numberOfItems]; ++i) { |
| 517 |
NSMenuItem* menuItem = [menu itemAtIndex:i]; |
| 518 |
if (!strcmp("Inspect Element", [[menuItem title] UTF8String])) |
| 519 |
continue; |
| 520 |
|
| 521 |
if ([menuItem isSeparatorItem]) |
| 522 |
[menuItemStrings addObject:@"<separator>"]; |
| 523 |
else |
| 524 |
[menuItemStrings addObject:[menuItem title]]; |
| 525 |
} |
| 526 |
} |
| 527 |
|
| 528 |
return menuItemStrings; |
| 529 |
} |
| 530 |
|
| 531 |
- (void)scheduleAsynchronousClick |
| 532 |
{ |
| 533 |
[self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0]; |
| 534 |
[self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0]; |
| 535 |
} |
| 536 |
|
| 537 |
+ (void)saveEvent:(NSInvocation *)event |
| 538 |
{ |
| 539 |
if (!savedMouseEvents) |
| 540 |
savedMouseEvents = [[NSMutableArray alloc] init]; |
| 541 |
[savedMouseEvents addObject:event]; |
| 542 |
} |
| 543 |
|
| 544 |
+ (void)replaySavedEvents |
| 545 |
{ |
| 546 |
replayingSavedEvents = YES; |
| 547 |
while ([savedMouseEvents count]) { |
| 548 |
// if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate |
| 549 |
NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease]; |
| 550 |
[savedMouseEvents removeObjectAtIndex:0]; |
| 551 |
[invocation invoke]; |
| 552 |
} |
| 553 |
replayingSavedEvents = NO; |
| 554 |
} |
| 555 |
|
| 556 |
+ (void)clearSavedEvents |
| 557 |
{ |
| 558 |
[savedMouseEvents release]; |
| 559 |
savedMouseEvents = nil; |
| 560 |
} |
| 561 |
|
| 562 |
- (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)keyLocation |
| 563 |
{ |
| 564 |
NSString *eventCharacter = character; |
| 565 |
unsigned short keyCode = 0; |
| 566 |
if ([character isEqualToString:@"leftArrow"]) { |
| 567 |
const unichar ch = NSLeftArrowFunctionKey; |
| 568 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 569 |
keyCode = 0x7B; |
| 570 |
} else if ([character isEqualToString:@"rightArrow"]) { |
| 571 |
const unichar ch = NSRightArrowFunctionKey; |
| 572 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 573 |
keyCode = 0x7C; |
| 574 |
} else if ([character isEqualToString:@"upArrow"]) { |
| 575 |
const unichar ch = NSUpArrowFunctionKey; |
| 576 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 577 |
keyCode = 0x7E; |
| 578 |
} else if ([character isEqualToString:@"downArrow"]) { |
| 579 |
const unichar ch = NSDownArrowFunctionKey; |
| 580 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 581 |
keyCode = 0x7D; |
| 582 |
} else if ([character isEqualToString:@"pageUp"]) { |
| 583 |
const unichar ch = NSPageUpFunctionKey; |
| 584 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 585 |
keyCode = 0x74; |
| 586 |
} else if ([character isEqualToString:@"pageDown"]) { |
| 587 |
const unichar ch = NSPageDownFunctionKey; |
| 588 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 589 |
keyCode = 0x79; |
| 590 |
} else if ([character isEqualToString:@"home"]) { |
| 591 |
const unichar ch = NSHomeFunctionKey; |
| 592 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 593 |
keyCode = 0x73; |
| 594 |
} else if ([character isEqualToString:@"end"]) { |
| 595 |
const unichar ch = NSEndFunctionKey; |
| 596 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 597 |
keyCode = 0x77; |
| 598 |
} else if ([character isEqualToString:@"insert"]) { |
| 599 |
const unichar ch = NSInsertFunctionKey; |
| 600 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 601 |
keyCode = 0x72; |
| 602 |
} else if ([character isEqualToString:@"delete"]) { |
| 603 |
const unichar ch = NSDeleteFunctionKey; |
| 604 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 605 |
keyCode = 0x75; |
| 606 |
} else if ([character isEqualToString:@"printScreen"]) { |
| 607 |
const unichar ch = NSPrintScreenFunctionKey; |
| 608 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 609 |
keyCode = 0x0; // There is no known virtual key code for PrintScreen. |
| 610 |
} |
| 611 |
|
| 612 |
// Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24"). |
| 613 |
// If the input string is a function-key name, set its key code. |
| 614 |
for (unsigned i = 1; i <= 24; i++) { |
| 615 |
if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) { |
| 616 |
const unichar ch = NSF1FunctionKey + (i - 1); |
| 617 |
eventCharacter = [NSString stringWithCharacters:&ch length:1]; |
| 618 |
switch (i) { |
| 619 |
case 1: keyCode = 0x7A; break; |
| 620 |
case 2: keyCode = 0x78; break; |
| 621 |
case 3: keyCode = 0x63; break; |
| 622 |
case 4: keyCode = 0x76; break; |
| 623 |
case 5: keyCode = 0x60; break; |
| 624 |
case 6: keyCode = 0x61; break; |
| 625 |
case 7: keyCode = 0x62; break; |
| 626 |
case 8: keyCode = 0x64; break; |
| 627 |
case 9: keyCode = 0x65; break; |
| 628 |
case 10: keyCode = 0x6D; break; |
| 629 |
case 11: keyCode = 0x67; break; |
| 630 |
case 12: keyCode = 0x6F; break; |
| 631 |
case 13: keyCode = 0x69; break; |
| 632 |
case 14: keyCode = 0x6B; break; |
| 633 |
case 15: keyCode = 0x71; break; |
| 634 |
case 16: keyCode = 0x6A; break; |
| 635 |
case 17: keyCode = 0x40; break; |
| 636 |
case 18: keyCode = 0x4F; break; |
| 637 |
case 19: keyCode = 0x50; break; |
| 638 |
case 20: keyCode = 0x5A; break; |
| 639 |
} |
| 640 |
} |
| 641 |
} |
| 642 |
|
| 643 |
// FIXME: No keyCode is set for most keys. |
| 644 |
if ([character isEqualToString:@"\t"]) |
| 645 |
keyCode = 0x30; |
| 646 |
else if ([character isEqualToString:@" "]) |
| 647 |
keyCode = 0x31; |
| 648 |
else if ([character isEqualToString:@"\r"]) |
| 649 |
keyCode = 0x24; |
| 650 |
else if ([character isEqualToString:@"\n"]) |
| 651 |
keyCode = 0x4C; |
| 652 |
else if ([character isEqualToString:@"\x8"]) |
| 653 |
keyCode = 0x33; |
| 654 |
else if ([character isEqualToString:@"7"]) |
| 655 |
keyCode = 0x1A; |
| 656 |
else if ([character isEqualToString:@"5"]) |
| 657 |
keyCode = 0x17; |
| 658 |
else if ([character isEqualToString:@"9"]) |
| 659 |
keyCode = 0x19; |
| 660 |
else if ([character isEqualToString:@"0"]) |
| 661 |
keyCode = 0x1D; |
| 662 |
else if ([character isEqualToString:@"a"]) |
| 663 |
keyCode = 0x00; |
| 664 |
else if ([character isEqualToString:@"b"]) |
| 665 |
keyCode = 0x0B; |
| 666 |
else if ([character isEqualToString:@"d"]) |
| 667 |
keyCode = 0x02; |
| 668 |
else if ([character isEqualToString:@"e"]) |
| 669 |
keyCode = 0x0E; |
| 670 |
|
| 671 |
NSString *charactersIgnoringModifiers = eventCharacter; |
| 672 |
|
| 673 |
int modifierFlags = 0; |
| 674 |
|
| 675 |
if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') { |
| 676 |
modifierFlags |= NSShiftKeyMask; |
| 677 |
charactersIgnoringModifiers = [character lowercaseString]; |
| 678 |
} |
| 679 |
|
| 680 |
modifierFlags |= buildModifierFlags(modifiers); |
| 681 |
|
| 682 |
if (keyLocation == DOM_KEY_LOCATION_NUMPAD) |
| 683 |
modifierFlags |= NSNumericPadKeyMask; |
| 684 |
|
| 685 |
[[[mainFrame frameView] documentView] layout]; |
| 686 |
|
| 687 |
NSEvent *event = [NSEvent keyEventWithType:NSKeyDown |
| 688 |
location:NSMakePoint(5, 5) |
| 689 |
modifierFlags:modifierFlags |
| 690 |
timestamp:[self currentEventTime] |
| 691 |
windowNumber:[[[mainFrame webView] window] windowNumber] |
| 692 |
context:[NSGraphicsContext currentContext] |
| 693 |
characters:eventCharacter |
| 694 |
charactersIgnoringModifiers:charactersIgnoringModifiers |
| 695 |
isARepeat:NO |
| 696 |
keyCode:keyCode]; |
| 697 |
|
| 698 |
[[[[mainFrame webView] window] firstResponder] keyDown:event]; |
| 699 |
|
| 700 |
event = [NSEvent keyEventWithType:NSKeyUp |
| 701 |
location:NSMakePoint(5, 5) |
| 702 |
modifierFlags:modifierFlags |
| 703 |
timestamp:[self currentEventTime] |
| 704 |
windowNumber:[[[mainFrame webView] window] windowNumber] |
| 705 |
context:[NSGraphicsContext currentContext] |
| 706 |
characters:eventCharacter |
| 707 |
charactersIgnoringModifiers:charactersIgnoringModifiers |
| 708 |
isARepeat:NO |
| 709 |
keyCode:keyCode]; |
| 710 |
|
| 711 |
[[[[mainFrame webView] window] firstResponder] keyUp:event]; |
| 712 |
} |
| 713 |
|
| 714 |
- (void)enableDOMUIEventLogging:(WebScriptObject *)node |
| 715 |
{ |
| 716 |
NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator]; |
| 717 |
id eventName; |
| 718 |
while ((eventName = [eventEnumerator nextObject])) { |
| 719 |
[(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO]; |
| 720 |
} |
| 721 |
} |
| 722 |
|
| 723 |
- (void)handleEvent:(DOMEvent *)event |
| 724 |
{ |
| 725 |
DOMNode *target = [event target]; |
| 726 |
|
| 727 |
printf("event type: %s\n", [[event type] UTF8String]); |
| 728 |
printf(" target: <%s>\n", [[[target nodeName] lowercaseString] UTF8String]); |
| 729 |
|
| 730 |
if ([event isKindOfClass:[DOMEvent class]]) { |
| 731 |
printf(" eventPhase: %d\n", [event eventPhase]); |
| 732 |
printf(" bubbles: %d\n", [event bubbles] ? 1 : 0); |
| 733 |
printf(" cancelable: %d\n", [event cancelable] ? 1 : 0); |
| 734 |
} |
| 735 |
|
| 736 |
if ([event isKindOfClass:[DOMUIEvent class]]) { |
| 737 |
printf(" detail: %d\n", [(DOMUIEvent*)event detail]); |
| 738 |
|
| 739 |
DOMAbstractView *view = [(DOMUIEvent*)event view]; |
| 740 |
if (view) { |
| 741 |
printf(" view: OK"); |
| 742 |
if ([view document]) |
| 743 |
printf(" (document: OK)"); |
| 744 |
printf("\n"); |
| 745 |
} |
| 746 |
} |
| 747 |
|
| 748 |
if ([event isKindOfClass:[DOMKeyboardEvent class]]) { |
| 749 |
printf(" keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]); |
| 750 |
printf(" keyLocation: %d\n", [(DOMKeyboardEvent*)event keyLocation]); |
| 751 |
printf(" modifier keys: c:%d s:%d a:%d m:%d\n", |
| 752 |
[(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0, |
| 753 |
[(DOMKeyboardEvent*)event shiftKey] ? 1 : 0, |
| 754 |
[(DOMKeyboardEvent*)event altKey] ? 1 : 0, |
| 755 |
[(DOMKeyboardEvent*)event metaKey] ? 1 : 0); |
| 756 |
printf(" keyCode: %d\n", [(DOMKeyboardEvent*)event keyCode]); |
| 757 |
printf(" charCode: %d\n", [(DOMKeyboardEvent*)event charCode]); |
| 758 |
} |
| 759 |
|
| 760 |
if ([event isKindOfClass:[DOMMouseEvent class]]) { |
| 761 |
printf(" button: %d\n", [(DOMMouseEvent*)event button]); |
| 762 |
printf(" clientX: %d\n", [(DOMMouseEvent*)event clientX]); |
| 763 |
printf(" clientY: %d\n", [(DOMMouseEvent*)event clientY]); |
| 764 |
printf(" screenX: %d\n", [(DOMMouseEvent*)event screenX]); |
| 765 |
printf(" screenY: %d\n", [(DOMMouseEvent*)event screenY]); |
| 766 |
printf(" modifier keys: c:%d s:%d a:%d m:%d\n", |
| 767 |
[(DOMMouseEvent*)event ctrlKey] ? 1 : 0, |
| 768 |
[(DOMMouseEvent*)event shiftKey] ? 1 : 0, |
| 769 |
[(DOMMouseEvent*)event altKey] ? 1 : 0, |
| 770 |
[(DOMMouseEvent*)event metaKey] ? 1 : 0); |
| 771 |
id relatedTarget = [(DOMMouseEvent*)event relatedTarget]; |
| 772 |
if (relatedTarget) { |
| 773 |
printf(" relatedTarget: %s", [[[relatedTarget class] description] UTF8String]); |
| 774 |
if ([relatedTarget isKindOfClass:[DOMNode class]]) |
| 775 |
printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]); |
| 776 |
printf("\n"); |
| 777 |
} |
| 778 |
} |
| 779 |
|
| 780 |
if ([event isKindOfClass:[DOMMutationEvent class]]) { |
| 781 |
printf(" prevValue: %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]); |
| 782 |
printf(" newValue: %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]); |
| 783 |
printf(" attrName: %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]); |
| 784 |
printf(" attrChange: %d\n", [(DOMMutationEvent*)event attrChange]); |
| 785 |
DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode]; |
| 786 |
if (relatedNode) { |
| 787 |
printf(" relatedNode: %s (nodeName: %s)\n", |
| 788 |
[[[relatedNode class] description] UTF8String], |
| 789 |
[[relatedNode nodeName] UTF8String]); |
| 790 |
} |
| 791 |
} |
| 792 |
|
| 793 |
if ([event isKindOfClass:[DOMWheelEvent class]]) { |
| 794 |
printf(" clientX: %d\n", [(DOMWheelEvent*)event clientX]); |
| 795 |
printf(" clientY: %d\n", [(DOMWheelEvent*)event clientY]); |
| 796 |
printf(" screenX: %d\n", [(DOMWheelEvent*)event screenX]); |
| 797 |
printf(" screenY: %d\n", [(DOMWheelEvent*)event screenY]); |
| 798 |
printf(" modifier keys: c:%d s:%d a:%d m:%d\n", |
| 799 |
[(DOMWheelEvent*)event ctrlKey] ? 1 : 0, |
| 800 |
[(DOMWheelEvent*)event shiftKey] ? 1 : 0, |
| 801 |
[(DOMWheelEvent*)event altKey] ? 1 : 0, |
| 802 |
[(DOMWheelEvent*)event metaKey] ? 1 : 0); |
| 803 |
printf(" isHorizontal: %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0); |
| 804 |
printf(" wheelDelta: %d\n", [(DOMWheelEvent*)event wheelDelta]); |
| 805 |
} |
| 806 |
} |
| 807 |
|
| 808 |
// FIXME: It's not good to have a test hard-wired into this controller like this. |
| 809 |
// Instead we need to get testing framework based on the Objective-C bindings |
| 810 |
// to work well enough that we can test that way instead. |
| 811 |
- (void)fireKeyboardEventsToElement:(WebScriptObject *)element { |
| 812 |
|
| 813 |
if (![element isKindOfClass:[DOMHTMLElement class]]) |
| 814 |
return; |
| 815 |
|
| 816 |
DOMHTMLElement *target = (DOMHTMLElement*)element; |
| 817 |
DOMDocument *document = [target ownerDocument]; |
| 818 |
|
| 819 |
// Keyboard Event 1 |
| 820 |
|
| 821 |
DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"]; |
| 822 |
[(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown" |
| 823 |
canBubble:YES |
| 824 |
cancelable:YES |
| 825 |
view:[document defaultView] |
| 826 |
keyIdentifier:@"U+000041" |
| 827 |
keyLocation:0 |
| 828 |
ctrlKey:YES |
| 829 |
altKey:NO |
| 830 |
shiftKey:NO |
| 831 |
metaKey:NO]; |
| 832 |
[target dispatchEvent:domEvent]; |
| 833 |
|
| 834 |
// Keyboard Event 2 |
| 835 |
|
| 836 |
domEvent = [document createEvent:@"KeyboardEvent"]; |
| 837 |
[(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress" |
| 838 |
canBubble:YES |
| 839 |
cancelable:YES |
| 840 |
view:[document defaultView] |
| 841 |
keyIdentifier:@"U+000045" |
| 842 |
keyLocation:1 |
| 843 |
ctrlKey:NO |
| 844 |
altKey:YES |
| 845 |
shiftKey:NO |
| 846 |
metaKey:NO]; |
| 847 |
[target dispatchEvent:domEvent]; |
| 848 |
|
| 849 |
// Keyboard Event 3 |
| 850 |
|
| 851 |
domEvent = [document createEvent:@"KeyboardEvent"]; |
| 852 |
[(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup" |
| 853 |
canBubble:YES |
| 854 |
cancelable:YES |
| 855 |
view:[document defaultView] |
| 856 |
keyIdentifier:@"U+000056" |
| 857 |
keyLocation:0 |
| 858 |
ctrlKey:NO |
| 859 |
altKey:NO |
| 860 |
shiftKey:NO |
| 861 |
metaKey:NO]; |
| 862 |
[target dispatchEvent:domEvent]; |
| 863 |
|
| 864 |
} |
| 865 |
|
| 866 |
@end |
| 867 |
#endif |