Unreviewed, rolling out r211193.
[WebKit-https.git] / Tools / TestWebKitAPI / cocoa / TestWKWebView.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 "TestWKWebView.h"
28
29 #if WK_API_ENABLED
30
31 #import "TestNavigationDelegate.h"
32 #import "Utilities.h"
33
34 #import <WebKit/WebKitPrivate.h>
35 #import <objc/runtime.h>
36 #import <wtf/RetainPtr.h>
37
38 #if PLATFORM(MAC)
39 #import <AppKit/AppKit.h>
40 #import <Carbon/Carbon.h>
41 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
42 #endif
43
44 #if PLATFORM(IOS)
45 #import <WebCore/SoftLinking.h>
46 SOFT_LINK_FRAMEWORK(UIKit)
47 SOFT_LINK_CLASS(UIKit, UIWindow)
48 #endif
49
50 @implementation TestMessageHandler {
51     NSMutableDictionary<NSString *, dispatch_block_t> *_messageHandlers;
52 }
53
54 - (void)addMessage:(NSString *)message withHandler:(dispatch_block_t)handler
55 {
56     if (!_messageHandlers)
57         _messageHandlers = [NSMutableDictionary dictionary];
58
59     _messageHandlers[message] = [handler copy];
60 }
61
62 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
63 {
64     dispatch_block_t handler = _messageHandlers[message.body];
65     if (handler)
66         handler();
67 }
68
69 @end
70
71 #if PLATFORM(MAC)
72 @interface TestWKWebViewHostWindow : NSWindow
73 #else
74 @interface TestWKWebViewHostWindow : UIWindow
75 #endif // PLATFORM(MAC)
76 @end
77
78 @implementation TestWKWebViewHostWindow {
79     BOOL _forceKeyWindow;
80 }
81
82 #if PLATFORM(MAC)
83 static int gEventNumber = 1;
84
85 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101003
86 NSEventMask __simulated_forceClickAssociatedEventsMask(id self, SEL _cmd)
87 {
88     return NSEventMaskPressure | NSEventMaskLeftMouseDown | NSEventMaskLeftMouseUp | NSEventMaskLeftMouseDragged;
89 }
90 #endif
91
92 - (void)_mouseDownAtPoint:(NSPoint)point simulatePressure:(BOOL)simulatePressure
93 {
94     NSEventType mouseEventType = NSEventTypeLeftMouseDown;
95
96     NSEventMask modifierFlags = 0;
97 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101003
98     if (simulatePressure)
99         modifierFlags |= NSEventMaskPressure;
100 #else
101     simulatePressure = NO;
102 #endif
103
104     NSEvent *event = [NSEvent mouseEventWithType:mouseEventType location:point modifierFlags:modifierFlags timestamp:GetCurrentEventTime() windowNumber:self.windowNumber context:[NSGraphicsContext currentContext] eventNumber:++gEventNumber clickCount:1 pressure:simulatePressure];
105     if (!simulatePressure) {
106         [self sendEvent:event];
107         return;
108     }
109
110 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101003
111     IMP simulatedAssociatedEventsMaskImpl = (IMP)__simulated_forceClickAssociatedEventsMask;
112     Method associatedEventsMaskMethod = class_getInstanceMethod([NSEvent class], @selector(associatedEventsMask));
113     IMP originalAssociatedEventsMaskImpl = method_setImplementation(associatedEventsMaskMethod, simulatedAssociatedEventsMaskImpl);
114     @try {
115         [self sendEvent:event];
116     } @finally {
117         // In the case where event sending raises an exception, we still want to restore the original implementation
118         // to prevent subsequent event sending tests from being affected.
119         method_setImplementation(associatedEventsMaskMethod, originalAssociatedEventsMaskImpl);
120     }
121 #endif
122 }
123 #endif // PLATFORM(MAC)
124
125 - (BOOL)isKeyWindow
126 {
127     return _forceKeyWindow || [super isKeyWindow];
128 }
129
130 - (void)makeKeyWindow
131 {
132     if (_forceKeyWindow)
133         return;
134
135     _forceKeyWindow = YES;
136 #if PLATFORM(MAC)
137     [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidBecomeKeyNotification object:self];
138 #else
139     [[NSNotificationCenter defaultCenter] postNotificationName:UIWindowDidBecomeKeyNotification object:self];
140 #endif
141 }
142
143 - (void)resignKeyWindow
144 {
145     _forceKeyWindow = NO;
146     [super resignKeyWindow];
147 }
148
149 @end
150
151 @implementation TestWKWebView {
152     TestWKWebViewHostWindow *_hostWindow;
153     RetainPtr<TestMessageHandler> _testHandler;
154 }
155
156 - (instancetype)initWithFrame:(CGRect)frame
157 {
158     WKWebViewConfiguration *defaultConfiguration = [[WKWebViewConfiguration alloc] init];
159     return [self initWithFrame:frame configuration:defaultConfiguration];
160 }
161
162 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
163 {
164     if (self = [super initWithFrame:frame configuration:configuration])
165         [self _setUpTestWindow:frame];
166
167     return self;
168 }
169
170 - (void)_setUpTestWindow:(NSRect)frame
171 {
172 #if PLATFORM(MAC)
173     _hostWindow = [[TestWKWebViewHostWindow alloc] initWithContentRect:frame styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO];
174     [_hostWindow setFrameOrigin:NSMakePoint(0, 0)];
175     [_hostWindow setIsVisible:YES];
176     [[_hostWindow contentView] addSubview:self];
177     [_hostWindow makeKeyAndOrderFront:self];
178 #else
179     _hostWindow = [[TestWKWebViewHostWindow alloc] initWithFrame:frame];
180     _hostWindow.hidden = NO;
181     [_hostWindow addSubview:self];
182 #endif
183 }
184
185 - (void)performAfterReceivingMessage:(NSString *)message action:(dispatch_block_t)action
186 {
187     if (!_testHandler) {
188         _testHandler = adoptNS([[TestMessageHandler alloc] init]);
189         [[[self configuration] userContentController] addScriptMessageHandler:_testHandler.get() name:@"testHandler"];
190     }
191
192     [_testHandler addMessage:message withHandler:action];
193 }
194
195 - (void)loadTestPageNamed:(NSString *)pageName
196 {
197     NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:pageName withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
198     [self loadRequest:request];
199 }
200
201 - (void)synchronouslyLoadTestPageNamed:(NSString *)pageName
202 {
203     [self loadTestPageNamed:pageName];
204     [self _test_waitForDidFinishNavigation];
205 }
206
207 - (NSString *)stringByEvaluatingJavaScript:(NSString *)script
208 {
209     __block bool isWaitingForJavaScript = false;
210     __block NSString *evalResult = nil;
211     [self evaluateJavaScript:script completionHandler:^(id result, NSError *error)
212     {
213         evalResult = [NSString stringWithFormat:@"%@", result];
214         isWaitingForJavaScript = true;
215         EXPECT_TRUE(!error);
216     }];
217
218     TestWebKitAPI::Util::run(&isWaitingForJavaScript);
219     return evalResult;
220 }
221
222 - (void)waitForMessage:(NSString *)message
223 {
224     __block bool isDoneWaiting = false;
225     [self performAfterReceivingMessage:message action:^()
226     {
227         isDoneWaiting = true;
228     }];
229     TestWebKitAPI::Util::run(&isDoneWaiting);
230 }
231
232 - (void)performAfterLoading:(dispatch_block_t)actions {
233     TestMessageHandler *handler = [[TestMessageHandler alloc] init];
234     [handler addMessage:@"loaded" withHandler:actions];
235
236     NSString *onloadScript = @"window.onload = () => window.webkit.messageHandlers.onloadHandler.postMessage('loaded')";
237     WKUserScript *script = [[WKUserScript alloc] initWithSource:onloadScript injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
238
239     WKUserContentController* contentController = [[self configuration] userContentController];
240     [contentController addUserScript:script];
241     [contentController addScriptMessageHandler:handler name:@"onloadHandler"];
242 }
243
244 @end
245
246 #if PLATFORM(MAC)
247 @implementation TestWKWebView (MacOnly)
248 - (void)mouseDownAtPoint:(NSPoint)point simulatePressure:(BOOL)simulatePressure
249 {
250     [_hostWindow _mouseDownAtPoint:point simulatePressure:simulatePressure];
251 }
252
253 - (void)typeCharacter:(char)character {
254     NSString *characterAsString = [NSString stringWithFormat:@"%c" , character];
255     NSEventType keyDownEventType = NSEventTypeKeyDown;
256     NSEventType keyUpEventType = NSEventTypeKeyUp;
257     [self keyDown:[NSEvent keyEventWithType:keyDownEventType location:NSZeroPoint modifierFlags:0 timestamp:GetCurrentEventTime() windowNumber:_hostWindow.windowNumber context:nil characters:characterAsString charactersIgnoringModifiers:characterAsString isARepeat:NO keyCode:character]];
258     [self keyUp:[NSEvent keyEventWithType:keyUpEventType location:NSZeroPoint modifierFlags:0 timestamp:GetCurrentEventTime() windowNumber:_hostWindow.windowNumber context:nil characters:characterAsString charactersIgnoringModifiers:characterAsString isARepeat:NO keyCode:character]];
259 }
260 @end
261 #endif
262
263 #endif // WK_API_ENABLED