Storage Access API: UI process should update network process about granted access
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / WKWebsiteDataStore.mm
1 /*
2  * Copyright (C) 2014-2017 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 "WKWebsiteDataStoreInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "WKHTTPCookieStoreInternal.h"
32 #import "WKNSArray.h"
33 #import "WKWebsiteDataRecordInternal.h"
34 #import "WebResourceLoadStatisticsStore.h"
35 #import "WebResourceLoadStatisticsTelemetry.h"
36 #import "WebsiteDataFetchOption.h"
37 #import "_WKWebsiteDataStoreConfiguration.h"
38 #import <WebCore/URL.h>
39 #import <wtf/BlockPtr.h>
40
41 using namespace WebCore;
42
43 @implementation WKWebsiteDataStore
44
45 + (WKWebsiteDataStore *)defaultDataStore
46 {
47     return WebKit::wrapper(API::WebsiteDataStore::defaultDataStore().get());
48 }
49
50 + (WKWebsiteDataStore *)nonPersistentDataStore
51 {
52     return [WebKit::wrapper(API::WebsiteDataStore::createNonPersistentDataStore().leakRef()) autorelease];
53 }
54
55 - (void)dealloc
56 {
57     _websiteDataStore->API::WebsiteDataStore::~WebsiteDataStore();
58
59     [super dealloc];
60 }
61
62 - (instancetype)initWithCoder:(NSCoder *)coder
63 {
64     if (!(self = [super init]))
65         return nil;
66
67     RetainPtr<WKWebsiteDataStore> dataStore;
68     if ([coder decodeBoolForKey:@"isDefaultDataStore"])
69         dataStore = [WKWebsiteDataStore defaultDataStore];
70     else
71         dataStore = [WKWebsiteDataStore nonPersistentDataStore];
72
73     [self release];
74
75     return dataStore.leakRef();
76 }
77
78 - (void)encodeWithCoder:(NSCoder *)coder
79 {
80     if (self == [WKWebsiteDataStore defaultDataStore]) {
81         [coder encodeBool:YES forKey:@"isDefaultDataStore"];
82         return;
83     }
84
85     ASSERT(!self.persistent);
86 }
87
88 - (BOOL)isPersistent
89 {
90     return _websiteDataStore->isPersistent();
91 }
92
93 + (NSSet *)allWebsiteDataTypes
94 {
95     static dispatch_once_t onceToken;
96     static NSSet *allWebsiteDataTypes;
97     dispatch_once(&onceToken, ^{
98         allWebsiteDataTypes = [[NSSet alloc] initWithArray:@[ WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeFetchCache, WKWebsiteDataTypeMemoryCache, WKWebsiteDataTypeOfflineWebApplicationCache, WKWebsiteDataTypeCookies, WKWebsiteDataTypeSessionStorage, WKWebsiteDataTypeLocalStorage, WKWebsiteDataTypeIndexedDBDatabases, WKWebsiteDataTypeServiceWorkerRegistrations, WKWebsiteDataTypeWebSQLDatabases ]];
99     });
100
101     return allWebsiteDataTypes;
102 }
103
104 - (WKHTTPCookieStore *)httpCookieStore
105 {
106     return WebKit::wrapper(_websiteDataStore->httpCookieStore());
107 }
108
109 static std::chrono::system_clock::time_point toSystemClockTime(NSDate *date)
110 {
111     ASSERT(date);
112     using namespace std::chrono;
113
114     return system_clock::time_point(duration_cast<system_clock::duration>(duration<double>(date.timeIntervalSince1970)));
115 }
116
117 - (void)fetchDataRecordsOfTypes:(NSSet *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler
118 {
119     [self _fetchDataRecordsOfTypes:dataTypes withOptions:0 completionHandler:completionHandler];
120 }
121
122 - (void)removeDataOfTypes:(NSSet *)dataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler
123 {
124     auto completionHandlerCopy = makeBlockPtr(completionHandler);
125     _websiteDataStore->websiteDataStore().removeData(WebKit::toWebsiteDataTypes(dataTypes), toSystemClockTime(date ? date : [NSDate distantPast]), [completionHandlerCopy] {
126         completionHandlerCopy();
127     });
128 }
129
130 static Vector<WebKit::WebsiteDataRecord> toWebsiteDataRecords(NSArray *dataRecords)
131 {
132     Vector<WebKit::WebsiteDataRecord> result;
133
134     for (WKWebsiteDataRecord *dataRecord in dataRecords)
135         result.append(dataRecord->_websiteDataRecord->websiteDataRecord());
136
137     return result;
138 }
139
140 - (void)removeDataOfTypes:(NSSet *)dataTypes forDataRecords:(NSArray *)dataRecords completionHandler:(void (^)(void))completionHandler
141 {
142     auto completionHandlerCopy = makeBlockPtr(completionHandler);
143
144     _websiteDataStore->websiteDataStore().removeData(WebKit::toWebsiteDataTypes(dataTypes), toWebsiteDataRecords(dataRecords), [completionHandlerCopy] {
145         completionHandlerCopy();
146     });
147 }
148
149 #pragma mark WKObject protocol implementation
150
151 - (API::Object&)_apiObject
152 {
153     return *_websiteDataStore;
154 }
155
156 @end
157
158 @implementation WKWebsiteDataStore (WKPrivate)
159
160 + (NSSet<NSString *> *)_allWebsiteDataTypesIncludingPrivate
161 {
162     static dispatch_once_t onceToken;
163     static NSSet *allWebsiteDataTypes;
164     dispatch_once(&onceToken, ^ {
165         auto *privateTypes = @[_WKWebsiteDataTypeHSTSCache, _WKWebsiteDataTypeMediaKeys, _WKWebsiteDataTypeSearchFieldRecentSearches, _WKWebsiteDataTypeResourceLoadStatistics, _WKWebsiteDataTypeCredentials
166 #if !TARGET_OS_IPHONE
167         , _WKWebsiteDataTypePlugInData
168 #endif
169         ];
170
171         allWebsiteDataTypes = [[[self allWebsiteDataTypes] setByAddingObjectsFromArray:privateTypes] retain];
172     });
173
174     return allWebsiteDataTypes;
175 }
176
177 + (BOOL)_defaultDataStoreExists
178 {
179     return API::WebsiteDataStore::defaultDataStoreExists();
180 }
181
182 - (instancetype)_initWithConfiguration:(_WKWebsiteDataStoreConfiguration *)configuration
183 {
184     if (!(self = [super init]))
185         return nil;
186
187     auto config = API::WebsiteDataStore::defaultDataStoreConfiguration();
188
189     if (configuration._webStorageDirectory)
190         config.localStorageDirectory = configuration._webStorageDirectory.path;
191     if (configuration._webSQLDatabaseDirectory)
192         config.webSQLDatabaseDirectory = configuration._webSQLDatabaseDirectory.path;
193     if (configuration._indexedDBDatabaseDirectory)
194         config.indexedDBDatabaseDirectory = configuration._indexedDBDatabaseDirectory.path;
195     if (configuration._cookieStorageFile)
196         config.cookieStorageFile = configuration._cookieStorageFile.path;
197     if (configuration._resourceLoadStatisticsDirectory)
198         config.resourceLoadStatisticsDirectory = configuration._resourceLoadStatisticsDirectory.path;
199
200     API::Object::constructInWrapper<API::WebsiteDataStore>(self, config, PAL::SessionID::generatePersistentSessionID());
201
202     return self;
203 }
204
205 - (void)_fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes withOptions:(_WKWebsiteDataStoreFetchOptions)options completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler
206 {
207     auto completionHandlerCopy = makeBlockPtr(completionHandler);
208
209     OptionSet<WebKit::WebsiteDataFetchOption> fetchOptions;
210     if (options & _WKWebsiteDataStoreFetchOptionComputeSizes)
211         fetchOptions |= WebKit::WebsiteDataFetchOption::ComputeSizes;
212
213     _websiteDataStore->websiteDataStore().fetchData(WebKit::toWebsiteDataTypes(dataTypes), fetchOptions, [completionHandlerCopy = WTFMove(completionHandlerCopy)](auto websiteDataRecords) {
214         Vector<RefPtr<API::Object>> elements;
215         elements.reserveInitialCapacity(websiteDataRecords.size());
216
217         for (auto& websiteDataRecord : websiteDataRecords)
218             elements.uncheckedAppend(API::WebsiteDataRecord::create(WTFMove(websiteDataRecord)));
219
220         completionHandlerCopy(wrapper(API::Array::create(WTFMove(elements))));
221     });
222 }
223
224 - (BOOL)_resourceLoadStatisticsEnabled
225 {
226     return _websiteDataStore->websiteDataStore().resourceLoadStatisticsEnabled();
227 }
228
229 - (void)_setResourceLoadStatisticsEnabled:(BOOL)enabled
230 {
231     _websiteDataStore->websiteDataStore().setResourceLoadStatisticsEnabled(enabled);
232 }
233
234 - (NSUInteger)_cacheStoragePerOriginQuota
235 {
236     return _websiteDataStore->websiteDataStore().cacheStoragePerOriginQuota();
237 }
238
239 - (void)_setCacheStoragePerOriginQuota:(NSUInteger)size
240 {
241     _websiteDataStore->websiteDataStore().setCacheStoragePerOriginQuota(size);
242 }
243
244 - (NSString *)_cacheStorageDirectory
245 {
246     return _websiteDataStore->websiteDataStore().cacheStorageDirectory();
247 }
248
249 - (void)_setCacheStorageDirectory:(NSString *)directory
250 {
251     _websiteDataStore->websiteDataStore().setCacheStorageDirectory(directory);
252 }
253
254 - (void)_setBoundInterfaceIdentifier:(NSString *)identifier
255 {
256     _websiteDataStore->websiteDataStore().setBoundInterfaceIdentifier(identifier);
257 }
258
259 - (NSString *)_boundInterfaceIdentifier
260 {
261     return _websiteDataStore->websiteDataStore().boundInterfaceIdentifier();
262 }
263
264 - (void)_setAllowsCellularAccess:(BOOL)allows
265 {
266     _websiteDataStore->websiteDataStore().setAllowsCellularAccess(allows ? WebKit::AllowsCellularAccess::Yes : WebKit::AllowsCellularAccess::No);
267 }
268
269 - (BOOL)_allowsCellularAccess
270 {
271     return _websiteDataStore->websiteDataStore().allowsCellularAccess() == WebKit::AllowsCellularAccess::Yes;
272 }
273
274 - (void)_resourceLoadStatisticsSetLastSeen:(double)seconds forHost:(NSString *)host
275 {
276     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
277     if (!store)
278         return;
279     
280     store->setLastSeen(URL(URL(), host), Seconds { seconds });
281 }
282
283 - (void)_resourceLoadStatisticsSetIsPrevalentResource:(BOOL)value forHost:(NSString *)host
284 {
285     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
286     if (!store)
287         return;
288
289     if (value)
290         store->setPrevalentResource(URL(URL(), host));
291     else
292         store->clearPrevalentResource(URL(URL(), host));
293 }
294
295 - (void)_resourceLoadStatisticsIsPrevalentResource:(NSString *)host completionHandler:(void (^)(BOOL))completionHandler
296 {
297     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
298     if (!store) {
299         completionHandler(NO);
300         return;
301     }
302
303     auto completionHandlerCopy = makeBlockPtr(completionHandler);
304     store->isPrevalentResource(URL(URL(), host), [completionHandlerCopy](bool isPrevalentResource) {
305         completionHandlerCopy(isPrevalentResource);
306     });
307 }
308
309 - (void)_resourceLoadStatisticsIsRegisteredAsSubFrameUnder:(NSString *)subFrameHost topFrameHost:(NSString *)topFrameHost completionHandler:(void (^)(BOOL))completionHandler
310 {
311     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
312     if (!store) {
313         completionHandler(NO);
314         return;
315     }
316     
317     auto completionHandlerCopy = makeBlockPtr(completionHandler);
318     store->isRegisteredAsSubFrameUnder(URL(URL(), subFrameHost), URL(URL(), topFrameHost), [completionHandlerCopy](bool isRegisteredAsSubFrameUnder) {
319         completionHandlerCopy(isRegisteredAsSubFrameUnder);
320     });
321 }
322
323 - (void)_resourceLoadStatisticsIsRegisteredAsRedirectingTo:(NSString *)hostRedirectedFrom hostRedirectedTo:(NSString *)hostRedirectedTo completionHandler:(void (^)(BOOL))completionHandler
324 {
325     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
326     if (!store) {
327         completionHandler(NO);
328         return;
329     }
330     
331     auto completionHandlerCopy = makeBlockPtr(completionHandler);
332     store->isRegisteredAsRedirectingTo(URL(URL(), hostRedirectedFrom), URL(URL(), hostRedirectedTo), [completionHandlerCopy](bool isRegisteredAsRedirectingTo) {
333         completionHandlerCopy(isRegisteredAsRedirectingTo);
334     });
335 }
336
337 - (void)_resourceLoadStatisticsSetHadUserInteraction:(BOOL)value forHost:(NSString *)host
338 {
339     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
340     if (!store)
341         return;
342
343     if (value)
344         store->logUserInteraction(URL(URL(), host));
345     else
346         store->clearUserInteraction(URL(URL(), host));
347 }
348
349 - (void)_resourceLoadStatisticsSetHasHadNonRecentUserInteractionForHost:(NSString *)host
350 {
351     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
352     if (!store)
353         return;
354     
355     store->logNonRecentUserInteraction(URL(URL(), host));
356 }
357
358 - (void)_resourceLoadStatisticsHadUserInteraction:(NSString *)host completionHandler:(void (^)(BOOL))completionHandler
359 {
360     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
361     if (!store) {
362         completionHandler(NO);
363         return;
364     }
365
366     auto completionHandlerCopy = makeBlockPtr(completionHandler);
367     store->hasHadUserInteraction(URL(URL(), host), [completionHandlerCopy](bool hasHadUserInteraction) {
368         completionHandlerCopy(hasHadUserInteraction);
369     });
370 }
371
372 - (void)_resourceLoadStatisticsSetIsGrandfathered:(BOOL)value forHost:(NSString *)host
373 {
374     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
375     if (!store)
376         return;
377
378     store->setGrandfathered(URL(URL(), host), value);
379 }
380
381 - (void)_resourceLoadStatisticsIsGrandfathered:(NSString *)host completionHandler:(void (^)(BOOL))completionHandler
382 {
383     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
384     if (!store) {
385         completionHandler(NO);
386         return;
387     }
388
389     auto completionHandlerCopy = makeBlockPtr(completionHandler);
390     store->isGrandfathered(URL(URL(), host), [completionHandlerCopy](bool isGrandfathered) {
391         completionHandlerCopy(isGrandfathered);
392     });
393 }
394
395 - (void)_resourceLoadStatisticsSetSubframeUnderTopFrameOrigin:(NSString *)topFrameHostName forHost:(NSString *)host
396 {
397     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
398     if (!store)
399         return;
400
401     store->setSubframeUnderTopFrameOrigin(URL(URL(), host), URL(URL(), topFrameHostName));
402 }
403
404 - (void)_resourceLoadStatisticsSetSubresourceUnderTopFrameOrigin:(NSString *)topFrameHostName forHost:(NSString *)host
405 {
406     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
407     if (!store)
408         return;
409
410     store->setSubresourceUnderTopFrameOrigin(URL(URL(), host), URL(URL(), topFrameHostName));
411 }
412
413 - (void)_resourceLoadStatisticsSetSubresourceUniqueRedirectTo:(NSString *)hostNameRedirectedTo forHost:(NSString *)host
414 {
415     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
416     if (!store)
417         return;
418
419     store->setSubresourceUniqueRedirectTo(URL(URL(), host), URL(URL(), hostNameRedirectedTo));
420 }
421
422 - (void)_resourceLoadStatisticsSetTimeToLiveUserInteraction:(double)seconds
423 {
424     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
425     if (!store)
426         return;
427
428     store->setTimeToLiveUserInteraction(Seconds { seconds });
429 }
430
431 - (void)_resourceLoadStatisticsSetTimeToLiveCookiePartitionFree:(double)seconds
432 {
433     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
434     if (!store)
435         return;
436
437     store->setTimeToLiveCookiePartitionFree(Seconds { seconds });
438 }
439
440 - (void)_resourceLoadStatisticsSetMinimumTimeBetweenDataRecordsRemoval:(double)seconds
441 {
442     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
443     if (!store)
444         return;
445
446     store->setMinimumTimeBetweenDataRecordsRemoval(Seconds { seconds });
447 }
448
449 - (void)_resourceLoadStatisticsSetGrandfatheringTime:(double)seconds
450 {
451     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
452     if (!store)
453         return;
454
455     store->setGrandfatheringTime(Seconds {seconds });
456 }
457
458 - (void)_resourceLoadStatisticsSetMaxStatisticsEntries:(size_t)entries
459 {
460     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
461     if (!store)
462         return;
463
464     store->setMaxStatisticsEntries(entries);
465 }
466
467 - (void)_resourceLoadStatisticsSetPruneEntriesDownTo:(size_t)entries
468 {
469     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
470     if (!store)
471         return;
472
473     store->setPruneEntriesDownTo(entries);
474 }
475
476 - (void)_resourceLoadStatisticsProcessStatisticsAndDataRecords
477 {
478     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
479     if (!store)
480         return;
481
482     store->scheduleStatisticsAndDataRecordsProcessing();
483 }
484
485 - (void)_resourceLoadStatisticsUpdateCookiePartitioning
486 {
487     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
488     if (!store)
489         return;
490
491     store->scheduleCookiePartitioningUpdate();
492 }
493
494 - (void)_resourceLoadStatisticsSetShouldPartitionCookies:(BOOL)value forHost:(NSString *)host
495 {
496     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
497     if (!store)
498         return;
499
500     if (value)
501         store->scheduleCookiePartitioningUpdateForDomains({ host }, { }, { }, WebKit::ShouldClearFirst::No);
502     else
503         store->scheduleClearPartitioningStateForDomains({ host });
504 }
505
506 - (void)_resourceLoadStatisticsSubmitTelemetry
507 {
508     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
509     if (!store)
510         return;
511
512     store->submitTelemetry();
513 }
514
515 - (void)_resourceLoadStatisticsSetNotifyPagesWhenDataRecordsWereScanned:(BOOL)value
516 {
517     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
518     if (!store)
519         return;
520
521     store->setNotifyPagesWhenDataRecordsWereScanned(value);
522 }
523
524 - (void)_resourceLoadStatisticsSetShouldClassifyResourcesBeforeDataRecordsRemoval:(BOOL)value
525 {
526     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
527     if (!store)
528         return;
529
530     store->setShouldClassifyResourcesBeforeDataRecordsRemoval(value);
531 }
532
533 - (void)_resourceLoadStatisticsSetNotifyPagesWhenTelemetryWasCaptured:(BOOL)value
534 {
535     WebKit::WebResourceLoadStatisticsTelemetry::setNotifyPagesWhenTelemetryWasCaptured(value);
536 }
537
538 - (void)_resourceLoadStatisticsSetShouldSubmitTelemetry:(BOOL)value
539 {
540     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
541     if (!store)
542         return;
543
544     store->setShouldSubmitTelemetry(value);
545 }
546
547 - (void)_resourceLoadStatisticsClearInMemoryAndPersistentStore
548 {
549     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
550     if (!store)
551         return;
552
553     store->scheduleClearInMemoryAndPersistent(WebKit::WebResourceLoadStatisticsStore::ShouldGrandfather::Yes);
554 }
555
556 - (void)_resourceLoadStatisticsClearInMemoryAndPersistentStoreModifiedSinceHours:(unsigned)hours
557 {
558     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
559     if (!store)
560         return;
561
562     store->scheduleClearInMemoryAndPersistent(std::chrono::system_clock::now() - std::chrono::hours(hours), WebKit::WebResourceLoadStatisticsStore::ShouldGrandfather::Yes);
563 }
564
565 - (void)_resourceLoadStatisticsResetToConsistentState
566 {
567     WebKit::WebResourceLoadStatisticsTelemetry::setNotifyPagesWhenTelemetryWasCaptured(false);
568
569     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
570     if (!store)
571         return;
572
573     store->resetParametersToDefaultValues();
574     store->scheduleClearInMemory();
575 }
576
577 - (void)_setResourceLoadStatisticsTestingCallback:(void (^)(WKWebsiteDataStore *, NSString *))callback
578 {
579     if (callback) {
580         _websiteDataStore->websiteDataStore().enableResourceLoadStatisticsAndSetTestingCallback([callback = makeBlockPtr(callback), self](const String& event) {
581             callback(self, (NSString *)event);
582         });
583         return;
584     }
585
586     auto* store = _websiteDataStore->websiteDataStore().resourceLoadStatistics();
587     if (!store)
588         return;
589
590     store->setStatisticsTestingCallback(nullptr);
591 }
592
593 @end
594
595 #endif // WK_API_ENABLED