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