REGRESSION (r232544): [iOS] TestWebKitAPI.WebKit.OverrideLayoutSizeChangesDuringAnima...
[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     auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
196     webView.get().navigationDelegate = navigationDelegate.get();
197     [navigationDelegate waitForDidFinishNavigation];
198
199     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
200     [window addSubview:webView.get()];
201     [window setHidden:NO];
202
203     [webView _beginAnimatedResizeWithUpdates:^ {
204         [webView setFrame:CGRectMake(0, 0, [webView frame].size.width + 100, 400)];
205     }];
206
207     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(100, 200) maximumUnobscuredSizeOverride:CGSizeMake(100, 200)];
208     [webView _endAnimatedResize];
209
210     __block bool didReadLayoutSize = false;
211
212     [webView _doAfterNextPresentationUpdate:^{
213         [webView evaluateJavaScript:@"[window.innerWidth, window.innerHeight]" completionHandler:^(id value, NSError *error) {
214             CGFloat innerWidth = [[value objectAtIndex:0] floatValue];
215             CGFloat innerHeight = [[value objectAtIndex:1] floatValue];
216
217             EXPECT_EQ(innerWidth, 100);
218             EXPECT_EQ(innerHeight, 200);
219
220             didReadLayoutSize = true;
221         }];
222     }];
223     
224     TestWebKitAPI::Util::run(&didReadLayoutSize);
225 }
226
227 TEST(WebKit, OverrideLayoutSizeIsRestoredAfterProcessRelaunch)
228 {
229     auto webView = createAnimatedResizeWebView();
230     [webView setUIDelegate:webView.get()];
231
232     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(200, 50) maximumUnobscuredSizeOverride:CGSizeMake(200, 50)];
233
234     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
235     [webView _test_waitForDidFinishNavigation];
236
237     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
238     [window addSubview:webView.get()];
239     [window setHidden:NO];
240
241     [webView _killWebContentProcessAndResetState];
242     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
243     [webView _test_waitForDidFinishNavigation];
244
245     __block bool didReadLayoutSize = false;
246     [webView evaluateJavaScript:@"[window.innerWidth, window.innerHeight]" completionHandler:^(id value, NSError *error) {
247         CGFloat innerWidth = [[value objectAtIndex:0] floatValue];
248         CGFloat innerHeight = [[value objectAtIndex:1] floatValue];
249
250         EXPECT_EQ(innerWidth, 200);
251         EXPECT_EQ(innerHeight, 50);
252
253         didReadLayoutSize = true;
254     }];
255     TestWebKitAPI::Util::run(&didReadLayoutSize);
256 }
257
258 TEST(WebKit, OverrideLayoutSizeIsRestoredAfterChangingDuringProcessRelaunch)
259 {
260     auto webView = createAnimatedResizeWebView();
261     [webView setUIDelegate:webView.get()];
262
263     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(100, 100) maximumUnobscuredSizeOverride:CGSizeMake(100, 100)];
264
265     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
266     [webView _test_waitForDidFinishNavigation];
267
268     auto window = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
269     [window addSubview:webView.get()];
270     [window setHidden:NO];
271
272     [webView _killWebContentProcessAndResetState];
273     [webView _overrideLayoutParametersWithMinimumLayoutSize:CGSizeMake(200, 50) maximumUnobscuredSizeOverride:CGSizeMake(200, 50)];
274
275     [webView loadHTMLString:@"<head><meta name='viewport' content='initial-scale=1'></head>" baseURL:nil];
276     [webView _test_waitForDidFinishNavigation];
277
278     __block bool didReadLayoutSize = false;
279     [webView evaluateJavaScript:@"[window.innerWidth, window.innerHeight]" completionHandler:^(id value, NSError *error) {
280         CGFloat innerWidth = [[value objectAtIndex:0] floatValue];
281         CGFloat innerHeight = [[value objectAtIndex:1] floatValue];
282
283         EXPECT_EQ(innerWidth, 200);
284         EXPECT_EQ(innerHeight, 50);
285
286         didReadLayoutSize = true;
287     }];
288     TestWebKitAPI::Util::run(&didReadLayoutSize);
289 }
290
291 #endif