EventSendingController.mm 39 KB

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