Delay WebProcess launch until a load is triggered in a Web view
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / UIDelegate.mm
1 /*
2  * Copyright (C) 2017-2018 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
28 #import "PlatformUtilities.h"
29 #import "TestWKWebView.h"
30 #import "Utilities.h"
31 #import "WKWebViewConfigurationExtras.h"
32 #import <WebKit/WKContext.h>
33 #import <WebKit/WKContextPrivateMac.h>
34 #import <WebKit/WKGeolocationManager.h>
35 #import <WebKit/WKGeolocationPosition.h>
36 #import <WebKit/WKPreferencesPrivate.h>
37 #import <WebKit/WKRetainPtr.h>
38 #import <WebKit/WKUIDelegatePrivate.h>
39 #import <WebKit/WKWebViewPrivate.h>
40 #import <WebKit/_WKHitTestResult.h>
41 #import <wtf/RetainPtr.h>
42 #import <wtf/Vector.h>
43
44 #if PLATFORM(MAC)
45 #import <Carbon/Carbon.h>
46 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
47 #endif
48
49 static bool done;
50
51 @interface AudioObserver : NSObject
52 @end
53
54 @implementation AudioObserver
55
56 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context
57 {
58     EXPECT_TRUE([keyPath isEqualToString:NSStringFromSelector(@selector(_isPlayingAudio))]);
59     EXPECT_TRUE([[object class] isEqual:[TestWKWebView class]]);
60     EXPECT_FALSE([[change objectForKey:NSKeyValueChangeOldKey] boolValue]);
61     EXPECT_TRUE([[change objectForKey:NSKeyValueChangeNewKey] boolValue]);
62     EXPECT_TRUE(context == nullptr);
63     done = true;
64 }
65
66 @end
67
68 TEST(WebKit, WKWebViewIsPlayingAudio)
69 {
70     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:[[[WKWebViewConfiguration alloc] init] autorelease]]);
71     auto observer = adoptNS([[AudioObserver alloc] init]);
72     [webView addObserver:observer.get() forKeyPath:@"_isPlayingAudio" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
73     [webView synchronouslyLoadTestPageNamed:@"file-with-video"];
74     [webView evaluateJavaScript:@"playVideo()" completionHandler:nil];
75     TestWebKitAPI::Util::run(&done);
76 }
77
78 @interface NoUIDelegate : NSObject <WKNavigationDelegate>
79 @end
80
81 @implementation NoUIDelegate
82
83 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
84 {
85     if ([navigationAction.request.URL.absoluteString isEqualToString:[[[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"] absoluteString]])
86         done = true;
87     decisionHandler(WKNavigationActionPolicyAllow);
88 }
89
90 @end
91
92 TEST(WebKit, WindowOpenWithoutUIDelegate)
93 {
94     done = false;
95     auto webView = adoptNS([[WKWebView alloc] init]);
96     auto delegate = adoptNS([[NoUIDelegate alloc] init]);
97     [webView setNavigationDelegate:delegate.get()];
98     [webView loadHTMLString:@"<script>window.open('simple2.html');window.location='simple.html'</script>" baseURL:[[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
99     TestWebKitAPI::Util::run(&done);
100 }
101
102 @interface GeolocationDelegate : NSObject <WKUIDelegatePrivate> {
103     bool _allowGeolocation;
104 }
105
106 - (id)initWithAllowGeolocation:(bool)allowGeolocation;
107
108 @end
109
110 @implementation GeolocationDelegate
111
112 - (id)initWithAllowGeolocation:(bool)allowGeolocation
113 {
114     if (!(self = [super init]))
115         return nil;
116     _allowGeolocation = allowGeolocation;
117     return self;
118 }
119
120 - (void)_webView:(WKWebView *)webView requestGeolocationPermissionForFrame:(WKFrameInfo *)frame decisionHandler:(void (^)(BOOL allowed))decisionHandler
121 {
122     EXPECT_TRUE(frame.isMainFrame);
123     EXPECT_STREQ(frame.request.URL.absoluteString.UTF8String, _allowGeolocation ? "https://example.org/" : "https://example.com/");
124     EXPECT_EQ(frame.securityOrigin.port, 0);
125     EXPECT_STREQ(frame.securityOrigin.protocol.UTF8String, "https");
126     EXPECT_STREQ(frame.securityOrigin.host.UTF8String, _allowGeolocation ? "example.org" : "example.com");
127     decisionHandler(_allowGeolocation);
128 }
129
130 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
131 {
132     if (_allowGeolocation)
133         EXPECT_STREQ(message.UTF8String, "position 50.644358 3.345453");
134     else
135         EXPECT_STREQ(message.UTF8String, "error 1 User denied Geolocation");
136     completionHandler();
137     done = true;
138 }
139
140 @end
141
142 TEST(WebKit, GeolocationPermission)
143 {
144     NSString *html = @"<script>navigator.geolocation.watchPosition("
145         "function(p) { alert('position ' + p.coords.latitude + ' ' + p.coords.longitude) },"
146         "function(e) { alert('error ' + e.code + ' ' + e.message) })"
147     "</script>";
148
149     auto pool = adoptNS([[WKProcessPool alloc] init]);
150     
151     WKGeolocationProviderV1 providerCallback;
152     memset(&providerCallback, 0, sizeof(WKGeolocationProviderV1));
153     providerCallback.base.version = 1;
154     providerCallback.startUpdating = [] (WKGeolocationManagerRef manager, const void*) {
155         WKGeolocationManagerProviderDidChangePosition(manager, adoptWK(WKGeolocationPositionCreate(0, 50.644358, 3.345453, 2.53)).get());
156     };
157     WKGeolocationManagerSetProvider(WKContextGetGeolocationManager((WKContextRef)pool.get()), &providerCallback.base);
158
159     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
160     configuration.get().processPool = pool.get();
161     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration.get()]);
162     auto delegate1 = adoptNS([[GeolocationDelegate alloc] initWithAllowGeolocation:false]);
163     [webView setUIDelegate:delegate1.get()];
164     [webView loadHTMLString:html baseURL:[NSURL URLWithString:@"https://example.com/"]];
165     TestWebKitAPI::Util::run(&done);
166
167     done = false;
168     auto delegate2 = adoptNS([[GeolocationDelegate alloc] initWithAllowGeolocation:true]);
169     [webView setUIDelegate:delegate2.get()];
170     [webView loadHTMLString:html baseURL:[NSURL URLWithString:@"https://example.org/"]];
171     TestWebKitAPI::Util::run(&done);
172 }
173
174 @interface InjectedBundleNodeHandleIsSelectElementDelegate : NSObject <WKUIDelegatePrivate>
175 @end
176
177 @implementation InjectedBundleNodeHandleIsSelectElementDelegate
178
179 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
180 {
181     completionHandler();
182     done = true;
183     ASSERT_STREQ(message.UTF8String, "isSelectElement success");
184 }
185
186 @end
187
188 TEST(WebKit, InjectedBundleNodeHandleIsSelectElement)
189 {
190     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"InjectedBundleNodeHandleIsSelectElement"];
191
192     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
193     auto delegate = adoptNS([[InjectedBundleNodeHandleIsSelectElementDelegate alloc] init]);
194     [webView setUIDelegate:delegate.get()];
195     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
196     TestWebKitAPI::Util::run(&done);
197 }
198
199 #if PLATFORM(MAC)
200
201 @class UITestDelegate;
202
203 static RetainPtr<WKWebView> webViewFromDelegateCallback;
204 static RetainPtr<WKWebView> createdWebView;
205 static RetainPtr<UITestDelegate> delegate;
206
207 @interface UITestDelegate : NSObject <WKUIDelegatePrivate, WKURLSchemeHandler>
208 @end
209
210 @implementation UITestDelegate
211
212 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
213 {
214     createdWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
215     [createdWebView setUIDelegate:delegate.get()];
216     return createdWebView.get();
217 }
218
219 - (void)_showWebView:(WKWebView *)webView
220 {
221     webViewFromDelegateCallback = webView;
222     done = true;
223 }
224
225 - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
226 {
227     NSString *data = @"<script>window.open('other.html');</script>";
228     [urlSchemeTask didReceiveResponse:[[[NSURLResponse alloc] initWithURL:urlSchemeTask.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
229     [urlSchemeTask didReceiveData:[data dataUsingEncoding:NSUTF8StringEncoding]];
230     [urlSchemeTask didFinish];
231 }
232
233 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
234 {
235 }
236
237 @end
238
239 TEST(WebKit, ShowWebView)
240 {
241     delegate = adoptNS([[UITestDelegate alloc] init]);
242     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
243     [configuration setURLSchemeHandler:delegate.get() forURLScheme:@"test"];
244     auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration.get()]);
245     [webView setUIDelegate:delegate.get()];
246     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"test:///first"]]];
247     TestWebKitAPI::Util::run(&done);
248     
249     ASSERT_EQ(webViewFromDelegateCallback, createdWebView);
250 }
251
252 @interface PointerLockDelegate : NSObject <WKUIDelegatePrivate>
253 @end
254
255 @implementation PointerLockDelegate
256
257 - (void)_webViewDidRequestPointerLock:(WKWebView *)webView completionHandler:(void (^)(BOOL))completionHandler
258 {
259     completionHandler(YES);
260     done = true;
261 }
262
263 @end
264
265 TEST(WebKit, PointerLock)
266 {
267     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
268     auto delegate = adoptNS([[PointerLockDelegate alloc] init]);
269     [webView setUIDelegate:delegate.get()];
270     [webView synchronouslyLoadHTMLString:
271         @"<canvas width='800' height='600'></canvas><script>"
272         @"var canvas = document.querySelector('canvas');"
273         @"canvas.onclick = ()=>{canvas.requestPointerLock()};"
274         @"</script>"
275     ];
276     [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
277     TestWebKitAPI::Util::run(&done);
278 }
279
280 static bool resizableSet;
281
282 @interface ModalDelegate : NSObject <WKUIDelegatePrivate>
283 @end
284
285 @implementation ModalDelegate
286
287 - (void)_webViewRunModal:(WKWebView *)webView
288 {
289     EXPECT_TRUE(resizableSet);
290     EXPECT_EQ(webView, createdWebView.get());
291     done = true;
292 }
293
294 - (void)_webView:(WKWebView *)webView setResizable:(BOOL)isResizable
295 {
296     EXPECT_FALSE(isResizable);
297     resizableSet = true;
298 }
299
300 - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
301 {
302     createdWebView = [[[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration] autorelease];
303     [createdWebView setUIDelegate:self];
304     return createdWebView.get();
305 }
306
307 @end
308
309 TEST(WebKit, RunModal)
310 {
311     auto delegate = adoptNS([[ModalDelegate alloc] init]);
312     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
313     [webView setUIDelegate:delegate.get()];
314     NSURL *url = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
315     NSString *html = [NSString stringWithFormat:@"%@%@%@", @"<script> function openModal() { window.showModalDialog('", url, @"'); } </script> <input type='button' value='Click to open modal' onclick='openModal();'>"];
316     [webView synchronouslyLoadHTMLString:html];
317     [webView sendClicksAtPoint:NSMakePoint(20, 600 - 20) numberOfClicks:1];
318     TestWebKitAPI::Util::run(&done);
319 }
320
321 static bool receivedWindowFrame;
322
323 @interface WindowFrameDelegate : NSObject <WKUIDelegatePrivate>
324 @end
325
326 @implementation WindowFrameDelegate
327
328 - (void)_webView:(WKWebView *)webView setWindowFrame:(CGRect)frame
329 {
330     EXPECT_EQ(frame.origin.x, 160);
331     EXPECT_EQ(frame.origin.y, 230);
332     EXPECT_EQ(frame.size.width, 350);
333     EXPECT_EQ(frame.size.height, 450);
334     receivedWindowFrame = true;
335 }
336
337 - (void)_webView:(WKWebView *)webView getWindowFrameWithCompletionHandler:(void (^)(CGRect))completionHandler
338 {
339     completionHandler(CGRectMake(150, 250, 350, 450));
340 }
341
342 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
343 {
344     EXPECT_STREQ("350", message.UTF8String);
345     completionHandler();
346     done = true;
347 }
348
349 @end
350
351 TEST(WebKit, WindowFrame)
352 {
353     auto delegate = adoptNS([[WindowFrameDelegate alloc] init]);
354     auto webView = adoptNS([[WKWebView alloc] init]);
355     [webView setUIDelegate:delegate.get()];
356     [webView loadHTMLString:@"<script>moveBy(10,20);alert(outerWidth);</script>" baseURL:nil];
357     TestWebKitAPI::Util::run(&receivedWindowFrame);
358     TestWebKitAPI::Util::run(&done);
359 }
360
361 static bool headerHeightCalled;
362 static bool footerHeightCalled;
363 static bool drawHeaderCalled;
364 static bool drawFooterCalled;
365
366 @interface PrintDelegate : NSObject <WKUIDelegatePrivate>
367 @end
368
369 @implementation PrintDelegate
370
371 - (void)_webView:(WKWebView *)webView printFrame:(_WKFrameHandle *)frame
372 {
373     done = true;
374 }
375
376 - (CGFloat)_webViewHeaderHeight:(WKWebView *)webView
377 {
378     headerHeightCalled = true;
379     return 3.14159;
380 }
381
382 - (CGFloat)_webViewFooterHeight:(WKWebView *)webView
383 {
384     footerHeightCalled = true;
385     return 2.71828;
386 }
387
388 - (void)_webView:(WKWebView *)webView drawHeaderInRect:(CGRect)rect forPageWithTitle:(NSString *)title URL:(NSURL *)url
389 {
390     EXPECT_EQ(rect.origin.x, 72);
391     EXPECT_TRUE(fabs(rect.origin.y - 698.858398) < .00001);
392     EXPECT_TRUE(fabs(rect.size.height - 3.141590) < .00001);
393     EXPECT_EQ(rect.size.width, 468.000000);
394     EXPECT_STREQ(title.UTF8String, "test_title");
395     EXPECT_STREQ(url.absoluteString.UTF8String, "http://example.com/");
396     drawHeaderCalled = true;
397 }
398
399 - (void)_webView:(WKWebView *)webView drawFooterInRect:(CGRect)rect forPageWithTitle:(NSString *)title URL:(NSURL *)url
400 {
401     EXPECT_EQ(rect.origin.x, 72);
402     EXPECT_EQ(rect.origin.y, 90);
403     EXPECT_TRUE(fabs(rect.size.height - 2.718280) < .00001);
404     EXPECT_EQ(rect.size.width, 468.000000);
405     EXPECT_STREQ(url.absoluteString.UTF8String, "http://example.com/");
406     drawFooterCalled = true;
407 }
408
409 @end
410
411 TEST(WebKit, PrintFrame)
412 {
413     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
414     auto delegate = adoptNS([[PrintDelegate alloc] init]);
415     [webView setUIDelegate:delegate.get()];
416     [webView loadHTMLString:@"<head><title>test_title</title></head><body onload='print()'>hello world!</body>" baseURL:[NSURL URLWithString:@"http://example.com/"]];
417     TestWebKitAPI::Util::run(&done);
418
419     NSPrintOperation *operation = [webView _printOperationWithPrintInfo:[NSPrintInfo sharedPrintInfo]];
420     EXPECT_TRUE(operation.canSpawnSeparateThread);
421     EXPECT_STREQ(operation.jobTitle.UTF8String, "test_title");
422
423     [operation runOperationModalForWindow:[webView hostWindow] delegate:nil didRunSelector:nil contextInfo:nil];
424     TestWebKitAPI::Util::run(&headerHeightCalled);
425     TestWebKitAPI::Util::run(&footerHeightCalled);
426     TestWebKitAPI::Util::run(&drawHeaderCalled);
427     TestWebKitAPI::Util::run(&drawFooterCalled);
428 }
429
430 @interface NotificationDelegate : NSObject <WKUIDelegatePrivate> {
431     bool _allowNotifications;
432 }
433 - (id)initWithAllowNotifications:(bool)allowNotifications;
434 @end
435
436 @implementation NotificationDelegate
437
438 - (id)initWithAllowNotifications:(bool)allowNotifications
439 {
440     if (!(self = [super init]))
441         return nil;
442     _allowNotifications = allowNotifications;
443     return self;
444 }
445
446 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
447 {
448     if (_allowNotifications)
449         EXPECT_STREQ(message.UTF8String, "permission granted");
450     else
451         EXPECT_STREQ(message.UTF8String, "permission denied");
452     completionHandler();
453     done = true;
454 }
455
456 - (void)_webView:(WKWebView *)webView requestNotificationPermissionForSecurityOrigin:(WKSecurityOrigin *)securityOrigin decisionHandler:(void (^)(BOOL))decisionHandler
457 {
458     if (_allowNotifications)
459         EXPECT_STREQ(securityOrigin.host.UTF8String, "example.org");
460     else
461         EXPECT_STREQ(securityOrigin.host.UTF8String, "example.com");
462     decisionHandler(_allowNotifications);
463 }
464
465 @end
466
467 TEST(WebKit, NotificationPermission)
468 {
469     NSString *html = @"<script>Notification.requestPermission(function(p){alert('permission '+p)})</script>";
470     auto webView = adoptNS([[WKWebView alloc] init]);
471     [webView setUIDelegate:[[[NotificationDelegate alloc] initWithAllowNotifications:YES] autorelease]];
472     [webView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://example.org"]];
473     TestWebKitAPI::Util::run(&done);
474     done = false;
475     [webView setUIDelegate:[[[NotificationDelegate alloc] initWithAllowNotifications:NO] autorelease]];
476     [webView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://example.com"]];
477     TestWebKitAPI::Util::run(&done);
478 }
479
480 @interface PlugInDelegate : NSObject <WKUIDelegatePrivate>
481 @end
482
483 @implementation PlugInDelegate
484
485 - (void)_webView:(WKWebView *)webView unavailablePlugInButtonClickedWithReason:(_WKPlugInUnavailabilityReason)reason plugInInfo:(NSDictionary *)plugInInfo
486 {
487     ASSERT_EQ(_WKPlugInUnavailabilityReasonPluginMissing, reason);
488     ASSERT_TRUE([@"application/x-shockwave-flash" isEqualToString:[plugInInfo objectForKey:@"PluginInformationMIMEType"]]);
489     done = true;
490 }
491
492 @end
493
494 TEST(WebKit, UnavailablePlugIn)
495 {
496     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
497     [[configuration preferences] setPlugInsEnabled:YES];
498     auto delegate = adoptNS([[PlugInDelegate alloc] init]);
499     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration.get()]);
500     [webView setUIDelegate:delegate.get()];
501     [webView synchronouslyLoadHTMLString:@"<object type='application/x-shockwave-flash'/>"];
502     [webView sendClicksAtPoint:NSMakePoint(210, 600 - 80) numberOfClicks:1];
503     TestWebKitAPI::Util::run(&done);
504 }
505
506 bool firstToolbarDone;
507
508 @interface ToolbarDelegate : NSObject <WKUIDelegatePrivate>
509 @end
510
511 @implementation ToolbarDelegate
512
513 - (void)_webView:(WKWebView *)webView getToolbarsAreVisibleWithCompletionHandler:(void(^)(BOOL))completionHandler
514 {
515     completionHandler(firstToolbarDone);
516 }
517
518 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
519 {
520     if (firstToolbarDone) {
521         EXPECT_STREQ(message.UTF8String, "visible:true");
522         done = true;
523     } else {
524         EXPECT_STREQ(message.UTF8String, "visible:false");
525         firstToolbarDone = true;
526     }
527     completionHandler();
528 }
529
530 @end
531
532 TEST(WebKit, ToolbarVisible)
533 {
534     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:[[[WKWebViewConfiguration alloc] init] autorelease]]);
535     auto delegate = adoptNS([[ToolbarDelegate alloc] init]);
536     [webView setUIDelegate:delegate.get()];
537     [webView synchronouslyLoadHTMLString:@"<script>alert('visible:' + window.toolbar.visible);alert('visible:' + window.toolbar.visible)</script>"];
538     TestWebKitAPI::Util::run(&done);
539 }
540
541 @interface MouseMoveOverElementDelegate : NSObject <WKUIDelegatePrivate>
542 @end
543
544 @implementation MouseMoveOverElementDelegate
545
546 - (void)_webView:(WKWebView *)webview mouseDidMoveOverElement:(_WKHitTestResult *)hitTestResult withFlags:(NSEventModifierFlags)flags userInfo:(id <NSSecureCoding>)userInfo
547 {
548     EXPECT_STREQ(hitTestResult.absoluteLinkURL.absoluteString.UTF8String, "http://example.com/path");
549     EXPECT_STREQ(hitTestResult.linkLabel.UTF8String, "link label");
550     EXPECT_STREQ(hitTestResult.linkTitle.UTF8String, "link title");
551     EXPECT_EQ(flags, NSEventModifierFlagShift);
552     EXPECT_STREQ(NSStringFromClass([(NSObject *)userInfo class]).UTF8String, "_WKFrameHandle");
553     done = true;
554 }
555
556 @end
557
558 TEST(WebKit, MouseMoveOverElement)
559 {
560     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"FrameHandleSerialization"];
561     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
562     [webView setUIDelegate:[[[MouseMoveOverElementDelegate alloc] init] autorelease]];
563     [webView synchronouslyLoadHTMLString:@"<a href='http://example.com/path' title='link title'>link label</a>"];
564     [webView mouseMoveToPoint:NSMakePoint(20, 600 - 20) withFlags:NSEventModifierFlagShift];
565     TestWebKitAPI::Util::run(&done);
566 }
567
568 static bool readyForClick;
569
570 @interface AutoFillDelegate : NSObject <WKUIDelegatePrivate>
571 @end
572
573 @implementation AutoFillDelegate
574
575 - (void)_webView:(WKWebView *)webView didClickAutoFillButtonWithUserInfo:(id <NSSecureCoding>)userInfo
576 {
577     done = true;
578     ASSERT_TRUE([(id<NSObject>)userInfo isKindOfClass:[NSString class]]);
579     ASSERT_STREQ([(NSString*)userInfo UTF8String], "user data string");
580 }
581
582 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
583 {
584     completionHandler();
585     ASSERT_STREQ(message.UTF8String, "ready for click!");
586     readyForClick = true;
587 }
588
589 @end
590
591 TEST(WebKit, ClickAutoFillButton)
592 {
593     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"ClickAutoFillButton"];
594
595     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
596     auto delegate = adoptNS([[AutoFillDelegate alloc] init]);
597     [webView setUIDelegate:delegate.get()];
598     [webView evaluateJavaScript:@"" completionHandler: nil]; // Ensure the WebProcess and injected bundle are running.
599     TestWebKitAPI::Util::run(&readyForClick);
600     NSPoint buttonLocation = NSMakePoint(130, 575);
601     [webView mouseDownAtPoint:buttonLocation simulatePressure:NO];
602     [webView mouseUpAtPoint:buttonLocation];
603     TestWebKitAPI::Util::run(&done);
604 }
605
606 static bool readytoResign;
607
608 @interface DidResignInputElementStrongPasswordAppearanceDelegate : NSObject <WKUIDelegatePrivate>
609 @end
610
611 @implementation DidResignInputElementStrongPasswordAppearanceDelegate
612
613 - (void)_webView:(WKWebView *)webView didResignInputElementStrongPasswordAppearanceWithUserInfo:(id <NSSecureCoding>)userInfo
614 {
615     done = true;
616     ASSERT_TRUE([(id<NSObject>)userInfo isKindOfClass:[NSString class]]);
617     ASSERT_STREQ([(NSString*)userInfo UTF8String], "user data string");
618 }
619
620 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
621 {
622     completionHandler();
623     ASSERT_STREQ(message.UTF8String, "ready to resign!");
624     readytoResign = true;
625 }
626
627 @end
628
629 static void testDidResignInputElementStrongPasswordAppearanceAfterEvaluatingJavaScript(NSString *script)
630 {
631     done = false;
632     readytoResign = false;
633     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"DidResignInputElementStrongPasswordAppearance"];
634
635     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
636     auto delegate = adoptNS([[DidResignInputElementStrongPasswordAppearanceDelegate alloc] init]);
637     [webView setUIDelegate:delegate.get()];
638     [webView evaluateJavaScript:@"" completionHandler:nil]; // Make sure WebProcess and injected bundle are running.
639     TestWebKitAPI::Util::run(&readytoResign);
640     [webView evaluateJavaScript:script completionHandler:nil];
641     TestWebKitAPI::Util::run(&done);
642 }
643
644 TEST(WebKit, DidResignInputElementStrongPasswordAppearanceWhenTypeDidChange)
645 {
646     testDidResignInputElementStrongPasswordAppearanceAfterEvaluatingJavaScript(@"document.querySelector('input').type = 'text'");
647 }
648
649 TEST(WebKit, DidResignInputElementStrongPasswordAppearanceWhenValueDidChange)
650 {
651     testDidResignInputElementStrongPasswordAppearanceAfterEvaluatingJavaScript(@"document.querySelector('input').value = ''");
652 }
653
654 TEST(WebKit, DidResignInputElementStrongPasswordAppearanceWhenFormIsReset)
655 {
656     testDidResignInputElementStrongPasswordAppearanceAfterEvaluatingJavaScript(@"document.forms[0].reset()");
657 }
658
659 @interface AutoFillAvailableDelegate : NSObject <WKUIDelegatePrivate>
660 @end
661
662 @implementation AutoFillAvailableDelegate
663
664 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
665 {
666     completionHandler();
667     done = true;
668     ASSERT_STREQ(message.UTF8String, "autofill available");
669 }
670
671 @end
672
673 TEST(WebKit, AutoFillAvailable)
674 {
675     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"AutoFillAvailable"];
676
677     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
678     auto delegate = adoptNS([[AutoFillAvailableDelegate alloc] init]);
679     [webView setUIDelegate:delegate.get()];
680     [webView evaluateJavaScript:@"" completionHandler: nil]; // Ensure the WebProcess and injected bundle are running.
681     TestWebKitAPI::Util::run(&done);
682 }
683
684 @interface InjectedBundleNodeHandleIsTextFieldDelegate : NSObject <WKUIDelegatePrivate>
685 @end
686
687 @implementation InjectedBundleNodeHandleIsTextFieldDelegate
688
689 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
690 {
691     completionHandler();
692     done = true;
693     ASSERT_STREQ(message.UTF8String, "isTextField success");
694 }
695
696 @end
697
698 TEST(WebKit, InjectedBundleNodeHandleIsTextField)
699 {
700     WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"InjectedBundleNodeHandleIsTextField"];
701
702     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
703     auto delegate = adoptNS([[InjectedBundleNodeHandleIsTextFieldDelegate alloc] init]);
704     [webView setUIDelegate:delegate.get()];
705     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
706     TestWebKitAPI::Util::run(&done);
707 }
708
709 @interface PinnedStateObserver : NSObject
710 @end
711
712 @implementation PinnedStateObserver
713
714 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context
715 {
716     EXPECT_TRUE([keyPath isEqualToString:NSStringFromSelector(@selector(_pinnedState))]);
717     EXPECT_TRUE([[object class] isEqual:[TestWKWebView class]]);
718     EXPECT_EQ([[change objectForKey:NSKeyValueChangeOldKey] unsignedIntegerValue], _WKRectEdgeAll);
719     EXPECT_EQ([[change objectForKey:NSKeyValueChangeNewKey] unsignedIntegerValue], _WKRectEdgeLeft | _WKRectEdgeRight);
720     EXPECT_TRUE(context == nullptr);
721     done = true;
722 }
723
724 @end
725
726 TEST(WebKit, PinnedState)
727 {
728     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
729     auto observer = adoptNS([[PinnedStateObserver alloc] init]);
730     [webView addObserver:observer.get() forKeyPath:@"_pinnedState" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
731     [webView loadHTMLString:@"<body onload='scroll(100, 100)' style='height:10000vh;'/>" baseURL:[NSURL URLWithString:@"http://example.com/"]];
732     TestWebKitAPI::Util::run(&done);
733 }
734
735 @interface DidScrollDelegate : NSObject <WKUIDelegatePrivate>
736 @end
737
738 @implementation DidScrollDelegate
739
740 - (void)_webViewDidScroll:(WKWebView *)webView
741 {
742     done = true;
743 }
744
745 @end
746
747 TEST(WebKit, DidScroll)
748 {
749     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
750     auto delegate = adoptNS([[DidScrollDelegate alloc] init]);
751     [webView setUIDelegate:delegate.get()];
752     [webView loadHTMLString:@"<body onload='scroll(100, 100)' style='height:10000vh;'/>" baseURL:[NSURL URLWithString:@"http://example.com/"]];
753     TestWebKitAPI::Util::run(&done);
754 }
755
756 static NSEvent *tabEvent(NSWindow *window, NSEventType type, NSEventModifierFlags flags)
757 {
758     return [NSEvent keyEventWithType:type location:NSMakePoint(5, 5) modifierFlags:flags timestamp:GetCurrentEventTime() windowNumber:[window windowNumber] context:[NSGraphicsContext currentContext] characters:@"\t" charactersIgnoringModifiers:@"\t" isARepeat:NO keyCode:0];
759 }
760
761 static void synthesizeTab(NSWindow *window, NSView *view, bool withShiftDown)
762 {
763     [view keyDown:tabEvent(window, NSEventTypeKeyDown, withShiftDown ? NSEventModifierFlagShift : 0)];
764     [view keyUp:tabEvent(window, NSEventTypeKeyUp, withShiftDown ? NSEventModifierFlagShift : 0)];
765 }
766
767 static _WKFocusDirection takenDirection;
768
769 @interface FocusDelegate : NSObject <WKUIDelegatePrivate>
770 @end
771
772 @implementation FocusDelegate
773
774 - (void)_webView:(WKWebView *)webView takeFocus:(_WKFocusDirection)direction
775 {
776     takenDirection = direction;
777     done = true;
778 }
779
780 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
781 {
782     completionHandler();
783     synthesizeTab([webView window], webView, true);
784 }
785
786 @end
787
788 TEST(WebKit, Focus)
789 {
790     auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
791     auto delegate = adoptNS([[FocusDelegate alloc] init]);
792     [webView setUIDelegate:delegate.get()];
793     NSString *html = @"<script>function loaded() { document.getElementById('in').focus(); alert('ready'); }</script>"
794     "<body onload='loaded()'><input type='text' id='in'></body>";
795     [webView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://example.com/"]];
796     TestWebKitAPI::Util::run(&done);
797     ASSERT_EQ(takenDirection, _WKFocusDirectionBackward);
798 }
799
800 #define MOUSE_EVENT_CAUSES_DOWNLOAD 0
801 // FIXME: At least on El Capitan, sending a mouse event does not cause the PDFPlugin to think the download button has been clicked.
802 // This test works on High Sierra, but it should be investigated on older platforms.
803 #if MOUSE_EVENT_CAUSES_DOWNLOAD
804
805 @interface SaveDataToFileDelegate : NSObject <WKUIDelegatePrivate, WKNavigationDelegate>
806 @end
807
808 @implementation SaveDataToFileDelegate
809
810 - (void)_webView:(WKWebView *)webView saveDataToFile:(NSData *)data suggestedFilename:(NSString *)suggestedFilename mimeType:(NSString *)mimeType originatingURL:(NSURL *)url
811 {
812     NSURL *pdfURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf" subdirectory:@"TestWebKitAPI.resources"];
813     EXPECT_TRUE([data isEqualToData:[NSData dataWithContentsOfURL:pdfURL]]);
814     EXPECT_STREQ([suggestedFilename UTF8String], "test.pdf");
815     EXPECT_STREQ([mimeType UTF8String], "application/pdf");
816     EXPECT_STREQ([[url absoluteString] UTF8String], [[pdfURL absoluteString] UTF8String]);
817     done = true;
818 }
819
820 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
821 {
822     NSPoint location = NSMakePoint(490, 70); // Location of button to download the pdf.
823     [(TestWKWebView *)webView mouseDownAtPoint:location simulatePressure:NO];
824     [(TestWKWebView *)webView mouseUpAtPoint:location];
825 }
826
827 @end
828
829 TEST(WebKit, SaveDataToFile)
830 {
831     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
832     auto delegate = adoptNS([[SaveDataToFileDelegate alloc] init]);
833     [webView setUIDelegate:delegate.get()];
834     [webView setNavigationDelegate:delegate.get()];
835     NSURL *pdfURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"pdf" subdirectory:@"TestWebKitAPI.resources"];
836     [webView loadRequest:[NSURLRequest requestWithURL:pdfURL]];
837     TestWebKitAPI::Util::run(&done);
838 }
839
840 #endif // MOUSE_EVENT_CAUSES_DOWNLOAD
841
842 #define RELIABLE_DID_NOT_HANDLE_WHEEL_EVENT 0
843 // FIXME: make wheel event handling more reliable.
844 // https://bugs.webkit.org/show_bug.cgi?id=175967
845 #if RELIABLE_DID_NOT_HANDLE_WHEEL_EVENT
846
847 static void synthesizeWheelEvents(NSView *view, int x, int y)
848 {
849     RetainPtr<CGEventRef> cgScrollEvent = adoptCF(CGEventCreateScrollWheelEvent(nullptr, kCGScrollEventUnitLine, 2, y, x));
850     NSEvent* event = [NSEvent eventWithCGEvent:cgScrollEvent.get()];
851     [view scrollWheel:event];
852     
853     // Wheel events get coalesced sometimes. Make more events until one is not handled.
854     dispatch_async(dispatch_get_main_queue(), ^ {
855         synthesizeWheelEvents(view, x, y);
856     });
857 }
858
859 @interface WheelDelegate : NSObject <WKUIDelegatePrivate>
860 @end
861
862 @implementation WheelDelegate
863
864 - (void)_webView:(WKWebView *)webView didNotHandleWheelEvent:(NSEvent *)event
865 {
866     done = true;
867 }
868
869 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
870 {
871     completionHandler();
872     synthesizeWheelEvents(webView, 1, 1);
873 }
874
875 @end
876
877 TEST(WebKit, DidNotHandleWheelEvent)
878 {
879     auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
880     auto delegate = adoptNS([[WheelDelegate alloc] init]);
881     [webView setUIDelegate:delegate.get()];
882     [webView loadHTMLString:@"<body onload='alert(\"ready\")' onwheel='()=>{}' style='overflow:hidden; height:10000vh;'></body>" baseURL:[NSURL URLWithString:@"http://example.com/"]];
883     TestWebKitAPI::Util::run(&done);
884 }
885
886 #endif // RELIABLE_DID_NOT_HANDLE_WHEEL_EVENT
887
888 #endif // PLATFORM(MAC)