1/*
2 * Copyright (C) 2010-2018 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 "AuxiliaryProcessMessages.h"
34#include "NetscapePlugin.h"
35#include "NetscapePluginModule.h"
36#include "PluginProcessConnectionMessages.h"
37#include "PluginProcessCreationParameters.h"
38#include "PluginProcessProxyMessages.h"
39#include "WebProcessConnection.h"
40#include <WebCore/NetworkStorageSession.h>
41#include <WebCore/NotImplemented.h>
42#include <unistd.h>
43#include <wtf/MemoryPressureHandler.h>
44#include <wtf/NeverDestroyed.h>
45#include <wtf/ProcessPrivilege.h>
46#include <wtf/RunLoop.h>
47
48#if PLATFORM(MAC)
49#include <crt_externs.h>
50#endif
51
52namespace WebKit {
53
54using namespace WebCore;
55
56NO_RETURN static void callExit(IPC::Connection*)
57{
58 _exit(EXIT_SUCCESS);
59}
60
61PluginProcess& PluginProcess::singleton()
62{
63 static NeverDestroyed<PluginProcess> pluginProcess;
64 return pluginProcess;
65}
66
67PluginProcess::PluginProcess()
68 : m_supportsAsynchronousPluginInitialization(false)
69 , m_minimumLifetimeTimer(RunLoop::main(), this, &PluginProcess::minimumLifetimeTimerFired)
70 , m_connectionActivity("PluginProcess connection activity.")
71{
72 NetscapePlugin::setSetExceptionFunction(WebProcessConnection::setGlobalException);
73}
74
75PluginProcess::~PluginProcess()
76{
77}
78
79void PluginProcess::initializeProcess(const AuxiliaryProcessInitializationParameters& parameters)
80{
81 WTF::setProcessPrivileges(allPrivileges());
82 WebCore::NetworkStorageSession::permitProcessToUseCookieAPI(true);
83 m_pluginPath = parameters.extraInitializationData.get("plugin-path");
84 platformInitializeProcess(parameters);
85}
86
87void PluginProcess::initializeConnection(IPC::Connection* connection)
88{
89 AuxiliaryProcess::initializeConnection(connection);
90
91 // We call _exit() directly from the background queue in case the main thread is unresponsive
92 // and AuxiliaryProcess::didClose() does not get called.
93 connection->setDidCloseOnConnectionWorkQueueCallback(callExit);
94}
95
96void PluginProcess::removeWebProcessConnection(WebProcessConnection* webProcessConnection)
97{
98 size_t vectorIndex = m_webProcessConnections.find(webProcessConnection);
99 ASSERT(vectorIndex != notFound);
100
101 m_webProcessConnections.remove(vectorIndex);
102
103 if (m_webProcessConnections.isEmpty() && m_pluginModule) {
104 // Decrement the load count. This is balanced by a call to incrementLoadCount in createWebProcessConnection.
105 m_pluginModule->decrementLoadCount();
106 }
107
108 enableTermination();
109}
110
111NetscapePluginModule* PluginProcess::netscapePluginModule()
112{
113 if (!m_pluginModule) {
114 ASSERT(!m_pluginPath.isNull());
115 m_pluginModule = NetscapePluginModule::getOrCreate(m_pluginPath);
116
117#if PLATFORM(MAC)
118 if (m_pluginModule) {
119 if (m_pluginModule->pluginQuirks().contains(PluginQuirks::PrognameShouldBeWebKitPluginHost))
120 *const_cast<const char**>(_NSGetProgname()) = "WebKitPluginHost";
121 }
122#endif
123 }
124
125 return m_pluginModule.get();
126}
127
128bool PluginProcess::shouldTerminate()
129{
130 return m_webProcessConnections.isEmpty();
131}
132
133void PluginProcess::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
134{
135#if OS(LINUX)
136 if (decoder.messageReceiverName() == Messages::AuxiliaryProcess::messageReceiverName()) {
137 AuxiliaryProcess::didReceiveMessage(connection, decoder);
138 return;
139 }
140#endif
141
142 didReceivePluginProcessMessage(connection, decoder);
143}
144
145void PluginProcess::initializePluginProcess(PluginProcessCreationParameters&& parameters)
146{
147 ASSERT(!m_pluginModule);
148
149 auto& memoryPressureHandler = MemoryPressureHandler::singleton();
150 memoryPressureHandler.setLowMemoryHandler([this] (Critical, Synchronous) {
151 if (shouldTerminate())
152 terminate();
153 });
154 memoryPressureHandler.install();
155
156 m_supportsAsynchronousPluginInitialization = parameters.supportsAsynchronousPluginInitialization;
157 setMinimumLifetime(parameters.minimumLifetime);
158 setTerminationTimeout(parameters.terminationTimeout);
159
160 platformInitializePluginProcess(WTFMove(parameters));
161}
162
163void PluginProcess::createWebProcessConnection()
164{
165 bool didHaveAnyWebProcessConnections = !m_webProcessConnections.isEmpty();
166
167#if USE(UNIX_DOMAIN_SOCKETS)
168 IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection();
169
170 auto connection = WebProcessConnection::create(socketPair.server);
171 m_webProcessConnections.append(WTFMove(connection));
172
173 IPC::Attachment clientSocket(socketPair.client);
174 parentProcessConnection()->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientSocket, m_supportsAsynchronousPluginInitialization), 0);
175#elif OS(DARWIN)
176 // Create the listening port.
177 mach_port_t listeningPort = MACH_PORT_NULL;
178 auto kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
179 if (kr != KERN_SUCCESS) {
180 LOG_ERROR("Could not allocate mach port, error %x", kr);
181 CRASH();
182 }
183
184 // Create a listening connection.
185 auto connection = WebProcessConnection::create(IPC::Connection::Identifier(listeningPort));
186
187 m_webProcessConnections.append(WTFMove(connection));
188
189 IPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND);
190 parentProcessConnection()->send(Messages::PluginProcessProxy::DidCreateWebProcessConnection(clientPort, m_supportsAsynchronousPluginInitialization), 0);
191#else
192 notImplemented();
193#endif
194
195 if (NetscapePluginModule* module = netscapePluginModule()) {
196 if (!didHaveAnyWebProcessConnections) {
197 // Increment the load count. This is matched by a call to decrementLoadCount in removeWebProcessConnection.
198 // We do this so that the plug-in module's NP_Shutdown won't be called until right before exiting.
199 module->incrementLoadCount();
200 }
201 }
202
203 disableTermination();
204}
205
206void PluginProcess::getSitesWithData(uint64_t callbackID)
207{
208 Vector<String> sites;
209 if (NetscapePluginModule* module = netscapePluginModule())
210 sites = module->sitesWithData();
211
212 parentProcessConnection()->send(Messages::PluginProcessProxy::DidGetSitesWithData(sites, callbackID), 0);
213}
214
215void PluginProcess::deleteWebsiteData(WallTime modifiedSince, uint64_t callbackID)
216{
217 if (auto* module = netscapePluginModule()) {
218 auto currentTime = WallTime::now();
219
220 if (currentTime > modifiedSince) {
221 uint64_t maximumAge = (currentTime - modifiedSince).secondsAs<uint64_t>();
222
223 module->clearSiteData(String(), NP_CLEAR_ALL, maximumAge);
224 }
225 }
226
227 parentProcessConnection()->send(Messages::PluginProcessProxy::DidDeleteWebsiteData(callbackID), 0);
228}
229
230void PluginProcess::deleteWebsiteDataForHostNames(const Vector<String>& hostNames, uint64_t callbackID)
231{
232 if (auto* module = netscapePluginModule()) {
233 for (auto& hostName : hostNames)
234 module->clearSiteData(hostName, NP_CLEAR_ALL, std::numeric_limits<uint64_t>::max());
235 }
236
237 parentProcessConnection()->send(Messages::PluginProcessProxy::DidDeleteWebsiteDataForHostNames(callbackID), 0);
238}
239
240void PluginProcess::setMinimumLifetime(Seconds lifetime)
241{
242 if (lifetime <= 0_s)
243 return;
244
245 disableTermination();
246
247 m_minimumLifetimeTimer.startOneShot(lifetime);
248}
249
250void PluginProcess::minimumLifetimeTimerFired()
251{
252 enableTermination();
253}
254
255#if !PLATFORM(COCOA)
256void PluginProcess::initializeProcessName(const AuxiliaryProcessInitializationParameters&)
257{
258}
259
260void PluginProcess::initializeSandbox(const AuxiliaryProcessInitializationParameters&, SandboxInitializationParameters&)
261{
262}
263#endif
264
265} // namespace WebKit
266
267#endif // ENABLE(NETSCAPE_PLUGIN_API)
268
269