TestController platformAdjustContext should use provided WKContext for Mac
[WebKit-https.git] / Tools / WebKitTestRunner / cocoa / TestControllerCocoa.mm
1 /*
2  * Copyright (C) 2015-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 #import "TestController.h"
28
29 #import "CrashReporterInfo.h"
30 #import "PlatformWebView.h"
31 #import "StringFunctions.h"
32 #import "TestInvocation.h"
33 #import "TestRunnerWKWebView.h"
34 #import <Foundation/Foundation.h>
35 #import <WebKit/WKContextConfigurationRef.h>
36 #import <WebKit/WKCookieManager.h>
37 #import <WebKit/WKPreferencesRefPrivate.h>
38 #import <WebKit/WKProcessPoolPrivate.h>
39 #import <WebKit/WKStringCF.h>
40 #import <WebKit/WKUserContentControllerPrivate.h>
41 #import <WebKit/WKWebView.h>
42 #import <WebKit/WKWebViewConfiguration.h>
43 #import <WebKit/WKWebViewConfigurationPrivate.h>
44 #import <WebKit/WKWebViewPrivate.h>
45 #import <WebKit/WKWebsiteDataRecordPrivate.h>
46 #import <WebKit/WKWebsiteDataStorePrivate.h>
47 #import <WebKit/WKWebsiteDataStoreRef.h>
48 #import <WebKit/_WKProcessPoolConfiguration.h>
49 #import <WebKit/_WKUserContentExtensionStore.h>
50 #import <WebKit/_WKUserContentExtensionStorePrivate.h>
51 #import <wtf/MainThread.h>
52
53 namespace WTR {
54
55 #if WK_API_ENABLED
56 static NSString* toNSString(WKStringRef string)
57 {
58     return [NSString stringWithCString:toWTFString(string).utf8().data()];
59 }
60 #endif
61
62 static WKWebViewConfiguration *globalWebViewConfiguration;
63
64 void initializeWebViewConfiguration(const char* libraryPath, WKStringRef injectedBundlePath, WKContextRef context, WKContextConfigurationRef contextConfiguration)
65 {
66 #if WK_API_ENABLED
67     [globalWebViewConfiguration release];
68     globalWebViewConfiguration = [[WKWebViewConfiguration alloc] init];
69
70     globalWebViewConfiguration.processPool = (WKProcessPool *)context;
71     globalWebViewConfiguration.websiteDataStore = (WKWebsiteDataStore *)WKContextGetWebsiteDataStore(context);
72     globalWebViewConfiguration._allowUniversalAccessFromFileURLs = YES;
73
74 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || PLATFORM(IOS)
75     globalWebViewConfiguration._applePayEnabled = YES;
76 #endif
77
78 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || PLATFORM(IOS)
79     WKCookieManagerSetCookieStoragePartitioningEnabled(WKContextGetCookieManager(context), true);
80 #endif
81
82     WKWebsiteDataStore* poolWebsiteDataStore = (WKWebsiteDataStore *)WKContextGetWebsiteDataStore((WKContextRef)globalWebViewConfiguration.processPool);
83     [poolWebsiteDataStore _setCacheStoragePerOriginQuota: 400 * 1024];
84     if (libraryPath) {
85         String cacheStorageDirectory = String(libraryPath) + '/' + "CacheStorage";
86         [poolWebsiteDataStore _setCacheStorageDirectory: cacheStorageDirectory];
87     }
88
89     [globalWebViewConfiguration.websiteDataStore _setResourceLoadStatisticsEnabled:YES];
90     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetShouldSubmitTelemetry:NO];
91
92 #if PLATFORM(IOS)
93     globalWebViewConfiguration.allowsInlineMediaPlayback = YES;
94     globalWebViewConfiguration._inlineMediaPlaybackRequiresPlaysInlineAttribute = NO;
95     globalWebViewConfiguration._invisibleAutoplayNotPermitted = NO;
96     globalWebViewConfiguration._mediaDataLoadsAutomatically = YES;
97     globalWebViewConfiguration.requiresUserActionForMediaPlayback = NO;
98 #endif
99     globalWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
100 #endif
101 }
102
103 void TestController::cocoaPlatformInitialize()
104 {
105     const char* dumpRenderTreeTemp = libraryPathForTesting();
106     if (!dumpRenderTreeTemp)
107         return;
108
109     String resourceLoadStatisticsFolder = String(dumpRenderTreeTemp) + '/' + "ResourceLoadStatistics";
110     [[NSFileManager defaultManager] createDirectoryAtPath:resourceLoadStatisticsFolder withIntermediateDirectories:YES attributes:nil error: nil];
111     String fullBrowsingSessionResourceLog = resourceLoadStatisticsFolder + '/' + "full_browsing_session_resourceLog.plist";
112     NSDictionary *resourceLogPlist = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:1], @"version", nil];
113     if (![resourceLogPlist writeToFile:fullBrowsingSessionResourceLog atomically:YES])
114         WTFCrash();
115     [resourceLogPlist release];
116 }
117
118 WKContextRef TestController::platformContext()
119 {
120 #if WK_API_ENABLED
121     return (WKContextRef)globalWebViewConfiguration.processPool;
122 #else
123     return nullptr;
124 #endif
125 }
126
127 WKPreferencesRef TestController::platformPreferences()
128 {
129 #if WK_API_ENABLED
130     return (WKPreferencesRef)globalWebViewConfiguration.preferences;
131 #else
132     return nullptr;
133 #endif
134 }
135
136 void TestController::platformCreateWebView(WKPageConfigurationRef, const TestOptions& options)
137 {
138 #if WK_API_ENABLED
139     RetainPtr<WKWebViewConfiguration> copiedConfiguration = adoptNS([globalWebViewConfiguration copy]);
140
141 #if PLATFORM(IOS)
142     if (options.useDataDetection)
143         [copiedConfiguration setDataDetectorTypes:WKDataDetectorTypeAll];
144     if (options.ignoresViewportScaleLimits)
145         [copiedConfiguration setIgnoresViewportScaleLimits:YES];
146     if (options.useCharacterSelectionGranularity)
147         [copiedConfiguration setSelectionGranularity:WKSelectionGranularityCharacter];
148     if (options.useCharacterSelectionGranularity)
149         [copiedConfiguration setSelectionGranularity:WKSelectionGranularityCharacter];
150 #endif
151
152     if (options.enableAttachmentElement)
153         [copiedConfiguration _setAttachmentElementEnabled: YES];
154
155     m_mainWebView = std::make_unique<PlatformWebView>(copiedConfiguration.get(), options);
156 #else
157     m_mainWebView = std::make_unique<PlatformWebView>(globalWebViewConfiguration, options);
158 #endif
159 }
160
161 PlatformWebView* TestController::platformCreateOtherPage(PlatformWebView* parentView, WKPageConfigurationRef, const TestOptions& options)
162 {
163 #if WK_API_ENABLED
164     WKWebViewConfiguration *newConfiguration = [[globalWebViewConfiguration copy] autorelease];
165     newConfiguration._relatedWebView = static_cast<WKWebView*>(parentView->platformView());
166     return new PlatformWebView(newConfiguration, options);
167 #else
168     return nullptr;
169 #endif
170 }
171
172 WKContextRef TestController::platformAdjustContext(WKContextRef context, WKContextConfigurationRef contextConfiguration)
173 {
174 #if WK_API_ENABLED
175     initializeWebViewConfiguration(libraryPathForTesting(), injectedBundlePath(), context, contextConfiguration);
176     return (WKContextRef)globalWebViewConfiguration.processPool;
177 #else
178     return nullptr;
179 #endif
180 }
181
182 void TestController::platformRunUntil(bool& done, double timeout)
183 {
184     NSDate *endDate = (timeout > 0) ? [NSDate dateWithTimeIntervalSinceNow:timeout] : [NSDate distantFuture];
185
186     while (!done && [endDate compare:[NSDate date]] == NSOrderedDescending)
187         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:endDate];
188 }
189
190 void TestController::cocoaResetStateToConsistentValues()
191 {
192 #if WK_API_ENABLED
193     __block bool doneRemoving = false;
194     [[_WKUserContentExtensionStore defaultStore] removeContentExtensionForIdentifier:@"TestContentExtensions" completionHandler:^(NSError *error) {
195         doneRemoving = true;
196     }];
197     platformRunUntil(doneRemoving, 0);
198     [[_WKUserContentExtensionStore defaultStore] _removeAllContentExtensions];
199
200     if (PlatformWebView* webView = mainWebView())
201         [webView->platformView().configuration.userContentController _removeAllUserContentFilters];
202 #endif
203 }
204
205 void TestController::platformWillRunTest(const TestInvocation& testInvocation)
206 {
207     setCrashReportApplicationSpecificInformationToURL(testInvocation.url());
208 }
209
210 static NSString * const WebArchivePboardType = @"Apple Web Archive pasteboard type";
211 static NSString * const WebSubresourcesKey = @"WebSubresources";
212 static NSString * const WebSubframeArchivesKey = @"WebResourceMIMEType like 'image*'";
213
214 unsigned TestController::imageCountInGeneralPasteboard() const
215 {
216 #if PLATFORM(MAC)
217     NSData *data = [[NSPasteboard generalPasteboard] dataForType:WebArchivePboardType];
218 #elif PLATFORM(IOS)
219     NSData *data = [[UIPasteboard generalPasteboard] valueForPasteboardType:WebArchivePboardType];
220 #endif
221     if (!data)
222         return 0;
223     
224     NSError *error = nil;
225     id webArchive = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:NULL error:&error];
226     if (error) {
227         NSLog(@"Encountered error while serializing Web Archive pasteboard data: %@", error);
228         return 0;
229     }
230     
231     NSArray *subItems = [NSArray arrayWithArray:[webArchive objectForKey:WebSubresourcesKey]];
232     NSPredicate *predicate = [NSPredicate predicateWithFormat:WebSubframeArchivesKey];
233     NSArray *imagesArray = [subItems filteredArrayUsingPredicate:predicate];
234     
235     if (!imagesArray)
236         return 0;
237     
238     return imagesArray.count;
239 }
240
241 void TestController::removeAllSessionCredentials()
242 {
243 #if WK_API_ENABLED
244     auto types = adoptNS([[NSSet alloc] initWithObjects:_WKWebsiteDataTypeCredentials, nil]);
245     [globalWebViewConfiguration.websiteDataStore removeDataOfTypes:types.get() modifiedSince:[NSDate distantPast] completionHandler:^() {
246         m_currentInvocation->didRemoveAllSessionCredentials();
247     }];
248 #endif
249 }
250
251 #if WK_API_ENABLED
252 void TestController::setStatisticsLastSeen(WKStringRef hostName, double seconds)
253 {
254     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetLastSeen:seconds forHost:toNSString(hostName)];
255 }
256     
257 void TestController::setStatisticsPrevalentResource(WKStringRef hostName, bool value)
258 {
259     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetIsPrevalentResource:value forHost:toNSString(hostName)];
260 }
261
262 bool TestController::isStatisticsPrevalentResource(WKStringRef hostName)
263 {
264     __block bool isDataReady = false;
265     __block bool isPrevalentResource = false;
266     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsIsPrevalentResource:toNSString(hostName) completionHandler:^(BOOL _isPrevalentResource) {
267         isPrevalentResource = _isPrevalentResource;
268         isDataReady = true;
269     }];
270     platformRunUntil(isDataReady, 0);
271
272     return isPrevalentResource;
273 }
274
275 bool TestController::isStatisticsRegisteredAsSubFrameUnder(WKStringRef subFrameHost, WKStringRef topFrameHost)
276 {
277     __block bool isDataReady = false;
278     __block bool isRegisteredAsSubFrameUnder = false;
279     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsIsRegisteredAsSubFrameUnder:toNSString(subFrameHost) topFrameHost:toNSString(topFrameHost) completionHandler:^(BOOL _isRegisteredAsSubFrameUnder) {
280         isRegisteredAsSubFrameUnder = _isRegisteredAsSubFrameUnder;
281         isDataReady = true;
282     }];
283     platformRunUntil(isDataReady, 0);
284     
285     return isRegisteredAsSubFrameUnder;
286 }
287
288 bool TestController::isStatisticsRegisteredAsRedirectingTo(WKStringRef hostRedirectedFrom, WKStringRef hostRedirectedTo)
289 {
290     __block bool isDataReady = false;
291     __block bool isRegisteredAsRedirectingTo = false;
292     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsIsRegisteredAsRedirectingTo:toNSString(hostRedirectedFrom) hostRedirectedTo:toNSString(hostRedirectedTo) completionHandler:^(BOOL _isRegisteredAsRedirectingTo) {
293         isRegisteredAsRedirectingTo = _isRegisteredAsRedirectingTo;
294         isDataReady = true;
295     }];
296     platformRunUntil(isDataReady, 0);
297     
298     return isRegisteredAsRedirectingTo;
299 }
300
301 void TestController::setStatisticsHasHadUserInteraction(WKStringRef hostName, bool value)
302 {
303     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetHadUserInteraction:value forHost:toNSString(hostName)];
304 }
305
306 bool TestController::isStatisticsHasHadUserInteraction(WKStringRef hostName)
307 {
308     __block bool isDataReady = false;
309     __block bool hasHadUserInteraction = false;
310     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsHadUserInteraction:toNSString(hostName) completionHandler:^(BOOL _hasHadUserInteraction) {
311         hasHadUserInteraction = _hasHadUserInteraction;
312         isDataReady = true;
313     }];
314     platformRunUntil(isDataReady, 0);
315
316     return hasHadUserInteraction;
317 }
318
319 void TestController::setStatisticsGrandfathered(WKStringRef hostName, bool value)
320 {
321     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetIsGrandfathered:value forHost:toNSString(hostName)];
322 }
323
324 bool TestController::isStatisticsGrandfathered(WKStringRef hostName)
325 {
326     __block bool isDataReady = false;
327     __block bool isGrandfathered = false;
328     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsIsGrandfathered:toNSString(hostName) completionHandler:^(BOOL _isGrandfathered) {
329         isGrandfathered = _isGrandfathered;
330         isDataReady = true;
331     }];
332     platformRunUntil(isDataReady, 0);
333
334     return isGrandfathered;
335 }
336
337 void TestController::setStatisticsSubframeUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName)
338 {
339     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetSubframeUnderTopFrameOrigin:toNSString(topFrameHostName) forHost:toNSString(hostName)];
340 }
341
342 void TestController::setStatisticsSubresourceUnderTopFrameOrigin(WKStringRef hostName, WKStringRef topFrameHostName)
343 {
344     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetSubresourceUnderTopFrameOrigin:toNSString(topFrameHostName) forHost:toNSString(hostName)];
345 }
346
347 void TestController::setStatisticsSubresourceUniqueRedirectTo(WKStringRef hostName, WKStringRef hostNameRedirectedTo)
348 {
349     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetSubresourceUniqueRedirectTo:toNSString(hostNameRedirectedTo) forHost:toNSString(hostName)];
350 }
351
352 void TestController::setStatisticsTimeToLiveUserInteraction(double seconds)
353 {
354     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetTimeToLiveUserInteraction:seconds];
355 }
356
357 void TestController::setStatisticsTimeToLiveCookiePartitionFree(double seconds)
358 {
359     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetTimeToLiveCookiePartitionFree:seconds];
360 }
361
362 void TestController::statisticsProcessStatisticsAndDataRecords()
363 {
364     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsProcessStatisticsAndDataRecords];
365 }
366
367 void TestController::statisticsUpdateCookiePartitioning()
368 {
369     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsUpdateCookiePartitioning];
370 }
371
372 void TestController::statisticsSetShouldPartitionCookiesForHost(WKStringRef hostName, bool value)
373 {
374     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetShouldPartitionCookies:value forHost:toNSString(hostName)];
375 }
376
377 void TestController::statisticsSubmitTelemetry()
378 {
379     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSubmitTelemetry];
380 }
381
382 void TestController::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value)
383 {
384     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetNotifyPagesWhenDataRecordsWereScanned:value];
385 }
386
387 void TestController::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value)
388 {
389     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetShouldClassifyResourcesBeforeDataRecordsRemoval:value];
390 }
391
392 void TestController::setStatisticsNotifyPagesWhenTelemetryWasCaptured(bool value)
393 {
394     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetNotifyPagesWhenTelemetryWasCaptured:value];
395 }
396
397 void TestController::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds)
398 {
399     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetMinimumTimeBetweenDataRecordsRemoval:seconds];
400 }
401
402 void TestController::setStatisticsGrandfatheringTime(double seconds)
403 {
404     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetGrandfatheringTime:seconds];
405 }
406
407 void TestController::setStatisticsMaxStatisticsEntries(unsigned entries)
408 {
409     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetMaxStatisticsEntries:entries];
410 }
411     
412 void TestController::setStatisticsPruneEntriesDownTo(unsigned entries)
413 {
414     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsSetPruneEntriesDownTo:entries];
415 }
416     
417 void TestController::statisticsClearInMemoryAndPersistentStore()
418 {
419     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsClearInMemoryAndPersistentStore];
420 }
421
422 void TestController::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours)
423 {
424     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours:hours];
425 }
426
427 void TestController::statisticsClearThroughWebsiteDataRemoval()
428 {
429 #if WK_API_ENABLED
430     auto types = adoptNS([[NSSet alloc] initWithObjects:_WKWebsiteDataTypeResourceLoadStatistics, nil]);
431     [globalWebViewConfiguration.websiteDataStore removeDataOfTypes:types.get() modifiedSince:[NSDate distantPast] completionHandler:^() {
432         m_currentInvocation->didClearStatisticsThroughWebsiteDataRemoval();
433     }];
434 #endif
435 }
436
437 void TestController::statisticsResetToConsistentState()
438 {
439     [globalWebViewConfiguration.websiteDataStore _resourceLoadStatisticsResetToConsistentState];
440 }
441 #endif // WK_API_ENABLED
442
443 } // namespace WTR