298b44e2f18f868d7941894d2da793abddd16261
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / WKProcessPool.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 "WKProcessPoolInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "AutomationClient.h"
32 #import "CacheModel.h"
33 #import "DownloadClient.h"
34 #import "Logging.h"
35 #import "PluginProcessManager.h"
36 #import "SandboxUtilities.h"
37 #import "UIGamepadProvider.h"
38 #import "WKObject.h"
39 #import "WKWebViewInternal.h"
40 #import "WebCertificateInfo.h"
41 #import "WebCookieManagerProxy.h"
42 #import "WebProcessMessages.h"
43 #import "WebProcessPool.h"
44 #import "_WKAutomationDelegate.h"
45 #import "_WKAutomationSessionInternal.h"
46 #import "_WKDownloadDelegate.h"
47 #import "_WKDownloadInternal.h"
48 #import "_WKProcessPoolConfigurationInternal.h"
49 #import <WebCore/CertificateInfo.h>
50 #import <WebCore/PluginData.h>
51 #import <pal/spi/cf/CFNetworkSPI.h>
52 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
53 #import <wtf/BlockPtr.h>
54 #import <wtf/RetainPtr.h>
55 #import <wtf/WeakObjCPtr.h>
56
57 #if PLATFORM(IOS_FAMILY)
58 #import <WebCore/WebCoreThreadSystemInterface.h>
59 #import "WKGeolocationProviderIOS.h"
60 #endif
61
62 static WKProcessPool *sharedProcessPool;
63
64 @implementation WKProcessPool {
65     WeakObjCPtr<id <_WKAutomationDelegate>> _automationDelegate;
66     WeakObjCPtr<id <_WKDownloadDelegate>> _downloadDelegate;
67
68     RetainPtr<_WKAutomationSession> _automationSession;
69 #if PLATFORM(IOS_FAMILY)
70     RetainPtr<WKGeolocationProviderIOS> _geolocationProvider;
71     RetainPtr<id <_WKGeolocationCoreLocationProvider>> _coreLocationProvider;
72 #endif // PLATFORM(IOS_FAMILY)
73 }
74
75 - (instancetype)_initWithConfiguration:(_WKProcessPoolConfiguration *)configuration
76 {
77     if (!(self = [super init]))
78         return nil;
79
80     API::Object::constructInWrapper<WebKit::WebProcessPool>(self, *configuration->_processPoolConfiguration);
81
82     return self;
83 }
84
85 - (instancetype)init
86 {
87     return [self _initWithConfiguration:adoptNS([[_WKProcessPoolConfiguration alloc] init]).get()];
88 }
89
90 - (void)dealloc
91 {
92     _processPool->~WebProcessPool();
93
94     [super dealloc];
95 }
96
97 + (BOOL)supportsSecureCoding
98 {
99     return YES;
100 }
101
102 - (void)encodeWithCoder:(NSCoder *)coder
103 {
104     if (self == sharedProcessPool) {
105         [coder encodeBool:YES forKey:@"isSharedProcessPool"];
106         return;
107     }
108 }
109
110 - (instancetype)initWithCoder:(NSCoder *)coder
111 {
112     if (!(self = [self init]))
113         return nil;
114
115     if ([coder decodeBoolForKey:@"isSharedProcessPool"]) {
116         [self release];
117
118         return [[WKProcessPool _sharedProcessPool] retain];
119     }
120
121     return self;
122 }
123
124 - (NSString *)description
125 {
126     return [NSString stringWithFormat:@"<%@: %p; configuration = %@>", NSStringFromClass(self.class), self, wrapper(_processPool->configuration())];
127 }
128
129 - (_WKProcessPoolConfiguration *)_configuration
130 {
131     return wrapper(_processPool->configuration().copy());
132 }
133
134 - (API::Object&)_apiObject
135 {
136     return *_processPool;
137 }
138
139 #if PLATFORM(IOS_FAMILY)
140 - (WKGeolocationProviderIOS *)_geolocationProvider
141 {
142     if (!_geolocationProvider)
143         _geolocationProvider = adoptNS([[WKGeolocationProviderIOS alloc] initWithProcessPool:*_processPool]);
144     return _geolocationProvider.get();
145 }
146 #endif // PLATFORM(IOS_FAMILY)
147
148 @end
149
150 @implementation WKProcessPool (WKPrivate)
151
152 + (WKProcessPool *)_sharedProcessPool
153 {
154     static dispatch_once_t onceToken;
155     dispatch_once(&onceToken, ^{
156         sharedProcessPool = [[WKProcessPool alloc] init];
157     });
158
159     return sharedProcessPool;
160 }
161
162 + (NSArray<WKProcessPool *> *)_allProcessPoolsForTesting
163 {
164     auto& allPools = WebKit::WebProcessPool::allProcessPools();
165     auto nsAllPools = adoptNS([[NSMutableArray alloc] initWithCapacity:allPools.size()]);
166     for (auto* pool : allPools)
167         [nsAllPools addObject:wrapper(*pool)];
168     return nsAllPools.autorelease();
169 }
170
171 + (NSURL *)_websiteDataURLForContainerWithURL:(NSURL *)containerURL
172 {
173     return [WKProcessPool _websiteDataURLForContainerWithURL:containerURL bundleIdentifierIfNotInContainer:nil];
174 }
175
176 + (NSURL *)_websiteDataURLForContainerWithURL:(NSURL *)containerURL bundleIdentifierIfNotInContainer:(NSString *)bundleIdentifier
177 {
178     NSURL *url = [containerURL URLByAppendingPathComponent:@"Library" isDirectory:YES];
179     url = [url URLByAppendingPathComponent:@"WebKit" isDirectory:YES];
180
181     if (!WebKit::processHasContainer() && bundleIdentifier)
182         url = [url URLByAppendingPathComponent:bundleIdentifier isDirectory:YES];
183
184     return [url URLByAppendingPathComponent:@"WebsiteData" isDirectory:YES];
185 }
186
187 - (void)_setAllowsSpecificHTTPSCertificate:(NSArray *)certificateChain forHost:(NSString *)host
188 {
189     _processPool->allowSpecificHTTPSCertificateForHost(WebKit::WebCertificateInfo::create(WebCore::CertificateInfo((__bridge CFArrayRef)certificateChain)).ptr(), host);
190 }
191
192 - (void)_registerURLSchemeServiceWorkersCanHandle:(NSString *)scheme
193 {
194     _processPool->registerURLSchemeServiceWorkersCanHandle(scheme);
195 }
196
197 - (void)_registerURLSchemeAsCanDisplayOnlyIfCanRequest:(NSString *)scheme
198 {
199     _processPool->registerURLSchemeAsCanDisplayOnlyIfCanRequest(scheme);
200 }
201
202 - (void)_setCanHandleHTTPSServerTrustEvaluation:(BOOL)value
203 {
204     _processPool->setCanHandleHTTPSServerTrustEvaluation(value);
205 }
206
207 static WebKit::HTTPCookieAcceptPolicy toHTTPCookieAcceptPolicy(NSHTTPCookieAcceptPolicy policy)
208 {
209     switch (static_cast<NSUInteger>(policy)) {
210     case NSHTTPCookieAcceptPolicyAlways:
211         return WebKit::HTTPCookieAcceptPolicyAlways;
212     case NSHTTPCookieAcceptPolicyNever:
213         return WebKit::HTTPCookieAcceptPolicyNever;
214     case NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain:
215         return WebKit::HTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
216     case NSHTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain:
217         return WebKit::HTTPCookieAcceptPolicyExclusivelyFromMainDocumentDomain;
218     }
219
220     ASSERT_NOT_REACHED();
221     return WebKit::HTTPCookieAcceptPolicyAlways;
222 }
223
224 - (void)_setCookieAcceptPolicy:(NSHTTPCookieAcceptPolicy)policy
225 {
226     _processPool->supplement<WebKit::WebCookieManagerProxy>()->setHTTPCookieAcceptPolicy(PAL::SessionID::defaultSessionID(), toHTTPCookieAcceptPolicy(policy), [](WebKit::CallbackBase::Error){});
227 }
228
229 - (id)_objectForBundleParameter:(NSString *)parameter
230 {
231     return [_processPool->bundleParameters() objectForKey:parameter];
232 }
233
234 - (void)_setObject:(id <NSCopying, NSSecureCoding>)object forBundleParameter:(NSString *)parameter
235 {
236     auto copy = adoptNS([(NSObject *)object copy]);
237     auto keyedArchiver = secureArchiver();
238
239     @try {
240         [keyedArchiver encodeObject:copy.get() forKey:@"parameter"];
241         [keyedArchiver finishEncoding];
242     } @catch (NSException *exception) {
243         LOG_ERROR("Failed to encode bundle parameter: %@", exception);
244     }
245
246     if (copy)
247         [_processPool->ensureBundleParameters() setObject:copy.get() forKey:parameter];
248     else
249         [_processPool->ensureBundleParameters() removeObjectForKey:parameter];
250
251     auto data = keyedArchiver.get().encodedData;
252     _processPool->sendToAllProcesses(Messages::WebProcess::SetInjectedBundleParameter(parameter, IPC::DataReference(static_cast<const uint8_t*>([data bytes]), [data length])));
253 }
254
255 - (void)_setObjectsForBundleParametersWithDictionary:(NSDictionary *)dictionary
256 {
257     auto copy = adoptNS([[NSDictionary alloc] initWithDictionary:dictionary copyItems:YES]);
258     auto keyedArchiver = secureArchiver();
259
260     @try {
261         [keyedArchiver encodeObject:copy.get() forKey:@"parameters"];
262         [keyedArchiver finishEncoding];
263     } @catch (NSException *exception) {
264         LOG_ERROR("Failed to encode bundle parameters: %@", exception);
265     }
266
267     [_processPool->ensureBundleParameters() setValuesForKeysWithDictionary:copy.get()];
268
269     auto data = keyedArchiver.get().encodedData;
270     _processPool->sendToAllProcesses(Messages::WebProcess::SetInjectedBundleParameters(IPC::DataReference(static_cast<const uint8_t*>([data bytes]), [data length])));
271 }
272
273 #if !TARGET_OS_IPHONE
274
275 #if ENABLE(NETSCAPE_PLUGIN_API)
276
277 static bool isPluginLoadClientPolicyAcceptable(unsigned policy)
278 {
279     return policy <= WebCore::PluginLoadClientPolicyMaximum;
280 }
281 static HashMap<String, HashMap<String, HashMap<String, uint8_t>>> toPluginLoadClientPoliciesHashMap(NSDictionary* dictionary)
282 {
283     __block HashMap<String, HashMap<String, HashMap<String, uint8_t>>> pluginLoadClientPolicies;
284     [dictionary enumerateKeysAndObjectsUsingBlock:^(id nsHost, id nsPoliciesForHost, BOOL *stop) {
285         if (![nsHost isKindOfClass:[NSString class]]) {
286             RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
287             return;
288         }
289         if (![nsPoliciesForHost isKindOfClass:[NSDictionary class]]) {
290             RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
291             return;
292         }
293
294         String host = (NSString *)nsHost;
295         __block HashMap<String, HashMap<String, uint8_t>> policiesForHost;
296         [nsPoliciesForHost enumerateKeysAndObjectsUsingBlock:^(id nsIdentifier, id nsVersionsToPolicies, BOOL *stop) {
297             if (![nsIdentifier isKindOfClass:[NSString class]]) {
298                 RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
299                 return;
300             }
301             if (![nsVersionsToPolicies isKindOfClass:[NSDictionary class]]) {
302                 RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
303                 return;
304             }
305
306             String bundleIdentifier = (NSString *)nsIdentifier;
307             __block HashMap<String, uint8_t> versionsToPolicies;
308             [nsVersionsToPolicies enumerateKeysAndObjectsUsingBlock:^(id nsVersion, id nsPolicy, BOOL *stop) {
309                 if (![nsVersion isKindOfClass:[NSString class]]) {
310                     RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
311                     return;
312                 }
313                 if (![nsPolicy isKindOfClass:[NSNumber class]]) {
314                     RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
315                     return;
316                 }
317                 unsigned policy = ((NSNumber *)nsPolicy).unsignedIntValue;
318                 if (!isPluginLoadClientPolicyAcceptable(policy)) {
319                     RELEASE_LOG_ERROR(Plugins, "_resetPluginLoadClientPolicies was called with dictionary in wrong format");
320                     return;
321                 }
322                 String version = (NSString *)nsVersion;
323                 versionsToPolicies.add(version, static_cast<uint8_t>(policy));
324             }];
325             if (!versionsToPolicies.isEmpty())
326                 policiesForHost.add(bundleIdentifier, WTFMove(versionsToPolicies));
327         }];
328         if (!policiesForHost.isEmpty())
329             pluginLoadClientPolicies.add(host, WTFMove(policiesForHost));
330     }];
331     return pluginLoadClientPolicies;
332 }
333
334 static NSDictionary *policiesHashMapToDictionary(const HashMap<String, HashMap<String, HashMap<String, uint8_t>>>& map)
335 {
336     auto policies = adoptNS([[NSMutableDictionary alloc] initWithCapacity:map.size()]);
337     for (auto& hostPair : map) {
338         NSString *host = hostPair.key;
339         policies.get()[host] = adoptNS([[NSMutableDictionary alloc] initWithCapacity:hostPair.value.size()]).get();
340         for (auto& bundleIdentifierPair : hostPair.value) {
341             NSString *bundlerIdentifier = bundleIdentifierPair.key;
342             policies.get()[host][bundlerIdentifier] = adoptNS([[NSMutableDictionary alloc] initWithCapacity:bundleIdentifierPair.value.size()]).get();
343             for (auto& versionPair : bundleIdentifierPair.value) {
344                 NSString *version = versionPair.key;
345                 policies.get()[host][bundlerIdentifier][version] = adoptNS([[NSNumber alloc] initWithUnsignedInt:versionPair.value]).get();
346             }
347         }
348     }
349     return policies.autorelease();
350 }
351
352 #endif
353
354 - (void)_resetPluginLoadClientPolicies:(NSDictionary *)policies
355 {
356 #if ENABLE(NETSCAPE_PLUGIN_API)
357     _processPool->resetPluginLoadClientPolicies(toPluginLoadClientPoliciesHashMap(policies));
358 #endif
359 }
360
361 -(NSDictionary *)_pluginLoadClientPolicies
362 {
363     auto& map = _processPool->pluginLoadClientPolicies();
364     return policiesHashMapToDictionary(map);
365 }
366 #endif
367
368
369 - (id <_WKDownloadDelegate>)_downloadDelegate
370 {
371     return _downloadDelegate.getAutoreleased();
372 }
373
374 - (void)_setDownloadDelegate:(id <_WKDownloadDelegate>)downloadDelegate
375 {
376     _downloadDelegate = downloadDelegate;
377     _processPool->setDownloadClient(std::make_unique<WebKit::DownloadClient>(downloadDelegate));
378 }
379
380 - (id <_WKAutomationDelegate>)_automationDelegate
381 {
382     return _automationDelegate.getAutoreleased();
383 }
384
385 - (void)_setAutomationDelegate:(id <_WKAutomationDelegate>)automationDelegate
386 {
387     _automationDelegate = automationDelegate;
388     _processPool->setAutomationClient(std::make_unique<WebKit::AutomationClient>(self, automationDelegate));
389 }
390
391 - (void)_warmInitialProcess
392 {
393     _processPool->prewarmProcess(WebKit::WebProcessPool::MayCreateDefaultDataStore::Yes);
394 }
395
396 - (void)_automationCapabilitiesDidChange
397 {
398     _processPool->updateAutomationCapabilities();
399 }
400
401 - (void)_setAutomationSession:(_WKAutomationSession *)automationSession
402 {
403     _automationSession = automationSession;
404     _processPool->setAutomationSession(automationSession ? automationSession->_session.get() : nullptr);
405 }
406
407 - (void)_addSupportedPlugin:(NSString *) domain named:(NSString *) name withMimeTypes: (NSSet<NSString *> *) nsMimeTypes withExtensions: (NSSet<NSString *> *) nsExtensions
408 {
409     HashSet<String> mimeTypes;
410     for (NSString *mimeType in nsMimeTypes)
411         mimeTypes.add(mimeType);
412     HashSet<String> extensions;
413     for (NSString *extension in nsExtensions)
414         extensions.add(extension);
415
416     _processPool->addSupportedPlugin(domain, name, WTFMove(mimeTypes), WTFMove(extensions));
417 }
418
419 - (void)_clearSupportedPlugins
420 {
421     _processPool->clearSupportedPlugins();
422 }
423
424 - (void)_terminateNetworkProcess
425 {
426     _processPool->terminateNetworkProcess();
427 }
428
429 - (void)_terminateServiceWorkerProcesses
430 {
431     _processPool->terminateServiceWorkerProcesses();
432 }
433
434 - (void)_disableServiceWorkerProcessTerminationDelay
435 {
436     _processPool->disableServiceWorkerProcessTerminationDelay();
437 }
438
439 - (pid_t)_networkProcessIdentifier
440 {
441     return _processPool->networkProcessIdentifier();
442 }
443
444 - (void)_syncNetworkProcessCookies
445 {
446     _processPool->syncNetworkProcessCookies();
447 }
448
449 - (size_t)_webProcessCount
450 {
451     return _processPool->processes().size();
452 }
453
454 - (void)_makeNextWebProcessLaunchFailForTesting
455 {
456     _processPool->setShouldMakeNextWebProcessLaunchFailForTesting(true);
457 }
458
459 - (void)_makeNextNetworkProcessLaunchFailForTesting
460 {
461     _processPool->setShouldMakeNextNetworkProcessLaunchFailForTesting(true);
462 }
463
464 - (BOOL)_hasPrewarmedWebProcess
465 {
466     for (auto& process : _processPool->processes()) {
467         if (process->isPrewarmed())
468             return YES;
469     }
470     return NO;
471 }
472
473 - (size_t)_webProcessCountIgnoringPrewarmed
474 {
475     return [self _webProcessCount] - ([self _hasPrewarmedWebProcess] ? 1 : 0);
476 }
477
478 - (size_t)_webPageContentProcessCount
479 {
480     auto allWebProcesses = _processPool->processes();
481 #if ENABLE(SERVICE_WORKER)
482     auto& serviceWorkerProcesses = _processPool->serviceWorkerProxies();
483     if (serviceWorkerProcesses.isEmpty())
484         return allWebProcesses.size();
485
486     return allWebProcesses.size() - serviceWorkerProcesses.size();
487 #else
488     return allWebProcesses.size();
489 #endif
490 }
491
492 - (void)_preconnectToServer:(NSURL *)serverURL
493 {
494     _processPool->preconnectToServer(serverURL);
495 }
496
497 - (size_t)_pluginProcessCount
498 {
499 #if !PLATFORM(IOS_FAMILY)
500     return WebKit::PluginProcessManager::singleton().pluginProcesses().size();
501 #else
502     return 0;
503 #endif
504 }
505
506 - (NSUInteger)_maximumSuspendedPageCount
507 {
508     return _processPool->maxSuspendedPageCount();
509 }
510
511 - (size_t)_serviceWorkerProcessCount
512 {
513 #if ENABLE(SERVICE_WORKER)
514     return _processPool->serviceWorkerProxies().size();
515 #else
516     return 0;
517 #endif
518 }
519
520 + (void)_forceGameControllerFramework
521 {
522 #if ENABLE(GAMEPAD)
523     WebKit::UIGamepadProvider::setUsesGameControllerFramework();
524 #endif
525 }
526
527 - (BOOL)_isCookieStoragePartitioningEnabled
528 {
529     return _processPool->cookieStoragePartitioningEnabled();
530 }
531
532 - (void)_setCookieStoragePartitioningEnabled:(BOOL)enabled
533 {
534     _processPool->setCookieStoragePartitioningEnabled(enabled);
535 }
536
537 - (BOOL)_isStorageAccessAPIEnabled
538 {
539     return _processPool->storageAccessAPIEnabled();
540 }
541
542 - (void)_setStorageAccessAPIEnabled:(BOOL)enabled
543 {
544     _processPool->setStorageAccessAPIEnabled(enabled);
545 }
546
547 - (void)_setAllowsAnySSLCertificateForServiceWorker:(BOOL) allows
548 {
549 #if ENABLE(SERVICE_WORKER)
550     _processPool->setAllowsAnySSLCertificateForServiceWorker(allows);
551 #endif
552 }
553
554 #if PLATFORM(IOS_FAMILY)
555 - (id <_WKGeolocationCoreLocationProvider>)_coreLocationProvider
556 {
557     return _coreLocationProvider.get();
558 }
559
560 - (void)_setCoreLocationProvider:(id<_WKGeolocationCoreLocationProvider>)coreLocationProvider
561 {
562     if (_geolocationProvider)
563         [NSException raise:NSGenericException format:@"Changing the location provider is not supported after a web view in the process pool has begun servicing geolocation requests."];
564
565     _coreLocationProvider = coreLocationProvider;
566 }
567 #endif // PLATFORM(IOS_FAMILY)
568
569 - (_WKDownload *)_downloadURLRequest:(NSURLRequest *)request originatingWebView:(WKWebView *)webView
570 {
571     return (_WKDownload *)_processPool->download([webView _page], request)->wrapper();
572 }
573
574 - (_WKDownload *)_resumeDownloadFromData:(NSData *)resumeData path:(NSString *)path originatingWebView:(WKWebView *)webView
575 {
576     return wrapper(_processPool->resumeDownload([webView _page], API::Data::createWithoutCopying(resumeData).ptr(), path));
577 }
578
579 - (void)_getActivePagesOriginsInWebProcessForTesting:(pid_t)pid completionHandler:(void(^)(NSArray<NSString *> *))completionHandler
580 {
581     _processPool->activePagesOriginsInWebProcessForTesting(pid, [completionHandler = makeBlockPtr(completionHandler)] (Vector<String>&& activePagesOrigins) {
582         NSMutableArray<NSString *> *array = [[[NSMutableArray alloc] initWithCapacity:activePagesOrigins.size()] autorelease];
583         for (auto& origin : activePagesOrigins)
584             [array addObject:origin];
585         completionHandler(array);
586     });
587 }
588
589 - (BOOL)_networkProcessHasEntitlementForTesting:(NSString *)entitlement
590 {
591     return _processPool->networkProcessHasEntitlementForTesting(entitlement);
592 }
593
594 @end
595
596 #endif // WK_API_ENABLED