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 "PluginProcessProxy.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "AuxiliaryProcessMessages.h"
32#include "PluginProcessConnectionManagerMessages.h"
33#include "PluginProcessCreationParameters.h"
34#include "PluginProcessManager.h"
35#include "PluginProcessMessages.h"
36#include "WebCoreArgumentCoders.h"
37#include "WebProcessPool.h"
38#include "WebProcessProxy.h"
39#include <WebCore/NotImplemented.h>
40#include <wtf/RunLoop.h>
41
42namespace WebKit {
43using namespace WebCore;
44
45static const Seconds minimumLifetime { 2_min };
46static const Seconds snapshottingMinimumLifetime { 30_s };
47
48static const Seconds shutdownTimeout { 1_min };
49static const Seconds snapshottingShutdownTimeout { 15_s };
50
51static uint64_t generatePluginProcessCallbackID()
52{
53 static uint64_t callbackID;
54
55 return ++callbackID;
56}
57
58Ref<PluginProcessProxy> PluginProcessProxy::create(PluginProcessManager* PluginProcessManager, const PluginProcessAttributes& pluginProcessAttributes, uint64_t pluginProcessToken)
59{
60 return adoptRef(*new PluginProcessProxy(PluginProcessManager, pluginProcessAttributes, pluginProcessToken));
61}
62
63PluginProcessProxy::PluginProcessProxy(PluginProcessManager* PluginProcessManager, const PluginProcessAttributes& pluginProcessAttributes, uint64_t pluginProcessToken)
64 : m_pluginProcessManager(PluginProcessManager)
65 , m_pluginProcessAttributes(pluginProcessAttributes)
66 , m_pluginProcessToken(pluginProcessToken)
67 , m_numPendingConnectionRequests(0)
68#if PLATFORM(COCOA)
69 , m_modalWindowIsShowing(false)
70 , m_fullscreenWindowIsShowing(false)
71 , m_preFullscreenAppPresentationOptions(0)
72#endif
73{
74 connect();
75}
76
77PluginProcessProxy::~PluginProcessProxy()
78{
79 if (m_connection)
80 m_connection->invalidate();
81
82 ASSERT(m_pendingFetchWebsiteDataRequests.isEmpty());
83 ASSERT(m_pendingFetchWebsiteDataCallbacks.isEmpty());
84 ASSERT(m_pendingDeleteWebsiteDataRequests.isEmpty());
85 ASSERT(m_pendingDeleteWebsiteDataCallbacks.isEmpty());
86}
87
88void PluginProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
89{
90 platformGetLaunchOptionsWithAttributes(launchOptions, m_pluginProcessAttributes);
91 AuxiliaryProcessProxy::getLaunchOptions(launchOptions);
92}
93
94void PluginProcessProxy::processWillShutDown(IPC::Connection& connection)
95{
96 ASSERT_UNUSED(connection, this->connection() == &connection);
97}
98
99// Asks the plug-in process to create a new connection to a web process. The connection identifier will be
100// encoded in the given argument encoder and sent back to the connection of the given web process.
101void PluginProcessProxy::getPluginProcessConnection(Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply&& reply)
102{
103 m_pendingConnectionReplies.append(WTFMove(reply));
104
105 if (state() == State::Launching) {
106 m_numPendingConnectionRequests++;
107 return;
108 }
109
110 // Ask the plug-in process to create a connection. Since the plug-in can be waiting for a synchronous reply
111 // we need to make sure that this message is always processed, even when the plug-in is waiting for a synchronus reply.
112 m_connection->send(Messages::PluginProcess::CreateWebProcessConnection(), 0, IPC::SendOption::DispatchMessageEvenWhenWaitingForSyncReply);
113}
114
115void PluginProcessProxy::fetchWebsiteData(CompletionHandler<void (Vector<String>)>&& completionHandler)
116{
117 uint64_t callbackID = generatePluginProcessCallbackID();
118 m_pendingFetchWebsiteDataCallbacks.set(callbackID, WTFMove(completionHandler));
119
120 if (state() == State::Launching) {
121 m_pendingFetchWebsiteDataRequests.append(callbackID);
122 return;
123 }
124
125 m_connection->send(Messages::PluginProcess::GetSitesWithData(callbackID), 0);
126}
127
128void PluginProcessProxy::deleteWebsiteData(WallTime modifiedSince, CompletionHandler<void ()>&& completionHandler)
129{
130 uint64_t callbackID = generatePluginProcessCallbackID();
131 m_pendingDeleteWebsiteDataCallbacks.set(callbackID, WTFMove(completionHandler));
132
133 if (state() == State::Launching) {
134 m_pendingDeleteWebsiteDataRequests.append({ modifiedSince, callbackID });
135 return;
136 }
137
138 m_connection->send(Messages::PluginProcess::DeleteWebsiteData(modifiedSince, callbackID), 0);
139}
140
141void PluginProcessProxy::deleteWebsiteDataForHostNames(const Vector<String>& hostNames, CompletionHandler<void ()>&& completionHandler)
142{
143 uint64_t callbackID = generatePluginProcessCallbackID();
144 m_pendingDeleteWebsiteDataForHostNamesCallbacks.set(callbackID, WTFMove(completionHandler));
145
146 if (state() == State::Launching) {
147 m_pendingDeleteWebsiteDataForHostNamesRequests.append({ hostNames, callbackID });
148 return;
149 }
150
151 m_connection->send(Messages::PluginProcess::DeleteWebsiteDataForHostNames(hostNames, callbackID), 0);
152}
153
154#if OS(LINUX)
155void PluginProcessProxy::sendMemoryPressureEvent(bool isCritical)
156{
157 if (state() == State::Launching)
158 return;
159
160 m_connection->send(Messages::AuxiliaryProcess::DidReceiveMemoryPressureEvent(isCritical), 0);
161}
162#endif
163
164void PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch()
165{
166 // The plug-in process must have crashed or exited, send any pending sync replies we might have.
167 while (!m_pendingConnectionReplies.isEmpty()) {
168 auto reply = m_pendingConnectionReplies.takeFirst();
169
170#if USE(UNIX_DOMAIN_SOCKETS)
171 reply(IPC::Attachment(), false);
172#elif OS(DARWIN)
173 reply(IPC::Attachment(0, MACH_MSG_TYPE_MOVE_SEND), false);
174#else
175 notImplemented();
176#endif
177 }
178
179 m_pendingFetchWebsiteDataRequests.clear();
180 for (auto&& callback : m_pendingFetchWebsiteDataCallbacks.values())
181 callback({ });
182 m_pendingFetchWebsiteDataCallbacks.clear();
183
184 m_pendingDeleteWebsiteDataRequests.clear();
185 for (auto&& callback : m_pendingDeleteWebsiteDataCallbacks.values())
186 callback();
187 m_pendingDeleteWebsiteDataRequests.clear();
188
189 m_pendingDeleteWebsiteDataForHostNamesRequests.clear();
190 for (auto&& callback : m_pendingDeleteWebsiteDataForHostNamesCallbacks.values())
191 callback();
192 m_pendingDeleteWebsiteDataForHostNamesCallbacks.clear();
193
194 // Tell the plug-in process manager to forget about this plug-in process proxy. This may cause us to be deleted.
195 m_pluginProcessManager->removePluginProcessProxy(this);
196}
197
198void PluginProcessProxy::didClose(IPC::Connection&)
199{
200#if PLATFORM(COCOA)
201 if (m_modalWindowIsShowing)
202 endModal();
203
204 if (m_fullscreenWindowIsShowing)
205 exitFullscreen();
206#endif
207
208 const Vector<WebProcessPool*>& processPools = WebProcessPool::allProcessPools();
209 for (size_t i = 0; i < processPools.size(); ++i)
210 processPools[i]->sendToAllProcesses(Messages::PluginProcessConnectionManager::PluginProcessCrashed(m_pluginProcessToken));
211
212 // This will cause us to be deleted.
213 pluginProcessCrashedOrFailedToLaunch();
214}
215
216void PluginProcessProxy::didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference, IPC::StringReference)
217{
218}
219
220void PluginProcessProxy::didFinishLaunching(ProcessLauncher*, IPC::Connection::Identifier connectionIdentifier)
221{
222 ASSERT(!m_connection);
223
224 if (!IPC::Connection::identifierIsValid(connectionIdentifier)) {
225 pluginProcessCrashedOrFailedToLaunch();
226 return;
227 }
228
229 m_connection = IPC::Connection::createServerConnection(connectionIdentifier, *this);
230
231 m_connection->open();
232
233 PluginProcessCreationParameters parameters;
234 parameters.processType = m_pluginProcessAttributes.processType;
235 if (parameters.processType == PluginProcessTypeSnapshot) {
236 parameters.minimumLifetime = snapshottingMinimumLifetime;
237 parameters.terminationTimeout = snapshottingShutdownTimeout;
238 } else {
239 parameters.minimumLifetime = minimumLifetime;
240 parameters.terminationTimeout = shutdownTimeout;
241 }
242
243 platformInitializePluginProcess(parameters);
244
245 // Initialize the plug-in host process.
246 m_connection->send(Messages::PluginProcess::InitializePluginProcess(parameters), 0);
247
248#if PLATFORM(COCOA)
249 m_connection->send(Messages::PluginProcess::SetQOS(pluginProcessLatencyQOS(), pluginProcessThroughputQOS()), 0);
250#endif
251
252 for (auto callbackID : m_pendingFetchWebsiteDataRequests)
253 m_connection->send(Messages::PluginProcess::GetSitesWithData(callbackID), 0);
254 m_pendingFetchWebsiteDataRequests.clear();
255
256 for (auto& request : m_pendingDeleteWebsiteDataRequests)
257 m_connection->send(Messages::PluginProcess::DeleteWebsiteData(request.modifiedSince, request.callbackID), 0);
258 m_pendingDeleteWebsiteDataRequests.clear();
259
260 for (auto& request : m_pendingDeleteWebsiteDataForHostNamesRequests)
261 m_connection->send(Messages::PluginProcess::DeleteWebsiteDataForHostNames(request.hostNames, request.callbackID), 0);
262 m_pendingDeleteWebsiteDataForHostNamesRequests.clear();
263
264 for (unsigned i = 0; i < m_numPendingConnectionRequests; ++i)
265 m_connection->send(Messages::PluginProcess::CreateWebProcessConnection(), 0);
266
267 m_numPendingConnectionRequests = 0;
268
269#if PLATFORM(COCOA)
270 if (!PluginProcessManager::singleton().processSuppressionDisabled())
271 setProcessSuppressionEnabled(true);
272#endif
273}
274
275void PluginProcessProxy::didCreateWebProcessConnection(const IPC::Attachment& connectionIdentifier, bool supportsAsynchronousPluginInitialization)
276{
277 ASSERT(!m_pendingConnectionReplies.isEmpty());
278
279 // Grab the first pending connection reply.
280 auto reply = m_pendingConnectionReplies.takeFirst();
281
282#if USE(UNIX_DOMAIN_SOCKETS)
283 reply(connectionIdentifier, supportsAsynchronousPluginInitialization);
284#elif OS(DARWIN)
285 reply(IPC::Attachment(connectionIdentifier.port(), MACH_MSG_TYPE_MOVE_SEND), supportsAsynchronousPluginInitialization);
286#else
287 notImplemented();
288#endif
289}
290
291void PluginProcessProxy::didGetSitesWithData(const Vector<String>& sites, uint64_t callbackID)
292{
293 auto callback = m_pendingFetchWebsiteDataCallbacks.take(callbackID);
294 callback(sites);
295}
296
297void PluginProcessProxy::didDeleteWebsiteData(uint64_t callbackID)
298{
299 auto callback = m_pendingDeleteWebsiteDataCallbacks.take(callbackID);
300 callback();
301}
302
303void PluginProcessProxy::didDeleteWebsiteDataForHostNames(uint64_t callbackID)
304{
305 auto callback = m_pendingDeleteWebsiteDataForHostNamesCallbacks.take(callbackID);
306 callback();
307}
308
309} // namespace WebKit
310
311#endif // ENABLE(NETSCAPE_PLUGIN_API)
312