597bdab0196787b6b308c0844f9d4c0ed99e2d62
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / AnimatedResize.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 #include "config.h"
27
28 #import "PlatformUtilities.h"
29 #import "TestNavigationDelegate.h"
30 #import <WebKit/WKPreferences.h>
31 #import <WebKit/WKProcessPoolPrivate.h>
32 #import <WebKit/WKWebView.h>
33 #import <WebKit/WKWebViewConfiguration.h>
34 #import <WebKit/WKWebViewPrivate.h>
35 #import <WebKit/_WKProcessPoolConfiguration.h>
36 #import <wtf/RetainPtr.h>
37
38 #if WK_API_ENABLED && PLATFORM(IOS)
39
40 static bool didLayout;
41 static bool didEndAnimatedResize;
42 static bool didChangeSafeAreaShouldAffectObscuredInsets;
43
44 @interface AnimatedResizeWebView : WKWebView <WKUIDelegate>
45
46 @end
47
48 @implementation AnimatedResizeWebView
49
50 - (void)_endAnimatedResize
51 {
52     [super _endAnimatedResize];
53
54     didEndAnimatedResize = true;
55 }
56
57 - (void)_webView:(WKWebView *)webView didChangeSafeAreaShouldAffectObscuredInsets:(BOOL)safeAreaShouldAffectObscuredInsets
58 {
59     didChangeSafeAreaShouldAffectObscuredInsets = true;
60 }
61
62 @end
63
64 static RetainPtr<AnimatedResizeWebView> createAnimatedResizeWebView()
65 {
66     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
67     [processPoolConfiguration setIgnoreSynchronousMessagingTimeoutsForTesting:YES];
68     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
69
70     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
71     [webViewConfiguration setProcessPool:processPool.get()];
72
73     auto webView = adoptNS([[AnimatedResizeWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
74
75     return webView;
76 }
77
78 static RetainPtr<TestNavigationDelegate> createFirstVisuallyNonEmptyWatchingNavigationDelegate()
79 {
80     auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
81     [navigationDelegate setRenderingProgressDidChange:^(WKWebView *, _WKRenderingProgressEvents progressEvents) {
82         if (progressEvents & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
83             didLayout = true;
84     }];
85     return navigationDelegate;
86 }
87
88 TEST(WebKit, DISABLED_ResizeWithHiddenContentDoesNotHang)
89 {
90     auto webView = createAnimatedResizeWebView();
91     [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"blinking-div" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
92
93     auto navigationDelegate = createFirstVisuallyNonEmptyWatchingNavigationDelegate();
94     [webView setNavigationDelegate:navigationDelegate.get()];
95     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
96     [window addSubview:webView.get()];
97     [window setHidden:NO];
98
99     TestWebKitAPI::Util::run(&didLayout);
100     didLayout = false;
101
102     for (unsigned i = 0; i < 50; i++) {
103         [webView _resizeWhileHidingContentWithUpdates:^{
104             [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
105         }];
106
107         TestWebKitAPI::Util::run(&didEndAnimatedResize);
108         didEndAnimatedResize = false;
109     }
110 }
111
112 TEST(WebKit, AnimatedResizeDoesNotHang)
113 {
114     auto webView = createAnimatedResizeWebView();
115     [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"blinking-div" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
116
117     auto navigationDelegate = createFirstVisuallyNonEmptyWatchingNavigationDelegate();
118     [webView setNavigationDelegate:navigationDelegate.get()];
119     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
120     [window addSubview:webView.get()];
121     [window setHidden:NO];
122
123     TestWebKitAPI::Util::run(&didLayout);
124     didLayout = false;
125
126     for (unsigned i = 0; i < 50; i++) {
127         [webView _beginAnimatedResizeWithUpdates:^{
128             [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
129         }];
130
131         dispatch_async(dispatch_get_main_queue(), ^{
132             [webView _endAnimatedResize];
133         });
134
135         TestWebKitAPI::Util::run(&didEndAnimatedResize);
136         didEndAnimatedResize = false;
137     }
138 }
139
140 TEST(WebKit, AnimatedResizeBlocksViewportFitChanges)
141 {
142     auto webView = createAnimatedResizeWebView();
143     [webView setUIDelegate:webView.get()];
144
145     // We need to have something loaded before beginning the animated
146     // resize, or it will bail.
147     [webView loadHTMLString:@"<head></head>" baseURL:nil];
148     [webView _test_waitForDidFinishNavigation];
149
150     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
151     [window addSubview:webView.get()];
152     [window setHidden:NO];
153
154     [webView _beginAnimatedResizeWithUpdates:^ {
155         [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
156     }];
157
158     // Load a page that will change the state of viewport-fit,
159     // in the middle of the resize.
160     [webView loadHTMLString:@"<head><meta name='viewport' content='viewport-fit=cover'></head>" baseURL:nil];
161     [webView _test_waitForDidFinishNavigation];
162
163     didChangeSafeAreaShouldAffectObscuredInsets = false;
164
165     // Wait for a commit to come in /after/ loading the viewport-fit=cover
166     // page, and ensure that we didn't call the UIDelegate callback,
167     // because we're still in the resize. Then, end the resize.
168     [webView _doAfterNextPresentationUpdate:^ {
169         EXPECT_FALSE(didChangeSafeAreaShouldAffectObscuredInsets);
170         [webView _endAnimatedResize];
171     }];
172
173     TestWebKitAPI::Util::run(&didEndAnimatedResize);
174     didEndAnimatedResize = false;
175
176     // Wait for one more commit so that we see the viewport-fit state
177     // change actually take place (post-resize), and ensure that it does.
178     __block bool didGetCommitAfterEndAnimatedResize = false;
179     [webView _doAfterNextPresentationUpdate:^ {
180         didGetCommitAfterEndAnimatedResize = true;
181     }];
182     TestWebKitAPI::Util::run(&didGetCommitAfterEndAnimatedResize);
183
184     EXPECT_TRUE(didChangeSafeAreaShouldAffectObscuredInsets);
185 }
186
187 TEST(WebKit, OverrideLayoutSizeChangesDuringAnimatedResizeSucceed)
188 {
189     auto webView = createAnimatedResizeWebView();
190     [webView setUIDelegate:webView.get()];
191
192     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(200, 50) maximumUnobscuredSizeOverride:CGSizeMake(200, 50)];
193
194     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
195     [webView _test_waitForDidFinishNavigation];
196
197     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
198     [window addSubview:webView.get()];
199     [window setHidden:NO];
200
201     [webView _beginAnimatedResizeWithUpdates:^ {
202         [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
203     }];
204
205     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(100, 200) maximumUnobscuredSizeOverride:CGSizeMake(100, 200)];
206     [webView _endAnimatedResize];
207
208     __block bool didReadLayoutSize = false;
209     [webView evaluateJavaScript:@"[window.innerWidth, window.innerHeight]" completionHandler:^(id value, NSError *error) {
210         CGFloat innerWidth = [[value objectAtIndex:0] floatValue];
211         CGFloat innerHeight = [[value objectAtIndex:1] floatValue];
212
213         EXPECT_EQ(innerWidth, 100);
214         EXPECT_EQ(innerHeight, 200);
215
216         didReadLayoutSize = true;
217     }];
218     TestWebKitAPI::Util::run(&didReadLayoutSize);
219 }
220
221 TEST(WebKit, OverrideLayoutSizeIsRestoredAfterProcessRelaunch)
222 {
223     auto webView = createAnimatedResizeWebView();
224     [webView setUIDelegate:webView.get()];
225
226     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(200, 50) maximumUnobscuredSizeOverride:CGSizeMake(200, 50)];
227
228     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
229     [webView _test_waitForDidFinishNavigation];
230
231     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
232     [window addSubview:webView.get()];
233     [window setHidden:NO];
234
235     [webView _killWebContentProcessAndResetState];
236     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
237     [webView _test_waitForDidFinishNavigation];
238
239     __block bool didReadLayoutSize = false;
240     [webView evaluateJavaScript:@"[window.innerWidth, window.innerHeight]" completionHandler:^(id value, NSError *error) {
241         CGFloat innerWidth = [[value objectAtIndex:0] floatValue];
242         CGFloat innerHeight = [[value objectAtIndex:1] floatValue];
243
244         EXPECT_EQ(innerWidth, 200);
245         EXPECT_EQ(innerHeight, 50);
246
247         didReadLayoutSize = true;
248     }];
249     TestWebKitAPI::Util::run(&didReadLayoutSize);
250 }
251
252 TEST(WebKit, OverrideLayoutSizeIsRestoredAfterChangingDuringProcessRelaunch)
253 {
254     auto webView = createAnimatedResizeWebView();
255     [webView setUIDelegate:webView.get()];
256
257     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(100, 100) maximumUnobscuredSizeOverride:CGSizeMake(100, 100)];
258
259     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
260     [webView _test_waitForDidFinishNavigation];
261
262     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
263     [window addSubview:webView.get()];
264     [window setHidden:NO];
265
266     [webView _killWebContentProcessAndResetState];
267     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(200, 50) maximumUnobscuredSizeOverride:CGSizeMake(200, 50)];
268
269     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
270     [webView _test_waitForDidFinishNavigation];
271
272     __block bool didReadLayoutSize = false;
273     [webView evaluateJavaScript:@"[window.innerWidth, window.innerHeight]" completionHandler:^(id value, NSError *error) {
274         CGFloat innerWidth = [[value objectAtIndex:0] floatValue];
275         CGFloat innerHeight = [[value objectAtIndex:1] floatValue];
276
277         EXPECT_EQ(innerWidth, 200);
278         EXPECT_EQ(innerHeight, 50);
279
280         didReadLayoutSize = true;
281     }];
282     TestWebKitAPI::Util::run(&didReadLayoutSize);
283 }
284
285 #endif