diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/WebProcessProxy.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/WebProcessProxy.cpp | 840 |
1 files changed, 617 insertions, 223 deletions
diff --git a/Source/WebKit2/UIProcess/WebProcessProxy.cpp b/Source/WebKit2/UIProcess/WebProcessProxy.cpp index be18b2c35..d8bc5b76f 100644 --- a/Source/WebKit2/UIProcess/WebProcessProxy.cpp +++ b/Source/WebKit2/UIProcess/WebProcessProxy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,31 +27,41 @@ #include "WebProcessProxy.h" #include "APIFrameHandle.h" -#include "CustomProtocolManagerProxyMessages.h" +#include "APIPageGroupHandle.h" +#include "APIPageHandle.h" #include "DataReference.h" #include "DownloadProxyMap.h" +#include "Logging.h" #include "PluginInfoStore.h" #include "PluginProcessManager.h" #include "TextChecker.h" #include "TextCheckerState.h" +#include "UnresponsiveWebProcessTerminator.h" #include "UserData.h" #include "WebBackForwardListItem.h" -#include "WebContext.h" +#include "WebIconDatabase.h" +#include "WebInspectorUtilities.h" #include "WebNavigationDataStore.h" #include "WebNotificationManagerProxy.h" +#include "WebPageGroup.h" #include "WebPageProxy.h" -#include "WebPluginSiteDataManager.h" +#include "WebPasteboardProxy.h" #include "WebProcessMessages.h" +#include "WebProcessPool.h" #include "WebProcessProxyMessages.h" +#include "WebUserContentControllerProxy.h" +#include "WebsiteData.h" #include <WebCore/SuddenTermination.h> #include <WebCore/URL.h> #include <stdio.h> #include <wtf/NeverDestroyed.h> #include <wtf/RunLoop.h> #include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> #include <wtf/text/WTFString.h> -#if PLATFORM(MAC) +#if PLATFORM(COCOA) +#include "ObjCObjectGraph.h" #include "PDFPlugin.h" #endif @@ -79,28 +89,33 @@ static WebProcessProxy::WebPageProxyMap& globalPageMap() return pageMap; } -PassRefPtr<WebProcessProxy> WebProcessProxy::create(WebContext& context) +Ref<WebProcessProxy> WebProcessProxy::create(WebProcessPool& processPool) { - return adoptRef(new WebProcessProxy(context)); + return adoptRef(*new WebProcessProxy(processPool)); } -WebProcessProxy::WebProcessProxy(WebContext& context) - : m_responsivenessTimer(this) - , m_context(context) +WebProcessProxy::WebProcessProxy(WebProcessPool& processPool) + : ChildProcessProxy(processPool.alwaysRunsAtBackgroundPriority()) + , m_responsivenessTimer(*this) + , m_processPool(processPool) , m_mayHaveUniversalFileReadSandboxExtension(false) -#if ENABLE(CUSTOM_PROTOCOLS) - , m_customProtocolManagerProxy(this, context) -#endif -#if PLATFORM(MAC) - , m_processSuppressionEnabled(false) -#endif , m_numberOfTimesSuddenTerminationWasDisabled(0) + , m_throttler(*this) + , m_isResponsive(NoOrMaybe::Maybe) + , m_visiblePageCounter([this](RefCounterEvent) { updateBackgroundResponsivenessTimer(); }) + , m_backgroundResponsivenessTimer(processPool.configuration().unresponsiveBackgroundProcessesTerminationEnabled() ? std::make_unique<UnresponsiveWebProcessTerminator>(*this) : nullptr) { + WebPasteboardProxy::singleton().addWebProcessProxy(*this); + connect(); } WebProcessProxy::~WebProcessProxy() { + ASSERT(m_pageURLRetainCountMap.isEmpty()); + + WebPasteboardProxy::singleton().removeWebProcessProxy(*this); + if (m_webConnection) m_webConnection->invalidate(); @@ -110,37 +125,50 @@ WebProcessProxy::~WebProcessProxy() void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions) { - launchOptions.processType = ProcessLauncher::WebProcess; - platformGetLaunchOptions(launchOptions); + launchOptions.processType = ProcessLauncher::ProcessType::Web; + + ChildProcessProxy::getLaunchOptions(launchOptions); + + if (WebKit::isInspectorProcessPool(m_processPool)) + launchOptions.extraInitializationData.add(ASCIILiteral("inspector-process"), ASCIILiteral("1")); + + auto overrideLanguages = m_processPool->configuration().overrideLanguages(); + if (overrideLanguages.size()) { + StringBuilder languageString; + for (size_t i = 0; i < overrideLanguages.size(); ++i) { + if (i) + languageString.append(','); + languageString.append(overrideLanguages[i]); + } + launchOptions.extraInitializationData.add(ASCIILiteral("OverrideLanguages"), languageString.toString()); + } } -void WebProcessProxy::connectionWillOpen(IPC::Connection* connection) +void WebProcessProxy::connectionWillOpen(IPC::Connection& connection) { - ASSERT(this->connection() == connection); + ASSERT(this->connection() == &connection); #if ENABLE(SEC_ITEM_SHIM) - SecItemShimProxy::shared().initializeConnection(connection); + SecItemShimProxy::singleton().initializeConnection(connection); #endif - for (WebPageProxyMap::iterator it = m_pageMap.begin(), end = m_pageMap.end(); it != end; ++it) - it->value->connectionWillOpen(connection); - - m_context->processWillOpenConnection(this); + for (auto& page : m_pageMap.values()) + page->connectionWillOpen(connection); } -void WebProcessProxy::connectionWillClose(IPC::Connection* connection) +void WebProcessProxy::processWillShutDown(IPC::Connection& connection) { - ASSERT(this->connection() == connection); + ASSERT_UNUSED(connection, this->connection() == &connection); - for (WebPageProxyMap::iterator it = m_pageMap.begin(), end = m_pageMap.end(); it != end; ++it) - it->value->connectionWillClose(connection); + for (auto& page : m_pageMap.values()) + page->webProcessWillShutDown(); - m_context->processWillCloseConnection(this); + releaseRemainingIconsForPageURLs(); } -void WebProcessProxy::disconnect() +void WebProcessProxy::shutDown() { - clearConnection(); + shutDownProcess(); if (m_webConnection) { m_webConnection->invalidate(); @@ -148,18 +176,26 @@ void WebProcessProxy::disconnect() } m_responsivenessTimer.invalidate(); + m_tokenForHoldingLockedFiles = nullptr; Vector<RefPtr<WebFrameProxy>> frames; copyValuesToVector(m_frameMap, frames); for (size_t i = 0, size = frames.size(); i < size; ++i) - frames[i]->disconnect(); + frames[i]->webProcessWillShutDown(); m_frameMap.clear(); - if (m_downloadProxyMap) - m_downloadProxyMap->processDidClose(); + for (VisitedLinkStore* visitedLinkStore : m_visitedLinkStores) + visitedLinkStore->removeProcess(*this); + m_visitedLinkStores.clear(); + + for (WebUserContentControllerProxy* webUserContentControllerProxy : m_webUserContentControllerProxies) + webUserContentControllerProxy->removeProcess(*this); + m_webUserContentControllerProxies.clear(); - m_context->disconnectProcess(this); + m_userInitiatedActionMap.clear(); + + m_processPool->disconnectProcess(this); } WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) @@ -167,53 +203,122 @@ WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) return globalPageMap().get(pageID); } -PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient& pageClient, const WebPageConfiguration& configuration) +void WebProcessProxy::deleteWebsiteDataForTopPrivatelyOwnedDomainsInAllPersistentDataStores(OptionSet<WebsiteDataType> dataTypes, Vector<String>& topPrivatelyOwnedDomains, bool shouldNotifyPage, std::function<void()> completionHandler) +{ + struct CallbackAggregator : ThreadSafeRefCounted<CallbackAggregator> { + explicit CallbackAggregator(std::function<void()> completionHandler) + : completionHandler(WTFMove(completionHandler)) + { + } + + void addPendingCallback() + { + ++pendingCallbacks; + } + + void removePendingCallback() + { + ASSERT(pendingCallbacks); + --pendingCallbacks; + + callIfNeeded(); + } + + void callIfNeeded() + { + if (!pendingCallbacks) + completionHandler(); + } + + unsigned pendingCallbacks = 0; + std::function<void()> completionHandler; + }; + + RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator(WTFMove(completionHandler))); + + for (auto& page : globalPageMap()) { + if (!page.value->websiteDataStore().isPersistent()) + continue; + callbackAggregator->addPendingCallback(); + page.value->websiteDataStore().removeDataForTopPrivatelyOwnedDomains(dataTypes, { }, topPrivatelyOwnedDomains, [callbackAggregator, shouldNotifyPage, page]() { + if (shouldNotifyPage) + page.value->postMessageToInjectedBundle("WebsiteDataDeletionForTopPrivatelyOwnedDomainsFinished", nullptr); + WTF::RunLoop::main().dispatch([callbackAggregator] { + callbackAggregator->removePendingCallback(); + }); + }); + } +} + +Ref<WebPageProxy> WebProcessProxy::createWebPage(PageClient& pageClient, Ref<API::PageConfiguration>&& pageConfiguration) { uint64_t pageID = generatePageID(); - RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, *this, pageID, configuration); - m_pageMap.set(pageID, webPage.get()); - globalPageMap().set(pageID, webPage.get()); -#if PLATFORM(MAC) - if (webPage->isProcessSuppressible()) - m_processSuppressiblePages.add(pageID); - updateProcessSuppressionState(); -#endif - return webPage.release(); + Ref<WebPageProxy> webPage = WebPageProxy::create(pageClient, *this, pageID, WTFMove(pageConfiguration)); + + m_pageMap.set(pageID, webPage.ptr()); + globalPageMap().set(pageID, webPage.ptr()); + + updateBackgroundResponsivenessTimer(); + + return webPage; } void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID) { + ASSERT(!m_pageMap.contains(pageID)); + ASSERT(!globalPageMap().contains(pageID)); + m_pageMap.set(pageID, webPage); globalPageMap().set(pageID, webPage); -#if PLATFORM(MAC) - if (webPage->isProcessSuppressible()) - m_processSuppressiblePages.add(pageID); - updateProcessSuppressionState(); -#endif + + updateBackgroundResponsivenessTimer(); } void WebProcessProxy::removeWebPage(uint64_t pageID) { m_pageMap.remove(pageID); globalPageMap().remove(pageID); -#if PLATFORM(MAC) - m_processSuppressiblePages.remove(pageID); - updateProcessSuppressionState(); -#endif + + updateBackgroundResponsivenessTimer(); + + Vector<uint64_t> itemIDsToRemove; + for (auto& idAndItem : m_backForwardListItemMap) { + if (idAndItem.value->pageID() == pageID) + itemIDsToRemove.append(idAndItem.key); + } + for (auto itemID : itemIDsToRemove) + m_backForwardListItemMap.remove(itemID); // If this was the last WebPage open in that web process, and we have no other reason to keep it alive, let it go. // We only allow this when using a network process, as otherwise the WebProcess needs to preserve its session state. - if (m_context->usesNetworkProcess() && canTerminateChildProcess()) { - abortProcessLaunchIfNeeded(); - disconnect(); - } + if (state() == State::Terminated || !canTerminateChildProcess()) + return; + + shutDown(); } -Vector<WebPageProxy*> WebProcessProxy::pages() const +void WebProcessProxy::addVisitedLinkStore(VisitedLinkStore& store) { - Vector<WebPageProxy*> result; - copyValuesToVector(m_pageMap, result); - return result; + m_visitedLinkStores.add(&store); + store.addProcess(*this); +} + +void WebProcessProxy::addWebUserContentControllerProxy(WebUserContentControllerProxy& proxy) +{ + m_webUserContentControllerProxies.add(&proxy); + proxy.addProcess(*this); +} + +void WebProcessProxy::didDestroyVisitedLinkStore(VisitedLinkStore& store) +{ + ASSERT(m_visitedLinkStores.contains(&store)); + m_visitedLinkStores.remove(&store); +} + +void WebProcessProxy::didDestroyWebUserContentControllerProxy(WebUserContentControllerProxy& proxy) +{ + ASSERT(m_webUserContentControllerProxies.contains(&proxy)); + m_webUserContentControllerProxies.remove(&proxy); } WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const @@ -230,6 +335,11 @@ void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* m_backForwardListItemMap.set(item->itemID(), item); } +void WebProcessProxy::removeBackForwardItem(uint64_t itemID) +{ + m_backForwardListItemMap.remove(itemID); +} + void WebProcessProxy::assumeReadAccessToBaseURL(const String& urlString) { URL url(URL(), urlString); @@ -251,11 +361,19 @@ bool WebProcessProxy::hasAssumedReadAccessToURL(const URL& url) const return false; String path = url.fileSystemPath(); - for (const String& assumedAccessPath : m_localPathsWithAssumedReadAccess) { + auto startsWithURLPath = [&path](const String& assumedAccessPath) { // There are no ".." components, because URL removes those. - if (path.startsWith(assumedAccessPath)) - return true; - } + return path.startsWith(assumedAccessPath); + }; + + auto& platformPaths = platformPathsWithAssumedReadAccess(); + auto platformPathsEnd = platformPaths.end(); + if (std::find_if(platformPaths.begin(), platformPathsEnd, startsWithURLPath) != platformPathsEnd) + return true; + + auto localPathsEnd = m_localPathsWithAssumedReadAccess.end(); + if (std::find_if(m_localPathsWithAssumedReadAccess.begin(), localPathsEnd, startsWithURLPath) != localPathsEnd) + return true; return false; } @@ -285,9 +403,11 @@ bool WebProcessProxy::checkURLReceivedFromWebProcess(const URL& url) // One case where we don't have sandbox extensions for file URLs in b/f list is if the list has been reinstated after a crash or a browser restart. String path = url.fileSystemPath(); for (WebBackForwardListItemMap::iterator iter = m_backForwardListItemMap.begin(), end = m_backForwardListItemMap.end(); iter != end; ++iter) { - if (URL(URL(), iter->value->url()).fileSystemPath() == path) + URL itemURL(URL(), iter->value->url()); + if (itemURL.isLocalFile() && itemURL.fileSystemPath() == path) return true; - if (URL(URL(), iter->value->originalURL()).fileSystemPath() == path) + URL itemOriginalURL(URL(), iter->value->originalURL()); + if (itemOriginalURL.isLocalFile() && itemOriginalURL.fileSystemPath() == path) return true; } @@ -296,44 +416,44 @@ bool WebProcessProxy::checkURLReceivedFromWebProcess(const URL& url) return false; } -#if !PLATFORM(MAC) +#if !PLATFORM(COCOA) bool WebProcessProxy::fullKeyboardAccessEnabled() { return false; } #endif -void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const IPC::DataReference& backForwardData) +void WebProcessProxy::addBackForwardItem(uint64_t itemID, uint64_t pageID, const PageState& pageState) { - MESSAGE_CHECK_URL(originalURL); - MESSAGE_CHECK_URL(url); - - WebBackForwardListItemMap::AddResult result = m_backForwardListItemMap.add(itemID, nullptr); - if (result.isNewEntry) { - result.iterator->value = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID); + MESSAGE_CHECK_URL(pageState.mainFrameState.originalURLString); + MESSAGE_CHECK_URL(pageState.mainFrameState.urlString); + + auto& backForwardListItem = m_backForwardListItemMap.add(itemID, nullptr).iterator->value; + if (!backForwardListItem) { + BackForwardListItemState backForwardListItemState; + backForwardListItemState.identifier = itemID; + backForwardListItemState.pageState = pageState; + backForwardListItem = WebBackForwardListItem::create(WTFMove(backForwardListItemState), pageID); return; } // Update existing item. - result.iterator->value->setOriginalURL(originalURL); - result.iterator->value->setURL(url); - result.iterator->value->setTitle(title); - result.iterator->value->setBackForwardData(backForwardData.data(), backForwardData.size()); + backForwardListItem->setPageState(pageState); } #if ENABLE(NETSCAPE_PLUGIN_API) void WebProcessProxy::getPlugins(bool refresh, Vector<PluginInfo>& plugins, Vector<PluginInfo>& applicationPlugins) { if (refresh) - m_context->pluginInfoStore().refresh(); + m_processPool->pluginInfoStore().refresh(); - Vector<PluginModuleInfo> pluginModules = m_context->pluginInfoStore().plugins(); + Vector<PluginModuleInfo> pluginModules = m_processPool->pluginInfoStore().plugins(); for (size_t i = 0; i < pluginModules.size(); ++i) plugins.append(pluginModules[i].info); #if ENABLE(PDFKIT_PLUGIN) // Add built-in PDF last, so that it's not used when a real plug-in is installed. - if (!m_context->omitPDFSupport()) { + if (!m_processPool->omitPDFSupport()) { plugins.append(PDFPlugin::pluginInfo()); applicationPlugins.append(PDFPlugin::pluginInfo()); } @@ -344,32 +464,84 @@ void WebProcessProxy::getPlugins(bool refresh, Vector<PluginInfo>& plugins, Vect #endif // ENABLE(NETSCAPE_PLUGIN_API) #if ENABLE(NETSCAPE_PLUGIN_API) -void WebProcessProxy::getPluginProcessConnection(uint64_t pluginProcessToken, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply) +void WebProcessProxy::getPluginProcessConnection(uint64_t pluginProcessToken, Ref<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply>&& reply) { - PluginProcessManager::shared().getPluginProcessConnection(pluginProcessToken, reply); + PluginProcessManager::singleton().getPluginProcessConnection(pluginProcessToken, WTFMove(reply)); } #endif -#if ENABLE(NETWORK_PROCESS) -void WebProcessProxy::getNetworkProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply> reply) +void WebProcessProxy::getNetworkProcessConnection(Ref<Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply>&& reply) { - m_context->getNetworkProcessConnection(reply); + m_processPool->getNetworkProcessConnection(WTFMove(reply)); } -#endif // ENABLE(NETWORK_PROCESS) #if ENABLE(DATABASE_PROCESS) -void WebProcessProxy::getDatabaseProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetDatabaseProcessConnection::DelayedReply> reply) +void WebProcessProxy::getDatabaseProcessConnection(Ref<Messages::WebProcessProxy::GetDatabaseProcessConnection::DelayedReply>&& reply) { - m_context->getDatabaseProcessConnection(reply); + m_processPool->getDatabaseProcessConnection(WTFMove(reply)); } #endif // ENABLE(DATABASE_PROCESS) -void WebProcessProxy::didReceiveMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder) +void WebProcessProxy::retainIconForPageURL(const String& pageURL) +{ + WebIconDatabase* iconDatabase = processPool().iconDatabase(); + if (!iconDatabase || pageURL.isEmpty()) + return; + + // Track retain counts so we can release them if the WebProcess terminates early. + auto result = m_pageURLRetainCountMap.add(pageURL, 1); + if (!result.isNewEntry) + ++result.iterator->value; + + iconDatabase->retainIconForPageURL(pageURL); +} + +void WebProcessProxy::releaseIconForPageURL(const String& pageURL) +{ + WebIconDatabase* iconDatabase = processPool().iconDatabase(); + if (!iconDatabase || pageURL.isEmpty()) + return; + + // Track retain counts so we can release them if the WebProcess terminates early. + auto result = m_pageURLRetainCountMap.find(pageURL); + if (result == m_pageURLRetainCountMap.end()) + return; + + --result->value; + if (!result->value) + m_pageURLRetainCountMap.remove(result); + + iconDatabase->releaseIconForPageURL(pageURL); +} + +void WebProcessProxy::releaseRemainingIconsForPageURLs() +{ + WebIconDatabase* iconDatabase = processPool().iconDatabase(); + if (!iconDatabase) + return; + + for (auto& entry : m_pageURLRetainCountMap) { + uint64_t count = entry.value; + for (uint64_t i = 0; i < count; ++i) + iconDatabase->releaseIconForPageURL(entry.key); + } + + m_pageURLRetainCountMap.clear(); +} + +#if !PLATFORM(COCOA) +bool WebProcessProxy::platformIsBeingDebugged() const +{ + return false; +} +#endif + +void WebProcessProxy::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder) { if (dispatchMessage(connection, decoder)) return; - if (m_context->dispatchMessage(connection, decoder)) + if (m_processPool->dispatchMessage(connection, decoder)) return; if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) { @@ -380,12 +552,12 @@ void WebProcessProxy::didReceiveMessage(IPC::Connection* connection, IPC::Messag // FIXME: Add unhandled message logging. } -void WebProcessProxy::didReceiveSyncMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder, std::unique_ptr<IPC::MessageEncoder>& replyEncoder) +void WebProcessProxy::didReceiveSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, std::unique_ptr<IPC::Encoder>& replyEncoder) { if (dispatchSyncMessage(connection, decoder, replyEncoder)) return; - if (m_context->dispatchSyncMessage(connection, decoder, replyEncoder)) + if (m_processPool->dispatchSyncMessage(connection, decoder, replyEncoder)) return; if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) { @@ -396,7 +568,7 @@ void WebProcessProxy::didReceiveSyncMessage(IPC::Connection* connection, IPC::Me // FIXME: Add unhandled message logging. } -void WebProcessProxy::didClose(IPC::Connection*) +void WebProcessProxy::didClose(IPC::Connection&) { // Protect ourselves, as the call to disconnect() below may otherwise cause us // to be deleted before we can finish our work. @@ -407,18 +579,18 @@ void WebProcessProxy::didClose(IPC::Connection*) Vector<RefPtr<WebPageProxy>> pages; copyValuesToVector(m_pageMap, pages); - disconnect(); + shutDown(); for (size_t i = 0, size = pages.size(); i < size; ++i) pages[i]->processDidCrash(); } -void WebProcessProxy::didReceiveInvalidMessage(IPC::Connection* connection, IPC::StringReference messageReceiverName, IPC::StringReference messageName) +void WebProcessProxy::didReceiveInvalidMessage(IPC::Connection& connection, IPC::StringReference messageReceiverName, IPC::StringReference messageName) { WTFLogAlways("Received an invalid message \"%s.%s\" from the web process.\n", messageReceiverName.toString().data(), messageName.toString().data()); - WebContext::didReceiveInvalidMessage(messageReceiverName, messageName); + WebProcessPool::didReceiveInvalidMessage(messageReceiverName, messageName); // Terminate the WebProcess. terminate(); @@ -428,40 +600,72 @@ void WebProcessProxy::didReceiveInvalidMessage(IPC::Connection* connection, IPC: didClose(connection); } -void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*) +void WebProcessProxy::didBecomeUnresponsive() { + m_isResponsive = NoOrMaybe::No; + Vector<RefPtr<WebPageProxy>> pages; copyValuesToVector(m_pageMap, pages); - for (size_t i = 0, size = pages.size(); i < size; ++i) - pages[i]->processDidBecomeUnresponsive(); + + auto isResponsiveCallbacks = WTFMove(m_isResponsiveCallbacks); + + for (auto& page : pages) + page->processDidBecomeUnresponsive(); + + bool isWebProcessResponsive = false; + for (auto& callback : isResponsiveCallbacks) + callback(isWebProcessResponsive); } -void WebProcessProxy::interactionOccurredWhileUnresponsive(ResponsivenessTimer*) +void WebProcessProxy::didBecomeResponsive() { + m_isResponsive = NoOrMaybe::Maybe; + Vector<RefPtr<WebPageProxy>> pages; copyValuesToVector(m_pageMap, pages); - for (size_t i = 0, size = pages.size(); i < size; ++i) - pages[i]->interactionOccurredWhileProcessUnresponsive(); + for (auto& page : pages) + page->processDidBecomeResponsive(); } -void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*) +void WebProcessProxy::willChangeIsResponsive() { Vector<RefPtr<WebPageProxy>> pages; copyValuesToVector(m_pageMap, pages); - for (size_t i = 0, size = pages.size(); i < size; ++i) - pages[i]->processDidBecomeResponsive(); + for (auto& page : pages) + page->willChangeProcessIsResponsive(); +} + +void WebProcessProxy::didChangeIsResponsive() +{ + Vector<RefPtr<WebPageProxy>> pages; + copyValuesToVector(m_pageMap, pages); + for (auto& page : pages) + page->didChangeProcessIsResponsive(); +} + +bool WebProcessProxy::mayBecomeUnresponsive() +{ + return !platformIsBeingDebugged(); } void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connection::Identifier connectionIdentifier) { ChildProcessProxy::didFinishLaunching(launcher, connectionIdentifier); + for (WebPageProxy* page : m_pageMap.values()) { + ASSERT(this == &page->process()); + page->processDidFinishLaunching(); + } + m_webConnection = WebConnectionToWebProcess::create(this); - m_context->processDidFinishLaunching(this); + m_processPool->processDidFinishLaunching(this); -#if PLATFORM(MAC) - updateProcessSuppressionState(); +#if PLATFORM(IOS) + if (connection()) { + if (xpc_connection_t xpcConnection = connection()->xpcConnection()) + m_throttler.didConnectToProcess(xpc_connection_get_pid(xpcConnection)); + } #endif } @@ -499,7 +703,7 @@ void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page) copyValuesToVector(m_frameMap, frames); for (size_t i = 0, size = frames.size(); i < size; ++i) { if (frames[i]->page() == page) - frames[i]->disconnect(); + frames[i]->webProcessWillShutDown(); } } @@ -513,15 +717,32 @@ size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const return result; } +auto WebProcessProxy::visiblePageToken() const -> VisibleWebPageToken +{ + return m_visiblePageCounter.count(); +} + +RefPtr<API::UserInitiatedAction> WebProcessProxy::userInitiatedActivity(uint64_t identifier) +{ + if (!UserInitiatedActionMap::isValidKey(identifier) || !identifier) + return nullptr; + + auto result = m_userInitiatedActionMap.ensure(identifier, [] { return API::UserInitiatedAction::create(); }); + return result.iterator->value; +} + +void WebProcessProxy::didDestroyUserGestureToken(uint64_t identifier) +{ + ASSERT(UserInitiatedActionMap::isValidKey(identifier)); + m_userInitiatedActionMap.remove(identifier); +} + bool WebProcessProxy::canTerminateChildProcess() { if (!m_pageMap.isEmpty()) return false; - if (m_downloadProxyMap && !m_downloadProxyMap->isEmpty()) - return false; - - if (!m_context->shouldTerminate(this)) + if (!m_processPool->shouldTerminate(this)) return false; return true; @@ -531,8 +752,8 @@ void WebProcessProxy::shouldTerminate(bool& shouldTerminate) { shouldTerminate = canTerminateChildProcess(); if (shouldTerminate) { - // We know that the web process is going to terminate so disconnect it from the context. - disconnect(); + // We know that the web process is going to terminate so start shutting it down in the UI process. + shutDown(); } } @@ -542,169 +763,342 @@ void WebProcessProxy::updateTextCheckerState() send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0); } -DownloadProxy* WebProcessProxy::createDownloadProxy() +void WebProcessProxy::didSaveToPageCache() { -#if ENABLE(NETWORK_PROCESS) - ASSERT(!m_context->usesNetworkProcess()); -#endif + m_processPool->processDidCachePage(this); +} - if (!m_downloadProxyMap) - m_downloadProxyMap = adoptPtr(new DownloadProxyMap(this)); +void WebProcessProxy::releasePageCache() +{ + if (canSendMessage()) + send(Messages::WebProcess::ReleasePageCache(), 0); +} - return m_downloadProxyMap->createDownloadProxy(m_context.get()); +void WebProcessProxy::windowServerConnectionStateChanged() +{ + for (const auto& page : m_pageMap.values()) + page->activityStateDidChange(ActivityState::IsVisuallyIdle); } -void WebProcessProxy::didNavigateWithNavigationData(uint64_t pageID, const WebNavigationDataStore& store, uint64_t frameID) +void WebProcessProxy::fetchWebsiteData(SessionID sessionID, OptionSet<WebsiteDataType> dataTypes, Function<void (WebsiteData)> completionHandler) { - WebPageProxy* page = webPage(pageID); - if (!page) - return; - - WebFrameProxy* frame = webFrame(frameID); - MESSAGE_CHECK(frame); - MESSAGE_CHECK(frame->page() == page); - - m_context->historyClient().didNavigateWithNavigationData(&m_context.get(), page, store, frame); + ASSERT(canSendMessage()); + + auto token = throttler().backgroundActivityToken(); + RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is taking a background assertion because the Web process is fetching Website data", this); + + connection()->sendWithReply(Messages::WebProcess::FetchWebsiteData(sessionID, dataTypes), 0, RunLoop::main(), [this, token, completionHandler = WTFMove(completionHandler), sessionID](auto reply) { + if (!reply) { + completionHandler(WebsiteData { }); + return; + } + + completionHandler(WTFMove(std::get<0>(*reply))); + RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is releasing a background assertion because the Web process is done fetching Website data", this); + }); } -void WebProcessProxy::didPerformClientRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID) +void WebProcessProxy::deleteWebsiteData(SessionID sessionID, OptionSet<WebsiteDataType> dataTypes, std::chrono::system_clock::time_point modifiedSince, Function<void ()> completionHandler) { - WebPageProxy* page = webPage(pageID); - if (!page) - return; + ASSERT(canSendMessage()); - if (sourceURLString.isEmpty() || destinationURLString.isEmpty()) - return; - - WebFrameProxy* frame = webFrame(frameID); - MESSAGE_CHECK(frame); - MESSAGE_CHECK(frame->page() == page); - MESSAGE_CHECK_URL(sourceURLString); - MESSAGE_CHECK_URL(destinationURLString); + auto token = throttler().backgroundActivityToken(); + RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is taking a background assertion because the Web process is deleting Website data", this); + + connection()->sendWithReply(Messages::WebProcess::DeleteWebsiteData(sessionID, dataTypes, modifiedSince), 0, RunLoop::main(), [this, token, completionHandler = WTFMove(completionHandler), sessionID](auto reply) { + completionHandler(); + RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is releasing a background assertion because the Web process is done deleting Website data", this); + }); +} + +void WebProcessProxy::deleteWebsiteDataForOrigins(SessionID sessionID, OptionSet<WebsiteDataType> dataTypes, const Vector<WebCore::SecurityOriginData>& origins, Function<void()> completionHandler) +{ + ASSERT(canSendMessage()); - m_context->historyClient().didPerformClientRedirect(&m_context.get(), page, sourceURLString, destinationURLString, frame); + auto token = throttler().backgroundActivityToken(); + RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is taking a background assertion because the Web process is deleting Website data for several origins", this); + + connection()->sendWithReply(Messages::WebProcess::DeleteWebsiteDataForOrigins(sessionID, dataTypes, origins), 0, RunLoop::main(), [this, token, completionHandler = WTFMove(completionHandler), sessionID](auto reply) { + completionHandler(); + RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - WebProcessProxy is releasing a background assertion because the Web process is done deleting Website data for several origins", this); + }); } -void WebProcessProxy::didPerformServerRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID) +void WebProcessProxy::requestTermination() { - WebPageProxy* page = webPage(pageID); - if (!page) + if (state() == State::Terminated) return; - - if (sourceURLString.isEmpty() || destinationURLString.isEmpty()) - return; - - WebFrameProxy* frame = webFrame(frameID); - MESSAGE_CHECK(frame); - MESSAGE_CHECK(frame->page() == page); - MESSAGE_CHECK_URL(sourceURLString); - MESSAGE_CHECK_URL(destinationURLString); - m_context->historyClient().didPerformServerRedirect(&m_context.get(), page, sourceURLString, destinationURLString, frame); + ChildProcessProxy::terminate(); + + if (webConnection()) + webConnection()->didClose(); + + shutDown(); } -void WebProcessProxy::didUpdateHistoryTitle(uint64_t pageID, const String& title, const String& url, uint64_t frameID) +void WebProcessProxy::enableSuddenTermination() { - WebPageProxy* page = webPage(pageID); - if (!page) + if (state() != State::Running) return; - WebFrameProxy* frame = webFrame(frameID); - MESSAGE_CHECK(frame); - MESSAGE_CHECK(frame->page() == page); - MESSAGE_CHECK_URL(url); + ASSERT(m_numberOfTimesSuddenTerminationWasDisabled); + WebCore::enableSuddenTermination(); + --m_numberOfTimesSuddenTerminationWasDisabled; +} - m_context->historyClient().didUpdateHistoryTitle(&m_context.get(), page, title, url, frame); +void WebProcessProxy::disableSuddenTermination() +{ + if (state() != State::Running) + return; + + WebCore::disableSuddenTermination(); + ++m_numberOfTimesSuddenTerminationWasDisabled; } -void WebProcessProxy::pageSuppressibilityChanged(WebKit::WebPageProxy *page) +RefPtr<API::Object> WebProcessProxy::transformHandlesToObjects(API::Object* object) { -#if PLATFORM(MAC) - if (page->isProcessSuppressible()) - m_processSuppressiblePages.add(page->pageID()); - else - m_processSuppressiblePages.remove(page->pageID()); - updateProcessSuppressionState(); -#else - UNUSED_PARAM(page); + struct Transformer final : UserData::Transformer { + Transformer(WebProcessProxy& webProcessProxy) + : m_webProcessProxy(webProcessProxy) + { + } + + 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(); + + case API::Object::Type::PageGroupHandle: +#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: + ASSERT(static_cast<API::FrameHandle&>(object).isAutoconverting()); + return m_webProcessProxy.webFrame(static_cast<API::FrameHandle&>(object).frameID()); + + case API::Object::Type::PageGroupHandle: + return WebPageGroup::get(static_cast<API::PageGroupHandle&>(object).webPageGroupData().pageGroupID); + + case API::Object::Type::PageHandle: + ASSERT(static_cast<API::PageHandle&>(object).isAutoconverting()); + return m_webProcessProxy.webPage(static_cast<API::PageHandle&>(object).pageID()); + +#if PLATFORM(COCOA) + case API::Object::Type::ObjCObjectGraph: + return m_webProcessProxy.transformHandlesToObjects(static_cast<ObjCObjectGraph&>(object)); #endif + default: + return &object; + } + } + + WebProcessProxy& m_webProcessProxy; + }; + + return UserData::transform(object, Transformer(*this)); } -void WebProcessProxy::pagePreferencesChanged(WebKit::WebPageProxy *page) +RefPtr<API::Object> WebProcessProxy::transformObjectsToHandles(API::Object* object) { -#if PLATFORM(MAC) - if (page->isProcessSuppressible()) - m_processSuppressiblePages.add(page->pageID()); - else - m_processSuppressiblePages.remove(page->pageID()); - updateProcessSuppressionState(); -#else - UNUSED_PARAM(page); + struct Transformer final : UserData::Transformer { + bool shouldTransformObject(const API::Object& object) const override + { + switch (object.type()) { + case API::Object::Type::Frame: + case API::Object::Type::Page: + case API::Object::Type::PageGroup: +#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::Frame: + return API::FrameHandle::createAutoconverting(static_cast<const WebFrameProxy&>(object).frameID()); + + case API::Object::Type::Page: + return API::PageHandle::createAutoconverting(static_cast<const WebPageProxy&>(object).pageID()); + + case API::Object::Type::PageGroup: + return API::PageGroupHandle::create(WebPageGroupData(static_cast<const WebPageGroup&>(object).data())); + +#if PLATFORM(COCOA) + case API::Object::Type::ObjCObjectGraph: + return transformObjectsToHandles(static_cast<ObjCObjectGraph&>(object)); #endif + + default: + return &object; + } + } + }; + + return UserData::transform(object, Transformer()); } -void WebProcessProxy::didSaveToPageCache() +void WebProcessProxy::sendProcessWillSuspendImminently() { - m_context->processDidCachePage(this); + if (!canSendMessage()) + return; + + bool handled = false; + sendSync(Messages::WebProcess::ProcessWillSuspendImminently(), Messages::WebProcess::ProcessWillSuspendImminently::Reply(handled), 0, 1_s); } -void WebProcessProxy::releasePageCache() +void WebProcessProxy::sendPrepareToSuspend() { if (canSendMessage()) - send(Messages::WebProcess::ReleasePageCache(), 0); + send(Messages::WebProcess::PrepareToSuspend(), 0); } -void WebProcessProxy::windowServerConnectionStateChanged() +void WebProcessProxy::sendCancelPrepareToSuspend() { - for (const auto& page : m_pageMap.values()) - page->viewStateDidChange(ViewState::IsVisuallyIdle); + if (canSendMessage()) + send(Messages::WebProcess::CancelPrepareToSuspend(), 0); } -void WebProcessProxy::requestTermination() +void WebProcessProxy::sendProcessDidResume() { - if (!isValid()) - return; + if (canSendMessage()) + send(Messages::WebProcess::ProcessDidResume(), 0); +} - ChildProcessProxy::terminate(); +void WebProcessProxy::processReadyToSuspend() +{ + m_throttler.processReadyToSuspend(); +} - if (webConnection()) - webConnection()->didClose(); +void WebProcessProxy::didCancelProcessSuspension() +{ + m_throttler.didCancelProcessSuspension(); +} - disconnect(); +void WebProcessProxy::reinstateNetworkProcessAssertionState(NetworkProcessProxy& newNetworkProcessProxy) +{ +#if PLATFORM(IOS) + ASSERT(!m_backgroundTokenForNetworkProcess || !m_foregroundTokenForNetworkProcess); + + // The network process crashed; take new tokens for the new network process. + if (m_backgroundTokenForNetworkProcess) + m_backgroundTokenForNetworkProcess = newNetworkProcessProxy.throttler().backgroundActivityToken(); + else if (m_foregroundTokenForNetworkProcess) + m_foregroundTokenForNetworkProcess = newNetworkProcessProxy.throttler().foregroundActivityToken(); +#else + UNUSED_PARAM(newNetworkProcessProxy); +#endif } -void WebProcessProxy::enableSuddenTermination() +void WebProcessProxy::didSetAssertionState(AssertionState state) { - if (!isValid()) - return; +#if PLATFORM(IOS) + ASSERT(!m_backgroundTokenForNetworkProcess || !m_foregroundTokenForNetworkProcess); + + switch (state) { + case AssertionState::Suspended: + RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::didSetAssertionState(Suspended) release all assertions for network process", this); + m_foregroundTokenForNetworkProcess = nullptr; + m_backgroundTokenForNetworkProcess = nullptr; + for (auto& page : m_pageMap.values()) + page->processWillBecomeSuspended(); + break; + + case AssertionState::Background: + RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::didSetAssertionState(Background) taking background assertion for network process", this); + m_backgroundTokenForNetworkProcess = processPool().ensureNetworkProcess().throttler().backgroundActivityToken(); + m_foregroundTokenForNetworkProcess = nullptr; + break; + + case AssertionState::Foreground: + RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::didSetAssertionState(Foreground) taking foreground assertion for network process", this); + m_foregroundTokenForNetworkProcess = processPool().ensureNetworkProcess().throttler().foregroundActivityToken(); + m_backgroundTokenForNetworkProcess = nullptr; + for (auto& page : m_pageMap.values()) + page->processWillBecomeForeground(); + break; + } - ASSERT(m_numberOfTimesSuddenTerminationWasDisabled); - WebCore::enableSuddenTermination(); - --m_numberOfTimesSuddenTerminationWasDisabled; + ASSERT(!m_backgroundTokenForNetworkProcess || !m_foregroundTokenForNetworkProcess); +#else + UNUSED_PARAM(state); +#endif +} + +void WebProcessProxy::setIsHoldingLockedFiles(bool isHoldingLockedFiles) +{ + if (!isHoldingLockedFiles) { + RELEASE_LOG(ProcessSuspension, "UIProcess is releasing a background assertion because the WebContent process is no longer holding locked files"); + m_tokenForHoldingLockedFiles = nullptr; + return; + } + if (!m_tokenForHoldingLockedFiles) { + RELEASE_LOG(ProcessSuspension, "UIProcess is taking a background assertion because the WebContent process is holding locked files"); + m_tokenForHoldingLockedFiles = m_throttler.backgroundActivityToken(); + } } -void WebProcessProxy::disableSuddenTermination() +void WebProcessProxy::isResponsive(std::function<void(bool isWebProcessResponsive)> callback) { - if (!isValid()) + if (m_isResponsive == NoOrMaybe::No) { + if (callback) { + RunLoop::main().dispatch([callback = WTFMove(callback)] { + bool isWebProcessResponsive = false; + callback(isWebProcessResponsive); + }); + } return; + } - WebCore::disableSuddenTermination(); - ++m_numberOfTimesSuddenTerminationWasDisabled; + if (callback) + m_isResponsiveCallbacks.append(callback); + + responsivenessTimer().start(); + send(Messages::WebProcess::MainThreadPing(), 0); } -RefPtr<API::Object> WebProcessProxy::apiObjectByConvertingToHandles(API::Object* object) +void WebProcessProxy::didReceiveMainThreadPing() { - return UserData::transform(object, [](const API::Object& object) -> RefPtr<API::Object> { - switch (object.type()) { - case API::Object::Type::Frame: { - auto& frame = static_cast<const WebFrameProxy&>(object); - return API::FrameHandle::create(frame.frameID()); - } + responsivenessTimer().stop(); - default: - return nullptr; - } - }); + auto isResponsiveCallbacks = WTFMove(m_isResponsiveCallbacks); + bool isWebProcessResponsive = true; + for (auto& callback : isResponsiveCallbacks) + callback(isWebProcessResponsive); } +void WebProcessProxy::updateBackgroundResponsivenessTimer() +{ + if (m_backgroundResponsivenessTimer) + m_backgroundResponsivenessTimer->updateState(); +} + +#if !PLATFORM(COCOA) +const HashSet<String>& WebProcessProxy::platformPathsWithAssumedReadAccess() +{ + static NeverDestroyed<HashSet<String>> platformPathsWithAssumedReadAccess; + return platformPathsWithAssumedReadAccess; +} +#endif + } // namespace WebKit |