diff options
Diffstat (limited to 'Source/WebKit2/WebProcess/WebProcess.cpp')
-rw-r--r-- | Source/WebKit2/WebProcess/WebProcess.cpp | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/Source/WebKit2/WebProcess/WebProcess.cpp b/Source/WebKit2/WebProcess/WebProcess.cpp new file mode 100644 index 0000000..47f4125 --- /dev/null +++ b/Source/WebKit2/WebProcess/WebProcess.cpp @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2009, 2010 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 "WebProcess.h" + +#include "AuthenticationManager.h" +#include "DownloadManager.h" +#include "InjectedBundle.h" +#include "InjectedBundleMessageKinds.h" +#include "InjectedBundleUserMessageCoders.h" +#include "RunLoop.h" +#include "SandboxExtension.h" +#include "WebContextMessages.h" +#include "WebCoreArgumentCoders.h" +#include "WebDatabaseManager.h" +#include "WebFrame.h" +#include "WebGeolocationManagerMessages.h" +#include "WebMemorySampler.h" +#include "WebPage.h" +#include "WebPageCreationParameters.h" +#include "WebPlatformStrategies.h" +#include "WebPreferencesStore.h" +#include "WebProcessCreationParameters.h" +#include "WebProcessMessages.h" +#include "WebProcessProxyMessages.h" +#include <WebCore/ApplicationCacheStorage.h> +#include <WebCore/CrossOriginPreflightResultCache.h> +#include <WebCore/Font.h> +#include <WebCore/Language.h> +#include <WebCore/Page.h> +#include <WebCore/PageGroup.h> +#include <WebCore/SchemeRegistry.h> +#include <WebCore/SecurityOrigin.h> +#include <WebCore/Settings.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RandomNumber.h> + +#ifndef NDEBUG +#include <WebCore/MemoryCache.h> +#include <WebCore/GCController.h> +#endif + +#if !OS(WINDOWS) +#include <unistd.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +#if OS(WINDOWS) +static void sleep(unsigned seconds) +{ + ::Sleep(seconds * 1000); +} +#endif + +static void* randomCrashThread(void*) +{ + // This delay was chosen semi-arbitrarily. We want the crash to happen somewhat quickly to + // enable useful stress testing, but not so quickly that the web process will always crash soon + // after launch. + static const unsigned maximumRandomCrashDelay = 180; + + sleep(randomNumber() * maximumRandomCrashDelay); + CRASH(); + return 0; +} + +static void startRandomCrashThreadIfRequested() +{ + if (!getenv("WEBKIT2_CRASH_WEB_PROCESS_RANDOMLY")) + return; + createThread(randomCrashThread, 0, "WebKit2: Random Crash Thread"); +} + +WebProcess& WebProcess::shared() +{ + static WebProcess& process = *new WebProcess; + return process; +} + +WebProcess::WebProcess() + : m_inDidClose(false) + , m_hasSetCacheModel(false) + , m_cacheModel(CacheModelDocumentViewer) +#if USE(ACCELERATED_COMPOSITING) && PLATFORM(MAC) + , m_compositingRenderServerPort(MACH_PORT_NULL) +#endif +#if PLATFORM(QT) + , m_networkAccessManager(0) +#endif + , m_textCheckerState() + , m_geolocationManager(this) +{ +#if USE(PLATFORM_STRATEGIES) + // Initialize our platform strategies. + WebPlatformStrategies::initialize(); +#endif // USE(PLATFORM_STRATEGIES) + +#if ENABLE(DATABASE) + // Make sure the WebDatabaseManager is initialized so that the Database directory is set. + WebDatabaseManager::shared(); +#endif +} + +void WebProcess::initialize(CoreIPC::Connection::Identifier serverIdentifier, RunLoop* runLoop) +{ + ASSERT(!m_connection); + + m_connection = CoreIPC::Connection::createClientConnection(serverIdentifier, this, runLoop); + m_connection->open(); + + m_runLoop = runLoop; + + startRandomCrashThreadIfRequested(); +} + +void WebProcess::initializeWebProcess(const WebProcessCreationParameters& parameters, CoreIPC::ArgumentDecoder* arguments) +{ + ASSERT(m_pageMap.isEmpty()); + + platformInitializeWebProcess(parameters, arguments); + + RefPtr<APIObject> injectedBundleInitializationUserData; + InjectedBundleUserMessageDecoder messageDecoder(injectedBundleInitializationUserData); + if (!arguments->decode(messageDecoder)) + return; + + if (!parameters.injectedBundlePath.isEmpty()) { + m_injectedBundle = InjectedBundle::create(parameters.injectedBundlePath); + m_injectedBundle->setSandboxExtension(SandboxExtension::create(parameters.injectedBundlePathExtensionHandle)); + + if (!m_injectedBundle->load(injectedBundleInitializationUserData.get())) { + // Don't keep around the InjectedBundle reference if the load fails. + m_injectedBundle.clear(); + } + } + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (!parameters.applicationCacheDirectory.isEmpty()) + cacheStorage().setCacheDirectory(parameters.applicationCacheDirectory); +#endif + + setShouldTrackVisitedLinks(parameters.shouldTrackVisitedLinks); + setCacheModel(static_cast<uint32_t>(parameters.cacheModel)); + + if (!parameters.languageCode.isEmpty()) + overrideDefaultLanguage(parameters.languageCode); + + m_textCheckerState = parameters.textCheckerState; + + for (size_t i = 0; i < parameters.urlSchemesRegistererdAsEmptyDocument.size(); ++i) + registerURLSchemeAsEmptyDocument(parameters.urlSchemesRegistererdAsEmptyDocument[i]); + + for (size_t i = 0; i < parameters.urlSchemesRegisteredAsSecure.size(); ++i) + registerURLSchemeAsSecure(parameters.urlSchemesRegisteredAsSecure[i]); + + for (size_t i = 0; i < parameters.urlSchemesForWhichDomainRelaxationIsForbidden.size(); ++i) + setDomainRelaxationForbiddenForURLScheme(parameters.urlSchemesForWhichDomainRelaxationIsForbidden[i]); + + for (size_t i = 0; i < parameters.mimeTypesWithCustomRepresentation.size(); ++i) + m_mimeTypesWithCustomRepresentations.add(parameters.mimeTypesWithCustomRepresentation[i]); + + if (parameters.clearResourceCaches) + clearResourceCaches(); + if (parameters.clearApplicationCache) + clearApplicationCache(); + +#if PLATFORM(MAC) + m_presenterApplicationPid = parameters.presenterApplicationPid; +#endif + + if (parameters.shouldAlwaysUseComplexTextCodePath) + setAlwaysUsesComplexTextCodePath(true); +} + +void WebProcess::setShouldTrackVisitedLinks(bool shouldTrackVisitedLinks) +{ + PageGroup::setShouldTrackVisitedLinks(shouldTrackVisitedLinks); +} + +void WebProcess::registerURLSchemeAsEmptyDocument(const String& urlScheme) +{ + SchemeRegistry::registerURLSchemeAsEmptyDocument(urlScheme); +} + +void WebProcess::registerURLSchemeAsSecure(const String& urlScheme) const +{ + SchemeRegistry::registerURLSchemeAsSecure(urlScheme); +} + +void WebProcess::setDomainRelaxationForbiddenForURLScheme(const String& urlScheme) const +{ + SecurityOrigin::setDomainRelaxationForbiddenForURLScheme(true, urlScheme); +} + +void WebProcess::setAlwaysUsesComplexTextCodePath(bool alwaysUseComplexText) +{ + Font::setCodePath(alwaysUseComplexText ? Font::Complex : Font::Auto); +} + +void WebProcess::languageChanged(const String& language) const +{ + overrideDefaultLanguage(language); +} + +void WebProcess::setVisitedLinkTable(const SharedMemory::Handle& handle) +{ + RefPtr<SharedMemory> sharedMemory = SharedMemory::create(handle, SharedMemory::ReadOnly); + if (!sharedMemory) + return; + + m_visitedLinkTable.setSharedMemory(sharedMemory.release()); +} + +PageGroup* WebProcess::sharedPageGroup() +{ + return PageGroup::pageGroup("WebKit2Group"); +} + +void WebProcess::visitedLinkStateChanged(const Vector<WebCore::LinkHash>& linkHashes) +{ + for (size_t i = 0; i < linkHashes.size(); ++i) + Page::visitedStateChanged(sharedPageGroup(), linkHashes[i]); +} + +void WebProcess::allVisitedLinkStateChanged() +{ + Page::allVisitedStateChanged(sharedPageGroup()); +} + +bool WebProcess::isLinkVisited(LinkHash linkHash) const +{ + return m_visitedLinkTable.isLinkVisited(linkHash); +} + +void WebProcess::addVisitedLink(WebCore::LinkHash linkHash) +{ + if (isLinkVisited(linkHash)) + return; + m_connection->send(Messages::WebContext::AddVisitedLinkHash(linkHash), 0); +} + +void WebProcess::setCacheModel(uint32_t cm) +{ + CacheModel cacheModel = static_cast<CacheModel>(cm); + + if (!m_hasSetCacheModel || cacheModel != m_cacheModel) { + m_hasSetCacheModel = true; + m_cacheModel = cacheModel; + platformSetCacheModel(cacheModel); + } +} + +void WebProcess::calculateCacheSizes(CacheModel cacheModel, uint64_t memorySize, uint64_t diskFreeSize, + unsigned& cacheTotalCapacity, unsigned& cacheMinDeadCapacity, unsigned& cacheMaxDeadCapacity, double& deadDecodedDataDeletionInterval, + unsigned& pageCacheCapacity, unsigned long& urlCacheMemoryCapacity, unsigned long& urlCacheDiskCapacity) +{ + switch (cacheModel) { + case CacheModelDocumentViewer: { + // Page cache capacity (in pages) + pageCacheCapacity = 0; + + // Object cache capacities (in bytes) + if (memorySize >= 2048) + cacheTotalCapacity = 96 * 1024 * 1024; + else if (memorySize >= 1536) + cacheTotalCapacity = 64 * 1024 * 1024; + else if (memorySize >= 1024) + cacheTotalCapacity = 32 * 1024 * 1024; + else if (memorySize >= 512) + cacheTotalCapacity = 16 * 1024 * 1024; + + cacheMinDeadCapacity = 0; + cacheMaxDeadCapacity = 0; + + // Foundation memory cache capacity (in bytes) + urlCacheMemoryCapacity = 0; + + // Foundation disk cache capacity (in bytes) + urlCacheDiskCapacity = 0; + + break; + } + case CacheModelDocumentBrowser: { + // Page cache capacity (in pages) + if (memorySize >= 1024) + pageCacheCapacity = 3; + else if (memorySize >= 512) + pageCacheCapacity = 2; + else if (memorySize >= 256) + pageCacheCapacity = 1; + else + pageCacheCapacity = 0; + + // Object cache capacities (in bytes) + if (memorySize >= 2048) + cacheTotalCapacity = 96 * 1024 * 1024; + else if (memorySize >= 1536) + cacheTotalCapacity = 64 * 1024 * 1024; + else if (memorySize >= 1024) + cacheTotalCapacity = 32 * 1024 * 1024; + else if (memorySize >= 512) + cacheTotalCapacity = 16 * 1024 * 1024; + + cacheMinDeadCapacity = cacheTotalCapacity / 8; + cacheMaxDeadCapacity = cacheTotalCapacity / 4; + + // Foundation memory cache capacity (in bytes) + if (memorySize >= 2048) + urlCacheMemoryCapacity = 4 * 1024 * 1024; + else if (memorySize >= 1024) + urlCacheMemoryCapacity = 2 * 1024 * 1024; + else if (memorySize >= 512) + urlCacheMemoryCapacity = 1 * 1024 * 1024; + else + urlCacheMemoryCapacity = 512 * 1024; + + // Foundation disk cache capacity (in bytes) + if (diskFreeSize >= 16384) + urlCacheDiskCapacity = 50 * 1024 * 1024; + else if (diskFreeSize >= 8192) + urlCacheDiskCapacity = 40 * 1024 * 1024; + else if (diskFreeSize >= 4096) + urlCacheDiskCapacity = 30 * 1024 * 1024; + else + urlCacheDiskCapacity = 20 * 1024 * 1024; + + break; + } + case CacheModelPrimaryWebBrowser: { + // Page cache capacity (in pages) + // (Research indicates that value / page drops substantially after 3 pages.) + if (memorySize >= 2048) + pageCacheCapacity = 5; + else if (memorySize >= 1024) + pageCacheCapacity = 4; + else if (memorySize >= 512) + pageCacheCapacity = 3; + else if (memorySize >= 256) + pageCacheCapacity = 2; + else + pageCacheCapacity = 1; + + // Object cache capacities (in bytes) + // (Testing indicates that value / MB depends heavily on content and + // browsing pattern. Even growth above 128MB can have substantial + // value / MB for some content / browsing patterns.) + if (memorySize >= 2048) + cacheTotalCapacity = 128 * 1024 * 1024; + else if (memorySize >= 1536) + cacheTotalCapacity = 96 * 1024 * 1024; + else if (memorySize >= 1024) + cacheTotalCapacity = 64 * 1024 * 1024; + else if (memorySize >= 512) + cacheTotalCapacity = 32 * 1024 * 1024; + + cacheMinDeadCapacity = cacheTotalCapacity / 4; + cacheMaxDeadCapacity = cacheTotalCapacity / 2; + + // This code is here to avoid a PLT regression. We can remove it if we + // can prove that the overall system gain would justify the regression. + cacheMaxDeadCapacity = std::max(24u, cacheMaxDeadCapacity); + + deadDecodedDataDeletionInterval = 60; + + // Foundation memory cache capacity (in bytes) + // (These values are small because WebCore does most caching itself.) + if (memorySize >= 1024) + urlCacheMemoryCapacity = 4 * 1024 * 1024; + else if (memorySize >= 512) + urlCacheMemoryCapacity = 2 * 1024 * 1024; + else if (memorySize >= 256) + urlCacheMemoryCapacity = 1 * 1024 * 1024; + else + urlCacheMemoryCapacity = 512 * 1024; + + // Foundation disk cache capacity (in bytes) + if (diskFreeSize >= 16384) + urlCacheDiskCapacity = 175 * 1024 * 1024; + else if (diskFreeSize >= 8192) + urlCacheDiskCapacity = 150 * 1024 * 1024; + else if (diskFreeSize >= 4096) + urlCacheDiskCapacity = 125 * 1024 * 1024; + else if (diskFreeSize >= 2048) + urlCacheDiskCapacity = 100 * 1024 * 1024; + else if (diskFreeSize >= 1024) + urlCacheDiskCapacity = 75 * 1024 * 1024; + else + urlCacheDiskCapacity = 50 * 1024 * 1024; + + break; + } + default: + ASSERT_NOT_REACHED(); + }; +} + +WebPage* WebProcess::webPage(uint64_t pageID) const +{ + return m_pageMap.get(pageID).get(); +} + +void WebProcess::createWebPage(uint64_t pageID, const 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. + std::pair<HashMap<uint64_t, RefPtr<WebPage> >::iterator, bool> result = m_pageMap.add(pageID, 0); + if (result.second) { + ASSERT(!result.first->second); + result.first->second = WebPage::create(pageID, parameters); + } + + ASSERT(result.first->second); +} + +void WebProcess::removeWebPage(uint64_t pageID) +{ + m_pageMap.remove(pageID); + + shutdownIfPossible(); +} + +bool WebProcess::isSeparateProcess() const +{ + // If we're running on the main run loop, we assume that we're in a separate process. + return m_runLoop == RunLoop::main(); +} + +void WebProcess::shutdownIfPossible() +{ + if (!m_pageMap.isEmpty()) + return; + + if (m_inDidClose) + return; + + if (DownloadManager::shared().isDownloading()) + return; + + // Keep running forever if we're running in the same process. + if (!isSeparateProcess()) + return; + + // Actually shut down the process. + +#ifndef NDEBUG + gcController().garbageCollectNow(); + memoryCache()->setDisabled(true); +#endif + + // Invalidate our connection. + m_connection->invalidate(); + m_connection = nullptr; + + platformShutdown(); + + m_runLoop->stop(); +} + +CoreIPC::SyncReplyMode WebProcess::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, CoreIPC::ArgumentEncoder* reply) +{ + uint64_t pageID = arguments->destinationID(); + if (!pageID) + return CoreIPC::AutomaticReply; + + WebPage* page = webPage(pageID); + if (!page) + return CoreIPC::AutomaticReply; + + page->didReceiveSyncMessage(connection, messageID, arguments, reply); + return CoreIPC::AutomaticReply; +} + +void WebProcess::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments) +{ + if (messageID.is<CoreIPC::MessageClassWebProcess>()) { + didReceiveWebProcessMessage(connection, messageID, arguments); + return; + } + + if (messageID.is<CoreIPC::MessageClassAuthenticationManager>()) { + AuthenticationManager::shared().didReceiveMessage(connection, messageID, arguments); + return; + } + + if (messageID.is<CoreIPC::MessageClassWebDatabaseManager>()) { + WebDatabaseManager::shared().didReceiveMessage(connection, messageID, arguments); + return; + } + + if (messageID.is<CoreIPC::MessageClassWebGeolocationManager>()) { + m_geolocationManager.didReceiveMessage(connection, messageID, arguments); + return; + } + + if (messageID.is<CoreIPC::MessageClassInjectedBundle>()) { + if (!m_injectedBundle) + return; + m_injectedBundle->didReceiveMessage(connection, messageID, arguments); + return; + } + + uint64_t pageID = arguments->destinationID(); + if (!pageID) + return; + + WebPage* page = webPage(pageID); + if (!page) + return; + + page->didReceiveMessage(connection, messageID, arguments); +} + +void WebProcess::didClose(CoreIPC::Connection*) +{ + // When running in the same process the connection will never be closed. + ASSERT(isSeparateProcess()); + +#ifndef NDEBUG + m_inDidClose = true; + + // Close all the live pages. + Vector<RefPtr<WebPage> > pages; + copyValuesToVector(m_pageMap, pages); + for (size_t i = 0; i < pages.size(); ++i) + pages[i]->close(); + pages.clear(); + + gcController().garbageCollectNow(); + memoryCache()->setDisabled(true); +#endif + + // The UI process closed this connection, shut down. + m_runLoop->stop(); +} + +void WebProcess::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID) +{ + // We received an invalid message, but since this is from the UI process (which we trust), + // we'll let it slide. +} + +WebFrame* WebProcess::webFrame(uint64_t frameID) const +{ + return m_frameMap.get(frameID); +} + +void WebProcess::addWebFrame(uint64_t frameID, WebFrame* frame) +{ + m_frameMap.set(frameID, frame); +} + +void WebProcess::removeWebFrame(uint64_t 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 (!m_connection) + return; + + m_connection->send(Messages::WebProcessProxy::DidDestroyFrame(frameID), 0); +} + +WebPageGroupProxy* WebProcess::webPageGroup(uint64_t pageGroupID) +{ + return m_pageGroupMap.get(pageGroupID).get(); +} + +WebPageGroupProxy* WebProcess::webPageGroup(const WebPageGroupData& pageGroupData) +{ + std::pair<HashMap<uint64_t, RefPtr<WebPageGroupProxy> >::iterator, bool> result = m_pageGroupMap.add(pageGroupData.pageGroupID, 0); + if (result.second) { + ASSERT(!result.first->second); + result.first->second = WebPageGroupProxy::create(pageGroupData); + } + + return result.first->second.get(); +} + +void WebProcess::clearResourceCaches() +{ + platformClearResourceCaches(); + + // Toggling the cache model like this forces the cache to evict all its in-memory resources. + // FIXME: We need a better way to do this. + CacheModel cacheModel = m_cacheModel; + setCacheModel(CacheModelDocumentViewer); + setCacheModel(cacheModel); + + // Empty the cross-origin preflight cache. + CrossOriginPreflightResultCache::shared().empty(); +} + +void WebProcess::clearApplicationCache() +{ +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + // Empty the application cache. + cacheStorage().empty(); +#endif +} + +void WebProcess::downloadRequest(uint64_t downloadID, uint64_t initiatingPageID, const ResourceRequest& request) +{ + WebPage* initiatingPage = initiatingPageID ? webPage(initiatingPageID) : 0; + + DownloadManager::shared().startDownload(downloadID, initiatingPage, request); +} + +void WebProcess::cancelDownload(uint64_t downloadID) +{ + DownloadManager::shared().cancelDownload(downloadID); +} + +void WebProcess::startMemorySampler(const SandboxExtension::Handle& sampleLogFileHandle, const String& sampleLogFilePath, const double interval) +{ +#if ENABLE(MEMORY_SAMPLER) + WebMemorySampler::shared()->start(sampleLogFileHandle, sampleLogFilePath, interval); +#endif +} + +void WebProcess::stopMemorySampler() +{ +#if ENABLE(MEMORY_SAMPLER) + WebMemorySampler::shared()->stop(); +#endif +} + +void WebProcess::setTextCheckerState(const TextCheckerState& textCheckerState) +{ + m_textCheckerState = textCheckerState; +} + +} // namespace WebKit |