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