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(NETSCAPE_PLUGIN_API)
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 <wtf/RunLoop.h>
42
43#if PLATFORM(MAC)
44#include <crt_externs.h>
45#endif
46
47using namespace WebCore;
48
49namespace WebKit {
50
51PluginProcess& PluginProcess::shared()
52{
53    static NeverDestroyed<PluginProcess> pluginProcess;
54    return pluginProcess;
55}
56
57PluginProcess::PluginProcess()
58    : m_supportsAsynchronousPluginInitialization(false)
59    , m_minimumLifetimeTimer(RunLoop::main(), this, &PluginProcess::minimumLifetimeTimerFired)
60#if PLATFORM(COCOA)
61    , m_compositingRenderServerPort(MACH_PORT_NULL)
62#endif
63    , m_connectionActivity("PluginProcess connection activity.")
64    , m_visiblePluginsActivity("Visible plugins from PluginProcess activity.")
65{
66    NetscapePlugin::setSetExceptionFunction(WebProcessConnection::setGlobalException);
67    m_audioHardwareListener = AudioHardwareListener::create(*this);
68}
69
70PluginProcess::~PluginProcess()
71{
72}
73
74void PluginProcess::lowMemoryHandler(bool critical)
75{
76    UNUSED_PARAM(critical);
77    if (shared().shouldTerminate())
78        shared().terminate();
79}
80
81void PluginProcess::initializeProcess(const ChildProcessInitializationParameters& parameters)
82{
83    m_pluginPath = parameters.extraInitializationData.get("plugin-path");
84    platformInitializeProcess(parameters);
85
86    memoryPressureHandler().setLowMemoryHandler(lowMemoryHandler);
87    memoryPressureHandler().install();
88}
89
90void PluginProcess::removeWebProcessConnection(WebProcessConnection* webProcessConnection)
91{
92    size_t vectorIndex = m_webProcessConnections.find(webProcessConnection);
93    ASSERT(vectorIndex != notFound);
94
95    m_webProcessConnections.remove(vectorIndex);
96
97    if (m_webProcessConnections.isEmpty() && m_pluginModule) {
98        // Decrement the load count. This is balanced by a call to incrementLoadCount in createWebProcessConnection.
99        m_pluginModule->decrementLoadCount();
100    }
101
102    enableTermination();
103}
104
105NetscapePluginModule* PluginProcess::netscapePluginModule()
106{
107    if (!m_pluginModule) {
108        ASSERT(!m_pluginPath.isNull());
109        m_pluginModule = NetscapePluginModule::getOrCreate(m_pluginPath);
110
111#if PLATFORM(MAC)
112        if (m_pluginModule) {
113            if (m_pluginModule->pluginQuirks().contains(PluginQuirks::PrognameShouldBeWebKitPluginHost))
114                *const_cast<const char**>(_NSGetProgname()) = "WebKitPluginHost";
115        }
116#endif
117    }
118
119    return m_pluginModule.get();
120}
121
122bool PluginProcess::shouldTerminate()
123{
124    return m_webProcessConnections.isEmpty();
125}
126
127void PluginProcess::didReceiveMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder)
128{
129    didReceivePluginProcessMessage(connection, decoder);
130}
131
132void PluginProcess::didClose(IPC::Connection*)
133{
134    // The UI process has crashed, just go ahead and quit.
135    // FIXME: If the plug-in is spinning in the main loop, we'll never get this message.
136    stopRunLoop();
137}
138
139void PluginProcess::didReceiveInvalidMessage(IPC::Connection*, IPC::StringReference, IPC::StringReference)
140{
141}
142
143void PluginProcess::initializePluginProcess(const PluginProcessCreationParameters& parameters)
144{
145    ASSERT(!m_pluginModule);
146
147    m_supportsAsynchronousPluginInitialization = parameters.supportsAsynchronousPluginInitialization;
148    setMinimumLifetime(parameters.minimumLifetime);
149    setTerminationTimeout(parameters.terminationTimeout);
150
151    platformInitializePluginProcess(parameters);
152}
153
154void PluginProcess::createWebProcessConnection()
155{
156    bool didHaveAnyWebProcessConnections = !m_webProcessConnections.isEmpty();
157
158#if OS(DARWIN)
159    // Create the listening port.
160    mach_port_t listeningPort;
161    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
162
163    // Create a listening connection.
164    RefPtr<WebProcessConnection> connection = WebProcessConnection::create(IPC::Connection::Identifier(listeningPort));
165
166    if (m_audioHardwareListener) {
167        if (m_audioHardwareListener->hardwareActivity() == WebCore::AudioHardwareActivityType::IsActive)
168            connection->audioHardwareDidBecomeActive();
169        else if (m_audioHardwareListener->hardwareActivity() == WebCore::AudioHardwareActivityType::IsInactive)
170            connection->audioHardwareDidBecomeInactive();
171    }
172
173    m_webProcessConnections.append(connection.release());
174
175    IPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND);
176    parentProcessConnection()->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientPort, m_supportsAsynchronousPluginInitialization), 0);
177#elif USE(UNIX_DOMAIN_SOCKETS)
178    IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection();
179
180    RefPtr<WebProcessConnection> connection = WebProcessConnection::create(socketPair.server);
181    m_webProcessConnections.append(connection.release());
182
183    IPC::Attachment clientSocket(socketPair.client);
184    parentProcessConnection()->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientSocket, m_supportsAsynchronousPluginInitialization), 0);
185#else
186    notImplemented();
187#endif
188
189    if (NetscapePluginModule* module = netscapePluginModule()) {
190        if (!didHaveAnyWebProcessConnections) {
191            // Increment the load count. This is matched by a call to decrementLoadCount in removeWebProcessConnection.
192            // We do this so that the plug-in module's NP_Shutdown won't be called until right before exiting.
193            module->incrementLoadCount();
194        }
195    }
196
197    disableTermination();
198}
199
200void PluginProcess::getSitesWithData(uint64_t callbackID)
201{
202    Vector<String> sites;
203    if (NetscapePluginModule* module = netscapePluginModule())
204        sites = module->sitesWithData();
205
206    parentProcessConnection()->send(Messages::PluginProcessProxy::DidGetSitesWithData(sites, callbackID), 0);
207}
208
209void PluginProcess::clearSiteData(const Vector<String>& sites, uint64_t flags, uint64_t maxAgeInSeconds, uint64_t callbackID)
210{
211    if (NetscapePluginModule* module = netscapePluginModule()) {
212        if (sites.isEmpty()) {
213            // Clear everything.
214            module->clearSiteData(String(), flags, maxAgeInSeconds);
215        } else {
216            for (size_t i = 0; i < sites.size(); ++i)
217                module->clearSiteData(sites[i], flags, maxAgeInSeconds);
218        }
219    }
220
221    parentProcessConnection()->send(Messages::PluginProcessProxy::DidClearSiteData(callbackID), 0);
222}
223
224void PluginProcess::setMinimumLifetime(double lifetime)
225{
226    if (lifetime <= 0.0)
227        return;
228
229    disableTermination();
230
231    m_minimumLifetimeTimer.startOneShot(lifetime);
232}
233
234void PluginProcess::minimumLifetimeTimerFired()
235{
236    enableTermination();
237}
238
239#if !PLATFORM(COCOA)
240void PluginProcess::initializeProcessName(const ChildProcessInitializationParameters&)
241{
242}
243
244void PluginProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&)
245{
246}
247#endif
248
249void PluginProcess::pluginsForWebProcessDidBecomeVisible()
250{
251    m_visiblePluginsActivity.increment();
252}
253
254void PluginProcess::pluginsForWebProcessDidBecomeHidden()
255{
256    m_visiblePluginsActivity.decrement();
257}
258
259void PluginProcess::audioHardwareDidBecomeActive()
260{
261    for (auto& connection : m_webProcessConnections)
262        connection->audioHardwareDidBecomeActive();
263}
264
265void PluginProcess::audioHardwareDidBecomeInactive()
266{
267    for (auto& connection : m_webProcessConnections)
268        connection->audioHardwareDidBecomeInactive();
269}
270
271} // namespace WebKit
272
273#endif // ENABLE(NETSCAPE_PLUGIN_API)
274
275