WebProcessProxy.cpp [plain text]
#include "config.h"
#include "WebProcessProxy.h"
#include "APIFrameHandle.h"
#include "APIHistoryClient.h"
#include "CustomProtocolManagerProxyMessages.h"
#include "DataReference.h"
#include "DownloadProxyMap.h"
#include "PluginInfoStore.h"
#include "PluginProcessManager.h"
#include "TextChecker.h"
#include "TextCheckerState.h"
#include "UserData.h"
#include "WebUserContentControllerProxy.h"
#include "WebBackForwardListItem.h"
#include "WebContext.h"
#include "WebNavigationDataStore.h"
#include "WebNotificationManagerProxy.h"
#include "WebPageGroup.h"
#include "WebPageProxy.h"
#include "WebPluginSiteDataManager.h"
#include "WebProcessMessages.h"
#include "WebProcessProxyMessages.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/WTFString.h>
#if PLATFORM(COCOA)
#include "PDFPlugin.h"
#endif
#if ENABLE(SEC_ITEM_SHIM)
#include "SecItemShimProxy.h"
#endif
using namespace WebCore;
#define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, connection())
#define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(checkURLReceivedFromWebProcess(url), connection())
namespace WebKit {
static uint64_t generatePageID()
{
static uint64_t uniquePageID;
return ++uniquePageID;
}
static WebProcessProxy::WebPageProxyMap& globalPageMap()
{
ASSERT(RunLoop::isMain());
static NeverDestroyed<WebProcessProxy::WebPageProxyMap> pageMap;
return pageMap;
}
PassRefPtr<WebProcessProxy> WebProcessProxy::create(WebContext& context)
{
return adoptRef(new WebProcessProxy(context));
}
WebProcessProxy::WebProcessProxy(WebContext& context)
: m_responsivenessTimer(this)
, m_context(context)
, m_mayHaveUniversalFileReadSandboxExtension(false)
#if ENABLE(CUSTOM_PROTOCOLS)
, m_customProtocolManagerProxy(this, context)
#endif
#if PLATFORM(COCOA)
, m_processSuppressionEnabled(false)
#endif
, m_numberOfTimesSuddenTerminationWasDisabled(0)
, m_throttler(std::make_unique<ProcessThrottler>(this))
{
connect();
}
WebProcessProxy::~WebProcessProxy()
{
if (m_webConnection)
m_webConnection->invalidate();
while (m_numberOfTimesSuddenTerminationWasDisabled-- > 0)
WebCore::enableSuddenTermination();
}
void WebProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
{
launchOptions.processType = ProcessLauncher::WebProcess;
platformGetLaunchOptions(launchOptions);
}
void WebProcessProxy::connectionWillOpen(IPC::Connection* connection)
{
ASSERT(this->connection() == connection);
#if ENABLE(SEC_ITEM_SHIM)
SecItemShimProxy::shared().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);
}
void WebProcessProxy::connectionWillClose(IPC::Connection* connection)
{
ASSERT(this->connection() == connection);
for (WebPageProxyMap::iterator it = m_pageMap.begin(), end = m_pageMap.end(); it != end; ++it)
it->value->connectionWillClose(connection);
m_context->processWillCloseConnection(this);
}
void WebProcessProxy::disconnect()
{
clearConnection();
if (m_webConnection) {
m_webConnection->invalidate();
m_webConnection = nullptr;
}
m_responsivenessTimer.invalidate();
Vector<RefPtr<WebFrameProxy>> frames;
copyValuesToVector(m_frameMap, frames);
for (size_t i = 0, size = frames.size(); i < size; ++i)
frames[i]->disconnect();
m_frameMap.clear();
if (m_downloadProxyMap)
m_downloadProxyMap->processDidClose();
for (VisitedLinkProvider* visitedLinkProvider : m_visitedLinkProviders)
visitedLinkProvider->removeProcess(*this);
m_visitedLinkProviders.clear();
for (WebUserContentControllerProxy* webUserContentControllerProxy : m_webUserContentControllerProxies)
webUserContentControllerProxy->removeProcess(*this);
m_webUserContentControllerProxies.clear();
m_context->disconnectProcess(this);
}
WebPageProxy* WebProcessProxy::webPage(uint64_t pageID)
{
return globalPageMap().get(pageID);
}
PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient& pageClient, const WebPageConfiguration& configuration)
{
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(COCOA)
if (webPage->isProcessSuppressible())
m_processSuppressiblePages.add(pageID);
updateProcessSuppressionState();
#endif
return webPage.release();
}
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(COCOA)
if (webPage->isProcessSuppressible())
m_processSuppressiblePages.add(pageID);
updateProcessSuppressionState();
#endif
}
void WebProcessProxy::removeWebPage(uint64_t pageID)
{
m_pageMap.remove(pageID);
globalPageMap().remove(pageID);
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 PLATFORM(COCOA)
m_processSuppressiblePages.remove(pageID);
updateProcessSuppressionState();
#endif
if (!m_context->usesNetworkProcess() || state() == State::Terminated || !canTerminateChildProcess())
return;
abortProcessLaunchIfNeeded();
#if PLATFORM(IOS)
if (state() == State::Running) {
connection()->terminateSoon(30);
}
#endif
disconnect();
}
void WebProcessProxy::addVisitedLinkProvider(VisitedLinkProvider& provider)
{
m_visitedLinkProviders.add(&provider);
provider.addProcess(*this);
}
void WebProcessProxy::addWebUserContentControllerProxy(WebUserContentControllerProxy& proxy)
{
m_webUserContentControllerProxies.add(&proxy);
proxy.addProcess(*this);
}
void WebProcessProxy::didDestroyVisitedLinkProvider(VisitedLinkProvider& provider)
{
ASSERT(m_visitedLinkProviders.contains(&provider));
m_visitedLinkProviders.remove(&provider);
}
void WebProcessProxy::didDestroyWebUserContentControllerProxy(WebUserContentControllerProxy& proxy)
{
ASSERT(m_webUserContentControllerProxies.contains(&proxy));
m_webUserContentControllerProxies.remove(&proxy);
}
WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
{
return m_backForwardListItemMap.get(itemID);
}
void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item)
{
ASSERT(!m_backForwardListItemMap.contains(item->itemID()));
m_backForwardListItemMap.set(item->itemID(), item);
}
void WebProcessProxy::assumeReadAccessToBaseURL(const String& urlString)
{
URL url(URL(), urlString);
if (!url.isLocalFile())
return;
URL baseURL(URL(), url.baseAsString());
m_localPathsWithAssumedReadAccess.add(baseURL.fileSystemPath());
}
bool WebProcessProxy::hasAssumedReadAccessToURL(const URL& url) const
{
if (!url.isLocalFile())
return false;
String path = url.fileSystemPath();
for (const String& assumedAccessPath : m_localPathsWithAssumedReadAccess) {
if (path.startsWith(assumedAccessPath))
return true;
}
return false;
}
bool WebProcessProxy::checkURLReceivedFromWebProcess(const String& urlString)
{
return checkURLReceivedFromWebProcess(URL(URL(), urlString));
}
bool WebProcessProxy::checkURLReceivedFromWebProcess(const URL& url)
{
if (!url.isLocalFile())
return true;
if (m_mayHaveUniversalFileReadSandboxExtension)
return true;
if (hasAssumedReadAccessToURL(url))
return true;
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)
return true;
if (URL(URL(), iter->value->originalURL()).fileSystemPath() == path)
return true;
}
WTFLogAlways("Received an unexpected URL from the web process: '%s'\n", url.string().utf8().data());
return false;
}
#if !PLATFORM(COCOA)
bool WebProcessProxy::fullKeyboardAccessEnabled()
{
return false;
}
#endif
void WebProcessProxy::addBackForwardItem(uint64_t itemID, uint64_t pageID, const PageState& pageState)
{
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(WTF::move(backForwardListItemState), pageID);
return;
}
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();
Vector<PluginModuleInfo> pluginModules = m_context->pluginInfoStore().plugins();
for (size_t i = 0; i < pluginModules.size(); ++i)
plugins.append(pluginModules[i].info);
#if ENABLE(PDFKIT_PLUGIN)
if (!m_context->omitPDFSupport()) {
plugins.append(PDFPlugin::pluginInfo());
applicationPlugins.append(PDFPlugin::pluginInfo());
}
#else
UNUSED_PARAM(applicationPlugins);
#endif
}
#endif // ENABLE(NETSCAPE_PLUGIN_API)
#if ENABLE(NETSCAPE_PLUGIN_API)
void WebProcessProxy::getPluginProcessConnection(uint64_t pluginProcessToken, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
{
PluginProcessManager::shared().getPluginProcessConnection(pluginProcessToken, reply);
}
#endif
#if ENABLE(NETWORK_PROCESS)
void WebProcessProxy::getNetworkProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetNetworkProcessConnection::DelayedReply> reply)
{
m_context->getNetworkProcessConnection(reply);
}
#endif // ENABLE(NETWORK_PROCESS)
#if ENABLE(DATABASE_PROCESS)
void WebProcessProxy::getDatabaseProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetDatabaseProcessConnection::DelayedReply> reply)
{
m_context->getDatabaseProcessConnection(reply);
}
#endif // ENABLE(DATABASE_PROCESS)
void WebProcessProxy::didReceiveMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder)
{
if (dispatchMessage(connection, decoder))
return;
if (m_context->dispatchMessage(connection, decoder))
return;
if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
didReceiveWebProcessProxyMessage(connection, decoder);
return;
}
}
void WebProcessProxy::didReceiveSyncMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder, std::unique_ptr<IPC::MessageEncoder>& replyEncoder)
{
if (dispatchSyncMessage(connection, decoder, replyEncoder))
return;
if (m_context->dispatchSyncMessage(connection, decoder, replyEncoder))
return;
if (decoder.messageReceiverName() == Messages::WebProcessProxy::messageReceiverName()) {
didReceiveSyncWebProcessProxyMessage(connection, decoder, replyEncoder);
return;
}
}
void WebProcessProxy::didClose(IPC::Connection*)
{
Ref<WebProcessProxy> protect(*this);
webConnection()->didClose();
Vector<RefPtr<WebPageProxy>> pages;
copyValuesToVector(m_pageMap, pages);
disconnect();
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)
{
WTFLogAlways("Received an invalid message \"%s.%s\" from the web process.\n", messageReceiverName.toString().data(), messageName.toString().data());
WebContext::didReceiveInvalidMessage(messageReceiverName, messageName);
terminate();
didClose(connection);
}
void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*)
{
Vector<RefPtr<WebPageProxy>> pages;
copyValuesToVector(m_pageMap, pages);
for (size_t i = 0, size = pages.size(); i < size; ++i)
pages[i]->processDidBecomeUnresponsive();
}
void WebProcessProxy::interactionOccurredWhileUnresponsive(ResponsivenessTimer*)
{
Vector<RefPtr<WebPageProxy>> pages;
copyValuesToVector(m_pageMap, pages);
for (size_t i = 0, size = pages.size(); i < size; ++i)
pages[i]->interactionOccurredWhileProcessUnresponsive();
}
void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*)
{
Vector<RefPtr<WebPageProxy>> pages;
copyValuesToVector(m_pageMap, pages);
for (size_t i = 0, size = pages.size(); i < size; ++i)
pages[i]->processDidBecomeResponsive();
}
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);
#if PLATFORM(COCOA)
updateProcessSuppressionState();
#endif
#if PLATFORM(IOS) && USE(XPC_SERVICES)
xpc_connection_t xpcConnection = connection()->xpcConnection();
ASSERT(xpcConnection);
m_throttler->didConnnectToProcess(xpc_connection_get_pid(xpcConnection));
#endif
}
WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
{
if (!WebFrameProxyMap::isValidKey(frameID))
return 0;
return m_frameMap.get(frameID);
}
bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
{
return WebFrameProxyMap::isValidKey(frameID) && !m_frameMap.contains(frameID);
}
void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
{
ASSERT(canCreateFrame(frameID));
m_frameMap.set(frameID, frameProxy);
}
void WebProcessProxy::didDestroyFrame(uint64_t frameID)
{
ASSERT(WebFrameProxyMap::isValidKey(frameID));
m_frameMap.remove(frameID);
}
void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
{
Vector<RefPtr<WebFrameProxy>> frames;
copyValuesToVector(m_frameMap, frames);
for (size_t i = 0, size = frames.size(); i < size; ++i) {
if (frames[i]->page() == page)
frames[i]->disconnect();
}
}
size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
{
size_t result = 0;
for (HashMap<uint64_t, RefPtr<WebFrameProxy>>::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) {
if (iter->value->page() == page)
++result;
}
return result;
}
bool WebProcessProxy::canTerminateChildProcess()
{
if (!m_pageMap.isEmpty())
return false;
if (m_downloadProxyMap && !m_downloadProxyMap->isEmpty())
return false;
if (!m_context->shouldTerminate(this))
return false;
return true;
}
void WebProcessProxy::shouldTerminate(bool& shouldTerminate)
{
shouldTerminate = canTerminateChildProcess();
if (shouldTerminate) {
disconnect();
}
}
void WebProcessProxy::updateTextCheckerState()
{
if (canSendMessage())
send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
}
DownloadProxy* WebProcessProxy::createDownloadProxy()
{
#if ENABLE(NETWORK_PROCESS)
ASSERT(!m_context->usesNetworkProcess());
#endif
if (!m_downloadProxyMap)
m_downloadProxyMap = std::make_unique<DownloadProxyMap>(this);
return m_downloadProxyMap->createDownloadProxy(m_context.get());
}
void WebProcessProxy::didNavigateWithNavigationData(uint64_t pageID, const WebNavigationDataStore& store, uint64_t frameID)
{
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);
}
void WebProcessProxy::didPerformClientRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
{
WebPageProxy* page = webPage(pageID);
if (!page)
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().didPerformClientRedirect(&m_context.get(), page, sourceURLString, destinationURLString, frame);
}
void WebProcessProxy::didPerformServerRedirect(uint64_t pageID, const String& sourceURLString, const String& destinationURLString, uint64_t frameID)
{
WebPageProxy* page = webPage(pageID);
if (!page)
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);
}
void WebProcessProxy::didUpdateHistoryTitle(uint64_t pageID, const String& title, const String& url, uint64_t frameID)
{
WebPageProxy* page = webPage(pageID);
if (!page)
return;
WebFrameProxy* frame = webFrame(frameID);
MESSAGE_CHECK(frame);
MESSAGE_CHECK(frame->page() == page);
MESSAGE_CHECK_URL(url);
m_context->historyClient().didUpdateHistoryTitle(&m_context.get(), page, title, url, frame);
}
void WebProcessProxy::pageSuppressibilityChanged(WebKit::WebPageProxy *page)
{
#if PLATFORM(COCOA)
if (page->isProcessSuppressible())
m_processSuppressiblePages.add(page->pageID());
else
m_processSuppressiblePages.remove(page->pageID());
updateProcessSuppressionState();
#else
UNUSED_PARAM(page);
#endif
}
void WebProcessProxy::pagePreferencesChanged(WebKit::WebPageProxy *page)
{
#if PLATFORM(COCOA)
if (page->isProcessSuppressible())
m_processSuppressiblePages.add(page->pageID());
else
m_processSuppressiblePages.remove(page->pageID());
updateProcessSuppressionState();
#else
UNUSED_PARAM(page);
#endif
}
void WebProcessProxy::didSaveToPageCache()
{
m_context->processDidCachePage(this);
}
void WebProcessProxy::releasePageCache()
{
if (canSendMessage())
send(Messages::WebProcess::ReleasePageCache(), 0);
}
void WebProcessProxy::windowServerConnectionStateChanged()
{
for (const auto& page : m_pageMap.values())
page->viewStateDidChange(ViewState::IsVisuallyIdle);
}
void WebProcessProxy::requestTermination()
{
if (state() != State::Running)
return;
ChildProcessProxy::terminate();
if (webConnection())
webConnection()->didClose();
disconnect();
}
void WebProcessProxy::enableSuddenTermination()
{
if (state() != State::Running)
return;
ASSERT(m_numberOfTimesSuddenTerminationWasDisabled);
WebCore::enableSuddenTermination();
--m_numberOfTimesSuddenTerminationWasDisabled;
}
void WebProcessProxy::disableSuddenTermination()
{
if (state() != State::Running)
return;
WebCore::disableSuddenTermination();
++m_numberOfTimesSuddenTerminationWasDisabled;
}
RefPtr<API::Object> WebProcessProxy::apiObjectByConvertingToHandles(API::Object* object)
{
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());
}
default:
return nullptr;
}
});
}
void WebProcessProxy::sendProcessWillSuspend()
{
if (canSendMessage())
send(Messages::WebProcess::ProcessWillSuspend(), 0);
}
void WebProcessProxy::sendCancelProcessWillSuspend()
{
if (canSendMessage())
send(Messages::WebProcess::CancelProcessWillSuspend(), 0);
}
void WebProcessProxy::processReadyToSuspend()
{
m_throttler->processReadyToSuspend();
}
void WebProcessProxy::didCancelProcessSuspension()
{
m_throttler->didCancelProcessSuspension();
}
}