Allow pages to trigger programmatic paste from script on iOS
[WebKit-https.git] / Tools / WebKitTestRunner / cocoa / TestRunnerWKWebView.mm
1 /*
2  * Copyright (C) 2015 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 "TestRunnerWKWebView.h"
28
29 #import "WebKitTestRunnerDraggingInfo.h"
30 #import <WebKit/WKUIDelegatePrivate.h>
31 #import <wtf/Assertions.h>
32 #import <wtf/RetainPtr.h>
33
34 #if PLATFORM(IOS_FAMILY)
35 #import "UIKitSPI.h"
36 #import <WebKit/WKWebViewPrivate.h>
37 @interface WKWebView ()
38
39 // FIXME: move these to WKWebView_Private.h
40 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view;
41 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale;
42 - (void)_didFinishScrolling;
43 - (void)_scheduleVisibleContentRectUpdate;
44
45 @end
46 #endif
47
48 #if WK_API_ENABLED
49
50 @interface TestRunnerWKWebView () <WKUIDelegatePrivate> {
51     RetainPtr<NSNumber> m_stableStateOverride;
52     BOOL m_isInteractingWithFormControl;
53 }
54
55 @property (nonatomic, copy) void (^zoomToScaleCompletionHandler)(void);
56 @property (nonatomic, copy) void (^retrieveSpeakSelectionContentCompletionHandler)(void);
57 @property (nonatomic, getter=isShowingKeyboard, setter=setIsShowingKeyboard:) BOOL showingKeyboard;
58 @property (nonatomic, getter=isShowingMenu, setter=setIsShowingMenu:) BOOL showingMenu;
59
60 @end
61
62 @implementation TestRunnerWKWebView
63
64 #if PLATFORM(MAC)
65 IGNORE_WARNINGS_BEGIN("deprecated-implementations")
66 - (void)dragImage:(NSImage *)anImage at:(NSPoint)viewLocation offset:(NSSize)initialOffset event:(NSEvent *)event pasteboard:(NSPasteboard *)pboard source:(id)sourceObj slideBack:(BOOL)slideFlag
67 IGNORE_WARNINGS_END
68 {
69     RetainPtr<WebKitTestRunnerDraggingInfo> draggingInfo = adoptNS([[WebKitTestRunnerDraggingInfo alloc] initWithImage:anImage offset:initialOffset pasteboard:pboard source:sourceObj]);
70     [self draggingUpdated:draggingInfo.get()];
71 }
72 #endif
73
74 #if PLATFORM(IOS_FAMILY)
75 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
76 {
77     if (self = [super initWithFrame:frame configuration:configuration]) {
78         NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
79         [center addObserver:self selector:@selector(_invokeShowKeyboardCallbackIfNecessary) name:UIKeyboardDidShowNotification object:nil];
80         [center addObserver:self selector:@selector(_invokeHideKeyboardCallbackIfNecessary) name:UIKeyboardDidHideNotification object:nil];
81         [center addObserver:self selector:@selector(_didShowMenu) name:UIMenuControllerDidShowMenuNotification object:nil];
82         [center addObserver:self selector:@selector(_didHideMenu) name:UIMenuControllerDidHideMenuNotification object:nil];
83         self.UIDelegate = self;
84     }
85     return self;
86 }
87
88 - (void)dealloc
89 {
90     [[NSNotificationCenter defaultCenter] removeObserver:self];
91
92     self.didStartFormControlInteractionCallback = nil;
93     self.didEndFormControlInteractionCallback = nil;
94     self.didShowForcePressPreviewCallback = nil;
95     self.didDismissForcePressPreviewCallback = nil;
96     self.willBeginZoomingCallback = nil;
97     self.didEndZoomingCallback = nil;
98     self.didShowKeyboardCallback = nil;
99     self.didHideKeyboardCallback = nil;
100     self.didShowMenuCallback = nil;
101     self.didHideMenuCallback = nil;
102     self.didEndScrollingCallback = nil;
103     self.rotationDidEndCallback = nil;
104
105     self.zoomToScaleCompletionHandler = nil;
106     self.retrieveSpeakSelectionContentCompletionHandler = nil;
107
108     [super dealloc];
109 }
110
111 - (void)didStartFormControlInteraction
112 {
113     m_isInteractingWithFormControl = YES;
114
115     if (self.didStartFormControlInteractionCallback)
116         self.didStartFormControlInteractionCallback();
117 }
118
119 - (void)didEndFormControlInteraction
120 {
121     m_isInteractingWithFormControl = NO;
122
123     if (self.didEndFormControlInteractionCallback)
124         self.didEndFormControlInteractionCallback();
125 }
126
127 - (BOOL)isInteractingWithFormControl
128 {
129     return m_isInteractingWithFormControl;
130 }
131
132 - (void)_didShowForcePressPreview
133 {
134     if (self.didShowForcePressPreviewCallback)
135         self.didShowForcePressPreviewCallback();
136 }
137
138 - (void)_didDismissForcePressPreview
139 {
140     if (self.didDismissForcePressPreviewCallback)
141         self.didDismissForcePressPreviewCallback();
142 }
143
144 - (void)zoomToScale:(double)scale animated:(BOOL)animated completionHandler:(void (^)(void))completionHandler
145 {
146     ASSERT(!self.zoomToScaleCompletionHandler);
147
148     if (self.scrollView.zoomScale == scale) {
149         dispatch_async(dispatch_get_main_queue(), ^{
150             completionHandler();
151         });
152         return;
153     }
154
155     self.zoomToScaleCompletionHandler = completionHandler;
156     [self.scrollView setZoomScale:scale animated:animated];
157 }
158
159 - (void)_invokeShowKeyboardCallbackIfNecessary
160 {
161     if (self.showingKeyboard)
162         return;
163
164     self.showingKeyboard = YES;
165     if (self.didShowKeyboardCallback)
166         self.didShowKeyboardCallback();
167 }
168
169 - (void)_invokeHideKeyboardCallbackIfNecessary
170 {
171     if (!self.showingKeyboard)
172         return;
173
174     self.showingKeyboard = NO;
175     if (self.didHideKeyboardCallback)
176         self.didHideKeyboardCallback();
177 }
178
179 - (void)_didShowMenu
180 {
181     if (self.showingMenu)
182         return;
183
184     self.showingMenu = YES;
185     if (self.didShowMenuCallback)
186         self.didShowMenuCallback();
187 }
188
189 - (void)_didHideMenu
190 {
191     if (!self.showingMenu)
192         return;
193
194     self.showingMenu = NO;
195     if (self.didHideMenuCallback)
196         self.didHideMenuCallback();
197 }
198
199 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
200 {
201     [super scrollViewWillBeginZooming:scrollView withView:view];
202
203     if (self.willBeginZoomingCallback)
204         self.willBeginZoomingCallback();
205 }
206
207 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
208 {
209     [super scrollViewDidEndZooming:scrollView withView:view atScale:scale];
210     
211     if (self.didEndZoomingCallback)
212         self.didEndZoomingCallback();
213
214     if (self.zoomToScaleCompletionHandler) {
215         self.zoomToScaleCompletionHandler();
216         self.zoomToScaleCompletionHandler = nullptr;
217     }
218 }
219
220 - (void)_didFinishScrolling
221 {
222     [super _didFinishScrolling];
223
224     if (self.didEndScrollingCallback)
225         self.didEndScrollingCallback();
226 }
227
228 - (NSNumber *)_stableStateOverride
229 {
230     return m_stableStateOverride.get();
231 }
232
233 - (void)_setStableStateOverride:(NSNumber *)overrideBoolean
234 {
235     m_stableStateOverride = overrideBoolean;
236     [self _scheduleVisibleContentRectUpdate];
237 }
238
239 - (void)_didEndRotation
240 {
241     if (self.rotationDidEndCallback)
242         self.rotationDidEndCallback();
243 }
244
245 - (void)_accessibilityDidGetSpeakSelectionContent:(NSString *)content
246 {
247     self.accessibilitySpeakSelectionContent = content;
248     if (self.retrieveSpeakSelectionContentCompletionHandler)
249         self.retrieveSpeakSelectionContentCompletionHandler();
250 }
251
252 - (void)accessibilityRetrieveSpeakSelectionContentWithCompletionHandler:(void (^)(void))completionHandler
253 {
254     self.retrieveSpeakSelectionContentCompletionHandler = completionHandler;
255     [self _accessibilityRetrieveSpeakSelectionContent];
256 }
257
258 - (void)setOverrideSafeAreaInsets:(UIEdgeInsets)insets
259 {
260     _overrideSafeAreaInsets = insets;
261 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
262     [self _updateSafeAreaInsets];
263 #endif
264 }
265
266 - (UIEdgeInsets)_safeAreaInsetsForFrame:(CGRect)frame inSuperview:(UIView *)view
267 {
268     return _overrideSafeAreaInsets;
269 }
270
271 #pragma mark - WKUIDelegatePrivate
272
273 // In extra zoom mode, fullscreen form control UI takes on the same role as keyboards and input view controllers
274 // in UIKit. As such, we allow keyboard presentation and dismissal callbacks to work in extra zoom mode as well.
275 - (void)_webView:(WKWebView *)webView didPresentFocusedElementViewController:(UIViewController *)controller
276 {
277     [self _invokeShowKeyboardCallbackIfNecessary];
278 }
279
280 - (void)_webView:(WKWebView *)webView didDismissFocusedElementViewController:(UIViewController *)controller
281 {
282     [self _invokeHideKeyboardCallbackIfNecessary];
283 }
284
285 #endif // PLATFORM(IOS_FAMILY)
286
287 @end
288
289 #endif // WK_API_ENABLED