Turn on IOSurface support in the iOS Simulator
[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 #import "PlatformUtilities.h"
29 #import "Test.h"
30 #import "TestNavigationDelegate.h"
31 #import <WebKit/WKSnapshotConfiguration.h>
32 #import <wtf/RetainPtr.h>
33
34 static bool isDone;
35
36 #if PLATFORM(MAC)
37 typedef NSImage *PlatformImage;
38 typedef NSWindow *PlatformWindow;
39
40 static RetainPtr<CGImageRef> convertToCGImage(NSImage *image)
41 {
42     return [image CGImageForProposedRect:nil context:nil hints:nil];
43 }
44
45 #else
46 typedef UIImage *PlatformImage;
47 typedef UIWindow *PlatformWindow;
48
49 static RetainPtr<CGImageRef> convertToCGImage(UIImage *image)
50 {
51     return image.CGImage;
52 }
53 #endif
54
55 static NSInteger getPixelIndex(NSInteger x, NSInteger y, NSInteger width)
56 {
57     return (y * width + x) * 4;
58 }
59
60 TEST(WKWebView, SnapshotImageError)
61 {
62     CGFloat viewWidth = 800;
63     CGFloat viewHeight = 600;
64     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
65     
66     [webView loadHTMLString:@"<body style='background-color: red;'></body>" baseURL:nil];
67     [webView _test_waitForDidFinishNavigation];
68     [webView _killWebContentProcessAndResetState];
69
70     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
71     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
72     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
73
74     isDone = false;
75     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
76         EXPECT_NULL(snapshotImage);
77         EXPECT_WK_STREQ(@"WKErrorDomain", error.domain);
78
79         isDone = true;
80     }];
81
82     TestWebKitAPI::Util::run(&isDone);
83 }
84
85 TEST(WKWebView, SnapshotImageBaseCase)
86 {
87     NSInteger viewWidth = 800;
88     NSInteger viewHeight = 600;
89     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
90
91     RetainPtr<PlatformWindow> window;
92     CGFloat backingScaleFactor;
93
94 #if PLATFORM(MAC)
95     window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
96     [[window contentView] addSubview:webView.get()];
97     backingScaleFactor = [window backingScaleFactor];
98 #elif PLATFORM(IOS_FAMILY)
99     window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
100     [window addSubview:webView.get()];
101     backingScaleFactor = [[window screen] scale];
102 #endif
103
104     [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];
105     [webView _test_waitForDidFinishNavigation];
106
107     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
108     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
109     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
110
111     isDone = false;
112     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
113         EXPECT_NULL(error);
114
115         EXPECT_EQ(viewWidth, snapshotImage.size.width);
116
117         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
118         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
119
120         NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
121         NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
122
123         uint8_t *rgba = (unsigned char *)calloc(viewWidthInPixels * viewHeightInPixels * 4, sizeof(unsigned char));
124         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
125         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
126
127         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidthInPixels);
128         EXPECT_EQ(255, rgba[pixelIndex]);
129         EXPECT_EQ(0, rgba[pixelIndex + 1]);
130         EXPECT_EQ(0, rgba[pixelIndex + 2]);
131
132         // Inside the blue div (50, 50, 100, 100)
133         pixelIndex = getPixelIndex(55 * backingScaleFactor, 55 * backingScaleFactor, viewWidthInPixels);
134         EXPECT_EQ(0, rgba[pixelIndex]);
135         EXPECT_EQ(0, rgba[pixelIndex + 1]);
136         EXPECT_EQ(255, rgba[pixelIndex + 2]);
137
138         pixelIndex = getPixelIndex(155 * backingScaleFactor, 155 * backingScaleFactor, viewWidthInPixels);
139         EXPECT_EQ(255, rgba[pixelIndex]);
140         EXPECT_EQ(0, rgba[pixelIndex + 1]);
141         EXPECT_EQ(0, rgba[pixelIndex + 2]);
142
143         free(rgba);
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         uint8_t *rgba = (unsigned char *)calloc(viewWidth * viewHeight * 4, sizeof(unsigned char));
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         free(rgba);
292
293         isDone = true;
294     }];
295
296     TestWebKitAPI::Util::run(&isDone);
297 }
298
299 TEST(WKWebView, SnapshotAfterScreenUpdates)
300 {
301     // The API tests currently cannot truly test SnapshotConfiguration.afterScreenUpdates since it is only needed
302     // on iOS devices, and we do not currently run API tests on iOS devices. So we expect this test to pass with
303     // afterScreenUpdates set to YES or NO on the configuration. On device, afterScreenUpdates must be YES in order
304     // pass this test.
305     NSInteger viewWidth = 800;
306     NSInteger viewHeight = 600;
307     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
308     
309     RetainPtr<PlatformWindow> window;
310     CGFloat backingScaleFactor;
311     
312 #if PLATFORM(MAC)
313     window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
314     [[window contentView] addSubview:webView.get()];
315     backingScaleFactor = [window backingScaleFactor];
316 #elif PLATFORM(IOS_FAMILY)
317     window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
318     [window addSubview:webView.get()];
319     backingScaleFactor = [[window screen] scale];
320 #endif
321     
322     [webView loadHTMLString:@"<body style='margin:0'><div id='change-me' style='background-color:red; position:fixed; width:100%; height:100%'></div></body>" baseURL:nil];
323     [webView _test_waitForDidFinishNavigation];
324     
325     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
326     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
327     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
328     [snapshotConfiguration setAfterScreenUpdates:YES];
329
330     [webView evaluateJavaScript:@"var div = document.getElementById('change-me');div.style.backgroundColor = 'blue';" completionHandler:nil];
331
332     isDone = false;
333     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
334         EXPECT_NULL(error);
335         
336         EXPECT_EQ(viewWidth, snapshotImage.size.width);
337         
338         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
339         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
340         
341         NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
342         NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
343         
344         uint8_t *rgba = (unsigned char *)calloc(viewWidthInPixels * viewHeightInPixels * 4, sizeof(unsigned char));
345         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
346         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
347         
348         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidthInPixels);
349         EXPECT_EQ(0, rgba[pixelIndex]);
350         EXPECT_EQ(0, rgba[pixelIndex + 1]);
351         EXPECT_EQ(255, rgba[pixelIndex + 2]);
352
353         free(rgba);
354         
355         isDone = true;
356     }];
357     
358     TestWebKitAPI::Util::run(&isDone);
359 }
360
361 TEST(WKWebView, SnapshotWithoutAfterScreenUpdates)
362 {
363     // SnapshotConfiguration.afterScreenUpdates tests currently cannot truly test this API since it is only needed
364     // on iOS devices, and we do not currently run API tests on iOS devices. The expectations below are based on
365     // what we expect in the simulator and on macOS, which is that setting afterScreenUpdates to NO will still
366     // result in a snapshot that includes the recent screen updates. If we get these tests running on iOS device,
367     // then we would expect the pixels to be red instead of blue.
368     NSInteger viewWidth = 800;
369     NSInteger viewHeight = 600;
370     RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, viewWidth, viewHeight)]);
371     
372     RetainPtr<PlatformWindow> window;
373     CGFloat backingScaleFactor;
374     
375 #if PLATFORM(MAC)
376     window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
377     [[window contentView] addSubview:webView.get()];
378     backingScaleFactor = [window backingScaleFactor];
379 #elif PLATFORM(IOS_FAMILY)
380     window = adoptNS([[UIWindow alloc] initWithFrame:[webView frame]]);
381     [window addSubview:webView.get()];
382     backingScaleFactor = [[window screen] scale];
383 #endif
384     
385     [webView loadHTMLString:@"<body style='margin:0'><div id='change-me' style='background-color:red; position:fixed; width:100%; height:100%'></div></body>" baseURL:nil];
386     [webView _test_waitForDidFinishNavigation];
387     
388     RetainPtr<WKSnapshotConfiguration> snapshotConfiguration = adoptNS([[WKSnapshotConfiguration alloc] init]);
389     [snapshotConfiguration setRect:NSMakeRect(0, 0, viewWidth, viewHeight)];
390     [snapshotConfiguration setSnapshotWidth:@(viewWidth)];
391     [snapshotConfiguration setAfterScreenUpdates:NO];
392     
393     [webView evaluateJavaScript:@"var div = document.getElementById('change-me');div.style.backgroundColor = 'blue';" completionHandler:nil];
394     
395     isDone = false;
396     [webView takeSnapshotWithConfiguration:snapshotConfiguration.get() completionHandler:^(PlatformImage snapshotImage, NSError *error) {
397         EXPECT_NULL(error);
398         
399         EXPECT_EQ(viewWidth, snapshotImage.size.width);
400         
401         RetainPtr<CGImageRef> cgImage = convertToCGImage(snapshotImage);
402         RetainPtr<CGColorSpaceRef> colorSpace = adoptCF(CGColorSpaceCreateDeviceRGB());
403         
404         NSInteger viewWidthInPixels = viewWidth * backingScaleFactor;
405         NSInteger viewHeightInPixels = viewHeight * backingScaleFactor;
406         
407         uint8_t *rgba = (unsigned char *)calloc(viewWidthInPixels * viewHeightInPixels * 4, sizeof(unsigned char));
408         RetainPtr<CGContextRef> context = CGBitmapContextCreate(rgba, viewWidthInPixels, viewHeightInPixels, 8, 4 * viewWidthInPixels, colorSpace.get(), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
409         CGContextDrawImage(context.get(), CGRectMake(0, 0, viewWidthInPixels, viewHeightInPixels), cgImage.get());
410
411         NSInteger pixelIndex = getPixelIndex(0, 0, viewWidthInPixels);
412         EXPECT_EQ(0, rgba[pixelIndex]);
413         EXPECT_EQ(0, rgba[pixelIndex + 1]);
414         EXPECT_EQ(255, rgba[pixelIndex + 2]);
415
416         free(rgba);
417         
418         isDone = true;
419     }];
420     
421     TestWebKitAPI::Util::run(&isDone);
422 }