121d8f08594f594fe8bf3e56a45a18b2cc445d1c
[WebKit-https.git] / Source / WebKit2 / UIProcess / Cocoa / WebAutomationSessionCocoa.mm
1 /*
2  * Copyright (C) 2016 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebAutomationSession.h"
28
29 #import "WebPageProxy.h"
30 #import "_WKAutomationSession.h"
31 #import <WebCore/IntPoint.h>
32 #import <WebCore/IntSize.h>
33 #import <WebCore/PlatformMouseEvent.h>
34 #import <objc/runtime.h>
35
36 #if USE(APPKIT)
37 #import <HIToolbox/Events.h>
38 #endif
39
40 using namespace WebCore;
41
42 namespace WebKit {
43
44 #if USE(APPKIT)
45
46 static const NSInteger synthesizedMouseEventMagicEventNumber = 0;
47 static const void *synthesizedAutomationEventAssociatedObjectKey = &synthesizedAutomationEventAssociatedObjectKey;
48
49 void WebAutomationSession::sendSynthesizedEventsToPage(WebPageProxy& page, NSArray *eventsToSend)
50 {
51     NSWindow *window = page.platformWindow();
52
53     for (NSEvent *event in eventsToSend) {
54         // Take focus back in case the Inspector became focused while the prior command or
55         // NSEvent was delivered to the window.
56         [window makeKeyAndOrderFront:nil];
57
58         markEventAsSynthesizedForAutomation(event);
59         [window sendEvent:event];
60     }
61 }
62
63 void WebAutomationSession::markEventAsSynthesizedForAutomation(NSEvent *event)
64 {
65     objc_setAssociatedObject(event, &synthesizedAutomationEventAssociatedObjectKey, m_sessionIdentifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
66 }
67
68 bool WebAutomationSession::wasEventSynthesizedForAutomation(NSEvent *event)
69 {
70     NSString *senderSessionIdentifier = objc_getAssociatedObject(event, &synthesizedAutomationEventAssociatedObjectKey);
71     if ([senderSessionIdentifier isEqualToString:m_sessionIdentifier])
72         return true;
73
74     switch (event.type) {
75     case NSEventTypeLeftMouseDown:
76     case NSEventTypeLeftMouseDragged:
77     case NSEventTypeLeftMouseUp:
78     case NSEventTypeMouseMoved:
79     case NSEventTypeOtherMouseDown:
80     case NSEventTypeOtherMouseDragged:
81     case NSEventTypeOtherMouseUp:
82     case NSEventTypeRightMouseDown:
83     case NSEventTypeRightMouseDragged:
84     case NSEventTypeRightMouseUp:
85         // Use this as a backup for checking mouse events, which are frequently copied
86         // and/or faked by AppKit, causing them to lose their associated object tag.
87         return event.eventNumber == synthesizedMouseEventMagicEventNumber;
88     default:
89         break;
90     }
91
92     return false;
93 }
94
95 void WebAutomationSession::platformSimulateMouseInteraction(WebPageProxy& page, const WebCore::IntPoint& viewPosition, Inspector::Protocol::Automation::MouseInteraction interaction, Inspector::Protocol::Automation::MouseButton button, WebEvent::Modifiers keyModifiers)
96 {
97     IntRect windowRect;
98     page.rootViewToWindow(IntRect(viewPosition, IntSize()), windowRect);
99     IntPoint windowPosition = windowRect.location();
100
101     NSEventModifierFlags modifiers = 0;
102     if (keyModifiers & WebEvent::MetaKey)
103         modifiers |= NSEventModifierFlagCommand;
104     if (keyModifiers & WebEvent::AltKey)
105         modifiers |= NSEventModifierFlagOption;
106     if (keyModifiers & WebEvent::ControlKey)
107         modifiers |= NSEventModifierFlagControl;
108     if (keyModifiers & WebEvent::ShiftKey)
109         modifiers |= NSEventModifierFlagShift;
110     if (keyModifiers & WebEvent::CapsLockKey)
111         modifiers |= NSEventModifierFlagCapsLock;
112
113     NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
114     NSWindow *window = page.platformWindow();
115     NSInteger windowNumber = window.windowNumber;
116
117     NSEventType downEventType;
118     NSEventType dragEventType;
119     NSEventType upEventType;
120     switch (button) {
121     case Inspector::Protocol::Automation::MouseButton::None:
122         downEventType = upEventType = dragEventType = NSEventTypeMouseMoved;
123         break;
124     case Inspector::Protocol::Automation::MouseButton::Left:
125         downEventType = NSEventTypeLeftMouseDown;
126         dragEventType = NSEventTypeLeftMouseDragged;
127         upEventType = NSEventTypeLeftMouseUp;
128         break;
129     case Inspector::Protocol::Automation::MouseButton::Middle:
130         downEventType = NSEventTypeOtherMouseDown;
131         dragEventType = NSEventTypeLeftMouseDragged;
132         upEventType = NSEventTypeOtherMouseUp;
133         break;
134     case Inspector::Protocol::Automation::MouseButton::Right:
135         downEventType = NSEventTypeRightMouseDown;
136         upEventType = NSEventTypeRightMouseUp;
137         break;
138     }
139
140     auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
141
142     NSInteger eventNumber = synthesizedMouseEventMagicEventNumber;
143
144     switch (interaction) {
145     case Inspector::Protocol::Automation::MouseInteraction::Move:
146         [eventsToBeSent addObject:[NSEvent mouseEventWithType:dragEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:0 pressure:0.0f]];
147         break;
148     case Inspector::Protocol::Automation::MouseInteraction::Down:
149         // Hard-code the click count to one, since clients don't expect successive simulated
150         // down/up events to be potentially counted as a double click event.
151         [eventsToBeSent addObject:[NSEvent mouseEventWithType:downEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:1 pressure:WebCore::ForceAtClick]];
152         break;
153     case Inspector::Protocol::Automation::MouseInteraction::Up:
154         // Hard-code the click count to one, since clients don't expect successive simulated
155         // down/up events to be potentially counted as a double click event.
156         [eventsToBeSent addObject:[NSEvent mouseEventWithType:upEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:1 pressure:0.0f]];
157         break;
158     case Inspector::Protocol::Automation::MouseInteraction::SingleClick:
159         // Send separate down and up events. WebCore will see this as a single-click event.
160         [eventsToBeSent addObject:[NSEvent mouseEventWithType:downEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:1 pressure:WebCore::ForceAtClick]];
161         [eventsToBeSent addObject:[NSEvent mouseEventWithType:upEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:1 pressure:0.0f]];
162         break;
163     case Inspector::Protocol::Automation::MouseInteraction::DoubleClick:
164         // Send multiple down and up events with proper click count.
165         // WebCore will see this as a single-click event then double-click event.
166         [eventsToBeSent addObject:[NSEvent mouseEventWithType:downEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:1 pressure:WebCore::ForceAtClick]];
167         [eventsToBeSent addObject:[NSEvent mouseEventWithType:upEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:1 pressure:0.0f]];
168         [eventsToBeSent addObject:[NSEvent mouseEventWithType:downEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:2 pressure:WebCore::ForceAtClick]];
169         [eventsToBeSent addObject:[NSEvent mouseEventWithType:upEventType location:windowPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:eventNumber clickCount:2 pressure:0.0f]];
170     }
171
172     sendSynthesizedEventsToPage(page, eventsToBeSent.get());
173 }
174
175 void WebAutomationSession::platformSimulateKeyStroke(WebPageProxy& page, Inspector::Protocol::Automation::KeyboardInteractionType interaction, Inspector::Protocol::Automation::VirtualKey key)
176 {
177     // If true, the key's modifier flags should affect other events while pressed down.
178     bool isStickyModifier = false;
179     // The modifiers changed by the virtual key when it is pressed or released.
180     // The mapping from keys to modifiers is specified in the documentation for NSEvent.
181     NSEventModifierFlags changedModifiers = 0;
182     // The likely keyCode for the virtual key as defined in <HIToolbox/Events.h>.
183     int keyCode = 0;
184     // Typical characters produced by the virtual key, if any.
185     NSString *characters = @"";
186
187     // FIXME: this function and the Automation protocol enum should probably adopt key names
188     // from W3C UIEvents standard. For more details: https://w3c.github.io/uievents-code/
189
190     switch (key) {
191     case Inspector::Protocol::Automation::VirtualKey::Shift:
192         isStickyModifier = true;
193         changedModifiers |= NSEventModifierFlagShift;
194         keyCode = kVK_Shift;
195         break;
196     case Inspector::Protocol::Automation::VirtualKey::Control:
197         isStickyModifier = true;
198         changedModifiers |= NSEventModifierFlagControl;
199         keyCode = kVK_Control;
200         break;
201     case Inspector::Protocol::Automation::VirtualKey::Alternate:
202         isStickyModifier = true;
203         changedModifiers |= NSEventModifierFlagOption;
204         keyCode = kVK_Option;
205         break;
206     case Inspector::Protocol::Automation::VirtualKey::Meta:
207         // The 'meta' key does not exist on Apple keyboards and is usually
208         // mapped to the Command key when using third-party keyboards.
209     case Inspector::Protocol::Automation::VirtualKey::Command:
210         isStickyModifier = true;
211         changedModifiers |= NSEventModifierFlagCommand;
212         keyCode = kVK_Command;
213         break;
214     case Inspector::Protocol::Automation::VirtualKey::Help:
215         changedModifiers |= NSEventModifierFlagHelp | NSEventModifierFlagFunction;
216         keyCode = kVK_Help;
217         break;
218     case Inspector::Protocol::Automation::VirtualKey::Backspace:
219         keyCode = kVK_Delete;
220         break;
221     case Inspector::Protocol::Automation::VirtualKey::Tab:
222         keyCode = kVK_Tab;
223         break;
224     case Inspector::Protocol::Automation::VirtualKey::Clear:
225         changedModifiers |= NSEventModifierFlagNumericPad;
226         keyCode = kVK_ANSI_KeypadClear;
227         break;
228     case Inspector::Protocol::Automation::VirtualKey::Enter:
229         keyCode = kVK_ANSI_KeypadEnter;
230         break;
231     case Inspector::Protocol::Automation::VirtualKey::Pause:
232         // The 'pause' key does not exist on Apple keyboards and has no keycode.
233         // The semantics are unclear so just abort and do nothing.
234         return;
235     case Inspector::Protocol::Automation::VirtualKey::Cancel:
236         // The 'cancel' key does not exist on Apple keyboards and has no keycode.
237         // According to the internet its functionality is similar to 'Escape'.
238     case Inspector::Protocol::Automation::VirtualKey::Escape:
239         keyCode = kVK_Escape;
240         break;
241     case Inspector::Protocol::Automation::VirtualKey::PageUp:
242         changedModifiers |= NSEventModifierFlagFunction;
243         keyCode = kVK_PageUp;
244         break;
245     case Inspector::Protocol::Automation::VirtualKey::PageDown:
246         changedModifiers |= NSEventModifierFlagFunction;
247         keyCode = kVK_PageDown;
248         break;
249     case Inspector::Protocol::Automation::VirtualKey::End:
250         changedModifiers |= NSEventModifierFlagFunction;
251         keyCode = kVK_End;
252         break;
253     case Inspector::Protocol::Automation::VirtualKey::Home:
254         changedModifiers |= NSEventModifierFlagFunction;
255         keyCode = kVK_Home;
256         break;
257     case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
258         changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
259         keyCode = kVK_LeftArrow;
260         break;
261     case Inspector::Protocol::Automation::VirtualKey::UpArrow:
262         changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
263         keyCode = kVK_UpArrow;
264         break;
265     case Inspector::Protocol::Automation::VirtualKey::RightArrow:
266         changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
267         keyCode = kVK_RightArrow;
268         break;
269     case Inspector::Protocol::Automation::VirtualKey::DownArrow:
270         changedModifiers |= NSEventModifierFlagNumericPad | NSEventModifierFlagFunction;
271         keyCode = kVK_DownArrow;
272         break;
273     case Inspector::Protocol::Automation::VirtualKey::Insert:
274         // The 'insert' key does not exist on Apple keyboards and has no keycode.
275         // The semantics are unclear so just abort and do nothing.
276         return;
277     case Inspector::Protocol::Automation::VirtualKey::Delete:
278         changedModifiers |= NSEventModifierFlagFunction;
279         keyCode = kVK_ForwardDelete;
280         break;
281     case Inspector::Protocol::Automation::VirtualKey::Space:
282         keyCode = kVK_Space;
283         characters = @" ";
284         break;
285     case Inspector::Protocol::Automation::VirtualKey::Semicolon:
286         keyCode = kVK_ANSI_Semicolon;
287         characters = @";";
288         break;
289     case Inspector::Protocol::Automation::VirtualKey::Equals:
290         keyCode = kVK_ANSI_Equal;
291         characters = @"=";
292         break;
293     case Inspector::Protocol::Automation::VirtualKey::Return:
294         keyCode = kVK_Return;
295         break;
296     case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
297         changedModifiers |= NSEventModifierFlagNumericPad;
298         keyCode = kVK_ANSI_Keypad0;
299         characters = @"0";
300         break;
301     case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
302         changedModifiers |= NSEventModifierFlagNumericPad;
303         keyCode = kVK_ANSI_Keypad1;
304         characters = @"1";
305         break;
306     case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
307         changedModifiers |= NSEventModifierFlagNumericPad;
308         keyCode = kVK_ANSI_Keypad2;
309         characters = @"2";
310         break;
311     case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
312         changedModifiers |= NSEventModifierFlagNumericPad;
313         keyCode = kVK_ANSI_Keypad3;
314         characters = @"3";
315         break;
316     case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
317         changedModifiers |= NSEventModifierFlagNumericPad;
318         keyCode = kVK_ANSI_Keypad4;
319         characters = @"4";
320         break;
321     case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
322         changedModifiers |= NSEventModifierFlagNumericPad;
323         keyCode = kVK_ANSI_Keypad5;
324         characters = @"5";
325         break;
326     case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
327         changedModifiers |= NSEventModifierFlagNumericPad;
328         keyCode = kVK_ANSI_Keypad6;
329         characters = @"6";
330         break;
331     case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
332         changedModifiers |= NSEventModifierFlagNumericPad;
333         keyCode = kVK_ANSI_Keypad7;
334         characters = @"7";
335         break;
336     case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
337         changedModifiers |= NSEventModifierFlagNumericPad;
338         keyCode = kVK_ANSI_Keypad8;
339         characters = @"8";
340         break;
341     case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
342         changedModifiers |= NSEventModifierFlagNumericPad;
343         keyCode = kVK_ANSI_Keypad9;
344         characters = @"9";
345         break;
346     case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
347         changedModifiers |= NSEventModifierFlagNumericPad;
348         keyCode = kVK_ANSI_KeypadMultiply;
349         characters = @"*";
350         break;
351     case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
352         changedModifiers |= NSEventModifierFlagNumericPad;
353         keyCode = kVK_ANSI_KeypadPlus;
354         characters = @"+";
355         break;
356     case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
357         changedModifiers |= NSEventModifierFlagNumericPad;
358         keyCode = kVK_ANSI_KeypadMinus;
359         characters = @"-";
360         break;
361     case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
362         changedModifiers |= NSEventModifierFlagNumericPad;
363         // The 'Separator' key is only present on a few international keyboards.
364         // It is usually mapped to the same character as Decimal ('.' or ',').
365         FALLTHROUGH;
366     case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
367         changedModifiers |= NSEventModifierFlagNumericPad;
368         keyCode = kVK_ANSI_KeypadDecimal;
369         // FIXME: this might be locale-dependent. See the above comment.
370         characters = @".";
371         break;
372     case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
373         changedModifiers |= NSEventModifierFlagNumericPad;
374         keyCode = kVK_ANSI_KeypadDivide;
375         characters = @"/";
376         break;
377     case Inspector::Protocol::Automation::VirtualKey::Function1:
378         changedModifiers |= NSEventModifierFlagFunction;
379         keyCode = kVK_F1;
380         break;
381     case Inspector::Protocol::Automation::VirtualKey::Function2:
382         changedModifiers |= NSEventModifierFlagFunction;
383         keyCode = kVK_F2;
384         break;
385     case Inspector::Protocol::Automation::VirtualKey::Function3:
386         changedModifiers |= NSEventModifierFlagFunction;
387         keyCode = kVK_F3;
388         break;
389     case Inspector::Protocol::Automation::VirtualKey::Function4:
390         changedModifiers |= NSEventModifierFlagFunction;
391         keyCode = kVK_F4;
392         break;
393     case Inspector::Protocol::Automation::VirtualKey::Function5:
394         changedModifiers |= NSEventModifierFlagFunction;
395         keyCode = kVK_F5;
396         break;
397     case Inspector::Protocol::Automation::VirtualKey::Function6:
398         changedModifiers |= NSEventModifierFlagFunction;
399         keyCode = kVK_F6;
400         break;
401     case Inspector::Protocol::Automation::VirtualKey::Function7:
402         changedModifiers |= NSEventModifierFlagFunction;
403         keyCode = kVK_F7;
404         break;
405     case Inspector::Protocol::Automation::VirtualKey::Function8:
406         changedModifiers |= NSEventModifierFlagFunction;
407         keyCode = kVK_F8;
408         break;
409     case Inspector::Protocol::Automation::VirtualKey::Function9:
410         changedModifiers |= NSEventModifierFlagFunction;
411         keyCode = kVK_F9;
412         break;
413     case Inspector::Protocol::Automation::VirtualKey::Function10:
414         changedModifiers |= NSEventModifierFlagFunction;
415         keyCode = kVK_F10;
416         break;
417     case Inspector::Protocol::Automation::VirtualKey::Function11:
418         changedModifiers |= NSEventModifierFlagFunction;
419         keyCode = kVK_F11;
420         break;
421     case Inspector::Protocol::Automation::VirtualKey::Function12:
422         changedModifiers |= NSEventModifierFlagFunction;
423         keyCode = kVK_F12;
424         break;
425     }
426
427     auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
428
429     ASSERT(isStickyModifier || interaction == Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress);
430
431     NSEventModifierFlags existingModifiers = [NSEvent modifierFlags];
432     NSEventModifierFlags updatedModifiers = 0;
433     NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
434     NSWindow *window = page.platformWindow();
435     NSInteger windowNumber = window.windowNumber;
436     NSPoint eventPosition = NSMakePoint(0, window.frame.size.height);
437
438     switch (interaction) {
439     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyPress: {
440         NSEventType eventType = isStickyModifier ? NSEventTypeFlagsChanged : NSEventTypeKeyDown;
441         updatedModifiers = existingModifiers | changedModifiers;
442         [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
443         break;
444     }
445     case Inspector::Protocol::Automation::KeyboardInteractionType::KeyRelease: {
446         NSEventType eventType = isStickyModifier ? NSEventTypeFlagsChanged : NSEventTypeKeyUp;
447         updatedModifiers = existingModifiers & ~changedModifiers;
448         [eventsToBeSent addObject:[NSEvent keyEventWithType:eventType location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
449         break;
450     }
451     case Inspector::Protocol::Automation::KeyboardInteractionType::InsertByKey: {
452         // Sticky modifiers should either be 'KeyPress' or 'KeyRelease'.
453         ASSERT(!isStickyModifier);
454         if (isStickyModifier)
455             return;
456
457         updatedModifiers = existingModifiers | changedModifiers;
458         [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyDown location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
459         [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyUp location:eventPosition modifierFlags:updatedModifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:characters isARepeat:NO keyCode:keyCode]];
460         break;
461     }
462     }
463
464     sendSynthesizedEventsToPage(page, eventsToBeSent.get());
465 }
466
467 void WebAutomationSession::platformSimulateKeySequence(WebPageProxy& page, const String& keySequence)
468 {
469     auto eventsToBeSent = adoptNS([[NSMutableArray alloc] init]);
470
471     // Split the text into combining character sequences and send each separately.
472     // This has no similarity to how keyboards work when inputting complex text.
473     // This command is more similar to the 'insertText:' editing command, except
474     // that this emits keyup/keydown/keypress events for roughly each character.
475     // This API should move more towards that direction in the future.
476     NSString *text = keySequence;
477
478     NSEventModifierFlags modifiers = [NSEvent modifierFlags];
479     NSTimeInterval timestamp = [NSDate timeIntervalSinceReferenceDate];
480     NSWindow *window = page.platformWindow();
481     NSInteger windowNumber = window.windowNumber;
482     NSPoint eventPosition = NSMakePoint(0, window.frame.size.height);
483
484     [text enumerateSubstringsInRange:NSMakeRange(0, text.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
485         [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyDown location:eventPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:substring charactersIgnoringModifiers:substring isARepeat:NO keyCode:0]];
486         [eventsToBeSent addObject:[NSEvent keyEventWithType:NSEventTypeKeyUp location:eventPosition modifierFlags:modifiers timestamp:timestamp windowNumber:windowNumber context:nil characters:substring charactersIgnoringModifiers:substring isARepeat:NO keyCode:0]];
487     }];
488
489     sendSynthesizedEventsToPage(page, eventsToBeSent.get());
490 }
491
492 String WebAutomationSession::platformGetBase64EncodedPNGData(const ShareableBitmap::Handle& imageDataHandle)
493 {
494     RefPtr<ShareableBitmap> bitmap = ShareableBitmap::create(imageDataHandle, SharedMemory::Protection::ReadOnly);
495     RetainPtr<CGImageRef> cgImage = bitmap->makeCGImage();
496     RetainPtr<NSMutableData> imageData = adoptNS([[NSMutableData alloc] init]);
497     RetainPtr<CGImageDestinationRef> destination = adoptCF(CGImageDestinationCreateWithData((CFMutableDataRef)imageData.get(), kUTTypePNG, 1, 0));
498     if (!destination)
499         return String();
500
501     CGImageDestinationAddImage(destination.get(), cgImage.get(), 0);
502     CGImageDestinationFinalize(destination.get());
503
504     return [imageData base64EncodedStringWithOptions:0];
505 }
506
507 #endif // USE(APPKIT)
508
509 } // namespace WebKit