Unreviewed, reverting r260769.
[WebKit.git] / Source / WebKit / WebProcess / cocoa / WebProcessCocoa.mm
1 /*
2  * Copyright (C) 2010-2020 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 "WebProcess.h"
28 #import "WebProcessCocoa.h"
29
30 #import "LegacyCustomProtocolManager.h"
31 #import "LogInitialization.h"
32 #import "Logging.h"
33 #import "ObjCObjectGraph.h"
34 #import "ProcessAssertion.h"
35 #import "SandboxExtension.h"
36 #import "SandboxInitializationParameters.h"
37 #import "WKAPICast.h"
38 #import "WKBrowsingContextHandleInternal.h"
39 #import "WKCrashReporter.h"
40 #import "WKFullKeyboardAccessWatcher.h"
41 #import "WKTypeRefWrapper.h"
42 #import "WKWebProcessPlugInBrowserContextControllerInternal.h"
43 #import "WebFrame.h"
44 #import "WebInspector.h"
45 #import "WebPage.h"
46 #import "WebProcessCreationParameters.h"
47 #import "WebProcessDataStoreParameters.h"
48 #import "WebProcessProxyMessages.h"
49 #import "WebSleepDisablerClient.h"
50 #import "WebsiteDataStoreParameters.h"
51 #import <JavaScriptCore/ConfigFile.h>
52 #import <JavaScriptCore/Options.h>
53 #import <WebCore/AVAssetMIMETypeCache.h>
54 #import <WebCore/AXObjectCache.h>
55 #import <WebCore/CPUMonitor.h>
56 #import <WebCore/DisplayRefreshMonitorManager.h>
57 #import <WebCore/FontCache.h>
58 #import <WebCore/FontCascade.h>
59 #import <WebCore/HistoryController.h>
60 #import <WebCore/HistoryItem.h>
61 #import <WebCore/LocalizedDeviceModel.h>
62 #import <WebCore/LocalizedStrings.h>
63 #import <WebCore/LogInitialization.h>
64 #import <WebCore/MIMETypeRegistry.h>
65 #import <WebCore/MemoryRelease.h>
66 #import <WebCore/NSScrollerImpDetails.h>
67 #import <WebCore/NetworkExtensionContentFilter.h>
68 #import <WebCore/PerformanceLogging.h>
69 #import <WebCore/PictureInPictureSupport.h>
70 #import <WebCore/RuntimeApplicationChecks.h>
71 #import <WebCore/SWContextManager.h>
72 #import <WebCore/SystemBattery.h>
73 #import <WebCore/UTIUtilities.h>
74 #import <algorithm>
75 #import <dispatch/dispatch.h>
76 #import <objc/runtime.h>
77 #import <pal/spi/cf/CFNetworkSPI.h>
78 #import <pal/spi/cf/CFUtilitiesSPI.h>
79 #import <pal/spi/cg/CoreGraphicsSPI.h>
80 #import <pal/spi/cocoa/CoreServicesSPI.h>
81 #import <pal/spi/cocoa/LaunchServicesSPI.h>
82 #import <pal/spi/cocoa/NSAccessibilitySPI.h>
83 #import <pal/spi/cocoa/QuartzCoreSPI.h>
84 #import <pal/spi/cocoa/pthreadSPI.h>
85 #import <pal/spi/mac/NSApplicationSPI.h>
86 #import <stdio.h>
87 #import <wtf/FileSystem.h>
88 #import <wtf/ProcessPrivilege.h>
89 #import <wtf/cocoa/NSURLExtras.h>
90 #import <wtf/cocoa/RuntimeApplicationChecksCocoa.h>
91 #import <wtf/cocoa/VectorCocoa.h>
92
93 #if ENABLE(REMOTE_INSPECTOR)
94 #import <JavaScriptCore/RemoteInspector.h>
95 #endif
96
97 #if PLATFORM(IOS)
98 #import <WebCore/ParentalControlsContentFilter.h>
99 #endif
100
101 #if PLATFORM(IOS_FAMILY)
102 #import "UIKitSPI.h"
103 #import <bmalloc/MemoryStatusSPI.h>
104 #endif
105
106 #if PLATFORM(IOS_FAMILY)
107 #import "AccessibilitySupportSPI.h"
108 #import "AssertionServicesSPI.h"
109 #import "RunningBoardServicesSPI.h"
110 #import "UserInterfaceIdiom.h"
111 #import "WKAccessibilityWebPageObjectIOS.h"
112 #import <MobileCoreServices/MobileCoreServices.h>
113 #import <UIKit/UIAccessibility.h>
114 #import <pal/spi/ios/GraphicsServicesSPI.h>
115 #endif
116
117 #if PLATFORM(IOS_FAMILY) && USE(APPLE_INTERNAL_SDK)
118 #import <AXRuntime/AXDefines.h>
119 #import <AXRuntime/AXNotificationConstants.h>
120 #endif
121
122 #if PLATFORM(IOS_FAMILY) && !USE(APPLE_INTERNAL_SDK)
123 #define kAXPidStatusChangedNotification 0
124 #endif
125
126 #if PLATFORM(MAC)
127 #import "WKAccessibilityWebPageObjectMac.h"
128 #import "WebSwitchingGPUClient.h"
129 #import <WebCore/GraphicsContextGLOpenGLManager.h>
130 #import <WebCore/ScrollbarThemeMac.h>
131 #import <pal/spi/mac/NSScrollerImpSPI.h>
132 #endif
133
134 #if USE(OS_STATE)
135 #import <os/state_private.h>
136 #endif
137
138 #define RELEASE_LOG_SESSION_ID (m_sessionID ? m_sessionID->toUInt64() : 0)
139 #define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), channel, "%p - [sessionID=%" PRIu64 "] WebProcess::" fmt, this, RELEASE_LOG_SESSION_ID, ##__VA_ARGS__)
140 #define RELEASE_LOG_ERROR_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), channel, "%p - [sessionID=%" PRIu64 "] WebProcess::" fmt, this, RELEASE_LOG_SESSION_ID, ##__VA_ARGS__)
141
142 namespace WebKit {
143 using namespace WebCore;
144
145 #if PLATFORM(MAC)
146 static const Seconds cpuMonitoringInterval { 8_min };
147 static const double serviceWorkerCPULimit { 0.5 }; // 50% average CPU usage over 8 minutes.
148 #endif
149
150 void WebProcess::platformSetCacheModel(CacheModel)
151 {
152 }
153
154 #if USE(APPKIT)
155 static id NSApplicationAccessibilityFocusedUIElement(NSApplication*, SEL)
156 {
157     WebPage* page = WebProcess::singleton().focusedWebPage();
158     if (!page || !page->accessibilityRemoteObject())
159         return 0;
160
161     return [page->accessibilityRemoteObject() accessibilityFocusedUIElement];
162 }
163 #endif
164
165 #if ENABLE(CFPREFS_DIRECT_MODE)
166 static void setGlobalPreferences(const String& encodedGlobalPreferences)
167 {
168     if (encodedGlobalPreferences.isEmpty())
169         return;
170
171     auto encodedData = adoptNS([[NSData alloc] initWithBase64EncodedString:encodedGlobalPreferences options:0]);
172     if (!encodedData)
173         return;
174
175     NSError *err = nil;
176     auto classes = [NSSet setWithArray:@[[NSString class], [NSNumber class], [NSDate class], [NSDictionary class], [NSArray class], [NSData class]]];
177     id globalPreferencesDictionary = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:encodedData.get() error:&err];
178     if (err) {
179         ASSERT_NOT_REACHED();
180         WTFLogAlways("Failed to unarchive global preferences dictionary with NSKeyedUnarchiver.");
181         return;
182     }
183     [globalPreferencesDictionary enumerateKeysAndObjectsUsingBlock: ^(NSString *key, id value, BOOL* stop) {
184         if (value)
185             CFPreferencesSetAppValue(static_cast<CFStringRef>(key), static_cast<CFPropertyListRef>(value), CFSTR("kCFPreferencesAnyApplication"));
186     }];
187 }
188 #endif
189
190 void WebProcess::platformInitializeWebProcess(WebProcessCreationParameters& parameters)
191 {
192 #if ENABLE(CFPREFS_DIRECT_MODE)
193     setGlobalPreferences(parameters.encodedGlobalPreferences);
194 #endif
195
196     // Map Launch Services database. This should be done as early as possible, as the mapping will fail
197     // if 'com.apple.lsd.mapdb' is being accessed before this.
198     if (parameters.mapDBExtensionHandle) {
199         auto extension = SandboxExtension::create(WTFMove(*parameters.mapDBExtensionHandle));
200         bool ok = extension->consume();
201         ASSERT_UNUSED(ok, ok);
202         // Perform API calls which will communicate with the database mapping service, and map the database.
203         auto uti = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, CFSTR("text/html"), 0));
204         ok = extension->revoke();
205         ASSERT_UNUSED(ok, ok);
206     }
207
208 #if PLATFORM(IOS_FAMILY)
209     if (parameters.runningboardExtensionHandle) {
210         auto extension = SandboxExtension::create(WTFMove(*parameters.runningboardExtensionHandle));
211         bool consumed = extension->consume();
212         ASSERT_UNUSED(consumed, consumed);
213
214         ASSERT(!m_uiProcessDependencyProcessAssertion);
215         if (auto remoteProcessID = parentProcessConnection()->remoteProcessID())
216             m_uiProcessDependencyProcessAssertion = makeUnique<ProcessAssertion>(remoteProcessID, "WebContent process dependency on UIProcess"_s, ProcessAssertionType::DependentProcessLink);
217         else
218             RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSuspension, "Unable to create a process dependency assertion on UIProcess because remoteProcessID is 0");
219
220         bool revoked = extension->revoke();
221         ASSERT_UNUSED(revoked, revoked);
222     }
223 #endif
224
225 #if !LOG_DISABLED || !RELEASE_LOG_DISABLED
226     WebCore::initializeLogChannelsIfNecessary(parameters.webCoreLoggingChannels);
227     WebKit::initializeLogChannelsIfNecessary(parameters.webKitLoggingChannels);
228 #endif
229
230     WebCore::setApplicationBundleIdentifier(parameters.uiProcessBundleIdentifier);
231     setApplicationSDKVersion(parameters.uiProcessSDKVersion);
232
233     m_uiProcessBundleIdentifier = parameters.uiProcessBundleIdentifier;
234
235 #if ENABLE(SANDBOX_EXTENSIONS)
236     SandboxExtension::consumePermanently(parameters.uiProcessBundleResourcePathExtensionHandle);
237 #if ENABLE(MEDIA_STREAM)
238     SandboxExtension::consumePermanently(parameters.audioCaptureExtensionHandle);
239 #endif
240 #if PLATFORM(IOS_FAMILY)
241     SandboxExtension::consumePermanently(parameters.cookieStorageDirectoryExtensionHandle);
242     SandboxExtension::consumePermanently(parameters.containerCachesDirectoryExtensionHandle);
243     SandboxExtension::consumePermanently(parameters.containerTemporaryDirectoryExtensionHandle);
244 #endif
245 #endif
246
247     // Disable NSURLCache.
248     auto urlCache = adoptNS([[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]);
249     [NSURLCache setSharedURLCache:urlCache.get()];
250
251 #if PLATFORM(MAC)
252     WebCore::FontCache::setFontWhitelist(parameters.fontWhitelist);
253 #endif
254
255     m_compositingRenderServerPort = WTFMove(parameters.acceleratedCompositingPort);
256
257     WebCore::registerMemoryReleaseNotifyCallbacks();
258     MemoryPressureHandler::ReliefLogger::setLoggingEnabled(parameters.shouldEnableMemoryPressureReliefLogging);
259
260     setEnhancedAccessibility(parameters.accessibilityEnhancedUserInterfaceEnabled);
261
262 #if PLATFORM(IOS_FAMILY)
263     setCurrentUserInterfaceIdiomIsPad(parameters.currentUserInterfaceIdiomIsPad);
264     setLocalizedDeviceModel(parameters.localizedDeviceModel);
265 #if ENABLE(VIDEO_PRESENTATION_MODE)
266     setSupportsPictureInPicture(parameters.supportsPictureInPicture);
267 #endif
268 #endif
269
270 #if USE(APPKIT)
271     [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions" : @YES }];
272
273     // rdar://9118639 accessibilityFocusedUIElement in NSApplication defaults to use the keyWindow. Since there's
274     // no window in WK2, NSApplication needs to use the focused page's focused element.
275     Method methodToPatch = class_getInstanceMethod([NSApplication class], @selector(accessibilityFocusedUIElement));
276     method_setImplementation(methodToPatch, (IMP)NSApplicationAccessibilityFocusedUIElement);
277 #endif
278     
279 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_NSRUNLOOP)
280     // Need to initialize accessibility for VoiceOver to work when the WebContent process is using NSRunLoop.
281     // Currently, it is also needed to allocate and initialize an NSApplication object.
282     // This method call will also call RegisterApplication, so there is no need for us to call this or
283     // check in with Launch Services
284     [NSApplication _accessibilityInitialize];
285 #endif
286
287 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
288     // App nap must be manually enabled when not running the NSApplication run loop.
289     __CFRunLoopSetOptionsReason(__CFRunLoopOptionsEnableAppNap, CFSTR("Finished checkin as application - enable app nap"));
290 #endif
291
292 #if TARGET_OS_IPHONE
293     // Priority decay on iOS 9 is impacting page load time so we fix the priority of the WebProcess' main thread (rdar://problem/22003112).
294     pthread_set_fixedpriority_self();
295 #endif
296
297     if (!parameters.mediaMIMETypes.isEmpty())
298         setMediaMIMETypes(parameters.mediaMIMETypes);
299     else {
300         AVAssetMIMETypeCache::singleton().setCacheMIMETypesCallback([this](const Vector<String>& types) {
301             parentProcessConnection()->send(Messages::WebProcessProxy::CacheMediaMIMETypes(types), 0);
302         });
303     }
304
305     WebCore::setScreenProperties(parameters.screenProperties);
306
307 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
308     scrollerStylePreferenceChanged(parameters.useOverlayScrollbars);
309 #endif
310
311 #if PLATFORM(IOS)
312     if (parameters.compilerServiceExtensionHandle)
313         SandboxExtension::consumePermanently(*parameters.compilerServiceExtensionHandle);
314
315     if (parameters.contentFilterExtensionHandle)
316         SandboxExtension::consumePermanently(*parameters.contentFilterExtensionHandle);
317     ParentalControlsContentFilter::setHasConsumedSandboxExtension(parameters.contentFilterExtensionHandle.hasValue());
318
319     if (parameters.frontboardServiceExtensionHandle)
320         SandboxExtension::consumePermanently(*parameters.frontboardServiceExtensionHandle);
321 #endif
322
323 #if PLATFORM(IOS_FAMILY)
324     if (parameters.diagnosticsExtensionHandle)
325         SandboxExtension::consumePermanently(*parameters.diagnosticsExtensionHandle);
326
327     SandboxExtension::consumePermanently(parameters.dynamicMachExtensionHandles);
328     SandboxExtension::consumePermanently(parameters.dynamicIOKitExtensionHandles);
329 #endif
330     
331     if (parameters.neHelperExtensionHandle)
332         SandboxExtension::consumePermanently(*parameters.neHelperExtensionHandle);
333     if (parameters.neSessionManagerExtensionHandle)
334         SandboxExtension::consumePermanently(*parameters.neSessionManagerExtensionHandle);
335     NetworkExtensionContentFilter::setHasConsumedSandboxExtensions(parameters.neHelperExtensionHandle.hasValue() && parameters.neSessionManagerExtensionHandle.hasValue());
336
337     setSystemHasBattery(parameters.systemHasBattery);
338
339     if (parameters.mimeTypesMap)
340         overriddenMimeTypesMap() = WTFMove(parameters.mimeTypesMap);
341
342 #if PLATFORM(IOS_FAMILY)
343     RenderThemeIOS::setCSSValueToSystemColorMap(WTFMove(parameters.cssValueToSystemColorMap));
344     RenderThemeIOS::setFocusRingColor(parameters.focusRingColor);
345 #endif
346
347     // FIXME(207716): The following should be removed when the GPU process is complete.
348     SandboxExtension::consumePermanently(parameters.mediaExtensionHandles);
349
350 #if ENABLE(CFPREFS_DIRECT_MODE)
351     if (parameters.preferencesExtensionHandles) {
352         SandboxExtension::consumePermanently(*parameters.preferencesExtensionHandles);
353         _CFPrefsSetDirectModeEnabled(false);
354     }
355 #endif
356         
357     WebCore::sleepDisablerClient() = makeUnique<WebSleepDisablerClient>();
358
359     updateProcessName();
360 }
361
362 void WebProcess::platformSetWebsiteDataStoreParameters(WebProcessDataStoreParameters&& parameters)
363 {
364 #if ENABLE(SANDBOX_EXTENSIONS)
365     SandboxExtension::consumePermanently(parameters.webSQLDatabaseDirectoryExtensionHandle);
366     SandboxExtension::consumePermanently(parameters.applicationCacheDirectoryExtensionHandle);
367     SandboxExtension::consumePermanently(parameters.mediaCacheDirectoryExtensionHandle);
368     SandboxExtension::consumePermanently(parameters.mediaKeyStorageDirectoryExtensionHandle);
369     SandboxExtension::consumePermanently(parameters.javaScriptConfigurationDirectoryExtensionHandle);
370 #endif
371
372     if (!parameters.javaScriptConfigurationDirectory.isEmpty()) {
373         String javaScriptConfigFile = parameters.javaScriptConfigurationDirectory + "/JSC.config";
374         JSC::processConfigFile(javaScriptConfigFile.latin1().data(), "com.apple.WebKit.WebContent", m_uiProcessBundleIdentifier.latin1().data());
375     }
376 }
377
378 void WebProcess::initializeProcessName(const AuxiliaryProcessInitializationParameters& parameters)
379 {
380 #if PLATFORM(MAC)
381     m_uiProcessName = parameters.uiProcessName;
382 #else
383     UNUSED_PARAM(parameters);
384 #endif
385 }
386
387 void WebProcess::updateProcessName()
388 {
389 #if PLATFORM(MAC)
390     RetainPtr<NSString> applicationName;
391     switch (m_processType) {
392     case ProcessType::Inspector:
393         applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ Web Inspector", "Visible name of Web Inspector's web process. The argument is the application name."), (NSString *)m_uiProcessName];
394         break;
395     case ProcessType::ServiceWorker:
396         applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ Service Worker (%@)", "Visible name of Service Worker process. The argument is the application name."), (NSString *)m_uiProcessName, (NSString *)m_registrableDomain.string()];
397         break;
398     case ProcessType::PrewarmedWebContent:
399         applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ Web Content (Prewarmed)", "Visible name of the web process. The argument is the application name."), (NSString *)m_uiProcessName];
400         break;
401     case ProcessType::CachedWebContent:
402         applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ Web Content (Cached)", "Visible name of the web process. The argument is the application name."), (NSString *)m_uiProcessName];
403         break;
404     case ProcessType::WebContent:
405         applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ Web Content", "Visible name of the web process. The argument is the application name."), (NSString *)m_uiProcessName];
406         break;
407     }
408
409     RunLoop::main().dispatch([this, applicationName = WTFMove(applicationName)] {
410         // Note that it is important for _RegisterApplication() to have been called before setting the display name.
411         auto error = _LSSetApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), _kLSDisplayNameKey, (CFStringRef)applicationName.get(), nullptr);
412         ASSERT(!error);
413         if (error) {
414             RELEASE_LOG_ERROR_IF_ALLOWED(Process, "updateProcessName: Failed to set the display name of the WebContent process, error code: %ld", static_cast<long>(error));
415             return;
416         }
417 #if ASSERT_ENABLED
418         // It is possible for _LSSetApplicationInformationItem() to return 0 and yet fail to set the display name so we make sure the display name has actually been set.
419         String actualApplicationName = adoptCF((CFStringRef)_LSCopyApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), _kLSDisplayNameKey)).get();
420         ASSERT(!actualApplicationName.isEmpty());
421 #endif
422     });
423 #endif // PLATFORM(MAC)
424 }
425
426 #if PLATFORM(IOS_FAMILY)
427 static NSString *webProcessLoaderAccessibilityBundlePath()
428 {
429 #if HAVE(ACCESSIBILITY_BUNDLES_PATH)
430     return (__bridge NSString *)CFAutorelease(_AXSCopyPathForAccessibilityBundle(CFSTR("WebProcessLoader")));
431 #else
432     NSString *path = (__bridge NSString *)GSSystemRootDirectory();
433 #if PLATFORM(MACCATALYST)
434     path = [path stringByAppendingPathComponent:@"System/iOSSupport"];
435 #endif
436     return [path stringByAppendingPathComponent:@"System/Library/AccessibilityBundles/WebProcessLoader.axbundle"];
437 #endif // HAVE(ACCESSIBILITY_BUNDLES_PATH)
438 }
439 #endif
440
441 static void registerWithAccessibility()
442 {
443 #if USE(APPKIT)
444     [NSAccessibilityRemoteUIElement setRemoteUIApp:YES];
445 #endif
446
447 #if PLATFORM(IOS_FAMILY)
448     NSString *bundlePath = webProcessLoaderAccessibilityBundlePath();
449     NSError *error = nil;
450     if (![[NSBundle bundleWithPath:bundlePath] loadAndReturnError:&error])
451         LOG_ERROR("Failed to load accessibility bundle at %@: %@", bundlePath, error);
452 #endif
453 }
454
455 #if USE(OS_STATE)
456 void WebProcess::registerWithStateDumper()
457 {
458     os_state_add_handler(dispatch_get_main_queue(), ^(os_state_hints_t hints) {
459
460         @autoreleasepool {
461             os_state_data_t os_state = nil;
462
463             // Only gather state on faults and sysdiagnose. It's overkill for
464             // general error messages.
465             if (hints->osh_api == OS_STATE_API_ERROR)
466                 return os_state;
467
468             // Create a dictionary to contain the collected state. This
469             // dictionary will be serialized and passed back to os_state.
470             auto stateDict = adoptNS([[NSMutableDictionary alloc] init]);
471
472             {
473                 auto memoryUsageStats = adoptNS([[NSMutableDictionary alloc] init]);
474                 for (auto& it : PerformanceLogging::memoryUsageStatistics(ShouldIncludeExpensiveComputations::Yes)) {
475                     auto keyString = adoptNS([[NSString alloc] initWithUTF8String:it.key]);
476                     [memoryUsageStats setObject:@(it.value) forKey:keyString.get()];
477                 }
478                 [stateDict setObject:memoryUsageStats.get() forKey:@"Memory Usage Stats"];
479             }
480
481             {
482                 auto jsObjectCounts = adoptNS([[NSMutableDictionary alloc] init]);
483                 for (auto& it : PerformanceLogging::javaScriptObjectCounts()) {
484                     auto keyString = adoptNS([[NSString alloc] initWithUTF8String:it.key]);
485                     [jsObjectCounts setObject:@(it.value) forKey:keyString.get()];
486                 }
487                 [stateDict setObject:jsObjectCounts.get() forKey:@"JavaScript Object Counts"];
488             }
489
490             auto pageLoadTimes = createNSArray(m_pageMap.values(), [] (auto& page) -> id {
491                 if (page->usesEphemeralSession())
492                     return nil;
493
494                 return [NSDate dateWithTimeIntervalSince1970:page->loadCommitTime().secondsSinceEpoch().seconds()];
495             });
496
497             // Adding an empty array to the process state may provide an
498             // indication of the existance of private sessions, which we'd like
499             // to hide, so don't add empty arrays.
500             if ([pageLoadTimes count])
501                 [stateDict setObject:pageLoadTimes.get() forKey:@"Page Load Times"];
502
503             // --- Possibly add other state here as other entries in the dictionary. ---
504
505             // Submitting an empty process state object may provide an
506             // indication of the existance of private sessions, which we'd like
507             // to hide, so don't return empty dictionaries.
508             if (![stateDict count])
509                 return os_state;
510
511             // Serialize the accumulated process state so that we can put the
512             // result in an os_state_data_t structure.
513             NSError* error = nil;
514             NSData* data = [NSPropertyListSerialization dataWithPropertyList:stateDict.get() format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error];
515
516             if (!data) {
517                 ASSERT(data);
518                 return os_state;
519             }
520
521             size_t neededSize = OS_STATE_DATA_SIZE_NEEDED(data.length);
522             os_state = (os_state_data_t)malloc(neededSize);
523             if (os_state) {
524                 memset(os_state, 0, neededSize);
525                 os_state->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT;
526                 os_state->osd_data_size = data.length;
527                 strlcpy(os_state->osd_title, "WebContent state", sizeof(os_state->osd_title));
528                 memcpy(os_state->osd_data, data.bytes, data.length);
529             }
530
531             return os_state;
532         }
533     });
534 }
535 #endif
536
537 void WebProcess::platformInitializeProcess(const AuxiliaryProcessInitializationParameters& parameters)
538 {
539 #if PLATFORM(MAC)
540 #if ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
541     // Deny the WebContent process access to the WindowServer.
542     // This call will not succeed if there are open WindowServer connections at this point.
543     auto retval = CGSSetDenyWindowServerConnections(true);
544     RELEASE_ASSERT(retval == kCGErrorSuccess);
545     // Make sure that we close any WindowServer connections after checking in with Launch Services.
546     CGSShutdownServerConnections();
547
548     SwitchingGPUClient::setSingleton(WebSwitchingGPUClient::singleton());
549 #else
550
551     if (![NSApp isRunning]) {
552         // This call is needed when the WebProcess is not running the NSApplication event loop.
553         // Otherwise, calling enableSandboxStyleFileQuarantine() will fail.
554         launchServicesCheckIn();
555     }
556 #endif // ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
557 #endif // PLATFORM(MAC)
558
559     if (parameters.extraInitializationData.get("inspector-process"_s) == "1")
560         m_processType = ProcessType::Inspector;
561 #if ENABLE(SERVICE_WORKER)
562     else if (parameters.extraInitializationData.get("service-worker-process"_s) == "1") {
563         m_processType = ProcessType::ServiceWorker;
564 #if PLATFORM(MAC)
565         m_registrableDomain = RegistrableDomain::uncheckedCreateFromRegistrableDomainString(parameters.extraInitializationData.get("registrable-domain"_s));
566 #endif
567     }
568 #endif
569     else if (parameters.extraInitializationData.get("is-prewarmed"_s) == "1")
570         m_processType = ProcessType::PrewarmedWebContent;
571     else
572         m_processType = ProcessType::WebContent;
573
574 #if USE(OS_STATE)
575     registerWithStateDumper();
576 #endif
577
578 #if HAVE(APP_SSO)
579     [NSURLSession _disableAppSSO];
580 #endif
581
582 #if HAVE(CSCHECKFIXDISABLE)
583     // _CSCheckFixDisable() needs to be called before checking in with Launch Services. The WebContent process is checking in
584     // with Launch Services in WebProcess::platformInitializeWebProcess when calling +[NSApplication _accessibilityInitialize].
585     _CSCheckFixDisable();
586 #endif
587 }
588
589 #if USE(APPKIT)
590 void WebProcess::stopRunLoop()
591 {
592 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_NSRUNLOOP)
593     AuxiliaryProcess::stopNSRunLoop();
594 #else
595     AuxiliaryProcess::stopNSAppRunLoop();
596 #endif
597 }
598 #endif
599
600 void WebProcess::platformTerminate()
601 {
602     AVAssetMIMETypeCache::singleton().setCacheMIMETypesCallback(nullptr);
603 }
604
605 RetainPtr<CFDataRef> WebProcess::sourceApplicationAuditData() const
606 {
607 #if USE(SOURCE_APPLICATION_AUDIT_DATA)
608     ASSERT(parentProcessConnection());
609     if (!parentProcessConnection())
610         return nullptr;
611     Optional<audit_token_t> auditToken = parentProcessConnection()->getAuditToken();
612     if (!auditToken)
613         return nullptr;
614     return adoptCF(CFDataCreate(nullptr, (const UInt8*)&*auditToken, sizeof(*auditToken)));
615 #else
616     return nullptr;
617 #endif
618 }
619
620 void WebProcess::initializeSandbox(const AuxiliaryProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
621 {
622 #if PLATFORM(MAC) || PLATFORM(MACCATALYST)
623     // Need to override the default, because service has a different bundle ID.
624     NSBundle *webKit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKWebView")];
625
626     sandboxParameters.setOverrideSandboxProfilePath([webKit2Bundle pathForResource:@"com.apple.WebProcess" ofType:@"sb"]);
627
628     AuxiliaryProcess::initializeSandbox(parameters, sandboxParameters);
629 #endif
630 }
631
632 #if PLATFORM(MAC)
633
634 static NSURL *origin(WebPage& page)
635 {
636     auto& mainFrame = page.mainWebFrame();
637
638     URL mainFrameURL = mainFrame.url();
639     Ref<SecurityOrigin> mainFrameOrigin = SecurityOrigin::create(mainFrameURL);
640     String mainFrameOriginString;
641     if (!mainFrameOrigin->isUnique())
642         mainFrameOriginString = mainFrameOrigin->toRawString();
643     else
644         mainFrameOriginString = makeString(mainFrameURL.protocol(), ':'); // toRawString() is not supposed to work with unique origins, and would just return "://".
645
646     // +[NSURL URLWithString:] returns nil when its argument is malformed. It's unclear when we would have a malformed URL here,
647     // but it happens in practice according to <rdar://problem/14173389>. Leaving an assertion in to catch a reproducible case.
648     ASSERT([NSURL URLWithString:mainFrameOriginString]);
649
650     return [NSURL URLWithString:mainFrameOriginString];
651 }
652
653 #endif
654
655 #if PLATFORM(MAC)
656
657 static RetainPtr<NSArray<NSString *>> activePagesOrigins(const HashMap<PageIdentifier, RefPtr<WebPage>>& pageMap)
658 {
659     return createNSArray(pageMap.values(), [] (auto& page) -> NSString * {
660         if (page->usesEphemeralSession())
661             return nil;
662
663         NSURL *originAsURL = origin(*page);
664         if (!originAsURL)
665             return nil;
666
667         return WTF::userVisibleString(originAsURL);
668     });
669 }
670
671 #endif
672
673 void WebProcess::updateActivePages(const String& overrideDisplayName)
674 {
675 #if PLATFORM(MAC)
676     if (!overrideDisplayName) {
677         RunLoop::main().dispatch([activeOrigins = activePagesOrigins(m_pageMap)] {
678             _LSSetApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), CFSTR("LSActivePageUserVisibleOriginsKey"), (__bridge CFArrayRef)activeOrigins.get(), nullptr);
679         });
680     } else {
681         RunLoop::main().dispatch([name = overrideDisplayName.createCFString()] {
682             _LSSetApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), _kLSDisplayNameKey, name.get(), nullptr);
683         });
684     }
685 #endif
686 }
687
688 void WebProcess::getActivePagesOriginsForTesting(CompletionHandler<void(Vector<String>&&)>&& completionHandler)
689 {
690 #if PLATFORM(MAC)
691     completionHandler(makeVector<String>(activePagesOrigins(m_pageMap).get()));
692 #else
693     completionHandler({ });
694 #endif
695 }
696
697 void WebProcess::updateCPULimit()
698 {
699 #if PLATFORM(MAC)
700     Optional<double> cpuLimit;
701     if (m_processType == ProcessType::ServiceWorker)
702         cpuLimit = serviceWorkerCPULimit;
703     else {
704         // Use the largest limit among all pages in this process.
705         for (auto& page : m_pageMap.values()) {
706             auto pageCPULimit = page->cpuLimit();
707             if (!pageCPULimit) {
708                 cpuLimit = WTF::nullopt;
709                 break;
710             }
711             if (!cpuLimit || pageCPULimit > cpuLimit.value())
712                 cpuLimit = pageCPULimit;
713         }
714     }
715
716     if (m_cpuLimit == cpuLimit)
717         return;
718
719     m_cpuLimit = cpuLimit;
720     updateCPUMonitorState(CPUMonitorUpdateReason::LimitHasChanged);
721 #endif
722 }
723
724 void WebProcess::updateCPUMonitorState(CPUMonitorUpdateReason reason)
725 {
726 #if PLATFORM(MAC)
727     if (!m_cpuLimit) {
728         if (m_cpuMonitor)
729             m_cpuMonitor->setCPULimit(WTF::nullopt);
730         return;
731     }
732
733     if (!m_cpuMonitor) {
734         m_cpuMonitor = makeUnique<CPUMonitor>(cpuMonitoringInterval, [this](double cpuUsage) {
735             if (m_processType == ProcessType::ServiceWorker)
736                 RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSuspension, "updateCPUMonitorState: Service worker process exceeded CPU limit of %.1f%% (was using %.1f%%)", m_cpuLimit.value() * 100, cpuUsage * 100);
737             else
738                 RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSuspension, "updateCPUMonitorState: WebProcess exceeded CPU limit of %.1f%% (was using %.1f%%) hasVisiblePages? %d", m_cpuLimit.value() * 100, cpuUsage * 100, hasVisibleWebPage());
739             parentProcessConnection()->send(Messages::WebProcessProxy::DidExceedCPULimit(), 0);
740         });
741     } else if (reason == CPUMonitorUpdateReason::VisibilityHasChanged) {
742         // If the visibility has changed, stop the CPU monitor before setting its limit. This is needed because the CPU usage can vary wildly based on visibility and we would
743         // not want to report that a process has exceeded its background CPU limit even though most of the CPU time was used while the process was visible.
744         m_cpuMonitor->setCPULimit(WTF::nullopt);
745     }
746     m_cpuMonitor->setCPULimit(m_cpuLimit);
747 #else
748     UNUSED_PARAM(reason);
749 #endif
750 }
751
752 RefPtr<ObjCObjectGraph> WebProcess::transformHandlesToObjects(ObjCObjectGraph& objectGraph)
753 {
754     struct Transformer final : ObjCObjectGraph::Transformer {
755         Transformer(WebProcess& webProcess)
756             : m_webProcess(webProcess)
757         {
758         }
759
760         bool shouldTransformObject(id object) const override
761         {
762             if (dynamic_objc_cast<WKBrowsingContextHandle>(object))
763                 return true;
764
765             ALLOW_DEPRECATED_DECLARATIONS_BEGIN
766             if (dynamic_objc_cast<WKTypeRefWrapper>(object))
767                 return true;
768             ALLOW_DEPRECATED_DECLARATIONS_END
769             return false;
770         }
771
772         RetainPtr<id> transformObject(id object) const override
773         {
774             if (auto* handle = dynamic_objc_cast<WKBrowsingContextHandle>(object)) {
775                 if (auto* webPage = m_webProcess.webPage(handle._webPageID))
776                     return wrapper(*webPage);
777
778                 return [NSNull null];
779             }
780
781             ALLOW_DEPRECATED_DECLARATIONS_BEGIN
782             if (auto* wrapper = dynamic_objc_cast<WKTypeRefWrapper>(object))
783                 return adoptNS([[WKTypeRefWrapper alloc] initWithObject:toAPI(m_webProcess.transformHandlesToObjects(toImpl(wrapper.object)).get())]);
784             ALLOW_DEPRECATED_DECLARATIONS_END
785             return object;
786         }
787
788         WebProcess& m_webProcess;
789     };
790
791     return ObjCObjectGraph::create(ObjCObjectGraph::transform(objectGraph.rootObject(), Transformer(*this)).get());
792 }
793
794 RefPtr<ObjCObjectGraph> WebProcess::transformObjectsToHandles(ObjCObjectGraph& objectGraph)
795 {
796     struct Transformer final : ObjCObjectGraph::Transformer {
797         bool shouldTransformObject(id object) const override
798         {
799             if (dynamic_objc_cast<WKWebProcessPlugInBrowserContextController>(object))
800                 return true;
801
802             ALLOW_DEPRECATED_DECLARATIONS_BEGIN
803             if (dynamic_objc_cast<WKTypeRefWrapper>(object))
804                 return true;
805             ALLOW_DEPRECATED_DECLARATIONS_END
806             return false;
807         }
808
809         RetainPtr<id> transformObject(id object) const override
810         {
811             if (auto* controller = dynamic_objc_cast<WKWebProcessPlugInBrowserContextController>(object))
812                 return controller.handle;
813
814             ALLOW_DEPRECATED_DECLARATIONS_BEGIN
815             if (auto* wrapper = dynamic_objc_cast<WKTypeRefWrapper>(object))
816                 return adoptNS([[WKTypeRefWrapper alloc] initWithObject:toAPI(transformObjectsToHandles(toImpl(wrapper.object)).get())]);
817             ALLOW_DEPRECATED_DECLARATIONS_END
818             return object;
819         }
820     };
821
822     return ObjCObjectGraph::create(ObjCObjectGraph::transform(objectGraph.rootObject(), Transformer()).get());
823 }
824
825 void WebProcess::destroyRenderingResources()
826 {
827 #if !RELEASE_LOG_DISABLED
828     MonotonicTime startTime = MonotonicTime::now();
829 #endif
830     CABackingStoreCollectBlocking();
831 #if !RELEASE_LOG_DISABLED
832     MonotonicTime endTime = MonotonicTime::now();
833 #endif
834     RELEASE_LOG_IF_ALLOWED(ProcessSuspension, "destroyRenderingResources: took %.2fms", (endTime - startTime).milliseconds());
835 }
836
837 // FIXME: This should live somewhere else, and it should have the implementation in line instead of calling out to WKSI.
838 void _WKSetCrashReportApplicationSpecificInformation(NSString *infoString)
839 {
840     return setCrashReportApplicationSpecificInformation((__bridge CFStringRef)infoString);
841 }
842
843 #if PLATFORM(IOS_FAMILY)
844 void WebProcess::accessibilityProcessSuspendedNotification(bool suspended)
845 {
846     UIAccessibilityPostNotification(kAXPidStatusChangedNotification, @{ @"pid" : @(getpid()), @"suspended" : @(suspended) });
847 }
848
849 bool WebProcess::shouldFreezeOnSuspension() const
850 {
851     switch (m_processType) {
852     case ProcessType::Inspector:
853     case ProcessType::ServiceWorker:
854     case ProcessType::PrewarmedWebContent:
855     case ProcessType::CachedWebContent:
856         return false;
857     case ProcessType::WebContent:
858         break;
859     }
860
861     for (auto& page : m_pageMap.values()) {
862         if (!page->isSuspended())
863             return true;
864     }
865
866     // Since all of the pages in this process were suspended, we should not bother freezing it.
867     return false;
868 }
869
870 void WebProcess::updateFreezerStatus()
871 {
872     bool isFreezable = shouldFreezeOnSuspension();
873     auto result = memorystatus_control(MEMORYSTATUS_CMD_SET_PROCESS_IS_FREEZABLE, getpid(), isFreezable ? 1 : 0, nullptr, 0);
874     if (result)
875         RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSuspension, "updateFreezerStatus: isFreezable: %d, error: %d", isFreezable, result);
876     else
877         RELEASE_LOG_IF_ALLOWED(ProcessSuspension, "updateFreezerStatus: isFreezable: %d, success", isFreezable);
878 }
879 #endif
880
881 #if PLATFORM(MAC) && ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
882 void WebProcess::scrollerStylePreferenceChanged(bool useOverlayScrollbars)
883 {
884     ScrollerStyle::setUseOverlayScrollbars(useOverlayScrollbars);
885
886     ScrollbarTheme& theme = ScrollbarTheme::theme();
887     if (theme.isMockTheme())
888         return;
889
890     static_cast<ScrollbarThemeMac&>(theme).preferencesChanged();
891     
892     NSScrollerStyle style = useOverlayScrollbars ? NSScrollerStyleOverlay : NSScrollerStyleLegacy;
893     [NSScrollerImpPair _updateAllScrollerImpPairsForNewRecommendedScrollerStyle:style];
894 }
895
896 void WebProcess::displayConfigurationChanged(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags)
897 {
898     GraphicsContextGLOpenGLManager::displayWasReconfigured(displayID, flags, nullptr);
899 }
900 #endif
901
902 #if PLATFORM(IOS_FAMILY) && !PLATFORM(MACCATALYST)
903 static float currentBacklightLevel()
904 {
905     return WebProcess::singleton().backlightLevel();
906 }
907
908 void WebProcess::backlightLevelDidChange(float backlightLevel)
909 {
910     m_backlightLevel = backlightLevel;
911
912     static std::once_flag onceFlag;
913     std::call_once(
914         onceFlag,
915         [] {
916             Method methodToPatch = class_getInstanceMethod([UIDevice class], @selector(_backlightLevel));
917             method_setImplementation(methodToPatch, reinterpret_cast<IMP>(currentBacklightLevel));
918         });
919 }
920 #endif
921
922 #if ENABLE(REMOTE_INSPECTOR)
923 void WebProcess::enableRemoteWebInspector(const SandboxExtension::Handle& handle)
924 {
925     SandboxExtension::consumePermanently(handle);
926     Inspector::RemoteInspector::setNeedMachSandboxExtension(false);
927     Inspector::RemoteInspector::singleton();
928 }
929 #endif
930
931 void WebProcess::setMediaMIMETypes(const Vector<String> types)
932 {
933     auto& cache = AVAssetMIMETypeCache::singleton();
934     if (cache.isEmpty())
935         cache.addSupportedTypes(types);
936 }
937
938 #if ENABLE(CFPREFS_DIRECT_MODE)
939 void WebProcess::notifyPreferencesChanged(const String& domain, const String& key, const Optional<String>& encodedValue)
940 {
941     auto defaults = adoptNS([[NSUserDefaults alloc] initWithSuiteName:domain]);
942     if (!encodedValue.hasValue()) {
943         [defaults setObject:nil forKey:key];
944         return;
945     }
946     auto encodedData = adoptNS([[NSData alloc] initWithBase64EncodedString:*encodedValue options:0]);
947     if (!encodedData)
948         return;
949     NSError *err = nil;
950     auto classes = [NSSet setWithArray:@[[NSString class], [NSNumber class], [NSDate class], [NSDictionary class], [NSArray class], [NSData class]]];
951     id object = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:encodedData.get() error:&err];
952     ASSERT(!err);
953     if (err)
954         return;
955     [defaults setObject:object forKey:key];
956 }
957
958 void WebProcess::unblockPreferenceService(SandboxExtension::HandleArray&& handleArray)
959 {
960     SandboxExtension::consumePermanently(handleArray);
961     _CFPrefsSetDirectModeEnabled(false);
962 }
963 #endif
964
965 #if PLATFORM(IOS)
966 void WebProcess::grantAccessToAssetServices(WebKit::SandboxExtension::Handle&& mobileAssetHandle,  WebKit::SandboxExtension::Handle&& mobileAssetV2Handle)
967 {
968     if (m_assetServiceExtension && m_assetServiceV2Extension)
969         return;
970     m_assetServiceExtension = SandboxExtension::create(WTFMove(mobileAssetHandle));
971     m_assetServiceExtension->consume();
972     m_assetServiceV2Extension = SandboxExtension::create(WTFMove(mobileAssetV2Handle));
973     m_assetServiceV2Extension->consume();
974 }
975
976 void WebProcess::revokeAccessToAssetServices()
977 {
978     if (!m_assetServiceExtension || !m_assetServiceV2Extension)
979         return;
980     m_assetServiceExtension->revoke();
981     m_assetServiceExtension = nullptr;
982     m_assetServiceV2Extension->revoke();
983     m_assetServiceV2Extension = nullptr;
984 }
985 #endif
986
987 void WebProcess::setScreenProperties(const ScreenProperties& properties)
988 {
989     WebCore::setScreenProperties(properties);
990     for (auto& page : m_pageMap.values())
991         page->screenPropertiesDidChange();
992 #if PLATFORM(MAC)
993     updatePageScreenProperties();
994 #endif
995 }
996
997 #if PLATFORM(MAC)
998 void WebProcess::updatePageScreenProperties()
999 {
1000     if (hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer)) {
1001         setShouldOverrideScreenSupportsHighDynamicRange(false, false);
1002         return;
1003     }
1004
1005     bool allPagesAreOnHDRScreens = allOf(m_pageMap.values(), [] (auto& page) {
1006         return screenSupportsHighDynamicRange(page->mainFrameView());
1007     });
1008     setShouldOverrideScreenSupportsHighDynamicRange(true, allPagesAreOnHDRScreens);
1009 }
1010 #endif
1011
1012 void WebProcess::unblockServicesRequiredByAccessibility(const SandboxExtension::HandleArray& handleArray)
1013 {
1014 #if PLATFORM(IOS_FAMILY)
1015     bool consumed = SandboxExtension::consumePermanently(handleArray);
1016     ASSERT_UNUSED(consumed, consumed);
1017 #endif
1018     registerWithAccessibility();
1019 }
1020
1021
1022 } // namespace WebKit
1023
1024 #undef RELEASE_LOG_SESSION_ID
1025 #undef RELEASE_LOG_IF_ALLOWED
1026 #undef RELEASE_LOG_ERROR_IF_ALLOWED