Add afterScreenUpdates to WKSnapshotConfiguration
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / WKWebViewSnapshot.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
28 #if WK_API_ENABLED
29
30 #import "PlatformUtilities.h"
31 #import "Test.h"
32 #import "TestNavigationDelegate.h"
33 #import <WebKit/WKSnapshotConfiguration.h>
34 #import <wtf/RetainPtr.h>
35
36 static bool isDone;
37
38 #if PLATFORM(MAC)
39 typedef NSImage *PlatformImage;
40 typedef NSWindow *PlatformWindow;
41
42 static RetainPtr<CGImageRef> convertToCGImage(NSImage *image)
43 {
44     return [image CGImageForProposedRect:nil context:nil hints:nil];
45 }
46
47 #else
48 typedef UIImage *PlatformImage;
49 typedef UIWindow *PlatformWindow;
50
51 static RetainPtr<CGImageRef> convertToCGImage(UIImage *image)
52 {
53     return image.CGImage;
54 }
55 #endif
56
57 static NSInteger getPixelIndex(NSInteger x, NSInteger y, NSInteger width)
58 {
59     return (y * width + x) * 4;
60 }
61
62 TEST(WKWebView, SnapshotImageError)
63 {
64     CGFloat viewWidth = 800;
65     CGFloat viewHeight = 600;
66     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
67     
68     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
69     [webView _test_waitForDidFinishNavigation];
70     [webView _killWebContentProcessAndResetState];
71
72     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
73     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
74     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
75
76     isDone = false;
77     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
78         EXPECT_NULL(snapshotImage);
79         EXPECT_WK_STREQ(@"WKErrorDomain", error.domain);
80
81         isDone = true;
82     }];
83
84     TestWebKitAPI::Util::run(&isDone);
85 }
86
87 TEST(WKWebView, SnapshotImageBaseCase)
88 {
89     NSInteger viewWidth = 800;
90     NSInteger viewHeight = 600;
91     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
92
93     RetainPtr<PlatformWindow> window;
94     CGFloat backingScaleFactor;
95
96 #if PLATFORM(MAC)
97     window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
98     [[window contentView] addSubview:webView.get()];
99     backingScaleFactor = [window backingScaleFactor];
100 #elif PLATFORM(IOS_FAMILY)
101     window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
102     [window addSubview:webView.get()];
103     backingScaleFactor = [[window screen] scale];
104 #endif
105
106     [webView loadHTMLString:@"<body style='background-color:red;'><div style='background-color:blue; position:absolute; width:100px; height:100px; top:50px; left:50px'></div></body>" baseURL:nil];
107     [webView _test_waitForDidFinishNavigation];
108
109     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
110     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
111     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
112
113     isDone = false;
114     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
115         EXPECT_NULL(error);
116
117         EXPECT_EQ(viewWidth, snapshotImage.size.width);
118
119         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
120         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
121
122         NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
123         NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
124
125         unsigned char rgba[viewWidthInPixels * viewHeightInPixels * 4];
126         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
127         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
128
129         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidthInPixels);
130         EXPECT_EQ(255, rgba[pixelIndex]);
131         EXPECT_EQ(0, rgba[pixelIndex + 1]);
132         EXPECT_EQ(0, rgba[pixelIndex + 2]);
133
134         // Inside the blue div (50, 50, 100, 100)
135         pixelIndex = getPixelIndex(55 * backingScaleFactor, 55 * backingScaleFactor, viewWidthInPixels);
136         EXPECT_EQ(0, rgba[pixelIndex]);
137         EXPECT_EQ(0, rgba[pixelIndex + 1]);
138         EXPECT_EQ(255, rgba[pixelIndex + 2]);
139
140         pixelIndex = getPixelIndex(155 * backingScaleFactor, 155 * backingScaleFactor, viewWidthInPixels);
141         EXPECT_EQ(255, rgba[pixelIndex]);
142         EXPECT_EQ(0, rgba[pixelIndex + 1]);
143         EXPECT_EQ(0, rgba[pixelIndex + 2]);
144
145         isDone = true;
146     }];
147
148     TestWebKitAPI::Util::run(&isDone);
149 }
150
151 TEST(WKWebView, SnapshotImageScale)
152 {
153     CGFloat viewWidth = 800;
154     CGFloat viewHeight = 600;
155     CGFloat scaleFactor = 2;
156     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
157
158     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
159     [webView _test_waitForDidFinishNavigation];
160
161     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
162     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
163     [snapshotConfiguration setSnapshotWidth:@(viewWidth * scaleFactor)];
164
165     isDone = false;
166     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
167         EXPECT_NULL(error);
168         EXPECT_NOT_NULL(snapshotImage);
169         EXPECT_EQ(viewWidth * scaleFactor, snapshotImage.size.width);
170         EXPECT_EQ(viewHeight * scaleFactor, snapshotImage.size.height);
171
172         isDone = true;
173     }];
174
175     TestWebKitAPI::Util::run(&isDone);
176 }
177
178 TEST(WKWebView, SnapshotImageNilConfiguration)
179 {
180     CGFloat viewWidth = 800;
181     CGFloat viewHeight = 600;
182     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
183
184     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
185     [webView _test_waitForDidFinishNavigation];
186
187     isDone = false;
188     [webView takeSnapshotWithConfiguration:nil completionHandler:^(PlatformImage snapshotImage, NSError *error) {
189         EXPECT_NULL(error);
190         EXPECT_NOT_NULL(snapshotImage);
191         EXPECT_EQ([webView bounds].size.width, snapshotImage.size.width);
192
193         isDone = true;
194     }];
195
196     TestWebKitAPI::Util::run(&isDone);
197 }
198
199 TEST(WKWebView, SnapshotImageUninitializedConfiguration)
200 {
201     CGFloat viewWidth = 800;
202     CGFloat viewHeight = 600;
203     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
204
205     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
206     [webView _test_waitForDidFinishNavigation];
207
208     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
209
210     isDone = false;
211     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
212         EXPECT_NULL(error);
213         EXPECT_NOT_NULL(snapshotImage);
214         EXPECT_EQ([webView bounds].size.width, snapshotImage.size.width);
215
216         isDone = true;
217     }];
218
219     TestWebKitAPI::Util::run(&isDone);
220 }
221
222 TEST(WKWebView, SnapshotImageUninitializedSnapshotWidth)
223 {
224     CGFloat viewWidth = 800;
225     CGFloat viewHeight = 600;
226     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
227
228     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
229     [webView _test_waitForDidFinishNavigation];
230
231     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
232     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
233
234     isDone = false;
235     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
236         EXPECT_NULL(error);
237         EXPECT_NOT_NULL(snapshotImage);
238         EXPECT_EQ([snapshotConfiguration rect].size.width, snapshotImage.size.width);
239
240         isDone = true;
241     }];
242
243     TestWebKitAPI::Util::run(&isDone);
244 }
245
246 TEST(WKWebView, SnapshotImageLargeAsyncDecoding)
247 {
248     NSInteger viewWidth = 800;
249     NSInteger viewHeight = 600;
250     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
251
252     NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"large-red-square-image" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
253     [webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
254     [webView _test_waitForDidFinishNavigation];
255
256     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
257     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
258     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
259
260     isDone = false;
261     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
262         EXPECT_NULL(error);
263
264         EXPECT_EQ(viewWidth, snapshotImage.size.width);
265
266         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
267         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
268
269         unsigned char rgba[viewWidth * viewHeight * 4];
270         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidth, viewHeight, 8, 4 * viewWidth, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
271         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidth, viewHeight), cgImage.get());
272
273         // Top-left corner of the div (0, 0, 100, 100)
274         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidth);
275         EXPECT_EQ(255, rgba[pixelIndex]);
276         EXPECT_EQ(0, rgba[pixelIndex + 1]);
277         EXPECT_EQ(0, rgba[pixelIndex + 2]);
278
279         // Right-bottom corner of the div (0, 0, 100, 100)
280         pixelIndex = getPixelIndex(99, 99, viewWidth);
281         EXPECT_EQ(255, rgba[pixelIndex]);
282         EXPECT_EQ(0, rgba[pixelIndex + 1]);
283         EXPECT_EQ(0, rgba[pixelIndex + 2]);
284
285         // Outside the div (0, 0, 100, 100)
286         pixelIndex = getPixelIndex(100, 100, viewWidth);
287         EXPECT_EQ(255, rgba[pixelIndex]);
288         EXPECT_EQ(255, rgba[pixelIndex + 1]);
289         EXPECT_EQ(255, rgba[pixelIndex + 2]);
290
291         isDone = true;
292     }];
293
294     TestWebKitAPI::Util::run(&isDone);
295 }
296
297 TEST(WKWebView, SnapshotAfterScreenUpdates)
298 {
299     // The API tests currently cannot truly test SnapshotConfiguration.afterScreenUpdates since it is only needed
300     // on iOS devices, and we do not currently run API tests on iOS devices. So we expect this test to pass with
301     // afterScreenUpdates set to YES or NO on the configuration. On device, afterScreenUpdates must be YES in order
302     // pass this test.
303     NSInteger viewWidth = 800;
304     NSInteger viewHeight = 600;
305     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
306     
307     RetainPtr<PlatformWindow> window;
308     CGFloat backingScaleFactor;
309     
310 #if PLATFORM(MAC)
311     window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
312     [[window contentView] addSubview:webView.get()];
313     backingScaleFactor = [window backingScaleFactor];
314 #elif PLATFORM(IOS_FAMILY)
315     window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
316     [window addSubview:webView.get()];
317     backingScaleFactor = [[window screen] scale];
318 #endif
319     
320     [webView loadHTMLString:@"<body style='margin:0'><div id='change-me' style='background-color:red; position:fixed; width:100%; height:100%'></div></body>" baseURL:nil];
321     [webView _test_waitForDidFinishNavigation];
322     
323     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
324     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
325     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
326     [snapshotConfiguration setAfterScreenUpdates:YES];
327
328     [webView evaluateJavaScript:@"var div = document.getElementById('change-me');div.style.backgroundColor = 'blue';" completionHandler:nil];
329
330     isDone = false;
331     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
332         EXPECT_NULL(error);
333         
334         EXPECT_EQ(viewWidth, snapshotImage.size.width);
335         
336         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
337         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
338         
339         NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
340         NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
341         
342         unsigned char rgba[viewWidthInPixels * viewHeightInPixels * 4];
343         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
344         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
345         
346         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidthInPixels);
347         EXPECT_EQ(0, rgba[pixelIndex]);
348         EXPECT_EQ(0, rgba[pixelIndex + 1]);
349         EXPECT_EQ(255, rgba[pixelIndex + 2]);
350         
351         isDone = true;
352     }];
353     
354     TestWebKitAPI::Util::run(&isDone);
355 }
356
357 TEST(WKWebView, SnapshotWithoutAfterScreenUpdates)
358 {
359     // SnapshotConfiguration.afterScreenUpdates tests currently cannot truly test this API since it is only needed
360     // on iOS devices, and we do not currently run API tests on iOS devices. The expectations below are based on
361     // what we expect in the simulator and on macOS, which is that setting afterScreenUpdates to NO will still
362     // result in a snapshot that includes the recent screen updates. If we get these tests running on iOS device,
363     // then we would expect the pixels to be red instead of blue.
364     NSInteger viewWidth = 800;
365     NSInteger viewHeight = 600;
366     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
367     
368     RetainPtr<PlatformWindow> window;
369     CGFloat backingScaleFactor;
370     
371 #if PLATFORM(MAC)
372     window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
373     [[window contentView] addSubview:webView.get()];
374     backingScaleFactor = [window backingScaleFactor];
375 #elif PLATFORM(IOS_FAMILY)
376     window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
377     [window addSubview:webView.get()];
378     backingScaleFactor = [[window screen] scale];
379 #endif
380     
381     [webView loadHTMLString:@"<body style='margin:0'><div id='change-me' style='background-color:red; position:fixed; width:100%; height:100%'></div></body>" baseURL:nil];
382     [webView _test_waitForDidFinishNavigation];
383     
384     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
385     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
386     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
387     [snapshotConfiguration setAfterScreenUpdates:NO];
388     
389     [webView evaluateJavaScript:@"var div = document.getElementById('change-me');div.style.backgroundColor = 'blue';" completionHandler:nil];
390     
391     isDone = false;
392     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
393         EXPECT_NULL(error);
394         
395         EXPECT_EQ(viewWidth, snapshotImage.size.width);
396         
397         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
398         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
399         
400         NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
401         NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
402         
403         unsigned char rgba[viewWidthInPixels * viewHeightInPixels * 4];
404         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
405         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
406         
407         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidthInPixels);
408         EXPECT_EQ(0, rgba[pixelIndex]);
409         EXPECT_EQ(0, rgba[pixelIndex + 1]);
410         EXPECT_EQ(255, rgba[pixelIndex + 2]);
411         
412         isDone = true;
413     }];
414     
415     TestWebKitAPI::Util::run(&isDone);
416 }
417
418 #endif