2 * Copyright (C) 2005-2018 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>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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 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.
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.
33 #import "EventSendingController.h"
35 #import "DumpRenderTree.h"
36 #import "DumpRenderTreeDraggingInfo.h"
37 #import "DumpRenderTreeFileDraggingSource.h"
38 #import "DumpRenderTreePasteboard.h"
39 #import "WebCoreTestSupport.h"
40 #import <WebKit/DOMPrivate.h>
41 #import <WebKit/WebKit.h>
42 #import <WebKit/WebViewPrivate.h>
44 #import <wtf/RetainPtr.h>
47 #import <Carbon/Carbon.h> // for GetCurrentEventTime()
48 #import <WebKit/WebHTMLView.h>
49 #import <objc/runtime.h>
50 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
54 #import <UIKit/UIKit.h>
55 #import <WebKit/KeyEventCodesIOS.h>
56 #import <WebKit/WAKWindow.h>
57 #import <WebKit/WebEvent.h>
58 #import <pal/spi/ios/GraphicsServicesSPI.h> // for GSCurrentEventTimestamp()
62 extern "C" void _NSNewKillRingSequence();
64 @interface NSApplication ()
65 - (void)_setCurrentEvent:(NSEvent *)event;
75 // Match the DOM spec (sadly the DOM spec does not provide an enum)
78 MiddleMouseButton = 1,
83 struct KeyMappingEntry {
87 NSString* characterName;
90 NSPoint lastMousePosition;
91 NSPoint lastClickPosition;
92 int lastClickButton = NoMouseButton;
93 NSArray *webkitDomEventNames;
94 NSMutableArray *savedMouseEvents; // mouse events sent between mouseDown and mouseUp are stored here, and then executed at once.
95 BOOL replayingSavedEvents;
99 @interface SyntheticTouch : NSObject {
103 unsigned _identifier;
106 @property (nonatomic) CGPoint location;
107 @property (nonatomic) UITouchPhase phase;
108 @property (nonatomic) unsigned identifier;
110 + (id)touchWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier;
111 - (id)initWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier;
114 @implementation SyntheticTouch
116 @synthesize location = _location;
117 @synthesize phase = _phase;
118 @synthesize identifier = _identifier;
120 + (id)touchWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier
122 return [[[SyntheticTouch alloc] initWithLocation:location phase:phase identifier:identifier] autorelease];
125 - (id)initWithLocation:(CGPoint)location phase:(UITouchPhase)phase identifier:(unsigned)identifier
127 if ((self = [super init])) {
128 _location = location;
130 _identifier = identifier;
134 @end // SyntheticTouch
138 @interface WebView (WebViewInternalForTesting)
139 - (WebCore::Frame*)_mainCoreFrame;
143 @implementation EventSendingController
146 static NSDraggingSession *drt_WebHTMLView_beginDraggingSessionWithItemsEventSource(WebHTMLView *self, id _cmd, NSArray<NSDraggingItem *> *items, NSEvent *event, id<NSDraggingSource> source)
148 ASSERT(!draggingInfo);
150 WebFrameView *webFrameView = ^ {
151 for (NSView *superview = self.superview; superview; superview = superview.superview) {
152 if ([superview isKindOfClass:WebFrameView.class])
153 return (WebFrameView *)superview;
156 ASSERT_NOT_REACHED();
157 return (WebFrameView *)nil;
160 WebView *webView = webFrameView.webFrame.webView;
162 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
163 for (NSDraggingItem *item in items)
164 [pasteboard writeObjects:@[ item.item ]];
166 draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pasteboard source:source];
167 [webView draggingUpdated:draggingInfo];
168 [EventSendingController replaySavedEvents];
176 webkitDomEventNames = [[NSArray alloc] initWithObjects:
226 // Add an implementation of -[WebHTMLView beginDraggingSessionWithItems:event:source:].
227 SEL selector = @selector(beginDraggingSessionWithItems:event:source:);
228 const char* typeEncoding = method_getTypeEncoding(class_getInstanceMethod(NSView.class, selector));
230 if (!class_addMethod(WebHTMLView.class, selector, reinterpret_cast<IMP>(drt_WebHTMLView_beginDraggingSessionWithItemsEventSource), typeEncoding))
231 ASSERT_NOT_REACHED();
235 + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
237 if (aSelector == @selector(clearKillRing)
238 || aSelector == @selector(contextClick)
239 || aSelector == @selector(enableDOMUIEventLogging:)
240 || aSelector == @selector(fireKeyboardEventsToElement:)
241 || aSelector == @selector(keyDown:withModifiers:withLocation:)
242 || aSelector == @selector(leapForward:)
243 || aSelector == @selector(mouseDown:withModifiers:)
244 || aSelector == @selector(mouseMoveToX:Y:)
245 || aSelector == @selector(mouseUp:withModifiers:)
246 || aSelector == @selector(scheduleAsynchronousClick)
247 || aSelector == @selector(scheduleAsynchronousKeyDown:withModifiers:withLocation:)
248 || aSelector == @selector(textZoomIn)
249 || aSelector == @selector(textZoomOut)
250 || aSelector == @selector(zoomPageIn)
251 || aSelector == @selector(zoomPageOut)
252 || aSelector == @selector(scalePageBy:atX:andY:)
253 || aSelector == @selector(mouseScrollByX:andY:)
254 || aSelector == @selector(mouseScrollByX:andY:withWheel:andMomentumPhases:)
255 || aSelector == @selector(continuousMouseScrollByX:andY:)
256 || aSelector == @selector(monitorWheelEvents)
257 || aSelector == @selector(callAfterScrollingCompletes:)
259 || aSelector == @selector(beginDragWithFiles:)
260 || aSelector == @selector(beginDragWithFilePromises:)
263 || aSelector == @selector(addTouchAtX:y:)
264 || aSelector == @selector(updateTouchAtIndex:x:y:)
265 || aSelector == @selector(cancelTouchAtIndex:)
266 || aSelector == @selector(clearTouchPoints)
267 || aSelector == @selector(markAllTouchesAsStationary)
268 || aSelector == @selector(releaseTouchAtIndex:)
269 || aSelector == @selector(setTouchModifier:value:)
270 || aSelector == @selector(touchStart)
271 || aSelector == @selector(touchMove)
272 || aSelector == @selector(touchEnd)
273 || aSelector == @selector(touchCancel)
280 + (BOOL)isKeyExcludedFromWebScript:(const char*)name
282 if (strcmp(name, "dragMode") == 0)
287 + (NSString *)webScriptNameForSelector:(SEL)aSelector
290 if (aSelector == @selector(beginDragWithFiles:))
291 return @"beginDragWithFiles";
292 if (aSelector == @selector(beginDragWithFilePromises:))
293 return @"beginDragWithFilePromises";
295 if (aSelector == @selector(contextClick))
296 return @"contextClick";
297 if (aSelector == @selector(enableDOMUIEventLogging:))
298 return @"enableDOMUIEventLogging";
299 if (aSelector == @selector(fireKeyboardEventsToElement:))
300 return @"fireKeyboardEventsToElement";
301 if (aSelector == @selector(keyDown:withModifiers:withLocation:))
303 if (aSelector == @selector(scheduleAsynchronousKeyDown:withModifiers:withLocation:))
304 return @"scheduleAsynchronousKeyDown";
305 if (aSelector == @selector(leapForward:))
306 return @"leapForward";
307 if (aSelector == @selector(mouseDown:withModifiers:))
309 if (aSelector == @selector(mouseUp:withModifiers:))
311 if (aSelector == @selector(mouseMoveToX:Y:))
312 return @"mouseMoveTo";
313 if (aSelector == @selector(mouseScrollByX:andY:))
314 return @"mouseScrollBy";
315 if (aSelector == @selector(mouseScrollByX:andY:withWheel:andMomentumPhases:))
316 return @"mouseScrollByWithWheelAndMomentumPhases";
317 if (aSelector == @selector(continuousMouseScrollByX:andY:))
318 return @"continuousMouseScrollBy";
319 if (aSelector == @selector(scalePageBy:atX:andY:))
320 return @"scalePageBy";
321 if (aSelector == @selector(monitorWheelEvents))
322 return @"monitorWheelEvents";
323 if (aSelector == @selector(callAfterScrollingCompletes:))
324 return @"callAfterScrollingCompletes";
326 if (aSelector == @selector(addTouchAtX:y:))
327 return @"addTouchPoint";
328 if (aSelector == @selector(updateTouchAtIndex:x:y:))
329 return @"updateTouchPoint";
330 if (aSelector == @selector(cancelTouchAtIndex:))
331 return @"cancelTouchPoint";
332 if (aSelector == @selector(clearTouchPoints))
333 return @"clearTouchPoints";
334 if (aSelector == @selector(markAllTouchesAsStationary))
335 return @"markAllTouchesAsStationary";
336 if (aSelector == @selector(releaseTouchAtIndex:))
337 return @"releaseTouchPoint";
338 if (aSelector == @selector(setTouchModifier:value:))
339 return @"setTouchModifier";
340 if (aSelector == @selector(touchStart))
341 return @"touchStart";
342 if (aSelector == @selector(touchMove))
344 if (aSelector == @selector(touchEnd))
346 if (aSelector == @selector(touchCancel))
347 return @"touchCancel";
368 - (double)currentEventTime
371 return GetCurrentEventTime() + timeOffset;
373 return GSCurrentEventTimestamp() + timeOffset;
377 - (void)leapForward:(int)milliseconds
379 if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
380 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(leapForward:)]];
381 [invocation setTarget:self];
382 [invocation setSelector:@selector(leapForward:)];
383 [invocation setArgument:&milliseconds atIndex:2];
385 [EventSendingController saveEvent:invocation];
390 timeOffset += milliseconds / 1000.0;
393 - (void)clearKillRing
396 _NSNewKillRingSequence();
401 static NSEventType eventTypeForMouseButtonAndAction(int button, MouseAction action)
404 case LeftMouseButton:
407 return NSEventTypeLeftMouseDown;
409 return NSEventTypeLeftMouseUp;
411 return NSEventTypeLeftMouseDragged;
413 case RightMouseButton:
416 return NSEventTypeRightMouseDown;
418 return NSEventTypeRightMouseUp;
420 return NSEventTypeRightMouseDragged;
425 return NSEventTypeOtherMouseDown;
427 return NSEventTypeOtherMouseUp;
429 return NSEventTypeOtherMouseDragged;
433 return static_cast<NSEventType>(0);
436 - (void)beginDragWithFiles:(WebScriptObject*)jsFilePaths
438 assert(!draggingInfo);
439 assert([jsFilePaths isKindOfClass:[WebScriptObject class]]);
441 NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName];
442 [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil];
444 NSURL *currentTestURL = [NSURL URLWithString:[[mainFrame webView] mainFrameURL]];
446 NSMutableArray *filePaths = [NSMutableArray array];
447 for (unsigned i = 0; [[jsFilePaths webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
448 NSString *filePath = (NSString *)[jsFilePaths webScriptValueAtIndex:i];
449 // Have NSURL encode the name so that we handle '?' in file names correctly.
450 NSURL *fileURL = [NSURL fileURLWithPath:filePath];
451 NSURL *absoluteFileURL = [NSURL URLWithString:[fileURL relativeString] relativeToURL:currentTestURL];
452 [filePaths addObject:[absoluteFileURL path]];
455 [pboard setPropertyList:filePaths forType:NSFilenamesPboardType];
456 assert([pboard propertyListForType:NSFilenamesPboardType]); // setPropertyList will silently fail on error, assert that it didn't fail
458 // Provide a source, otherwise [DumpRenderTreeDraggingInfo draggingSourceOperationMask] defaults to NSDragOperationNone
459 DumpRenderTreeFileDraggingSource *source = [[[DumpRenderTreeFileDraggingSource alloc] init] autorelease];
460 draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pboard source:source];
461 [[mainFrame webView] draggingEntered:draggingInfo];
463 dragMode = NO; // dragMode saves events and then replays them later. We don't need/want that.
464 leftMouseButtonDown = YES; // Make the rest of eventSender think a drag is in progress
467 - (void)beginDragWithFilePromises:(WebScriptObject *)filePaths
469 assert(!draggingInfo);
471 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
472 [pasteboard declareTypes:@[NSFilesPromisePboardType] owner:nil];
474 NSURL *currentTestURL = [NSURL URLWithString:mainFrame.webView.mainFrameURL];
477 NSMutableArray *fileURLs = [NSMutableArray array];
478 NSMutableArray *fileUTIs = [NSMutableArray array];
480 id filePath = [filePaths webScriptValueAtIndex:i++];
481 if (![filePath isKindOfClass:NSString.class])
484 NSURL *fileURL = [NSURL fileURLWithPath:(NSString *)filePath relativeToURL:currentTestURL];
485 [fileURLs addObject:fileURL];
488 if (![fileURL getResourceValue:&fileUTI forKey:NSURLTypeIdentifierKey error:nil])
490 [fileUTIs addObject:fileUTI];
493 [pasteboard setPropertyList:fileUTIs forType:NSFilesPromisePboardType];
494 assert([pasteboard propertyListForType:NSFilesPromisePboardType]);
496 auto source = adoptNS([[DumpRenderTreeFileDraggingSource alloc] initWithPromisedFileURLs:fileURLs]);
497 draggingInfo = [[DumpRenderTreeDraggingInfo alloc] initWithImage:nil offset:NSZeroSize pasteboard:pasteboard source:source.get()];
498 [mainFrame.webView draggingEntered:draggingInfo];
501 leftMouseButtonDown = YES;
503 #endif // !PLATFORM(IOS)
505 - (void)updateClickCountForButton:(int)buttonNumber
507 if (([self currentEventTime] - lastClick >= 1) ||
508 !NSEqualPoints(lastMousePosition, lastClickPosition) ||
509 lastClickButton != buttonNumber) {
511 lastClickButton = buttonNumber;
516 static int modifierFlags(const NSString* modifierName)
519 const int controlKeyMask = NSEventModifierFlagControl;
520 const int shiftKeyMask = NSEventModifierFlagShift;
521 const int alternateKeyMask = NSEventModifierFlagOption;
522 const int commandKeyMask = NSEventModifierFlagCommand;
523 const int capsLockKeyMask = NSEventModifierFlagCapsLock;
525 const int controlKeyMask = WebEventFlagMaskControl;
526 const int shiftKeyMask = WebEventFlagMaskShift;
527 const int alternateKeyMask = WebEventFlagMaskAlternate;
528 const int commandKeyMask = WebEventFlagMaskCommand;
529 const int capsLockKeyMask = WebEventFlagMaskAlphaShift;
533 if ([modifierName isEqual:@"ctrlKey"])
534 flags |= controlKeyMask;
535 else if ([modifierName isEqual:@"shiftKey"] || [modifierName isEqual:@"rangeSelectionKey"])
536 flags |= shiftKeyMask;
537 else if ([modifierName isEqual:@"altKey"])
538 flags |= alternateKeyMask;
539 else if ([modifierName isEqual:@"metaKey"] || [modifierName isEqual:@"addSelectionKey"])
540 flags |= commandKeyMask;
541 else if ([modifierName isEqual:@"capsLockKey"])
542 flags |= capsLockKeyMask;
547 static int buildModifierFlags(const WebScriptObject* modifiers)
550 if ([modifiers isKindOfClass:[NSString class]])
551 return modifierFlags((NSString*)modifiers);
552 else if (![modifiers isKindOfClass:[WebScriptObject class]])
554 for (unsigned i = 0; [[modifiers webScriptValueAtIndex:i] isKindOfClass:[NSString class]]; i++) {
555 NSString* modifierName = (NSString*)[modifiers webScriptValueAtIndex:i];
556 flags |= modifierFlags(modifierName);
561 - (void)mouseDown:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
563 [[[mainFrame frameView] documentView] layout];
564 [self updateClickCountForButton:buttonNumber];
567 NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseDown);
568 NSEvent *event = [NSEvent mouseEventWithType:eventType
569 location:lastMousePosition
570 modifierFlags:buildModifierFlags(modifiers)
571 timestamp:[self currentEventTime]
572 windowNumber:[[[mainFrame webView] window] windowNumber]
573 context:[NSGraphicsContext currentContext]
574 eventNumber:++eventNumber
575 clickCount:clickCount
578 WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseDown
579 timeStamp:[self currentEventTime]
580 location:lastMousePosition];
583 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
586 [NSApp _setCurrentEvent:event];
588 [subView mouseDown:event];
590 [NSApp _setCurrentEvent:nil];
592 if (buttonNumber == LeftMouseButton)
593 leftMouseButtonDown = YES;
601 - (void)mouseDown:(int)buttonNumber
603 [self mouseDown:buttonNumber withModifiers:nil];
608 [[mainFrame webView] makeTextLarger:self];
613 [[mainFrame webView] makeTextSmaller:self];
618 [[mainFrame webView] zoomPageIn:self];
623 [[mainFrame webView] zoomPageOut:self];
626 - (void)scalePageBy:(float)scale atX:(float)x andY:(float)y
629 // -[WebView _scaleWebView:] is Mac-specific API, and calls functions that
630 // assert to not be used in iOS.
631 [[mainFrame webView] _scaleWebView:scale atOrigin:NSMakePoint(x, y)];
635 - (void)mouseUp:(int)buttonNumber withModifiers:(WebScriptObject*)modifiers
637 if (dragMode && !replayingSavedEvents) {
638 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseUp:withModifiers:)]];
639 [invocation setTarget:self];
640 [invocation setSelector:@selector(mouseUp:withModifiers:)];
641 [invocation setArgument:&buttonNumber atIndex:2];
642 [invocation setArgument:&modifiers atIndex:3];
644 [EventSendingController saveEvent:invocation];
645 [EventSendingController replaySavedEvents];
650 [[[mainFrame frameView] documentView] layout];
652 NSEventType eventType = eventTypeForMouseButtonAndAction(buttonNumber, MouseUp);
653 NSEvent *event = [NSEvent mouseEventWithType:eventType
654 location:lastMousePosition
655 modifierFlags:buildModifierFlags(modifiers)
656 timestamp:[self currentEventTime]
657 windowNumber:[[[mainFrame webView] window] windowNumber]
658 context:[NSGraphicsContext currentContext]
659 eventNumber:++eventNumber
660 clickCount:clickCount
663 WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseUp
664 timeStamp:[self currentEventTime]
665 location:lastMousePosition];
668 NSView *targetView = [[mainFrame webView] hitTest:[event locationInWindow]];
669 // FIXME: Silly hack to teach DRT to respect capturing mouse events outside the WebView.
670 // The right solution is just to use NSApplication's built-in event sending methods,
671 // instead of rolling our own algorithm for selecting an event target.
672 targetView = targetView ? targetView : [[mainFrame frameView] documentView];
675 [NSApp _setCurrentEvent:event];
677 [targetView mouseUp:event];
679 [NSApp _setCurrentEvent:nil];
681 if (buttonNumber == LeftMouseButton)
682 leftMouseButtonDown = NO;
683 lastClick = [event timestamp];
684 lastClickPosition = lastMousePosition;
687 WebView *webView = [mainFrame webView];
689 NSDragOperation dragOperation = [webView draggingUpdated:draggingInfo];
691 if (dragOperation != NSDragOperationNone)
692 [webView performDragOperation:draggingInfo];
694 [webView draggingExited:draggingInfo];
695 // Per NSDragging.h: draggingSources may not implement draggedImage:endedAt:operation:
696 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:endedAt:operation:)])
697 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] endedAt:lastMousePosition operation:dragOperation];
698 [draggingInfo release];
708 - (void)mouseUp:(int)buttonNumber
710 [self mouseUp:buttonNumber withModifiers:nil];
713 - (void)mouseMoveToX:(int)x Y:(int)y
715 if (dragMode && leftMouseButtonDown && !replayingSavedEvents) {
716 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(mouseMoveToX:Y:)]];
717 [invocation setTarget:self];
718 [invocation setSelector:@selector(mouseMoveToX:Y:)];
719 [invocation setArgument:&x atIndex:2];
720 [invocation setArgument:&y atIndex:3];
722 [EventSendingController saveEvent:invocation];
726 NSView *view = [mainFrame webView];
728 NSPoint newMousePosition = [view convertPoint:NSMakePoint(x, [view frame].size.height - y) toView:nil];
729 NSEvent *event = [NSEvent mouseEventWithType:(leftMouseButtonDown ? NSEventTypeLeftMouseDragged : NSEventTypeMouseMoved)
730 location:newMousePosition
732 timestamp:[self currentEventTime]
733 windowNumber:[[view window] windowNumber]
734 context:[NSGraphicsContext currentContext]
735 eventNumber:++eventNumber
736 clickCount:(leftMouseButtonDown ? clickCount : 0)
738 CGEventRef cgEvent = event.CGEvent;
739 CGEventSetIntegerValueField(cgEvent, kCGMouseEventDeltaX, newMousePosition.x - lastMousePosition.x);
740 CGEventSetIntegerValueField(cgEvent, kCGMouseEventDeltaY, newMousePosition.y - lastMousePosition.y);
741 event = [NSEvent eventWithCGEvent:cgEvent];
742 lastMousePosition = newMousePosition;
744 lastMousePosition = [view convertPoint:NSMakePoint(x, y) toView:nil];
745 WebEvent *event = [[WebEvent alloc] initWithMouseEventType:WebEventMouseMoved
746 timeStamp:[self currentEventTime]
747 location:lastMousePosition];
748 #endif // !PLATFORM(IOS)
750 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
753 [NSApp _setCurrentEvent:event];
755 if (leftMouseButtonDown) {
758 // Per NSDragging.h: draggingSources may not implement draggedImage:movedTo:
759 if ([[draggingInfo draggingSource] respondsToSelector:@selector(draggedImage:movedTo:)])
760 [[draggingInfo draggingSource] draggedImage:[draggingInfo draggedImage] movedTo:lastMousePosition];
761 [[mainFrame webView] draggingUpdated:draggingInfo];
763 [subView mouseDragged:event];
766 [subView mouseMoved:event];
768 [NSApp _setCurrentEvent:nil];
777 - (void)mouseScrollByX:(int)x andY:(int)y continuously:(BOOL)continuously
780 CGScrollEventUnit unit = continuously ? kCGScrollEventUnitPixel : kCGScrollEventUnitLine;
781 CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, unit, 2, y, x);
783 // Set the CGEvent location in flipped coords relative to the first screen, which
784 // compensates for the behavior of +[NSEvent eventWithCGEvent:] when the event has
785 // no associated window. See <rdar://problem/17180591>.
786 CGPoint lastGlobalMousePosition = CGPointMake(lastMousePosition.x, [[[NSScreen screens] objectAtIndex:0] frame].size.height - lastMousePosition.y);
787 CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition);
789 NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent];
790 CFRelease(cgScrollEvent);
792 NSView *subView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]];
794 [NSApp _setCurrentEvent:scrollEvent];
795 [subView scrollWheel:scrollEvent];
796 [NSApp _setCurrentEvent:nil];
798 printf("mouseScrollByXandYContinuously: Unable to locate target view for current mouse location.");
802 - (void)continuousMouseScrollByX:(int)x andY:(int)y
804 [self mouseScrollByX:x andY:y continuously:YES];
807 - (void)mouseScrollByX:(int)x andY:(int)y
809 [self mouseScrollByX:x andY:y continuously:NO];
812 - (void)mouseScrollByX:(int)x andY:(int)y withWheel:(NSString*)phaseName andMomentumPhases:(NSString*)momentumName
816 if ([phaseName isEqualToString: @"none"])
818 else if ([phaseName isEqualToString: @"began"])
819 phase = 1; // kCGScrollPhaseBegan
820 else if ([phaseName isEqualToString: @"changed"])
821 phase = 2; // kCGScrollPhaseChanged;
822 else if ([phaseName isEqualToString: @"ended"])
823 phase = 4; // kCGScrollPhaseEnded
824 else if ([phaseName isEqualToString: @"cancelled"])
825 phase = 8; // kCGScrollPhaseCancelled
826 else if ([phaseName isEqualToString: @"maybegin"])
827 phase = 128; // kCGScrollPhaseMayBegin
829 uint32_t momentum = 0;
830 if ([momentumName isEqualToString: @"none"])
831 momentum = 0; //kCGMomentumScrollPhaseNone;
832 else if ([momentumName isEqualToString:@"begin"])
833 momentum = 1; // kCGMomentumScrollPhaseBegin;
834 else if ([momentumName isEqualToString:@"continue"])
835 momentum = 2; // kCGMomentumScrollPhaseContinue;
836 else if ([momentumName isEqualToString:@"end"])
837 momentum = 3; // kCGMomentumScrollPhaseEnd;
839 CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 2, y, x);
841 // Set the CGEvent location in flipped coords relative to the first screen, which
842 // compensates for the behavior of +[NSEvent eventWithCGEvent:] when the event has
843 // no associated window. See <rdar://problem/17180591>.
844 CGPoint lastGlobalMousePosition = CGPointMake(lastMousePosition.x, [[[NSScreen screens] objectAtIndex:0] frame].size.height - lastMousePosition.y);
845 CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition);
846 CGEventSetIntegerValueField(cgScrollEvent, kCGScrollWheelEventIsContinuous, 1);
847 CGEventSetIntegerValueField(cgScrollEvent, kCGScrollWheelEventScrollPhase, phase);
848 CGEventSetIntegerValueField(cgScrollEvent, kCGScrollWheelEventMomentumPhase, momentum);
850 NSEvent* scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent];
851 CFRelease(cgScrollEvent);
853 if (NSView* targetView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]]) {
854 [NSApp _setCurrentEvent:scrollEvent];
855 [targetView scrollWheel:scrollEvent];
856 [NSApp _setCurrentEvent:nil];
858 printf("mouseScrollByX...andMomentumPhases: Unable to locate target view for current mouse location.");
862 - (NSArray *)contextClick
865 [[[mainFrame frameView] documentView] layout];
866 [self updateClickCountForButton:RightMouseButton];
868 NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeRightMouseDown
869 location:lastMousePosition
871 timestamp:[self currentEventTime]
872 windowNumber:[[[mainFrame webView] window] windowNumber]
873 context:[NSGraphicsContext currentContext]
874 eventNumber:++eventNumber
875 clickCount:clickCount
878 NSView *subView = [[mainFrame webView] hitTest:[event locationInWindow]];
879 NSMutableArray *menuItemStrings = [NSMutableArray array];
882 [NSApp _setCurrentEvent:event];
883 NSMenu* menu = [subView menuForEvent:event];
884 [NSApp _setCurrentEvent:nil];
886 for (int i = 0; i < [menu numberOfItems]; ++i) {
887 NSMenuItem* menuItem = [menu itemAtIndex:i];
888 if (!strcmp("Inspect Element", [[menuItem title] UTF8String]))
891 if ([menuItem isSeparatorItem])
892 [menuItemStrings addObject:@"<separator>"];
894 [menuItemStrings addObject:[menuItem title]];
898 return menuItemStrings;
904 - (void)scheduleAsynchronousClick
906 [self performSelector:@selector(mouseDown:) withObject:nil afterDelay:0];
907 [self performSelector:@selector(mouseUp:) withObject:nil afterDelay:0];
910 + (void)saveEvent:(NSInvocation *)event
912 if (!savedMouseEvents)
913 savedMouseEvents = [[NSMutableArray alloc] init];
914 [savedMouseEvents addObject:event];
917 + (void)replaySavedEvents
919 replayingSavedEvents = YES;
920 while ([savedMouseEvents count]) {
921 // if a drag is initiated, the remaining saved events will be dispatched from our dragging delegate
922 NSInvocation *invocation = [[[savedMouseEvents objectAtIndex:0] retain] autorelease];
923 [savedMouseEvents removeObjectAtIndex:0];
926 replayingSavedEvents = NO;
929 + (void)clearSavedEvents
931 [savedMouseEvents release];
932 savedMouseEvents = nil;
935 - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
937 NSString *eventCharacter = character;
938 unsigned short keyCode = 0;
939 if ([character isEqualToString:@"leftArrow"]) {
940 const unichar ch = NSLeftArrowFunctionKey;
941 eventCharacter = [NSString stringWithCharacters:&ch length:1];
943 } else if ([character isEqualToString:@"rightArrow"]) {
944 const unichar ch = NSRightArrowFunctionKey;
945 eventCharacter = [NSString stringWithCharacters:&ch length:1];
947 } else if ([character isEqualToString:@"upArrow"]) {
948 const unichar ch = NSUpArrowFunctionKey;
949 eventCharacter = [NSString stringWithCharacters:&ch length:1];
951 } else if ([character isEqualToString:@"downArrow"]) {
952 const unichar ch = NSDownArrowFunctionKey;
953 eventCharacter = [NSString stringWithCharacters:&ch length:1];
955 } else if ([character isEqualToString:@"pageUp"]) {
956 const unichar ch = NSPageUpFunctionKey;
957 eventCharacter = [NSString stringWithCharacters:&ch length:1];
959 } else if ([character isEqualToString:@"pageDown"]) {
960 const unichar ch = NSPageDownFunctionKey;
961 eventCharacter = [NSString stringWithCharacters:&ch length:1];
963 } else if ([character isEqualToString:@"home"]) {
964 const unichar ch = NSHomeFunctionKey;
965 eventCharacter = [NSString stringWithCharacters:&ch length:1];
967 } else if ([character isEqualToString:@"end"]) {
968 const unichar ch = NSEndFunctionKey;
969 eventCharacter = [NSString stringWithCharacters:&ch length:1];
971 } else if ([character isEqualToString:@"insert"]) {
972 const unichar ch = NSInsertFunctionKey;
973 eventCharacter = [NSString stringWithCharacters:&ch length:1];
975 } else if ([character isEqualToString:@"delete"]) {
976 const unichar ch = NSDeleteFunctionKey;
977 eventCharacter = [NSString stringWithCharacters:&ch length:1];
979 } else if ([character isEqualToString:@"escape"]) {
980 const unichar ch = 0x1B;
981 eventCharacter = [NSString stringWithCharacters:&ch length:1];
983 } else if ([character isEqualToString:@"printScreen"]) {
984 const unichar ch = NSPrintScreenFunctionKey;
985 eventCharacter = [NSString stringWithCharacters:&ch length:1];
986 keyCode = 0x0; // There is no known virtual key code for PrintScreen.
987 } else if ([character isEqualToString:@"cyrillicSmallLetterA"]) {
988 const unichar ch = 0x0430;
989 eventCharacter = [NSString stringWithCharacters:&ch length:1];
990 keyCode = 0x3; // Shares key with "F" on Russian layout.
991 } else if ([character isEqualToString:@"leftControl"]) {
992 const unichar ch = 0xFFE3;
993 eventCharacter = [NSString stringWithCharacters:&ch length:1];
995 } else if ([character isEqualToString:@"leftShift"]) {
996 const unichar ch = 0xFFE1;
997 eventCharacter = [NSString stringWithCharacters:&ch length:1];
999 } else if ([character isEqualToString:@"leftAlt"]) {
1000 const unichar ch = 0xFFE7;
1001 eventCharacter = [NSString stringWithCharacters:&ch length:1];
1003 } else if ([character isEqualToString:@"rightControl"]) {
1004 const unichar ch = 0xFFE4;
1005 eventCharacter = [NSString stringWithCharacters:&ch length:1];
1007 } else if ([character isEqualToString:@"rightShift"]) {
1008 const unichar ch = 0xFFE2;
1009 eventCharacter = [NSString stringWithCharacters:&ch length:1];
1011 } else if ([character isEqualToString:@"rightAlt"]) {
1012 const unichar ch = 0xFFE8;
1013 eventCharacter = [NSString stringWithCharacters:&ch length:1];
1017 // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24").
1018 // If the input string is a function-key name, set its key code.
1019 for (unsigned i = 1; i <= 24; i++) {
1020 if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) {
1021 const unichar ch = NSF1FunctionKey + (i - 1);
1022 eventCharacter = [NSString stringWithCharacters:&ch length:1];
1024 case 1: keyCode = 0x7A; break;
1025 case 2: keyCode = 0x78; break;
1026 case 3: keyCode = 0x63; break;
1027 case 4: keyCode = 0x76; break;
1028 case 5: keyCode = 0x60; break;
1029 case 6: keyCode = 0x61; break;
1030 case 7: keyCode = 0x62; break;
1031 case 8: keyCode = 0x64; break;
1032 case 9: keyCode = 0x65; break;
1033 case 10: keyCode = 0x6D; break;
1034 case 11: keyCode = 0x67; break;
1035 case 12: keyCode = 0x6F; break;
1036 case 13: keyCode = 0x69; break;
1037 case 14: keyCode = 0x6B; break;
1038 case 15: keyCode = 0x71; break;
1039 case 16: keyCode = 0x6A; break;
1040 case 17: keyCode = 0x40; break;
1041 case 18: keyCode = 0x4F; break;
1042 case 19: keyCode = 0x50; break;
1043 case 20: keyCode = 0x5A; break;
1048 // FIXME: No keyCode is set for most keys.
1049 if ([character isEqualToString:@"\t"])
1051 else if ([character isEqualToString:@" "])
1053 else if ([character isEqualToString:@"\r"])
1055 else if ([character isEqualToString:@"\n"])
1057 else if ([character isEqualToString:@"\x8"])
1059 else if ([character isEqualToString:@"a"])
1061 else if ([character isEqualToString:@"b"])
1063 else if ([character isEqualToString:@"d"])
1065 else if ([character isEqualToString:@"e"])
1067 else if ([character isEqualToString:@"\x1b"])
1070 KeyMappingEntry table[] = {
1071 {0x2F, 0x41, '.', nil},
1072 {0, 0x43, '*', nil},
1073 {0, 0x45, '+', nil},
1074 {0, 0x47, NSClearLineFunctionKey, @"clear"},
1075 {0x2C, 0x4B, '/', nil},
1076 {0, 0x4C, 3, @"enter" },
1077 {0x1B, 0x4E, '-', nil},
1078 {0x18, 0x51, '=', nil},
1079 {0x1D, 0x52, '0', nil},
1080 {0x12, 0x53, '1', nil},
1081 {0x13, 0x54, '2', nil},
1082 {0x14, 0x55, '3', nil},
1083 {0x15, 0x56, '4', nil},
1084 {0x17, 0x57, '5', nil},
1085 {0x16, 0x58, '6', nil},
1086 {0x1A, 0x59, '7', nil},
1087 {0x1C, 0x5B, '8', nil},
1088 {0x19, 0x5C, '9', nil},
1090 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(table); ++i) {
1091 NSString* currentCharacterString = [NSString stringWithCharacters:&table[i].character length:1];
1092 if ([character isEqualToString:currentCharacterString] || [character isEqualToString:table[i].characterName]) {
1093 if (location == DOM_KEY_LOCATION_NUMPAD)
1094 keyCode = table[i].macNumpadKeyCode;
1096 keyCode = table[i].macKeyCode;
1097 eventCharacter = currentCharacterString;
1102 NSString *charactersIgnoringModifiers = eventCharacter;
1104 int modifierFlags = 0;
1106 if ([character length] == 1 && [character characterAtIndex:0] >= 'A' && [character characterAtIndex:0] <= 'Z') {
1108 modifierFlags |= NSEventModifierFlagShift;
1110 modifierFlags |= WebEventFlagMaskAlphaShift;
1112 charactersIgnoringModifiers = [character lowercaseString];
1115 modifierFlags |= buildModifierFlags(modifiers);
1117 if (location == DOM_KEY_LOCATION_NUMPAD)
1118 modifierFlags |= NSEventModifierFlagNumericPad;
1120 [[[mainFrame frameView] documentView] layout];
1123 NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown
1124 location:NSMakePoint(5, 5)
1125 modifierFlags:modifierFlags
1126 timestamp:[self currentEventTime]
1127 windowNumber:[[[mainFrame webView] window] windowNumber]
1128 context:[NSGraphicsContext currentContext]
1129 characters:eventCharacter
1130 charactersIgnoringModifiers:charactersIgnoringModifiers
1134 WebEvent *event = [[WebEvent alloc] initWithKeyEventType:WebEventKeyDown
1135 timeStamp:[self currentEventTime]
1136 characters:eventCharacter
1137 charactersIgnoringModifiers:charactersIgnoringModifiers
1138 modifiers:(WebEventFlags)modifierFlags
1141 keyCode:[character characterAtIndex:0]
1142 isTabKey:([character characterAtIndex:0] == '\t')
1143 characterSet:WebEventCharacterSetASCII];
1147 [NSApp _setCurrentEvent:event];
1149 [[[[mainFrame webView] window] firstResponder] keyDown:event];
1151 [NSApp _setCurrentEvent:nil];
1155 event = [NSEvent keyEventWithType:NSEventTypeKeyUp
1156 location:NSMakePoint(5, 5)
1157 modifierFlags:modifierFlags
1158 timestamp:[self currentEventTime]
1159 windowNumber:[[[mainFrame webView] window] windowNumber]
1160 context:[NSGraphicsContext currentContext]
1161 characters:eventCharacter
1162 charactersIgnoringModifiers:charactersIgnoringModifiers
1167 event = [[WebEvent alloc] initWithKeyEventType:WebEventKeyUp
1168 timeStamp:[self currentEventTime]
1169 characters:eventCharacter
1170 charactersIgnoringModifiers:charactersIgnoringModifiers
1171 modifiers:(WebEventFlags)modifierFlags
1174 keyCode:[character characterAtIndex:0]
1175 isTabKey:([character characterAtIndex:0] == '\t')
1176 characterSet:WebEventCharacterSetASCII];
1180 [NSApp _setCurrentEvent:event];
1182 [[[[mainFrame webView] window] firstResponder] keyUp:event];
1184 [NSApp _setCurrentEvent:nil];
1192 - (void)keyDownWrapper:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
1194 [self keyDown:character withModifiers:modifiers withLocation:location];
1197 - (void)scheduleAsynchronousKeyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)location
1199 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[EventSendingController instanceMethodSignatureForSelector:@selector(keyDownWrapper:withModifiers:withLocation:)]];
1200 [invocation retainArguments];
1201 [invocation setTarget:self];
1202 [invocation setSelector:@selector(keyDownWrapper:withModifiers:withLocation:)];
1203 [invocation setArgument:&character atIndex:2];
1204 [invocation setArgument:&modifiers atIndex:3];
1205 [invocation setArgument:&location atIndex:4];
1206 [invocation performSelector:@selector(invoke) withObject:nil afterDelay:0];
1209 - (void)enableDOMUIEventLogging:(WebScriptObject *)node
1211 NSEnumerator *eventEnumerator = [webkitDomEventNames objectEnumerator];
1213 while ((eventName = [eventEnumerator nextObject])) {
1214 [(id<DOMEventTarget>)node addEventListener:eventName listener:self useCapture:NO];
1218 - (void)handleEvent:(DOMEvent *)event
1220 DOMNode *target = [event target];
1222 printf("event type: %s\n", [[event type] UTF8String]);
1223 printf(" target: <%s>\n", [[[target nodeName] lowercaseString] UTF8String]);
1225 if ([event isKindOfClass:[DOMEvent class]]) {
1226 printf(" eventPhase: %d\n", [event eventPhase]);
1227 printf(" bubbles: %d\n", [event bubbles] ? 1 : 0);
1228 printf(" cancelable: %d\n", [event cancelable] ? 1 : 0);
1231 if ([event isKindOfClass:[DOMUIEvent class]]) {
1232 printf(" detail: %d\n", [(DOMUIEvent*)event detail]);
1234 DOMAbstractView *view = [(DOMUIEvent*)event view];
1236 printf(" view: OK");
1237 if ([view document])
1238 printf(" (document: OK)");
1243 if ([event isKindOfClass:[DOMKeyboardEvent class]]) {
1244 printf(" keyIdentifier: %s\n", [[(DOMKeyboardEvent*)event keyIdentifier] UTF8String]);
1245 printf(" keyLocation: %d\n", [(DOMKeyboardEvent*)event location]);
1246 printf(" modifier keys: c:%d s:%d a:%d m:%d\n",
1247 [(DOMKeyboardEvent*)event ctrlKey] ? 1 : 0,
1248 [(DOMKeyboardEvent*)event shiftKey] ? 1 : 0,
1249 [(DOMKeyboardEvent*)event altKey] ? 1 : 0,
1250 [(DOMKeyboardEvent*)event metaKey] ? 1 : 0);
1251 printf(" keyCode: %d\n", [(DOMKeyboardEvent*)event keyCode]);
1252 printf(" charCode: %d\n", [(DOMKeyboardEvent*)event charCode]);
1255 if ([event isKindOfClass:[DOMMouseEvent class]]) {
1256 printf(" button: %d\n", [(DOMMouseEvent*)event button]);
1257 printf(" clientX: %d\n", [(DOMMouseEvent*)event clientX]);
1258 printf(" clientY: %d\n", [(DOMMouseEvent*)event clientY]);
1259 printf(" screenX: %d\n", [(DOMMouseEvent*)event screenX]);
1260 printf(" screenY: %d\n", [(DOMMouseEvent*)event screenY]);
1261 printf(" modifier keys: c:%d s:%d a:%d m:%d\n",
1262 [(DOMMouseEvent*)event ctrlKey] ? 1 : 0,
1263 [(DOMMouseEvent*)event shiftKey] ? 1 : 0,
1264 [(DOMMouseEvent*)event altKey] ? 1 : 0,
1265 [(DOMMouseEvent*)event metaKey] ? 1 : 0);
1266 id relatedTarget = [(DOMMouseEvent*)event relatedTarget];
1267 if (relatedTarget) {
1268 printf(" relatedTarget: %s", [[[relatedTarget class] description] UTF8String]);
1269 if ([relatedTarget isKindOfClass:[DOMNode class]])
1270 printf(" (nodeName: %s)", [[(DOMNode*)relatedTarget nodeName] UTF8String]);
1275 if ([event isKindOfClass:[DOMMutationEvent class]]) {
1276 printf(" prevValue: %s\n", [[(DOMMutationEvent*)event prevValue] UTF8String]);
1277 printf(" newValue: %s\n", [[(DOMMutationEvent*)event newValue] UTF8String]);
1278 printf(" attrName: %s\n", [[(DOMMutationEvent*)event attrName] UTF8String]);
1279 printf(" attrChange: %d\n", [(DOMMutationEvent*)event attrChange]);
1280 DOMNode *relatedNode = [(DOMMutationEvent*)event relatedNode];
1282 printf(" relatedNode: %s (nodeName: %s)\n",
1283 [[[relatedNode class] description] UTF8String],
1284 [[relatedNode nodeName] UTF8String]);
1288 if ([event isKindOfClass:[DOMWheelEvent class]]) {
1289 printf(" clientX: %d\n", [(DOMWheelEvent*)event clientX]);
1290 printf(" clientY: %d\n", [(DOMWheelEvent*)event clientY]);
1291 printf(" screenX: %d\n", [(DOMWheelEvent*)event screenX]);
1292 printf(" screenY: %d\n", [(DOMWheelEvent*)event screenY]);
1293 printf(" modifier keys: c:%d s:%d a:%d m:%d\n",
1294 [(DOMWheelEvent*)event ctrlKey] ? 1 : 0,
1295 [(DOMWheelEvent*)event shiftKey] ? 1 : 0,
1296 [(DOMWheelEvent*)event altKey] ? 1 : 0,
1297 [(DOMWheelEvent*)event metaKey] ? 1 : 0);
1298 printf(" isHorizontal: %d\n", [(DOMWheelEvent*)event isHorizontal] ? 1 : 0);
1299 printf(" wheelDelta: %d\n", [(DOMWheelEvent*)event wheelDelta]);
1303 // FIXME: It's not good to have a test hard-wired into this controller like this.
1304 // Instead we need to get testing framework based on the Objective-C bindings
1305 // to work well enough that we can test that way instead.
1306 - (void)fireKeyboardEventsToElement:(WebScriptObject *)element {
1308 if (![element isKindOfClass:[DOMHTMLElement class]])
1311 DOMHTMLElement *target = (DOMHTMLElement*)element;
1312 DOMDocument *document = [target ownerDocument];
1316 DOMEvent *domEvent = [document createEvent:@"KeyboardEvent"];
1317 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keydown"
1320 view:[document defaultView]
1321 keyIdentifier:@"U+000041"
1327 [target dispatchEvent:domEvent];
1331 domEvent = [document createEvent:@"KeyboardEvent"];
1332 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keypress"
1335 view:[document defaultView]
1336 keyIdentifier:@"U+000045"
1342 [target dispatchEvent:domEvent];
1346 domEvent = [document createEvent:@"KeyboardEvent"];
1347 [(DOMKeyboardEvent*)domEvent initKeyboardEvent:@"keyup"
1350 view:[document defaultView]
1351 keyIdentifier:@"U+000056"
1357 [target dispatchEvent:domEvent];
1361 - (void)monitorWheelEvents
1364 WebCore::Frame* frame = [[mainFrame webView] _mainCoreFrame];
1368 WebCoreTestSupport::monitorWheelEvents(*frame);
1372 - (void)callAfterScrollingCompletes:(WebScriptObject*)callback
1375 JSObjectRef jsCallbackFunction = [callback JSObject];
1376 if (!jsCallbackFunction)
1379 WebCore::Frame* frame = [[mainFrame webView] _mainCoreFrame];
1383 JSGlobalContextRef globalContext = [mainFrame globalContext];
1384 WebCoreTestSupport::setTestCallbackAndStartNotificationTimer(*frame, globalContext, jsCallbackFunction);
1389 - (void)addTouchAtX:(int)x y:(int)y
1392 touches = [[NSMutableArray alloc] init];
1394 [touches addObject:[SyntheticTouch touchWithLocation:CGPointMake(x, y) phase:UITouchPhaseBegan identifier:currentTouchIdentifier++]];
1397 - (void)cancelTouchAtIndex:(unsigned)index
1399 if (index < [touches count])
1400 [[touches objectAtIndex:index] setPhase:UITouchPhaseCancelled];
1403 - (void)clearTouchPoints
1405 [touches removeAllObjects];
1408 - (void)releaseTouchAtIndex:(unsigned)index
1410 if (index < [touches count]) {
1411 SyntheticTouch *touch = [touches objectAtIndex:index];
1412 [touch setPhase:UITouchPhaseEnded];
1416 - (void)markAllTouchesAsStationary
1418 for (SyntheticTouch *touch in touches)
1419 [touch setPhase:UITouchPhaseStationary];
1422 - (void)updateTouchAtIndex:(unsigned)index x:(int)x y:(int)y
1424 if (index < [touches count]) {
1425 SyntheticTouch *touch = [touches objectAtIndex:index];
1426 [touch setPhase:UITouchPhaseMoved];
1427 [touch setLocation:CGPointMake(x, y)];
1431 - (void)setTouchModifier:(NSString*)modifierName value:(BOOL)flag
1433 unsigned modifier = 0;
1435 if ([modifierName isEqualToString:@"alt"])
1436 modifier = WebEventFlagMaskAlternate;
1437 else if ([modifierName isEqualToString:@"shift"])
1438 modifier = WebEventFlagMaskShift;
1439 else if ([modifierName isEqualToString:@"meta"])
1440 modifier = WebEventFlagMaskCommand;
1441 else if ([modifierName isEqualToString:@"ctrl"])
1442 modifier = WebEventFlagMaskControl;
1448 nextEventFlags |= modifier;
1450 nextEventFlags &= ~modifier;
1453 - (void)sentTouchEventOfType:(WebEventType)type
1455 NSMutableArray *touchLocations = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1456 NSMutableArray *touchIdentifiers = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1457 NSMutableArray *touchPhases = [[NSMutableArray alloc] initWithCapacity:[touches count]];
1459 CGPoint centroid = CGPointZero;
1460 NSUInteger touchesDownCount = 0;
1462 for (SyntheticTouch *currTouch in touches) {
1463 [touchLocations addObject:[NSValue valueWithCGPoint:currTouch.location]];
1464 [touchIdentifiers addObject:[NSNumber numberWithUnsignedInt:currTouch.identifier]];
1465 [touchPhases addObject:[NSNumber numberWithUnsignedInt:currTouch.phase]];
1467 if ((currTouch.phase == UITouchPhaseEnded) || (currTouch.phase == UITouchPhaseCancelled))
1470 centroid.x += currTouch.location.x;
1471 centroid.y += currTouch.location.y;
1476 if (touchesDownCount > 0)
1477 centroid = CGPointMake(centroid.x / touchesDownCount, centroid.y / touchesDownCount);
1479 centroid = CGPointZero;
1481 WebEvent *event = [[WebEvent alloc] initWithTouchEventType:type
1482 timeStamp:[self currentEventTime]
1484 modifiers:(WebEventFlags)nextEventFlags
1485 touchCount:[touches count]
1486 touchLocations:touchLocations
1487 touchIdentifiers:touchIdentifiers
1488 touchPhases:touchPhases
1489 isGesture:(touchesDownCount > 1)
1492 // Ensure that layout is up-to-date so that hit-testing through WAKViews works correctly.
1493 [mainFrame updateLayout];
1494 [[[mainFrame webView] window] sendEventSynchronously:event];
1497 [touchLocations release];
1498 [touchIdentifiers release];
1499 [touchPhases release];
1506 [self sentTouchEventOfType:WebEventTouchBegin];
1511 [self sentTouchEventOfType:WebEventTouchChange];
1516 [self sentTouchEventOfType:WebEventTouchEnd];
1518 NSMutableArray *touchesToRemove = [[NSMutableArray alloc] init];
1519 for (SyntheticTouch *currTouch in touches) {
1520 if (currTouch.phase == UITouchPhaseEnded)
1521 [touchesToRemove addObject:currTouch];
1524 [touches removeObjectsInArray:touchesToRemove];
1525 [touchesToRemove release];
1530 [self sentTouchEventOfType:WebEventTouchCancel];
1532 NSMutableArray *touchesToRemove = [[NSMutableArray alloc] init];
1533 for (SyntheticTouch *currTouch in touches) {
1534 if (currTouch.phase == UITouchPhaseCancelled)
1535 [touchesToRemove addObject:currTouch];
1538 [touches removeObjectsInArray:touchesToRemove];
1539 [touchesToRemove release];