1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "PluginProcess.h" 28 29#if ENABLE(PLUGIN_PROCESS) 30 31#include "ArgumentCoders.h" 32#include "Attachment.h" 33#include "NetscapePlugin.h" 34#include "NetscapePluginModule.h" 35#include "PluginProcessConnectionMessages.h" 36#include "PluginProcessCreationParameters.h" 37#include "PluginProcessProxyMessages.h" 38#include "WebProcessConnection.h" 39#include <WebCore/MemoryPressureHandler.h> 40#include <WebCore/NotImplemented.h> 41#include <WebCore/RunLoop.h> 42 43#if PLATFORM(MAC) 44#include <crt_externs.h> 45#endif 46 47#if USE(UNIX_DOMAIN_SOCKETS) 48#include <errno.h> 49#include <fcntl.h> 50#include <sys/resource.h> 51#include <sys/socket.h> 52#include <unistd.h> 53 54#ifdef SOCK_SEQPACKET 55#define SOCKET_TYPE SOCK_SEQPACKET 56#else 57#if PLATFORM(GTK) 58#define SOCKET_TYPE SOCK_STREAM 59#else 60#define SOCKET_TYPE SOCK_DGRAM 61#endif 62#endif // SOCK_SEQPACKET 63#endif // USE(UNIX_DOMAIN_SOCKETS) 64 65using namespace WebCore; 66 67namespace WebKit { 68 69PluginProcess& PluginProcess::shared() 70{ 71 DEFINE_STATIC_LOCAL(PluginProcess, pluginProcess, ()); 72 return pluginProcess; 73} 74 75PluginProcess::PluginProcess() 76 : m_supportsAsynchronousPluginInitialization(false) 77 , m_minimumLifetimeTimer(RunLoop::main(), this, &PluginProcess::minimumLifetimeTimerFired) 78#if PLATFORM(MAC) 79 , m_compositingRenderServerPort(MACH_PORT_NULL) 80#endif 81{ 82 NetscapePlugin::setSetExceptionFunction(WebProcessConnection::setGlobalException); 83} 84 85PluginProcess::~PluginProcess() 86{ 87} 88 89void PluginProcess::lowMemoryHandler(bool critical) 90{ 91 UNUSED_PARAM(critical); 92 if (shared().shouldTerminate()) 93 shared().terminate(); 94} 95 96void PluginProcess::initializeProcess(const ChildProcessInitializationParameters& parameters) 97{ 98 m_pluginPath = parameters.extraInitializationData.get("plugin-path"); 99 platformInitializeProcess(parameters); 100 101 memoryPressureHandler().setLowMemoryHandler(lowMemoryHandler); 102 memoryPressureHandler().install(); 103} 104 105void PluginProcess::removeWebProcessConnection(WebProcessConnection* webProcessConnection) 106{ 107 size_t vectorIndex = m_webProcessConnections.find(webProcessConnection); 108 ASSERT(vectorIndex != notFound); 109 110 m_webProcessConnections.remove(vectorIndex); 111 112 if (m_webProcessConnections.isEmpty() && m_pluginModule) { 113 // Decrement the load count. This is balanced by a call to incrementLoadCount in createWebProcessConnection. 114 m_pluginModule->decrementLoadCount(); 115 } 116 117 enableTermination(); 118} 119 120NetscapePluginModule* PluginProcess::netscapePluginModule() 121{ 122 if (!m_pluginModule) { 123 ASSERT(!m_pluginPath.isNull()); 124 m_pluginModule = NetscapePluginModule::getOrCreate(m_pluginPath); 125 126#if PLATFORM(MAC) 127 if (m_pluginModule) { 128 if (m_pluginModule->pluginQuirks().contains(PluginQuirks::PrognameShouldBeWebKitPluginHost)) 129 *const_cast<const char**>(_NSGetProgname()) = "WebKitPluginHost"; 130 } 131#endif 132 } 133 134 return m_pluginModule.get(); 135} 136 137bool PluginProcess::shouldTerminate() 138{ 139 return m_webProcessConnections.isEmpty(); 140} 141 142void PluginProcess::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder) 143{ 144 didReceivePluginProcessMessage(connection, decoder); 145} 146 147void PluginProcess::didClose(CoreIPC::Connection*) 148{ 149 // The UI process has crashed, just go ahead and quit. 150 // FIXME: If the plug-in is spinning in the main loop, we'll never get this message. 151 stopRunLoop(); 152} 153 154void PluginProcess::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::StringReference, CoreIPC::StringReference) 155{ 156} 157 158void PluginProcess::initializePluginProcess(const PluginProcessCreationParameters& parameters) 159{ 160 ASSERT(!m_pluginModule); 161 162 m_supportsAsynchronousPluginInitialization = parameters.supportsAsynchronousPluginInitialization; 163 setMinimumLifetime(parameters.minimumLifetime); 164 setTerminationTimeout(parameters.terminationTimeout); 165 166 platformInitializePluginProcess(parameters); 167} 168 169void PluginProcess::createWebProcessConnection() 170{ 171 bool didHaveAnyWebProcessConnections = !m_webProcessConnections.isEmpty(); 172 173#if PLATFORM(MAC) 174 // Create the listening port. 175 mach_port_t listeningPort; 176 mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort); 177 178 // Create a listening connection. 179 RefPtr<WebProcessConnection> connection = WebProcessConnection::create(CoreIPC::Connection::Identifier(listeningPort)); 180 m_webProcessConnections.append(connection.release()); 181 182 CoreIPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND); 183 parentProcessConnection()->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientPort, m_supportsAsynchronousPluginInitialization), 0); 184#elif USE(UNIX_DOMAIN_SOCKETS) 185 int sockets[2]; 186 if (socketpair(AF_UNIX, SOCKET_TYPE, 0, sockets) == -1) { 187 ASSERT_NOT_REACHED(); 188 return; 189 } 190 191 // Don't expose the plugin process socket to the web process. 192 while (fcntl(sockets[1], F_SETFD, FD_CLOEXEC) == -1) { 193 if (errno != EINTR) { 194 ASSERT_NOT_REACHED(); 195 while (close(sockets[0]) == -1 && errno == EINTR) { } 196 while (close(sockets[1]) == -1 && errno == EINTR) { } 197 return; 198 } 199 } 200 201 // Don't expose the web process socket to possible future web processes. 202 while (fcntl(sockets[0], F_SETFD, FD_CLOEXEC) == -1) { 203 if (errno != EINTR) { 204 ASSERT_NOT_REACHED(); 205 while (close(sockets[0]) == -1 && errno == EINTR) { } 206 while (close(sockets[1]) == -1 && errno == EINTR) { } 207 return; 208 } 209 } 210 211 RefPtr<WebProcessConnection> connection = WebProcessConnection::create(sockets[1]); 212 m_webProcessConnections.append(connection.release()); 213 214 CoreIPC::Attachment clientSocket(sockets[0]); 215 parentProcessConnection()->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientSocket, m_supportsAsynchronousPluginInitialization), 0); 216#else 217 notImplemented(); 218#endif 219 220 if (NetscapePluginModule* module = netscapePluginModule()) { 221 if (!didHaveAnyWebProcessConnections) { 222 // Increment the load count. This is matched by a call to decrementLoadCount in removeWebProcessConnection. 223 // We do this so that the plug-in module's NP_Shutdown won't be called until right before exiting. 224 module->incrementLoadCount(); 225 } 226 } 227 228 disableTermination(); 229} 230 231void PluginProcess::getSitesWithData(uint64_t callbackID) 232{ 233 Vector<String> sites; 234 if (NetscapePluginModule* module = netscapePluginModule()) 235 sites = module->sitesWithData(); 236 237 parentProcessConnection()->send(Messages::PluginProcessProxy::DidGetSitesWithData(sites, callbackID), 0); 238} 239 240void PluginProcess::clearSiteData(const Vector<String>& sites, uint64_t flags, uint64_t maxAgeInSeconds, uint64_t callbackID) 241{ 242 if (NetscapePluginModule* module = netscapePluginModule()) { 243 if (sites.isEmpty()) { 244 // Clear everything. 245 module->clearSiteData(String(), flags, maxAgeInSeconds); 246 } else { 247 for (size_t i = 0; i < sites.size(); ++i) 248 module->clearSiteData(sites[i], flags, maxAgeInSeconds); 249 } 250 } 251 252 parentProcessConnection()->send(Messages::PluginProcessProxy::DidClearSiteData(callbackID), 0); 253} 254 255void PluginProcess::setMinimumLifetime(double lifetime) 256{ 257 if (lifetime <= 0.0) 258 return; 259 260 disableTermination(); 261 262 m_minimumLifetimeTimer.startOneShot(lifetime); 263} 264 265void PluginProcess::minimumLifetimeTimerFired() 266{ 267 enableTermination(); 268} 269 270#if !PLATFORM(MAC) 271void PluginProcess::initializeProcessName(const ChildProcessInitializationParameters&) 272{ 273} 274 275void PluginProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&) 276{ 277} 278#endif 279 280} // namespace WebKit 281 282#endif // ENABLE(PLUGIN_PROCESS) 283 284