blob: ee1434bda87b4391145c4689537096d06ae39880 [file] [log] [blame]
/*
* Copyright (C) 2009-2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WebProcess.h"
#include "APIFrameHandle.h"
#include "APIPageHandle.h"
#include "AudioMediaStreamTrackRendererInternalUnitManager.h"
#include "AuthenticationManager.h"
#include "AuxiliaryProcessMessages.h"
#include "DrawingArea.h"
#include "EventDispatcher.h"
#include "GPUProcessConnectionParameters.h"
#include "InjectedBundle.h"
#include "LibWebRTCNetwork.h"
#include "Logging.h"
#include "NetworkConnectionToWebProcessMessages.h"
#include "NetworkProcessConnection.h"
#include "NetworkProcessConnectionInfo.h"
#include "NetworkSession.h"
#include "NetworkSessionCreationParameters.h"
#include "ProcessAssertion.h"
#include "RemoteAudioHardwareListener.h"
#include "RemoteAudioSession.h"
#include "RemoteLegacyCDMFactory.h"
#include "RemoteMediaEngineConfigurationFactory.h"
#include "RemoteRemoteCommandListener.h"
#include "RemoteWebLockRegistry.h"
#include "SpeechRecognitionRealtimeMediaSourceManager.h"
#include "StorageAreaMap.h"
#include "UserData.h"
#include "WebAutomationSessionProxy.h"
#include "WebBroadcastChannelRegistry.h"
#include "WebCacheStorageProvider.h"
#include "WebConnectionToUIProcess.h"
#include "WebCookieJar.h"
#include "WebCoreArgumentCoders.h"
#include "WebFileSystemStorageConnection.h"
#include "WebFrame.h"
#include "WebFrameNetworkingContext.h"
#include "WebGamepadProvider.h"
#include "WebGeolocationManager.h"
#include "WebIDBConnectionToServer.h"
#include "WebLoaderStrategy.h"
#include "WebMediaKeyStorageManager.h"
#include "WebMemorySampler.h"
#include "WebMessagePortChannelProvider.h"
#include "WebPage.h"
#include "WebPageCreationParameters.h"
#include "WebPageGroupProxy.h"
#include "WebPaymentCoordinator.h"
#include "WebPlatformStrategies.h"
#include "WebPluginInfoProvider.h"
#include "WebProcessCreationParameters.h"
#include "WebProcessDataStoreParameters.h"
#include "WebProcessMessages.h"
#include "WebProcessPoolMessages.h"
#include "WebProcessProxyMessages.h"
#include "WebResourceLoadObserver.h"
#include "WebSWClientConnection.h"
#include "WebSWContextManagerConnection.h"
#include "WebSWContextManagerConnectionMessages.h"
#include "WebServiceWorkerProvider.h"
#include "WebSocketStream.h"
#include "WebsiteData.h"
#include "WebsiteDataStoreParameters.h"
#include "WebsiteDataType.h"
#include <JavaScriptCore/JSLock.h>
#include <JavaScriptCore/MemoryStatistics.h>
#include <JavaScriptCore/WasmFaultSignalHandler.h>
#include <WebCore/AXObjectCache.h>
#include <WebCore/ApplicationCacheStorage.h>
#include <WebCore/AuthenticationChallenge.h>
#include <WebCore/BackForwardCache.h>
#include <WebCore/CPUMonitor.h>
#include <WebCore/CommonVM.h>
#include <WebCore/CrossOriginPreflightResultCache.h>
#include <WebCore/DNS.h>
#include <WebCore/DOMWindow.h>
#include <WebCore/DatabaseTracker.h>
#include <WebCore/DeprecatedGlobalSettings.h>
#include <WebCore/DiagnosticLoggingClient.h>
#include <WebCore/DiagnosticLoggingKeys.h>
#include <WebCore/FontCache.h>
#include <WebCore/FontCascade.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameLoader.h>
#include <WebCore/GCController.h>
#include <WebCore/GlyphPage.h>
#include <WebCore/HTMLMediaElement.h>
#include <WebCore/JSDOMWindow.h>
#include <WebCore/LegacySchemeRegistry.h>
#include <WebCore/MediaEngineConfigurationFactory.h>
#include <WebCore/MemoryCache.h>
#include <WebCore/MemoryRelease.h>
#include <WebCore/MessagePort.h>
#include <WebCore/MockRealtimeMediaSourceCenter.h>
#include <WebCore/NetworkStorageSession.h>
#include <WebCore/Page.h>
#include <WebCore/PageGroup.h>
#include <WebCore/PlatformKeyboardEvent.h>
#include <WebCore/PlatformMediaSessionManager.h>
#include <WebCore/ProcessWarming.h>
#include <WebCore/RegistrableDomain.h>
#include <WebCore/RemoteCommandListener.h>
#include <WebCore/ResourceLoadStatistics.h>
#include <WebCore/RuntimeApplicationChecks.h>
#include <WebCore/RuntimeEnabledFeatures.h>
#include <WebCore/ScriptExecutionContext.h>
#include <WebCore/SecurityOrigin.h>
#include <WebCore/ServiceWorkerContextData.h>
#include <WebCore/Settings.h>
#include <WebCore/UserGestureIndicator.h>
#include <pal/Logging.h>
#include <wtf/Algorithms.h>
#include <wtf/CallbackAggregator.h>
#include <wtf/Language.h>
#include <wtf/ProcessPrivilege.h>
#include <wtf/RunLoop.h>
#include <wtf/SystemTracing.h>
#include <wtf/URLParser.h>
#include <wtf/text/StringHash.h>
#if ENABLE(ARKIT_INLINE_PREVIEW_MAC)
#include "ARKitInlinePreviewModelPlayerMac.h"
#endif
#if !OS(WINDOWS)
#include <unistd.h>
#endif
#if PLATFORM(WAYLAND)
#include "WaylandCompositorDisplay.h"
#endif
#if PLATFORM(COCOA)
#include "ObjCObjectGraph.h"
#include "UserMediaCaptureManager.h"
#endif
#if PLATFORM(MAC)
#include <WebCore/DisplayRefreshMonitorManager.h>
#endif
#if PLATFORM(IOS_FAMILY)
#include "WebSQLiteDatabaseTracker.h"
#endif
#if ENABLE(SEC_ITEM_SHIM)
#include "SecItemShim.h"
#endif
#if ENABLE(NOTIFICATIONS)
#include "WebNotificationManager.h"
#endif
#if ENABLE(GPU_PROCESS)
#include "GPUConnectionToWebProcessMessages.h"
#include "GPUProcessConnection.h"
#include "GPUProcessConnectionInfo.h"
#endif
#if ENABLE(WEB_AUTHN)
#include "WebAuthnConnectionToWebProcessMessages.h"
#include "WebAuthnProcessConnection.h"
#include "WebAuthnProcessConnectionInfo.h"
#endif
#if ENABLE(REMOTE_INSPECTOR)
#include <JavaScriptCore/RemoteInspector.h>
#endif
#if ENABLE(GPU_PROCESS)
#include "RemoteMediaPlayerManager.h"
#endif
#if USE(LIBWEBRTC) && PLATFORM(COCOA) && ENABLE(GPU_PROCESS)
#include "LibWebRTCCodecs.h"
#endif
#if ENABLE(ENCRYPTED_MEDIA)
#include "RemoteCDMFactory.h"
#endif
#if PLATFORM(IOS_FAMILY)
#include "RemoteMediaSessionHelper.h"
#endif
#if ENABLE(ROUTING_ARBITRATION)
#include "AudioSessionRoutingArbitrator.h"
#endif
#if ENABLE(GPU_PROCESS) && HAVE(AVASSETREADER)
#include "RemoteImageDecoderAVF.h"
#include <WebCore/ImageDecoder.h>
#endif
#if PLATFORM(COCOA)
#include <WebCore/SystemBattery.h>
#include <WebCore/VP9UtilitiesCocoa.h>
#endif
#if OS(LINUX)
#include <wtf/linux/RealTimeThreads.h>
#endif
#undef WEBPROCESS_RELEASE_LOG
#define RELEASE_LOG_SESSION_ID (m_sessionID ? m_sessionID->toUInt64() : 0)
#if RELEASE_LOG_DISABLED
#define WEBPROCESS_RELEASE_LOG(channel, fmt, ...) UNUSED_VARIABLE(this)
#define WEBPROCESS_RELEASE_LOG_ERROR(channel, fmt, ...) UNUSED_VARIABLE(this)
#else
#define WEBPROCESS_RELEASE_LOG(channel, fmt, ...) RELEASE_LOG(channel, "%p - [sessionID=%" PRIu64 "] WebProcess::" fmt, this, RELEASE_LOG_SESSION_ID, ##__VA_ARGS__)
#define WEBPROCESS_RELEASE_LOG_ERROR(channel, fmt, ...) RELEASE_LOG_ERROR(channel, "%p - [sessionID=%" PRIu64 "] WebProcess::" fmt, this, RELEASE_LOG_SESSION_ID, ##__VA_ARGS__)
#endif
// This should be less than plugInAutoStartExpirationTimeThreshold in PlugInAutoStartProvider.
static const Seconds plugInAutoStartExpirationTimeUpdateThreshold { 29 * 24 * 60 * 60 };
// This should be greater than tileRevalidationTimeout in TileController.
static const Seconds nonVisibleProcessGraphicsCleanupDelay { 10_s };
#if ENABLE(NON_VISIBLE_WEBPROCESS_MEMORY_CLEANUP_TIMER)
// This should be long enough to support a workload where a user is actively switching between multiple tabs,
// since our memory cleanup routine could potentially delete a good amount of JIT code.
static const Seconds nonVisibleProcessMemoryCleanupDelay { 120_s };
#endif
namespace WebKit {
using namespace JSC;
using namespace WebCore;
NO_RETURN static void callExit(IPC::Connection*)
{
#if OS(WINDOWS)
// Calling _exit in non-main threads may cause a deadlock in WTF::Thread::ThreadHolder::~ThreadHolder.
TerminateProcess(GetCurrentProcess(), EXIT_SUCCESS);
#else
_exit(EXIT_SUCCESS);
#endif
}
WebProcess& WebProcess::singleton()
{
static WebProcess& process = *new WebProcess;
return process;
}
WebProcess::WebProcess()
: m_eventDispatcher(EventDispatcher::create())
#if PLATFORM(IOS_FAMILY)
, m_viewUpdateDispatcher(ViewUpdateDispatcher::create())
#endif
, m_webInspectorInterruptDispatcher(WebInspectorInterruptDispatcher::create())
, m_webLoaderStrategy(*new WebLoaderStrategy)
, m_cacheStorageProvider(WebCacheStorageProvider::create())
, m_broadcastChannelRegistry(WebBroadcastChannelRegistry::create())
, m_webLockRegistry(RemoteWebLockRegistry::create(*this))
, m_cookieJar(WebCookieJar::create())
, m_dnsPrefetchHystereris([this](PAL::HysteresisState state) { if (state == PAL::HysteresisState::Stopped) m_dnsPrefetchedHosts.clear(); })
, m_nonVisibleProcessGraphicsCleanupTimer(*this, &WebProcess::nonVisibleProcessGraphicsCleanupTimerFired)
#if ENABLE(NON_VISIBLE_WEBPROCESS_MEMORY_CLEANUP_TIMER)
, m_nonVisibleProcessMemoryCleanupTimer(*this, &WebProcess::nonVisibleProcessMemoryCleanupTimerFired)
#endif
#if PLATFORM(IOS_FAMILY)
, m_webSQLiteDatabaseTracker([this](bool isHoldingLockedFiles) { parentProcessConnection()->send(Messages::WebProcessProxy::SetIsHoldingLockedFiles(isHoldingLockedFiles), 0); })
#endif
{
// Initialize our platform strategies.
WebPlatformStrategies::initialize();
// FIXME: This should moved to where WebProcess::initialize is called,
// so that ports have a chance to customize, and ifdefs in this file are
// limited.
addSupplement<WebGeolocationManager>();
#if ENABLE(NOTIFICATIONS)
addSupplement<WebNotificationManager>();
#endif
#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
addSupplement<WebMediaKeyStorageManager>();
#endif
#if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
addSupplement<UserMediaCaptureManager>();
#endif
#if ENABLE(GPU_PROCESS)
addSupplement<RemoteMediaPlayerManager>();
#endif
#if ENABLE(GPU_PROCESS) && HAVE(AVASSETREADER)
addSupplement<RemoteImageDecoderAVFManager>();
#endif
#if ENABLE(GPU_PROCESS) && ENABLE(ENCRYPTED_MEDIA)
addSupplement<RemoteCDMFactory>();
#endif
#if ENABLE(GPU_PROCESS) && ENABLE(LEGACY_ENCRYPTED_MEDIA)
addSupplement<RemoteLegacyCDMFactory>();
#endif
#if ENABLE(ROUTING_ARBITRATION)
addSupplement<AudioSessionRoutingArbitrator>();
#endif
#if ENABLE(GPU_PROCESS)
addSupplement<RemoteMediaEngineConfigurationFactory>();
#endif
Gigacage::forbidDisablingPrimitiveGigacage();
}
WebProcess::~WebProcess()
{
ASSERT_NOT_REACHED();
}
void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameters& parameters)
{
WTF::setProcessPrivileges({ });
MessagePortChannelProvider::setSharedProvider(WebMessagePortChannelProvider::singleton());
platformInitializeProcess(parameters);
updateCPULimit();
}
void WebProcess::initializeConnection(IPC::Connection* connection)
{
AuxiliaryProcess::initializeConnection(connection);
// We call _exit() directly from the background queue in case the main thread is unresponsive
// and AuxiliaryProcess::didClose() does not get called.
connection->setDidCloseOnConnectionWorkQueueCallback(callExit);
#if !PLATFORM(GTK) && !PLATFORM(WPE) && !ENABLE(IPC_TESTING_API)
connection->setShouldExitOnSyncMessageSendFailure(true);
#endif
m_eventDispatcher->initializeConnection(connection);
#if PLATFORM(IOS_FAMILY)
m_viewUpdateDispatcher->initializeConnection(connection);
#endif // PLATFORM(IOS_FAMILY)
m_webInspectorInterruptDispatcher->initializeConnection(connection);
for (auto& supplement : m_supplements.values())
supplement->initializeConnection(connection);
m_webConnection = WebConnectionToUIProcess::create(this);
}
static void scheduleLogMemoryStatistics(LogMemoryStatisticsReason reason)
{
// Log stats in the next turn of the run loop so that it runs after the low memory handler.
RunLoop::main().dispatch([reason] {
WebCore::logMemoryStatistics(reason);
});
}
void WebProcess::initializeWebProcess(WebProcessCreationParameters&& parameters)
{
TraceScope traceScope(InitializeWebProcessStart, InitializeWebProcessEnd);
ASSERT(m_pageMap.isEmpty());
if (parameters.websiteDataStoreParameters)
setWebsiteDataStoreParameters(WTFMove(*parameters.websiteDataStoreParameters));
WebCore::setPresentingApplicationPID(parameters.presentingApplicationPID);
#if OS(LINUX)
MemoryPressureHandler::ReliefLogger::setLoggingEnabled(parameters.shouldEnableMemoryPressureReliefLogging);
#endif
platformInitializeWebProcess(parameters);
// Match the QoS of the UIProcess and the scrolling thread but use a slightly lower priority.
WTF::Thread::setCurrentThreadIsUserInteractive(-1);
m_suppressMemoryPressureHandler = parameters.shouldSuppressMemoryPressureHandler;
if (!m_suppressMemoryPressureHandler) {
auto& memoryPressureHandler = MemoryPressureHandler::singleton();
memoryPressureHandler.setLowMemoryHandler([this] (Critical critical, Synchronous synchronous) {
// If this process contains only non-visible content (e.g. only contains background
// tabs), then treat the memory warning as if it was a critical warning to maximize the
// amount of memory released for foreground apps to use.
if (m_pagesInWindows.isEmpty() && critical == Critical::No)
critical = Critical::Yes;
#if PLATFORM(MAC)
// If this is a process we keep around for performance, kill it on memory pressure instead of trying to free up its memory.
if (m_processType == ProcessType::CachedWebContent || m_processType == ProcessType::PrewarmedWebContent || areAllPagesSuspended()) {
if (m_processType == ProcessType::CachedWebContent)
WEBPROCESS_RELEASE_LOG(Process, "initializeWebProcess: Cached WebProcess is exiting due to memory pressure");
else if (m_processType == ProcessType::PrewarmedWebContent)
WEBPROCESS_RELEASE_LOG(Process, "initializeWebProcess: Prewarmed WebProcess is exiting due to memory pressure");
else
WEBPROCESS_RELEASE_LOG(Process, "initializeWebProcess: Suspended WebProcess is exiting due to memory pressure");
stopRunLoop();
return;
}
#endif
auto maintainBackForwardCache = m_isSuspending ? WebCore::MaintainBackForwardCache::Yes : WebCore::MaintainBackForwardCache::No;
auto maintainMemoryCache = m_isSuspending && m_hasSuspendedPageProxy ? WebCore::MaintainMemoryCache::Yes : WebCore::MaintainMemoryCache::No;
WebCore::releaseMemory(critical, synchronous, maintainBackForwardCache, maintainMemoryCache);
for (auto& page : m_pageMap.values())
page->releaseMemory(critical);
});
#if ENABLE(PERIODIC_MEMORY_MONITOR)
memoryPressureHandler.setShouldUsePeriodicMemoryMonitor(true);
memoryPressureHandler.setMemoryKillCallback([this] () {
WebCore::logMemoryStatistics(LogMemoryStatisticsReason::OutOfMemoryDeath);
if (MemoryPressureHandler::singleton().processState() == WebsamProcessState::Active)
parentProcessConnection()->send(Messages::WebProcessProxy::DidExceedActiveMemoryLimit(), 0);
else
parentProcessConnection()->send(Messages::WebProcessProxy::DidExceedInactiveMemoryLimit(), 0);
});
memoryPressureHandler.setDidExceedInactiveLimitWhileActiveCallback([this] () {
parentProcessConnection()->send(Messages::WebProcessProxy::DidExceedInactiveMemoryLimitWhileActive(), 0);
if (!m_loggedProcessLimitCriticalMemoryStatistics) {
m_loggedProcessLimitCriticalMemoryStatistics = true;
scheduleLogMemoryStatistics(LogMemoryStatisticsReason::CriticalMemoryPressureNotification);
}
});
#endif
memoryPressureHandler.setMemoryPressureStatusChangedCallback([this](WTF::MemoryPressureStatus memoryPressureStatus) {
if (parentProcessConnection())
parentProcessConnection()->send(Messages::WebProcessProxy::MemoryPressureStatusChanged(MemoryPressureHandler::singleton().isUnderMemoryPressure()), 0);
if (memoryPressureStatus == WTF::MemoryPressureStatus::ProcessLimitWarning && !m_loggedProcessLimitWarningMemoryStatistics) {
m_loggedProcessLimitWarningMemoryStatistics = true;
scheduleLogMemoryStatistics(LogMemoryStatisticsReason::WarningMemoryPressureNotification);
} else if (memoryPressureStatus == WTF::MemoryPressureStatus::ProcessLimitCritical && !m_loggedProcessLimitCriticalMemoryStatistics) {
m_loggedProcessLimitCriticalMemoryStatistics = true;
scheduleLogMemoryStatistics(LogMemoryStatisticsReason::CriticalMemoryPressureNotification);
}
});
memoryPressureHandler.install();
PAL::registerNotifyCallback("com.apple.WebKit.logMemStats", [] {
WebCore::logMemoryStatistics(LogMemoryStatisticsReason::DebugNotification);
});
}
SandboxExtension::consumePermanently(parameters.additionalSandboxExtensionHandles);
if (!parameters.injectedBundlePath.isEmpty())
m_injectedBundle = InjectedBundle::create(parameters, transformHandlesToObjects(parameters.initializationUserData.object()).get());
for (auto& supplement : m_supplements.values())
supplement->initialize(parameters);
setCacheModel(parameters.cacheModel);
if (!parameters.overrideLanguages.isEmpty()) {
LOG_WITH_STREAM(Language, stream << "Web Process initialization is setting overrideLanguages: " << parameters.overrideLanguages);
overrideUserPreferredLanguages(parameters.overrideLanguages);
} else
LOG(Language, "Web process initialization is not setting overrideLanguages");
m_textCheckerState = parameters.textCheckerState;
m_fullKeyboardAccessEnabled = parameters.fullKeyboardAccessEnabled;
#if HAVE(MOUSE_DEVICE_OBSERVATION)
m_hasMouseDevice = parameters.hasMouseDevice;
#endif
#if HAVE(STYLUS_DEVICE_OBSERVATION)
m_hasStylusDevice = parameters.hasStylusDevice;
#endif
for (auto& scheme : parameters.urlSchemesRegisteredAsEmptyDocument)
registerURLSchemeAsEmptyDocument(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsSecure)
registerURLSchemeAsSecure(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsBypassingContentSecurityPolicy)
registerURLSchemeAsBypassingContentSecurityPolicy(scheme);
for (auto& scheme : parameters.urlSchemesForWhichDomainRelaxationIsForbidden)
setDomainRelaxationForbiddenForURLScheme(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsLocal)
registerURLSchemeAsLocal(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsNoAccess)
registerURLSchemeAsNoAccess(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsDisplayIsolated)
registerURLSchemeAsDisplayIsolated(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsCORSEnabled)
LegacySchemeRegistry::registerURLSchemeAsCORSEnabled(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsAlwaysRevalidated)
registerURLSchemeAsAlwaysRevalidated(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsCachePartitioned)
registerURLSchemeAsCachePartitioned(scheme);
for (auto& scheme : parameters.urlSchemesRegisteredAsCanDisplayOnlyIfCanRequest)
registerURLSchemeAsCanDisplayOnlyIfCanRequest(scheme);
setDefaultRequestTimeoutInterval(parameters.defaultRequestTimeoutInterval);
setBackForwardCacheCapacity(parameters.backForwardCacheCapacity);
setAlwaysUsesComplexTextCodePath(parameters.shouldAlwaysUseComplexTextCodePath);
setShouldUseFontSmoothing(parameters.shouldUseFontSmoothing);
setTerminationTimeout(parameters.terminationTimeout);
setMemoryCacheDisabled(parameters.memoryCacheDisabled);
WebCore::RuntimeEnabledFeatures::sharedFeatures().setAttrStyleEnabled(parameters.attrStyleEnabled);
commonVM().setGlobalConstRedeclarationShouldThrow(parameters.shouldThrowExceptionForGlobalConstantRedeclaration);
ScriptExecutionContext::setCrossOriginMode(parameters.crossOriginMode);
m_isCaptivePortalModeEnabled = parameters.isCaptivePortalModeEnabled;
#if ENABLE(SERVICE_CONTROLS)
setEnabledServices(parameters.hasImageServices, parameters.hasSelectionServices, parameters.hasRichContentServices);
#endif
#if ENABLE(REMOTE_INSPECTOR) && PLATFORM(COCOA)
if (std::optional<audit_token_t> auditToken = parentProcessConnection()->getAuditToken()) {
RetainPtr<CFDataRef> auditData = adoptCF(CFDataCreate(nullptr, (const UInt8*)&*auditToken, sizeof(*auditToken)));
Inspector::RemoteInspector::singleton().setParentProcessInformation(WebCore::presentingApplicationPID(), auditData);
}
#endif
#if ENABLE(GAMEPAD)
GamepadProvider::singleton().setSharedProvider(WebGamepadProvider::singleton());
#endif
#if ENABLE(SERVICE_WORKER)
ServiceWorkerProvider::setSharedProvider(WebServiceWorkerProvider::singleton());
#endif
#if ENABLE(WEBASSEMBLY)
JSC::Wasm::enableFastMemory();
#endif
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION) && !RELEASE_LOG_DISABLED
WebResourceLoadObserver::setShouldLogUserInteraction(parameters.shouldLogUserInteraction);
#endif
#if PLATFORM(COCOA)
if (m_processType == ProcessType::PrewarmedWebContent)
prewarmGlobally();
#endif
WEBPROCESS_RELEASE_LOG(Process, "initializeWebProcess: Presenting processPID=%d", WebCore::presentingApplicationPID());
}
void WebProcess::setWebsiteDataStoreParameters(WebProcessDataStoreParameters&& parameters)
{
ASSERT(!m_sessionID);
m_sessionID = parameters.sessionID;
// FIXME: This should be constructed per data store, not per process.
m_applicationCacheStorage = ApplicationCacheStorage::create(parameters.applicationCacheDirectory, parameters.applicationCacheFlatFileSubdirectoryName);
#if PLATFORM(IOS_FAMILY)
m_applicationCacheStorage->setDefaultOriginQuota(25ULL * 1024 * 1024);
#endif
#if ENABLE(VIDEO)
if (!parameters.mediaCacheDirectory.isEmpty())
WebCore::HTMLMediaElement::setMediaCacheDirectory(parameters.mediaCacheDirectory);
#endif
#if ENABLE(ARKIT_INLINE_PREVIEW_MAC)
if (!parameters.modelElementCacheDirectory.isEmpty())
ARKitInlinePreviewModelPlayerMac::setModelElementCacheDirectory(parameters.modelElementCacheDirectory);
#endif
setResourceLoadStatisticsEnabled(parameters.resourceLoadStatisticsEnabled);
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
m_thirdPartyCookieBlockingMode = parameters.thirdPartyCookieBlockingMode;
if (parameters.resourceLoadStatisticsEnabled) {
if (!ResourceLoadObserver::sharedIfExists())
ResourceLoadObserver::setShared(*new WebResourceLoadObserver(parameters.sessionID.isEphemeral() ? WebCore::ResourceLoadStatistics::IsEphemeral::Yes : WebCore::ResourceLoadStatistics::IsEphemeral::No));
ResourceLoadObserver::shared().setDomainsWithUserInteraction(WTFMove(parameters.domainsWithUserInteraction));
if (!parameters.sessionID.isEphemeral())
ResourceLoadObserver::shared().setDomainsWithCrossPageStorageAccess(WTFMove(parameters.domainsWithStorageAccessQuirk), [] { });
}
#endif
for (auto& supplement : m_supplements.values())
supplement->setWebsiteDataStore(parameters);
platformSetWebsiteDataStoreParameters(WTFMove(parameters));
ensureNetworkProcessConnection();
}
bool WebProcess::areAllPagesSuspended() const
{
for (auto& page : m_pageMap.values()) {
if (!page->isSuspended())
return false;
}
return true;
}
void WebProcess::setHasSuspendedPageProxy(bool hasSuspendedPageProxy)
{
ASSERT(m_hasSuspendedPageProxy != hasSuspendedPageProxy);
m_hasSuspendedPageProxy = hasSuspendedPageProxy;
}
void WebProcess::setIsInProcessCache(bool isInProcessCache)
{
#if PLATFORM(COCOA)
if (isInProcessCache) {
ASSERT(m_processType == ProcessType::WebContent);
m_processType = ProcessType::CachedWebContent;
} else {
ASSERT(m_processType == ProcessType::CachedWebContent);
m_processType = ProcessType::WebContent;
}
updateProcessName(IsInProcessInitialization::No);
IPC::AccessibilityProcessSuspendedNotification(isInProcessCache);
#else
UNUSED_PARAM(isInProcessCache);
#endif
}
void WebProcess::markIsNoLongerPrewarmed()
{
#if PLATFORM(COCOA)
ASSERT(m_processType == ProcessType::PrewarmedWebContent);
m_processType = ProcessType::WebContent;
updateProcessName(IsInProcessInitialization::No);
#endif
}
void WebProcess::prewarmGlobally()
{
if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) {
RELEASE_LOG(PerformanceLogging, "WebProcess::prewarmGlobally: Not prewarming because the system in under memory pressure");
return;
}
WebCore::ProcessWarming::prewarmGlobally();
}
void WebProcess::prewarmWithDomainInformation(const WebCore::PrewarmInformation& prewarmInformation)
{
WebCore::ProcessWarming::prewarmWithInformation(prewarmInformation);
}
void WebProcess::registerURLSchemeAsEmptyDocument(const String& urlScheme)
{
LegacySchemeRegistry::registerURLSchemeAsEmptyDocument(urlScheme);
}
void WebProcess::registerURLSchemeAsSecure(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsSecure(urlScheme);
}
void WebProcess::registerURLSchemeAsBypassingContentSecurityPolicy(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(urlScheme);
}
void WebProcess::setDomainRelaxationForbiddenForURLScheme(const String& urlScheme) const
{
LegacySchemeRegistry::setDomainRelaxationForbiddenForURLScheme(true, urlScheme);
}
void WebProcess::registerURLSchemeAsLocal(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsLocal(urlScheme);
}
void WebProcess::registerURLSchemeAsNoAccess(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsNoAccess(urlScheme);
}
void WebProcess::registerURLSchemeAsDisplayIsolated(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsDisplayIsolated(urlScheme);
}
void WebProcess::registerURLSchemeAsCORSEnabled(const String& urlScheme)
{
LegacySchemeRegistry::registerURLSchemeAsCORSEnabled(urlScheme);
ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::RegisterURLSchemesAsCORSEnabled({ urlScheme }), 0);
}
void WebProcess::registerURLSchemeAsAlwaysRevalidated(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsAlwaysRevalidated(urlScheme);
}
void WebProcess::registerURLSchemeAsCachePartitioned(const String& urlScheme) const
{
LegacySchemeRegistry::registerURLSchemeAsCachePartitioned(urlScheme);
}
void WebProcess::registerURLSchemeAsCanDisplayOnlyIfCanRequest(const String& urlScheme) const
{
LegacySchemeRegistry::registerAsCanDisplayOnlyIfCanRequest(urlScheme);
}
void WebProcess::setDefaultRequestTimeoutInterval(double timeoutInterval)
{
ResourceRequest::setDefaultTimeoutInterval(timeoutInterval);
}
void WebProcess::setAlwaysUsesComplexTextCodePath(bool alwaysUseComplexText)
{
WebCore::FontCascade::setCodePath(alwaysUseComplexText ? WebCore::FontCascade::CodePath::Complex : WebCore::FontCascade::CodePath::Auto);
}
void WebProcess::setShouldUseFontSmoothing(bool useFontSmoothing)
{
WebCore::FontCascade::setShouldUseSmoothing(useFontSmoothing);
}
void WebProcess::userPreferredLanguagesChanged(const Vector<String>& languages) const
{
LOG_WITH_STREAM(Language, stream << "The web process's userPreferredLanguagesChanged: " << languages);
overrideUserPreferredLanguages(languages);
}
void WebProcess::fullKeyboardAccessModeChanged(bool fullKeyboardAccessEnabled)
{
m_fullKeyboardAccessEnabled = fullKeyboardAccessEnabled;
}
void WebProcess::setCacheModel(CacheModel cacheModel)
{
if (m_hasSetCacheModel && (cacheModel == m_cacheModel))
return;
m_hasSetCacheModel = true;
m_cacheModel = cacheModel;
unsigned cacheTotalCapacity = 0;
unsigned cacheMinDeadCapacity = 0;
unsigned cacheMaxDeadCapacity = 0;
Seconds deadDecodedDataDeletionInterval;
unsigned backForwardCacheSize = 0;
calculateMemoryCacheSizes(cacheModel, cacheTotalCapacity, cacheMinDeadCapacity, cacheMaxDeadCapacity, deadDecodedDataDeletionInterval, backForwardCacheSize);
auto& memoryCache = MemoryCache::singleton();
memoryCache.setCapacities(cacheMinDeadCapacity, cacheMaxDeadCapacity, cacheTotalCapacity);
memoryCache.setDeadDecodedDataDeletionInterval(deadDecodedDataDeletionInterval);
BackForwardCache::singleton().setMaxSize(backForwardCacheSize);
platformSetCacheModel(cacheModel);
}
WebPage* WebProcess::focusedWebPage() const
{
for (auto& page : m_pageMap.values()) {
if (page->windowAndWebPageAreFocused())
return page.get();
}
return 0;
}
WebPage* WebProcess::webPage(PageIdentifier pageID) const
{
return m_pageMap.get(pageID);
}
void WebProcess::createWebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters)
{
// It is necessary to check for page existence here since during a window.open() (or targeted
// link) the WebPage gets created both in the synchronous handler and through the normal way.
auto result = m_pageMap.add(pageID, nullptr);
if (result.isNewEntry) {
ASSERT(!result.iterator->value);
auto page = WebPage::create(pageID, WTFMove(parameters));
result.iterator->value = page.ptr();
#if ENABLE(GPU_PROCESS)
if (m_gpuProcessConnection)
page->gpuProcessConnectionDidBecomeAvailable(*m_gpuProcessConnection);
#endif
// Balanced by an enableTermination in removeWebPage.
disableTermination();
updateCPULimit();
#if OS(LINUX)
RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
#endif
} else
result.iterator->value->reinitializeWebPage(WTFMove(parameters));
ASSERT(result.iterator->value);
}
void WebProcess::removeWebPage(PageIdentifier pageID)
{
ASSERT(m_pageMap.contains(pageID));
flushResourceLoadStatistics();
pageWillLeaveWindow(pageID);
m_pageMap.remove(pageID);
enableTermination();
updateCPULimit();
#if OS(LINUX)
RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
#endif
}
bool WebProcess::shouldTerminate()
{
ASSERT(m_pageMap.isEmpty());
// FIXME: the ShouldTerminate message should also send termination parameters, such as any session cookies that need to be preserved.
bool shouldTerminate = false;
if (parentProcessConnection()->sendSync(Messages::WebProcessProxy::ShouldTerminate(), Messages::WebProcessProxy::ShouldTerminate::Reply(shouldTerminate), 0)
&& !shouldTerminate)
return false;
return true;
}
void WebProcess::terminate()
{
#ifndef NDEBUG
// These are done in an attempt to reduce LEAK output.
GCController::singleton().garbageCollectNow();
FontCache::forCurrentThread().invalidate();
MemoryCache::singleton().setDisabled(true);
#endif
m_webConnection->invalidate();
m_webConnection = nullptr;
platformTerminate();
AuxiliaryProcess::terminate();
}
bool WebProcess::didReceiveSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, UniqueRef<IPC::Encoder>& replyEncoder)
{
if (messageReceiverMap().dispatchSyncMessage(connection, decoder, replyEncoder))
return true;
return false;
}
void WebProcess::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
{
if (messageReceiverMap().dispatchMessage(connection, decoder))
return;
if (decoder.messageReceiverName() == Messages::WebProcess::messageReceiverName()) {
didReceiveWebProcessMessage(connection, decoder);
return;
}
if (decoder.messageReceiverName() == Messages::AuxiliaryProcess::messageReceiverName()) {
AuxiliaryProcess::didReceiveMessage(connection, decoder);
return;
}
#if ENABLE(SERVICE_WORKER)
// FIXME: Remove?
if (decoder.messageReceiverName() == Messages::WebSWContextManagerConnection::messageReceiverName()) {
ASSERT(SWContextManager::singleton().connection());
if (auto* contextManagerConnection = SWContextManager::singleton().connection())
static_cast<WebSWContextManagerConnection&>(*contextManagerConnection).didReceiveMessage(connection, decoder);
return;
}
#endif
LOG_ERROR("Unhandled web process message '%s' (destination: %" PRIu64 " pid: %d)", description(decoder.messageName()), decoder.destinationID(), static_cast<int>(getCurrentProcessID()));
}
WebFrame* WebProcess::webFrame(FrameIdentifier frameID) const
{
return m_frameMap.get(frameID);
}
Vector<WebFrame*> WebProcess::webFrames() const
{
return copyToVector(m_frameMap.values());
}
void WebProcess::addWebFrame(FrameIdentifier frameID, WebFrame* frame)
{
m_frameMap.set(frameID, frame);
}
void WebProcess::removeWebFrame(FrameIdentifier frameID)
{
m_frameMap.remove(frameID);
// We can end up here after our connection has closed when WebCore's frame life-support timer
// fires when the application is shutting down. There's no need (and no way) to update the UI
// process in this case.
if (!parentProcessConnection())
return;
parentProcessConnection()->send(Messages::WebProcessProxy::DidDestroyFrame(frameID), 0);
}
WebPageGroupProxy* WebProcess::webPageGroup(PageGroup* pageGroup)
{
for (auto& page : m_pageGroupMap.values()) {
if (page->corePageGroup() == pageGroup)
return page.get();
}
return 0;
}
WebPageGroupProxy* WebProcess::webPageGroup(PageGroupIdentifier pageGroupID)
{
return m_pageGroupMap.get(pageGroupID);
}
WebPageGroupProxy* WebProcess::webPageGroup(const WebPageGroupData& pageGroupData)
{
auto result = m_pageGroupMap.add(pageGroupData.pageGroupID, nullptr);
if (result.isNewEntry) {
ASSERT(!result.iterator->value);
result.iterator->value = WebPageGroupProxy::create(pageGroupData);
}
return result.iterator->value.get();
}
static uint64_t nextUserGestureTokenIdentifier()
{
static uint64_t identifier = 1;
return identifier++;
}
uint64_t WebProcess::userGestureTokenIdentifier(RefPtr<UserGestureToken> token)
{
if (!token || !token->processingUserGesture())
return 0;
auto result = m_userGestureTokens.ensure(token.get(), [] { return nextUserGestureTokenIdentifier(); });
if (result.isNewEntry) {
result.iterator->key->addDestructionObserver([] (UserGestureToken& tokenBeingDestroyed) {
WebProcess::singleton().userGestureTokenDestroyed(tokenBeingDestroyed);
});
}
return result.iterator->value;
}
void WebProcess::userGestureTokenDestroyed(UserGestureToken& token)
{
auto identifier = m_userGestureTokens.take(&token);
parentProcessConnection()->send(Messages::WebProcessProxy::DidDestroyUserGestureToken(identifier), 0);
}
void WebProcess::isJITEnabled(CompletionHandler<void(bool)>&& completionHandler)
{
completionHandler(JSC::Options::useJIT());
}
void WebProcess::refreshPlugins()
{
}
void WebProcess::garbageCollectJavaScriptObjects()
{
GCController::singleton().garbageCollectNow();
}
void WebProcess::backgroundResponsivenessPing()
{
parentProcessConnection()->send(Messages::WebProcessProxy::DidReceiveBackgroundResponsivenessPing(), 0);
}
void WebProcess::messagesAvailableForPort(const MessagePortIdentifier& identifier)
{
MessagePort::notifyMessageAvailable(identifier);
}
#if HAVE(MOUSE_DEVICE_OBSERVATION)
void WebProcess::setHasMouseDevice(bool hasMouseDevice)
{
if (hasMouseDevice == m_hasMouseDevice)
return;
m_hasMouseDevice = hasMouseDevice;
Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
}
#endif // HAVE(MOUSE_DEVICE_OBSERVATION)
#if HAVE(STYLUS_DEVICE_OBSERVATION)
void WebProcess::setHasStylusDevice(bool hasStylusDevice)
{
if (hasStylusDevice == m_hasStylusDevice)
return;
m_hasStylusDevice = hasStylusDevice;
Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
}
#endif // HAVE(STYLUS_DEVICE_OBSERVATION)
#if ENABLE(GAMEPAD)
void WebProcess::setInitialGamepads(const Vector<WebKit::GamepadData>& gamepadDatas)
{
WebGamepadProvider::singleton().setInitialGamepads(gamepadDatas);
}
void WebProcess::gamepadConnected(const GamepadData& gamepadData, WebCore::EventMakesGamepadsVisible eventVisibility)
{
WebGamepadProvider::singleton().gamepadConnected(gamepadData, eventVisibility);
}
void WebProcess::gamepadDisconnected(unsigned index)
{
WebGamepadProvider::singleton().gamepadDisconnected(index);
}
#endif
void WebProcess::setJavaScriptGarbageCollectorTimerEnabled(bool flag)
{
GCController::singleton().setJavaScriptGarbageCollectorTimerEnabled(flag);
}
void WebProcess::handleInjectedBundleMessage(const String& messageName, const UserData& messageBody)
{
InjectedBundle* injectedBundle = WebProcess::singleton().injectedBundle();
if (!injectedBundle)
return;
injectedBundle->didReceiveMessage(messageName, transformHandlesToObjects(messageBody.object()).get());
}
void WebProcess::setInjectedBundleParameter(const String& key, const IPC::DataReference& value)
{
InjectedBundle* injectedBundle = WebProcess::singleton().injectedBundle();
if (!injectedBundle)
return;
injectedBundle->setBundleParameter(key, value);
}
void WebProcess::setInjectedBundleParameters(const IPC::DataReference& value)
{
InjectedBundle* injectedBundle = WebProcess::singleton().injectedBundle();
if (!injectedBundle)
return;
injectedBundle->setBundleParameters(value);
}
static NetworkProcessConnectionInfo getNetworkProcessConnection(IPC::Connection& connection)
{
NetworkProcessConnectionInfo connectionInfo;
auto requestConnection = [&] {
if (!connection.isValid()) {
// Connection to UIProcess has been severed, exit cleanly.
exit(0);
}
if (!connection.sendSync(Messages::WebProcessProxy::GetNetworkProcessConnection(), Messages::WebProcessProxy::GetNetworkProcessConnection::Reply(connectionInfo), 0)) {
RELEASE_LOG_ERROR(Process, "getNetworkProcessConnection: Failed to send or receive message");
return false;
}
return IPC::Connection::identifierIsValid(connectionInfo.identifier());
};
static constexpr unsigned maxFailedAttempts = 10;
unsigned failedAttempts = 0;
while (!requestConnection()) {
if (++failedAttempts >= maxFailedAttempts)
CRASH();
RELEASE_LOG_ERROR(Process, "getNetworkProcessConnection: Failed to get connection to network process, will retry...");
// If we failed, retry after a delay. The attachment may have become invalid
// before it was received by the web process if the network process crashed.
sleep(100_ms);
}
return connectionInfo;
}
NetworkProcessConnection& WebProcess::ensureNetworkProcessConnection()
{
RELEASE_ASSERT(RunLoop::isMain());
ASSERT(m_sessionID);
// If we've lost our connection to the network process (e.g. it crashed) try to re-establish it.
if (!m_networkProcessConnection) {
auto connectionInfo = getNetworkProcessConnection(*parentProcessConnection());
m_networkProcessConnection = NetworkProcessConnection::create(connectionInfo.releaseIdentifier(), connectionInfo.cookieAcceptPolicy);
#if HAVE(AUDIT_TOKEN)
m_networkProcessConnection->setNetworkProcessAuditToken(WTFMove(connectionInfo.auditToken));
#endif
m_networkProcessConnection->connection().send(Messages::NetworkConnectionToWebProcess::RegisterURLSchemesAsCORSEnabled(WebCore::LegacySchemeRegistry::allURLSchemesRegisteredAsCORSEnabled()), 0);
#if ENABLE(SERVICE_WORKER)
if (!Document::allDocuments().isEmpty())
m_networkProcessConnection->serviceWorkerConnection().registerServiceWorkerClients();
#endif
// This can be called during a WebPage's constructor, so wait until after the constructor returns to touch the WebPage.
RunLoop::main().dispatch([this] {
for (auto& webPage : m_pageMap.values())
webPage->synchronizeCORSDisablingPatternsWithNetworkProcess();
});
}
return *m_networkProcessConnection;
}
void WebProcess::logDiagnosticMessageForNetworkProcessCrash()
{
WebCore::Page* page = nullptr;
if (auto* webPage = focusedWebPage())
page = webPage->corePage();
if (!page) {
for (auto& webPage : m_pageMap.values()) {
if (auto* corePage = webPage->corePage()) {
page = corePage;
break;
}
}
}
if (page)
page->diagnosticLoggingClient().logDiagnosticMessage(WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::networkProcessCrashedKey(), WebCore::ShouldSample::No);
}
void WebProcess::networkProcessConnectionClosed(NetworkProcessConnection* connection)
{
#if OS(DARWIN)
WEBPROCESS_RELEASE_LOG(Loading, "networkProcessConnectionClosed: NetworkProcess (%d) closed its connection (Crashed)", connection ? connection->connection().remoteProcessID() : 0);
#else
WEBPROCESS_RELEASE_LOG(Loading, "networkProcessConnectionClosed: NetworkProcess closed its connection (Crashed)");
#endif
ASSERT(m_networkProcessConnection);
ASSERT_UNUSED(connection, m_networkProcessConnection == connection);
for (auto key : copyToVector(m_storageAreaMaps.keys())) {
if (auto map = m_storageAreaMaps.get(key))
map->disconnect();
}
for (auto& page : m_pageMap.values()) {
auto idbConnection = page->corePage()->optionalIDBConnection();
if (!idbConnection)
continue;
if (auto* existingIDBConnectionToServer = connection->existingIDBConnectionToServer()) {
ASSERT_UNUSED(existingIDBConnectionToServer, idbConnection == &existingIDBConnectionToServer->coreConnectionToServer());
page->corePage()->clearIDBConnection();
}
}
#if ENABLE(SERVICE_WORKER)
if (SWContextManager::singleton().connection())
SWContextManager::singleton().stopAllServiceWorkers();
#endif
m_networkProcessConnection = nullptr;
logDiagnosticMessageForNetworkProcessCrash();
m_webLoaderStrategy.networkProcessCrashed();
WebSocketStream::networkProcessCrashed();
m_webSocketChannelManager.networkProcessCrashed();
m_broadcastChannelRegistry->networkProcessCrashed();
if (m_libWebRTCNetwork)
m_libWebRTCNetwork->networkProcessCrashed();
for (auto& page : m_pageMap.values()) {
page->stopAllURLSchemeTasks();
#if ENABLE(APPLE_PAY)
if (auto paymentCoordinator = page->paymentCoordinator())
paymentCoordinator->networkProcessConnectionClosed();
#endif
}
// Recreate a new connection with valid IPC connection on next operation.
if (m_fileSystemStorageConnection) {
m_fileSystemStorageConnection->connectionClosed();
m_fileSystemStorageConnection = nullptr;
}
}
WebFileSystemStorageConnection& WebProcess::fileSystemStorageConnection()
{
if (!m_fileSystemStorageConnection)
m_fileSystemStorageConnection = WebFileSystemStorageConnection::create(ensureNetworkProcessConnection().connection());
return *m_fileSystemStorageConnection;
}
WebLoaderStrategy& WebProcess::webLoaderStrategy()
{
return m_webLoaderStrategy;
}
#if ENABLE(GPU_PROCESS)
#if !PLATFORM(COCOA)
void WebProcess::platformInitializeGPUProcessConnectionParameters(GPUProcessConnectionParameters&)
{
}
#endif
GPUProcessConnectionInfo WebProcess::getGPUProcessConnection(IPC::Connection& connection)
{
GPUProcessConnectionParameters parameters;
platformInitializeGPUProcessConnectionParameters(parameters);
IPC::UnboundedSynchronousIPCScope unboundedSynchronousIPCScope;
GPUProcessConnectionInfo connectionInfo;
if (!connection.sendSync(Messages::WebProcessProxy::GetGPUProcessConnection(parameters), Messages::WebProcessProxy::GetGPUProcessConnection::Reply(connectionInfo), 0)) {
// If we failed the first time, retry once. The attachment may have become invalid
// before it was received by the web process if the network process crashed.
if (!connection.sendSync(Messages::WebProcessProxy::GetGPUProcessConnection(parameters), Messages::WebProcessProxy::GetGPUProcessConnection::Reply(connectionInfo), 0))
CRASH();
}
return connectionInfo;
}
GPUProcessConnection& WebProcess::ensureGPUProcessConnection()
{
RELEASE_ASSERT(RunLoop::isMain());
// If we've lost our connection to the GPU process (e.g. it crashed) try to re-establish it.
if (!m_gpuProcessConnection) {
auto connectionInfo = getGPUProcessConnection(*parentProcessConnection());
// Retry once if the IPC to get the connectionIdentifier succeeded but the connectionIdentifier we received
// is invalid. This may indicate that the GPU process has crashed.
if (!IPC::Connection::identifierIsValid(connectionInfo.identifier()))
connectionInfo = getGPUProcessConnection(*parentProcessConnection());
if (!IPC::Connection::identifierIsValid(connectionInfo.identifier()))
CRASH();
m_gpuProcessConnection = GPUProcessConnection::create(connectionInfo.releaseIdentifier(), connectionInfo.parameters);
#if HAVE(AUDIT_TOKEN)
ASSERT(connectionInfo.auditToken);
m_gpuProcessConnection->setAuditToken(WTFMove(connectionInfo.auditToken));
#endif
#if ENABLE(IPC_TESTING_API)
if (parentProcessConnection()->ignoreInvalidMessageForTesting())
m_gpuProcessConnection->connection().setIgnoreInvalidMessageForTesting();
#endif
for (auto& page : m_pageMap.values()) {
// If page is null, then it is currently being constructed.
if (page)
page->gpuProcessConnectionDidBecomeAvailable(*m_gpuProcessConnection);
}
}
return *m_gpuProcessConnection;
}
void WebProcess::gpuProcessConnectionClosed(GPUProcessConnection& connection)
{
ASSERT(m_gpuProcessConnection);
ASSERT_UNUSED(connection, m_gpuProcessConnection == &connection);
m_gpuProcessConnection = nullptr;
#if ENABLE(MEDIA_STREAM) && PLATFORM(COCOA)
if (m_audioMediaStreamTrackRendererInternalUnitManager)
m_audioMediaStreamTrackRendererInternalUnitManager->restartAllUnits();
#endif
}
#if PLATFORM(COCOA) && USE(LIBWEBRTC)
LibWebRTCCodecs& WebProcess::libWebRTCCodecs()
{
if (!m_libWebRTCCodecs)
m_libWebRTCCodecs = LibWebRTCCodecs::create();
return *m_libWebRTCCodecs;
}
#endif
#if ENABLE(MEDIA_STREAM) && PLATFORM(COCOA)
AudioMediaStreamTrackRendererInternalUnitManager& WebProcess::audioMediaStreamTrackRendererInternalUnitManager()
{
if (!m_audioMediaStreamTrackRendererInternalUnitManager)
m_audioMediaStreamTrackRendererInternalUnitManager = makeUnique<AudioMediaStreamTrackRendererInternalUnitManager>();
return *m_audioMediaStreamTrackRendererInternalUnitManager;
}
#endif
#endif // ENABLE(GPU_PROCESS)
#if ENABLE(WEB_AUTHN)
static WebAuthnProcessConnectionInfo getWebAuthnProcessConnection(IPC::Connection& connection)
{
WebAuthnProcessConnectionInfo connectionInfo;
if (!connection.sendSync(Messages::WebProcessProxy::GetWebAuthnProcessConnection(), Messages::WebProcessProxy::GetWebAuthnProcessConnection::Reply(connectionInfo), 0)) {
// If we failed the first time, retry once. The attachment may have become invalid
// before it was received by the web process if the network process crashed.
if (!connection.sendSync(Messages::WebProcessProxy::GetWebAuthnProcessConnection(), Messages::WebProcessProxy::GetWebAuthnProcessConnection::Reply(connectionInfo), 0)) {
RELEASE_LOG_ERROR(WebAuthn, "getWebAuthnProcessConnection: Unable to connect to WebAuthn process (Terminating)");
CRASH();
}
}
return connectionInfo;
}
WebAuthnProcessConnection& WebProcess::ensureWebAuthnProcessConnection()
{
RELEASE_ASSERT(RunLoop::isMain());
// If we've lost our connection to the WebAuthn process (e.g. it crashed) try to re-establish it.
if (!m_webAuthnProcessConnection) {
auto connectionInfo = getWebAuthnProcessConnection(*parentProcessConnection());
// Retry once if the IPC to get the connectionIdentifier succeeded but the connectionIdentifier we received
// is invalid. This may indicate that the WebAuthn process has crashed.
if (!IPC::Connection::identifierIsValid(connectionInfo.identifier()))
connectionInfo = getWebAuthnProcessConnection(*parentProcessConnection());
if (!IPC::Connection::identifierIsValid(connectionInfo.identifier())) {
RELEASE_LOG_ERROR(WebAuthn, "ensureWebAuthnProcessConnection: Connection identifier for WebAuthn process is invalid.");
CRASH();
}
m_webAuthnProcessConnection = WebAuthnProcessConnection::create(connectionInfo.releaseIdentifier());
}
return *m_webAuthnProcessConnection;
}
void WebProcess::webAuthnProcessConnectionClosed(WebAuthnProcessConnection* connection)
{
ASSERT(m_webAuthnProcessConnection);
ASSERT_UNUSED(connection, m_webAuthnProcessConnection == connection);
m_webAuthnProcessConnection = nullptr;
}
#endif // ENABLE(WEB_AUTHN)
void WebProcess::setEnhancedAccessibility(bool flag)
{
WebCore::AXObjectCache::setEnhancedUserInterfaceAccessibility(flag);
}
void WebProcess::startMemorySampler(SandboxExtension::Handle&& sampleLogFileHandle, const String& sampleLogFilePath, const double interval)
{
#if ENABLE(MEMORY_SAMPLER)
WebMemorySampler::singleton()->start(WTFMove(sampleLogFileHandle), sampleLogFilePath, interval);
#else
UNUSED_PARAM(sampleLogFileHandle);
UNUSED_PARAM(sampleLogFilePath);
UNUSED_PARAM(interval);
#endif
}
void WebProcess::stopMemorySampler()
{
#if ENABLE(MEMORY_SAMPLER)
WebMemorySampler::singleton()->stop();
#endif
}
void WebProcess::setTextCheckerState(const TextCheckerState& textCheckerState)
{
bool continuousSpellCheckingTurnedOff = !textCheckerState.isContinuousSpellCheckingEnabled && m_textCheckerState.isContinuousSpellCheckingEnabled;
bool grammarCheckingTurnedOff = !textCheckerState.isGrammarCheckingEnabled && m_textCheckerState.isGrammarCheckingEnabled;
m_textCheckerState = textCheckerState;
if (!continuousSpellCheckingTurnedOff && !grammarCheckingTurnedOff)
return;
for (auto& page : m_pageMap.values()) {
if (continuousSpellCheckingTurnedOff)
page->unmarkAllMisspellings();
if (grammarCheckingTurnedOff)
page->unmarkAllBadGrammar();
}
}
void WebProcess::fetchWebsiteData(OptionSet<WebsiteDataType> websiteDataTypes, CompletionHandler<void(WebsiteData&&)>&& completionHandler)
{
WebsiteData websiteData;
if (websiteDataTypes.contains(WebsiteDataType::MemoryCache)) {
for (auto& origin : MemoryCache::singleton().originsWithCache(sessionID()))
websiteData.entries.append(WebsiteData::Entry { origin->data(), WebsiteDataType::MemoryCache, 0 });
}
completionHandler(WTFMove(websiteData));
}
void WebProcess::deleteWebsiteData(OptionSet<WebsiteDataType> websiteDataTypes, WallTime modifiedSince, CompletionHandler<void()>&& completionHandler)
{
UNUSED_PARAM(modifiedSince);
if (websiteDataTypes.contains(WebsiteDataType::MemoryCache)) {
BackForwardCache::singleton().pruneToSizeNow(0, PruningReason::None);
MemoryCache::singleton().evictResources(sessionID());
CrossOriginPreflightResultCache::singleton().clear();
}
completionHandler();
}
void WebProcess::deleteWebsiteDataForOrigins(OptionSet<WebsiteDataType> websiteDataTypes, const Vector<WebCore::SecurityOriginData>& originDatas, CompletionHandler<void()>&& completionHandler)
{
if (websiteDataTypes.contains(WebsiteDataType::MemoryCache)) {
HashSet<RefPtr<SecurityOrigin>> origins;
for (auto& originData : originDatas)
origins.add(originData.securityOrigin());
MemoryCache::singleton().removeResourcesWithOrigins(sessionID(), origins);
}
completionHandler();
}
void WebProcess::setHiddenPageDOMTimerThrottlingIncreaseLimit(int milliseconds)
{
for (auto& page : m_pageMap.values())
page->setHiddenPageDOMTimerThrottlingIncreaseLimit(Seconds::fromMilliseconds(milliseconds));
}
#if !PLATFORM(COCOA)
void WebProcess::initializeProcessName(const AuxiliaryProcessInitializationParameters&)
{
}
void WebProcess::initializeSandbox(const AuxiliaryProcessInitializationParameters&, SandboxInitializationParameters&)
{
}
void WebProcess::updateActivePages(const String& overrideDisplayName)
{
}
void WebProcess::getActivePagesOriginsForTesting(CompletionHandler<void(Vector<String>&&)>&& completionHandler)
{
completionHandler({ });
}
void WebProcess::updateCPULimit()
{
}
void WebProcess::updateCPUMonitorState(CPUMonitorUpdateReason)
{
}
#endif
void WebProcess::pageActivityStateDidChange(PageIdentifier, OptionSet<WebCore::ActivityState::Flag> changed)
{
if (changed & WebCore::ActivityState::IsVisible) {
updateCPUMonitorState(CPUMonitorUpdateReason::VisibilityHasChanged);
#if OS(LINUX)
RealTimeThreads::singleton().setEnabled(hasVisibleWebPage());
#endif
}
}
#if PLATFORM(IOS_FAMILY)
void WebProcess::resetAllGeolocationPermissions()
{
for (auto& page : m_pageMap.values()) {
if (Frame* mainFrame = page->mainFrame())
mainFrame->resetAllGeolocationPermission();
}
}
#endif
void WebProcess::prepareToSuspend(bool isSuspensionImminent, CompletionHandler<void()>&& completionHandler)
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "prepareToSuspend: isSuspensionImminent=%d", isSuspensionImminent);
SetForScope<bool> suspensionScope(m_isSuspending, true);
m_processIsSuspended = true;
flushResourceLoadStatistics();
#if PLATFORM(COCOA)
if (m_processType == ProcessType::PrewarmedWebContent) {
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "prepareToSuspend: Process is ready to suspend");
return completionHandler();
}
#endif
#if ENABLE(VIDEO)
suspendAllMediaBuffering();
if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
platformMediaSessionManager->processWillSuspend();
#endif
if (!m_suppressMemoryPressureHandler) {
MemoryPressureHandler::singleton().releaseMemory(Critical::Yes, Synchronous::Yes);
for (auto& page : m_pageMap.values())
page->releaseMemory(Critical::Yes);
}
freezeAllLayerTrees();
#if PLATFORM(COCOA)
destroyRenderingResources();
#endif
#if PLATFORM(IOS_FAMILY)
m_webSQLiteDatabaseTracker.setIsSuspended(true);
SQLiteDatabase::setIsDatabaseOpeningForbidden(true);
if (DatabaseTracker::isInitialized())
DatabaseTracker::singleton().closeAllDatabases(CurrentQueryBehavior::Interrupt);
IPC::AccessibilityProcessSuspendedNotification(true);
updateFreezerStatus();
#endif
markAllLayersVolatile([this, completionHandler = WTFMove(completionHandler)]() mutable {
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "prepareToSuspend: Process is ready to suspend");
completionHandler();
});
}
void WebProcess::markAllLayersVolatile(CompletionHandler<void()>&& completionHandler)
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "markAllLayersVolatile:");
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
for (auto& page : m_pageMap.values()) {
page->markLayersVolatile([this, callbackAggregator, pageID = page->identifier()] (bool succeeded) {
if (succeeded)
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "markAllLayersVolatile: Successfuly marked layers as volatile for webPageID=%" PRIu64, pageID.toUInt64());
else
WEBPROCESS_RELEASE_LOG_ERROR(ProcessSuspension, "markAllLayersVolatile: Failed to mark layers as volatile for webPageID=%" PRIu64, pageID.toUInt64());
});
}
}
void WebProcess::cancelMarkAllLayersVolatile()
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "cancelMarkAllLayersVolatile:");
for (auto& page : m_pageMap.values())
page->cancelMarkLayersVolatile();
}
void WebProcess::freezeAllLayerTrees()
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "freezeAllLayerTrees: WebProcess is freezing all layer trees");
for (auto& page : m_pageMap.values())
page->freezeLayerTree(WebPage::LayerTreeFreezeReason::ProcessSuspended);
}
void WebProcess::unfreezeAllLayerTrees()
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "unfreezeAllLayerTrees: WebProcess is unfreezing all layer trees");
for (auto& page : m_pageMap.values())
page->unfreezeLayerTree(WebPage::LayerTreeFreezeReason::ProcessSuspended);
}
void WebProcess::processDidResume()
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "processDidResume:");
m_processIsSuspended = false;
#if PLATFORM(COCOA)
if (m_processType == ProcessType::PrewarmedWebContent)
return;
#endif
cancelMarkAllLayersVolatile();
unfreezeAllLayerTrees();
#if PLATFORM(IOS_FAMILY)
m_webSQLiteDatabaseTracker.setIsSuspended(false);
SQLiteDatabase::setIsDatabaseOpeningForbidden(false);
IPC::AccessibilityProcessSuspendedNotification(false);
#endif
#if ENABLE(VIDEO)
if (auto* platformMediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists())
platformMediaSessionManager->processDidResume();
resumeAllMediaBuffering();
#endif
}
void WebProcess::sendPrewarmInformation(const URL& url)
{
auto registrableDomain = WebCore::RegistrableDomain { url };
if (registrableDomain.isEmpty())
return;
parentProcessConnection()->send(Messages::WebProcessProxy::DidCollectPrewarmInformation(registrableDomain, WebCore::ProcessWarming::collectPrewarmInformation()), 0);
}
void WebProcess::pageDidEnterWindow(PageIdentifier pageID)
{
m_pagesInWindows.add(pageID);
m_nonVisibleProcessGraphicsCleanupTimer.stop();
#if ENABLE(NON_VISIBLE_WEBPROCESS_MEMORY_CLEANUP_TIMER)
m_nonVisibleProcessMemoryCleanupTimer.stop();
#endif
}
void WebProcess::pageWillLeaveWindow(PageIdentifier pageID)
{
m_pagesInWindows.remove(pageID);
if (m_pagesInWindows.isEmpty()) {
if (!m_nonVisibleProcessGraphicsCleanupTimer.isActive())
m_nonVisibleProcessGraphicsCleanupTimer.startOneShot(nonVisibleProcessGraphicsCleanupDelay);
#if ENABLE(NON_VISIBLE_WEBPROCESS_MEMORY_CLEANUP_TIMER)
if (!m_nonVisibleProcessMemoryCleanupTimer.isActive())
m_nonVisibleProcessMemoryCleanupTimer.startOneShot(nonVisibleProcessMemoryCleanupDelay);
#endif
}
}
void WebProcess::nonVisibleProcessGraphicsCleanupTimerFired()
{
ASSERT(m_pagesInWindows.isEmpty());
if (!m_pagesInWindows.isEmpty())
return;
#if PLATFORM(COCOA)
destroyRenderingResources();
#endif
}
#if ENABLE(NON_VISIBLE_WEBPROCESS_MEMORY_CLEANUP_TIMER)
void WebProcess::nonVisibleProcessMemoryCleanupTimerFired()
{
ASSERT(m_pagesInWindows.isEmpty());
if (!m_pagesInWindows.isEmpty())
return;
// If this is a process that we keep around for performance, then don't proactively slim it down until absolutely necessary (in the memory pressure handler).
if (m_processType == ProcessType::CachedWebContent || areAllPagesSuspended())
return;
WebCore::releaseMemory(Critical::Yes, Synchronous::No, MaintainBackForwardCache::Yes, MaintainMemoryCache::No);
for (auto& page : m_pageMap.values())
page->releaseMemory(Critical::Yes);
}
#endif
void WebProcess::registerStorageAreaMap(StorageAreaMap& storageAreaMap)
{
auto identifier = storageAreaMap.identifier();
ASSERT(!m_storageAreaMaps.contains(identifier));
m_storageAreaMaps.add(identifier, storageAreaMap);
}
void WebProcess::unregisterStorageAreaMap(StorageAreaMap& storageAreaMap)
{
auto identifier = storageAreaMap.identifier();
ASSERT(m_storageAreaMaps.contains(identifier));
ASSERT(m_storageAreaMaps.get(identifier).get() == &storageAreaMap);
m_storageAreaMaps.remove(identifier);
}
WeakPtr<StorageAreaMap> WebProcess::storageAreaMap(StorageAreaMapIdentifier identifier) const
{
return m_storageAreaMaps.get(identifier);
}
void WebProcess::setResourceLoadStatisticsEnabled(bool enabled)
{
if (WebCore::DeprecatedGlobalSettings::resourceLoadStatisticsEnabled() == enabled)
return;
WebCore::DeprecatedGlobalSettings::setResourceLoadStatisticsEnabled(enabled);
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (enabled && !ResourceLoadObserver::sharedIfExists())
WebCore::ResourceLoadObserver::setShared(*new WebResourceLoadObserver(m_sessionID && m_sessionID->isEphemeral() ? WebCore::ResourceLoadStatistics::IsEphemeral::Yes : WebCore::ResourceLoadStatistics::IsEphemeral::No));
#endif
}
void WebProcess::clearResourceLoadStatistics()
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (auto* observer = ResourceLoadObserver::sharedIfExists())
observer->clearState();
for (auto& page : m_pageMap.values())
page->clearPageLevelStorageAccess();
#endif
}
void WebProcess::flushResourceLoadStatistics()
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (auto* observer = ResourceLoadObserver::sharedIfExists())
observer->updateCentralStatisticsStore([] { });
#endif
}
void WebProcess::seedResourceLoadStatisticsForTesting(const RegistrableDomain& firstPartyDomain, const RegistrableDomain& thirdPartyDomain, bool shouldScheduleNotification, CompletionHandler<void()>&& completionHandler)
{
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
if (auto* observer = ResourceLoadObserver::sharedIfExists())
observer->logSubresourceLoadingForTesting(firstPartyDomain, thirdPartyDomain, shouldScheduleNotification);
#endif
completionHandler();
}
RefPtr<API::Object> WebProcess::transformHandlesToObjects(API::Object* object)
{
struct Transformer final : UserData::Transformer {
Transformer(WebProcess& webProcess)
: m_webProcess(webProcess)
{
}
bool shouldTransformObject(const API::Object& object) const override
{
switch (object.type()) {
case API::Object::Type::FrameHandle:
return static_cast<const API::FrameHandle&>(object).isAutoconverting();
case API::Object::Type::PageHandle:
return static_cast<const API::PageHandle&>(object).isAutoconverting();
#if PLATFORM(COCOA)
case API::Object::Type::ObjCObjectGraph:
#endif
return true;
default:
return false;
}
}
RefPtr<API::Object> transformObject(API::Object& object) const override
{
switch (object.type()) {
case API::Object::Type::FrameHandle:
return m_webProcess.webFrame(static_cast<const API::FrameHandle&>(object).frameID());
case API::Object::Type::PageHandle:
return m_webProcess.webPage(static_cast<const API::PageHandle&>(object).webPageID());
#if PLATFORM(COCOA)
case API::Object::Type::ObjCObjectGraph:
return m_webProcess.transformHandlesToObjects(static_cast<ObjCObjectGraph&>(object));
#endif
default:
return &object;
}
}
WebProcess& m_webProcess;
};
return UserData::transform(object, Transformer(*this));
}
RefPtr<API::Object> WebProcess::transformObjectsToHandles(API::Object* object)
{
struct Transformer final : UserData::Transformer {
bool shouldTransformObject(const API::Object& object) const override
{
switch (object.type()) {
case API::Object::Type::BundleFrame:
case API::Object::Type::BundlePage:
#if PLATFORM(COCOA)
case API::Object::Type::ObjCObjectGraph:
#endif
return true;
default:
return false;
}
}
RefPtr<API::Object> transformObject(API::Object& object) const override
{
switch (object.type()) {
case API::Object::Type::BundleFrame:
return API::FrameHandle::createAutoconverting(static_cast<const WebFrame&>(object).frameID());
case API::Object::Type::BundlePage:
return API::PageHandle::createAutoconverting(static_cast<const WebPage&>(object).webPageProxyIdentifier(), static_cast<const WebPage&>(object).identifier());
#if PLATFORM(COCOA)
case API::Object::Type::ObjCObjectGraph:
return transformObjectsToHandles(static_cast<ObjCObjectGraph&>(object));
#endif
default:
return &object;
}
}
};
return UserData::transform(object, Transformer());
}
void WebProcess::setMemoryCacheDisabled(bool disabled)
{
auto& memoryCache = MemoryCache::singleton();
if (memoryCache.disabled() != disabled)
memoryCache.setDisabled(disabled);
}
#if ENABLE(SERVICE_CONTROLS)
void WebProcess::setEnabledServices(bool hasImageServices, bool hasSelectionServices, bool hasRichContentServices)
{
m_hasImageServices = hasImageServices;
m_hasSelectionServices = hasSelectionServices;
m_hasRichContentServices = hasRichContentServices;
}
#endif
void WebProcess::ensureAutomationSessionProxy(const String& sessionIdentifier)
{
m_automationSessionProxy = makeUnique<WebAutomationSessionProxy>(sessionIdentifier);
}
void WebProcess::destroyAutomationSessionProxy()
{
m_automationSessionProxy = nullptr;
}
void WebProcess::prefetchDNS(const String& hostname)
{
if (hostname.isEmpty())
return;
if (m_dnsPrefetchedHosts.add(hostname).isNewEntry)
ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::PrefetchDNS(hostname), 0);
// The DNS prefetched hosts cache is only to avoid asking for the same hosts too many times
// in a very short period of time, producing a lot of IPC traffic. So we clear this cache after
// some time of no DNS requests.
m_dnsPrefetchHystereris.impulse();
}
bool WebProcess::hasVisibleWebPage() const
{
for (auto& page : m_pageMap.values()) {
if (page->isVisible())
return true;
}
return false;
}
void WebProcess::setBackForwardCacheCapacity(unsigned capacity)
{
BackForwardCache::singleton().setMaxSize(capacity);
}
void WebProcess::clearCachedPage(BackForwardItemIdentifier backForwardItemID, CompletionHandler<void()>&& completionHandler)
{
HistoryItem* item = WebBackForwardListProxy::itemForID(backForwardItemID);
if (!item)
return completionHandler();
BackForwardCache::singleton().remove(*item);
completionHandler();
}
LibWebRTCNetwork& WebProcess::libWebRTCNetwork()
{
if (!m_libWebRTCNetwork)
m_libWebRTCNetwork = LibWebRTCNetwork::create();
return *m_libWebRTCNetwork;
}
#if ENABLE(SERVICE_WORKER)
void WebProcess::establishWorkerContextConnectionToNetworkProcess(PageGroupIdentifier pageGroupID, WebPageProxyIdentifier webPageProxyID, PageIdentifier pageID, const WebPreferencesStore& store, RegistrableDomain&& registrableDomain, std::optional<ScriptExecutionContextIdentifier> serviceWorkerPageIdentifier, ServiceWorkerInitializationData&& initializationData, CompletionHandler<void()>&& completionHandler)
{
// We are in the Service Worker context process and the call below establishes our connection to the Network Process
// by calling ensureNetworkProcessConnection. SWContextManager needs to use the same underlying IPC::Connection as the
// NetworkProcessConnection for synchronization purposes.
auto& ipcConnection = ensureNetworkProcessConnection().connection();
SWContextManager::singleton().setConnection(makeUnique<WebSWContextManagerConnection>(ipcConnection, WTFMove(registrableDomain), serviceWorkerPageIdentifier, pageGroupID, webPageProxyID, pageID, store, WTFMove(initializationData)));
SWContextManager::singleton().connection()->establishConnection(WTFMove(completionHandler));
}
void WebProcess::addServiceWorkerRegistration(WebCore::ServiceWorkerRegistrationIdentifier identifier)
{
m_swRegistrationCounts.add(identifier);
}
bool WebProcess::removeServiceWorkerRegistration(WebCore::ServiceWorkerRegistrationIdentifier identifier)
{
ASSERT(m_swRegistrationCounts.contains(identifier));
return m_swRegistrationCounts.remove(identifier);
}
#endif
#if ENABLE(MEDIA_STREAM)
void WebProcess::addMockMediaDevice(const WebCore::MockMediaDevice& device)
{
MockRealtimeMediaSourceCenter::addDevice(device);
}
void WebProcess::clearMockMediaDevices()
{
MockRealtimeMediaSourceCenter::setDevices({ });
}
void WebProcess::removeMockMediaDevice(const String& persistentId)
{
MockRealtimeMediaSourceCenter::removeDevice(persistentId);
}
void WebProcess::resetMockMediaDevices()
{
MockRealtimeMediaSourceCenter::resetDevices();
}
#if ENABLE(SANDBOX_EXTENSIONS)
void WebProcess::grantUserMediaDeviceSandboxExtensions(MediaDeviceSandboxExtensions&& extensions)
{
for (size_t i = 0; i < extensions.size(); i++) {
const auto& extension = extensions[i];
extension.second->consume();
WEBPROCESS_RELEASE_LOG(WebRTC, "grantUserMediaDeviceSandboxExtensions: granted extension %s", extension.first.utf8().data());
m_mediaCaptureSandboxExtensions.add(extension.first, extension.second.copyRef());
}
}
static inline void checkDocumentsCaptureStateConsistency(const Vector<String>& extensionIDs)
{
#if ASSERT_ENABLED
bool isCapturingAudio = WTF::anyOf(Document::allDocumentsMap().values(), [](auto* document) {
return document->mediaState() & MediaProducer::AudioCaptureMask;
});
bool isCapturingVideo = WTF::anyOf(Document::allDocumentsMap().values(), [](auto* document) {
return document->mediaState() & MediaProducer::VideoCaptureMask;
});
if (isCapturingAudio)
ASSERT(extensionIDs.findIf([](auto& id) { return id.contains("microphone"); }) == notFound);
if (isCapturingVideo)
ASSERT(extensionIDs.findIf([](auto& id) { return id.contains("camera"); }) == notFound);
#endif // ASSERT_ENABLED
}
void WebProcess::revokeUserMediaDeviceSandboxExtensions(const Vector<String>& extensionIDs)
{
checkDocumentsCaptureStateConsistency(extensionIDs);
for (const auto& extensionID : extensionIDs) {
auto extension = m_mediaCaptureSandboxExtensions.take(extensionID);
ASSERT(extension || MockRealtimeMediaSourceCenter::mockRealtimeMediaSourceCenterEnabled());
if (extension) {
extension->revoke();
WEBPROCESS_RELEASE_LOG(WebRTC, "revokeUserMediaDeviceSandboxExtensions: revoked extension %s", extensionID.utf8().data());
}
}
}
#endif
#endif
#if ENABLE(VIDEO)
void WebProcess::suspendAllMediaBuffering()
{
for (auto& page : m_pageMap.values())
page->suspendAllMediaBuffering();
}
void WebProcess::resumeAllMediaBuffering()
{
for (auto& page : m_pageMap.values())
page->resumeAllMediaBuffering();
}
#endif
void WebProcess::clearCurrentModifierStateForTesting()
{
PlatformKeyboardEvent::setCurrentModifierState({ });
}
bool WebProcess::areAllPagesThrottleable() const
{
return WTF::allOf(m_pageMap.values(), [](auto& page) {
return page->isThrottleable();
});
}
#if HAVE(CVDISPLAYLINK)
void WebProcess::displayWasRefreshed(uint32_t displayID, const DisplayUpdate& displayUpdate)
{
ASSERT(RunLoop::isMain());
m_eventDispatcher->notifyScrollingTreesDisplayWasRefreshed(displayID);
DisplayRefreshMonitorManager::sharedManager().displayWasUpdated(displayID, displayUpdate);
}
#endif
#if ENABLE(INTELLIGENT_TRACKING_PREVENTION)
void WebProcess::setThirdPartyCookieBlockingMode(ThirdPartyCookieBlockingMode thirdPartyCookieBlockingMode, CompletionHandler<void()>&& completionHandler)
{
m_thirdPartyCookieBlockingMode = thirdPartyCookieBlockingMode;
completionHandler();
}
void WebProcess::setDomainsWithUserInteraction(HashSet<WebCore::RegistrableDomain>&& domains)
{
ResourceLoadObserver::shared().setDomainsWithUserInteraction(WTFMove(domains));
}
void WebProcess::setDomainsWithCrossPageStorageAccess(HashMap<TopFrameDomain, SubResourceDomain>&& domains, CompletionHandler<void()>&& completionHandler)
{
for (auto& domain : domains.keys()) {
for (auto& webPage : m_pageMap.values())
webPage->addDomainWithPageLevelStorageAccess(domain, domains.get(domain));
}
ResourceLoadObserver::shared().setDomainsWithCrossPageStorageAccess(WTFMove(domains), WTFMove(completionHandler));
}
void WebProcess::sendResourceLoadStatisticsDataImmediately(CompletionHandler<void()>&& completionHandler)
{
ResourceLoadObserver::shared().updateCentralStatisticsStore(WTFMove(completionHandler));
}
#endif
#if ENABLE(GPU_PROCESS)
void WebProcess::setUseGPUProcessForCanvasRendering(bool useGPUProcessForCanvasRendering)
{
m_useGPUProcessForCanvasRendering = useGPUProcessForCanvasRendering;
}
void WebProcess::setUseGPUProcessForDOMRendering(bool useGPUProcessForDOMRendering)
{
m_useGPUProcessForDOMRendering = useGPUProcessForDOMRendering;
}
void WebProcess::setUseGPUProcessForMedia(bool useGPUProcessForMedia)
{
if (useGPUProcessForMedia == m_useGPUProcessForMedia)
return;
m_useGPUProcessForMedia = useGPUProcessForMedia;
#if ENABLE(ENCRYPTED_MEDIA)
auto& cdmFactories = CDMFactory::registeredFactories();
cdmFactories.clear();
if (useGPUProcessForMedia)
cdmFactory().registerFactory(cdmFactories);
else
CDMFactory::platformRegisterFactories(cdmFactories);
#endif
#if USE(AUDIO_SESSION)
if (useGPUProcessForMedia)
AudioSession::setSharedSession(RemoteAudioSession::create(*this));
else
AudioSession::setSharedSession(AudioSession::create());
#endif
#if PLATFORM(IOS_FAMILY)
if (useGPUProcessForMedia)
MediaSessionHelper::setSharedHelper(makeUniqueRef<RemoteMediaSessionHelper>(*this));
else
MediaSessionHelper::resetSharedHelper();
#endif
#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
if (useGPUProcessForMedia)
legacyCDMFactory().registerFactory();
else
LegacyCDM::resetFactories();
#endif
if (useGPUProcessForMedia)
mediaEngineConfigurationFactory().registerFactory();
else
MediaEngineConfigurationFactory::resetFactories();
if (useGPUProcessForMedia)
WebCore::AudioHardwareListener::setCreationFunction([this] (WebCore::AudioHardwareListener::Client& client) { return RemoteAudioHardwareListener::create(client, *this); });
else
WebCore::AudioHardwareListener::resetCreationFunction();
if (useGPUProcessForMedia)
WebCore::RemoteCommandListener::setCreationFunction([this] (WebCore::RemoteCommandListenerClient& client) { return RemoteRemoteCommandListener::create(client, *this); });
else
WebCore::RemoteCommandListener::resetCreationFunction();
#if PLATFORM(COCOA)
if (useGPUProcessForMedia) {
SystemBatteryStatusTestingOverrides::singleton().setConfigurationChangedCallback([this] () {
ensureGPUProcessConnection().updateMediaConfiguration();
});
#if ENABLE(VP9)
VP9TestingOverrides::singleton().setConfigurationChangedCallback([this] () {
ensureGPUProcessConnection().updateMediaConfiguration();
});
#endif
} else {
SystemBatteryStatusTestingOverrides::singleton().setConfigurationChangedCallback(nullptr);
#if ENABLE(VP9)
VP9TestingOverrides::singleton().setConfigurationChangedCallback(nullptr);
#endif
}
#endif
}
bool WebProcess::shouldUseRemoteRenderingFor(RenderingPurpose purpose)
{
switch (purpose) {
case RenderingPurpose::Canvas:
return m_useGPUProcessForCanvasRendering;
case RenderingPurpose::DOM:
return m_useGPUProcessForDOMRendering;
case RenderingPurpose::MediaPainting:
return m_useGPUProcessForMedia;
default:
break;
}
return false;
}
#if ENABLE(WEBGL)
void WebProcess::setUseGPUProcessForWebGL(bool useGPUProcessForWebGL)
{
m_useGPUProcessForWebGL = useGPUProcessForWebGL;
}
bool WebProcess::shouldUseRemoteRenderingForWebGL() const
{
return m_useGPUProcessForWebGL;
}
#endif
#endif
#if ENABLE(MEDIA_STREAM)
SpeechRecognitionRealtimeMediaSourceManager& WebProcess::ensureSpeechRecognitionRealtimeMediaSourceManager()
{
if (!m_speechRecognitionRealtimeMediaSourceManager)
m_speechRecognitionRealtimeMediaSourceManager = makeUnique<SpeechRecognitionRealtimeMediaSourceManager>(*parentProcessConnection());
return *m_speechRecognitionRealtimeMediaSourceManager;
}
#endif
#if ENABLE(GPU_PROCESS) && ENABLE(LEGACY_ENCRYPTED_MEDIA)
RemoteLegacyCDMFactory& WebProcess::legacyCDMFactory()
{
return *supplement<RemoteLegacyCDMFactory>();
}
#endif
#if ENABLE(GPU_PROCESS) && ENABLE(ENCRYPTED_MEDIA)
RemoteCDMFactory& WebProcess::cdmFactory()
{
return *supplement<RemoteCDMFactory>();
}
#endif
#if ENABLE(GPU_PROCESS)
RemoteMediaEngineConfigurationFactory& WebProcess::mediaEngineConfigurationFactory()
{
return *supplement<RemoteMediaEngineConfigurationFactory>();
}
#endif
} // namespace WebKit
#undef RELEASE_LOG_SESSION_ID
#undef WEBPROCESS_RELEASE_LOG
#undef WEBPROCESS_RELEASE_LOG_ERROR