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 "WebProcessConnection.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "ActivityAssertion.h"
32#include "ArgumentCoders.h"
33#include "NPObjectMessageReceiverMessages.h"
34#include "NPRemoteObjectMap.h"
35#include "PluginControllerProxy.h"
36#include "PluginCreationParameters.h"
37#include "PluginProcess.h"
38#include "PluginProcessConnectionMessages.h"
39#include "PluginProxyMessages.h"
40#include "WebProcessConnectionMessages.h"
41#include <wtf/SetForScope.h>
42
43#if !OS(WINDOWS)
44#include <unistd.h>
45#endif
46
47namespace WebKit {
48using namespace WebCore;
49
50static IPC::Connection* currentConnection;
51
52Ref<WebProcessConnection> WebProcessConnection::create(IPC::Connection::Identifier connectionIdentifier)
53{
54 return adoptRef(*new WebProcessConnection(connectionIdentifier));
55}
56
57WebProcessConnection::~WebProcessConnection()
58{
59 ASSERT(m_pluginControllers.isEmpty());
60 ASSERT(!m_npRemoteObjectMap);
61 ASSERT(!m_connection);
62}
63
64WebProcessConnection::WebProcessConnection(IPC::Connection::Identifier connectionIdentifier)
65{
66 m_connection = IPC::Connection::createServerConnection(connectionIdentifier, *this);
67 m_npRemoteObjectMap = NPRemoteObjectMap::create(m_connection.get());
68
69 // Use this flag to force synchronous messages to be treated as asynchronous messages in the WebProcess.
70 // Otherwise, the WebProcess would process incoming synchronous IPC while waiting for a synchronous IPC
71 // reply from the Plugin process, which would be unsafe.
72 m_connection->setOnlySendMessagesAsDispatchWhenWaitingForSyncReplyWhenProcessingSuchAMessage(true);
73 m_connection->open();
74}
75
76void WebProcessConnection::addPluginControllerProxy(std::unique_ptr<PluginControllerProxy> pluginController)
77{
78 uint64_t pluginInstanceID = pluginController->pluginInstanceID();
79
80 ASSERT(!m_pluginControllers.contains(pluginInstanceID));
81 m_pluginControllers.set(pluginInstanceID, WTFMove(pluginController));
82}
83
84void WebProcessConnection::destroyPluginControllerProxy(PluginControllerProxy* pluginController)
85{
86 // This may end up calling removePluginControllerProxy which ends up deleting
87 // the WebProcessConnection object if this was the last object.
88 pluginController->destroy();
89}
90
91void WebProcessConnection::removePluginControllerProxy(PluginControllerProxy* pluginController, Plugin* plugin)
92{
93 unsigned pluginInstanceID = pluginController->pluginInstanceID();
94 {
95 ASSERT(m_pluginControllers.contains(pluginInstanceID));
96
97 std::unique_ptr<PluginControllerProxy> pluginControllerUniquePtr = m_pluginControllers.take(pluginInstanceID);
98 ASSERT(pluginControllerUniquePtr.get() == pluginController);
99 }
100
101 // Invalidate all objects related to this plug-in.
102 if (plugin)
103 m_npRemoteObjectMap->pluginDestroyed(plugin);
104
105 if (!m_pluginControllers.isEmpty())
106 return;
107
108 m_npRemoteObjectMap = nullptr;
109
110 // The last plug-in went away, close this connection.
111 m_connection->invalidate();
112 m_connection = nullptr;
113
114 // This will cause us to be deleted.
115 PluginProcess::singleton().removeWebProcessConnection(this);
116}
117
118void WebProcessConnection::setGlobalException(const String& exceptionString)
119{
120 if (!currentConnection)
121 return;
122
123 currentConnection->sendSync(Messages::PluginProcessConnection::SetException(exceptionString), Messages::PluginProcessConnection::SetException::Reply(), 0);
124}
125
126void WebProcessConnection::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
127{
128 SetForScope<IPC::Connection*> currentConnectionChange(currentConnection, &connection);
129
130 if (decoder.messageReceiverName() == Messages::WebProcessConnection::messageReceiverName()) {
131 didReceiveWebProcessConnectionMessage(connection, decoder);
132 return;
133 }
134
135 if (!decoder.destinationID()) {
136 ASSERT_NOT_REACHED();
137 return;
138 }
139
140 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(decoder.destinationID());
141 if (!pluginControllerProxy)
142 return;
143
144 PluginController::PluginDestructionProtector protector(pluginControllerProxy->asPluginController());
145 pluginControllerProxy->didReceivePluginControllerProxyMessage(connection, decoder);
146}
147
148void WebProcessConnection::didReceiveSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, std::unique_ptr<IPC::Encoder>& replyEncoder)
149{
150 SetForScope<IPC::Connection*> currentConnectionChange(currentConnection, &connection);
151
152 uint64_t destinationID = decoder.destinationID();
153
154 if (!destinationID) {
155 didReceiveSyncWebProcessConnectionMessage(connection, decoder, replyEncoder);
156 return;
157 }
158
159 if (decoder.messageReceiverName() == Messages::NPObjectMessageReceiver::messageReceiverName()) {
160 m_npRemoteObjectMap->didReceiveSyncMessage(connection, decoder, replyEncoder);
161 return;
162 }
163
164 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(decoder.destinationID());
165 if (!pluginControllerProxy)
166 return;
167
168 PluginController::PluginDestructionProtector protector(pluginControllerProxy->asPluginController());
169 pluginControllerProxy->didReceiveSyncPluginControllerProxyMessage(connection, decoder, replyEncoder);
170}
171
172void WebProcessConnection::didClose(IPC::Connection&)
173{
174 // The web process crashed. Destroy all the plug-in controllers. Destroying the last plug-in controller
175 // will cause the web process connection itself to be destroyed.
176 Vector<PluginControllerProxy*> pluginControllers;
177 for (auto it = m_pluginControllers.values().begin(), end = m_pluginControllers.values().end(); it != end; ++it)
178 pluginControllers.append(it->get());
179
180 for (size_t i = 0; i < pluginControllers.size(); ++i)
181 destroyPluginControllerProxy(pluginControllers[i]);
182}
183
184void WebProcessConnection::destroyPlugin(uint64_t pluginInstanceID, bool asynchronousCreationIncomplete, Messages::WebProcessConnection::DestroyPlugin::DelayedReply&& reply)
185{
186 // We return immediately from this synchronous IPC. We want to make sure the plugin destruction is just about to start so audio playback
187 // will finish soon after returning. However we don't want to wait for destruction to complete fully as that may take a while.
188 reply();
189
190 // Ensure we don't clamp any timers during destruction
191 ActivityAssertion activityAssertion(PluginProcess::singleton().connectionActivity());
192
193 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(pluginInstanceID);
194
195 // If there is no PluginControllerProxy then this plug-in doesn't exist yet and we probably have nothing to do.
196 if (!pluginControllerProxy) {
197 // If the plugin we're supposed to destroy was requested asynchronously and doesn't exist yet,
198 // we need to flag the instance ID so it is not created later.
199 if (asynchronousCreationIncomplete)
200 m_asynchronousInstanceIDsToIgnore.add(pluginInstanceID);
201
202 return;
203 }
204
205 destroyPluginControllerProxy(pluginControllerProxy);
206}
207
208void WebProcessConnection::didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference, IPC::StringReference)
209{
210 // FIXME: Implement.
211}
212
213void WebProcessConnection::createPluginInternal(const PluginCreationParameters& creationParameters, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID)
214{
215 auto pluginControllerProxy = std::make_unique<PluginControllerProxy>(this, creationParameters);
216
217 PluginControllerProxy* pluginControllerProxyPtr = pluginControllerProxy.get();
218
219 // Make sure to add the proxy to the map before initializing it, since the plug-in might call out to the web process from
220 // its NPP_New function. This will hand over ownership of the proxy to the web process connection.
221 addPluginControllerProxy(WTFMove(pluginControllerProxy));
222
223 // Now try to initialize the plug-in.
224 result = pluginControllerProxyPtr->initialize(creationParameters);
225
226 if (!result)
227 return;
228
229 wantsWheelEvents = pluginControllerProxyPtr->wantsWheelEvents();
230#if PLATFORM(COCOA)
231 remoteLayerClientID = pluginControllerProxyPtr->remoteLayerClientID();
232#else
233 UNUSED_PARAM(remoteLayerClientID);
234#endif
235}
236
237void WebProcessConnection::createPlugin(const PluginCreationParameters& creationParameters, Messages::WebProcessConnection::CreatePlugin::DelayedReply&& reply)
238{
239 // Ensure we don't clamp any timers during initialization
240 ActivityAssertion activityAssertion(PluginProcess::singleton().connectionActivity());
241
242 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(creationParameters.pluginInstanceID);
243
244 // The controller proxy for the plug-in we're being asked to create synchronously might already exist if it was requested asynchronously before.
245 if (pluginControllerProxy) {
246 // It might still be in the middle of initialization in which case we have to let that initialization complete and respond to this message later.
247 if (pluginControllerProxy->isInitializing()) {
248 pluginControllerProxy->setInitializationReply(WTFMove(reply));
249 return;
250 }
251
252 // If its initialization is complete then we need to respond to this message with the correct information about its creation.
253#if PLATFORM(COCOA)
254 reply(true, pluginControllerProxy->wantsWheelEvents(), pluginControllerProxy->remoteLayerClientID());
255#else
256 reply(true, pluginControllerProxy->wantsWheelEvents(), 0);
257#endif
258 return;
259 }
260
261 // The plugin we're supposed to create might have been requested asynchronously before.
262 // In that case we need to create it synchronously now but flag the instance ID so we don't recreate it asynchronously later.
263 if (creationParameters.asynchronousCreationIncomplete)
264 m_asynchronousInstanceIDsToIgnore.add(creationParameters.pluginInstanceID);
265
266 bool result = false;
267 bool wantsWheelEvents = false;
268 uint32_t remoteLayerClientID = 0;
269 createPluginInternal(creationParameters, result, wantsWheelEvents, remoteLayerClientID);
270
271 reply(result, wantsWheelEvents, remoteLayerClientID);
272}
273
274void WebProcessConnection::createPluginAsynchronously(const PluginCreationParameters& creationParameters)
275{
276 // In the time since this plugin was requested asynchronously we might have created it synchronously or destroyed it.
277 // In either of those cases we need to ignore this creation request.
278 if (m_asynchronousInstanceIDsToIgnore.contains(creationParameters.pluginInstanceID)) {
279 m_asynchronousInstanceIDsToIgnore.remove(creationParameters.pluginInstanceID);
280 return;
281 }
282
283 // This version of CreatePlugin is only used by plug-ins that are known to behave when started asynchronously.
284 bool result = false;
285 bool wantsWheelEvents = false;
286 uint32_t remoteLayerClientID = 0;
287
288 if (creationParameters.artificialPluginInitializationDelayEnabled) {
289 Seconds artificialPluginInitializationDelay { 5_s };
290 sleep(artificialPluginInitializationDelay);
291 }
292
293 // Since plug-in creation can often message to the WebProcess synchronously (with NPP_Evaluate for example)
294 // we need to make sure that the web process will handle the plug-in process's synchronous messages,
295 // even if the web process is waiting on a synchronous reply itself.
296 // Normally the plug-in process doesn't give its synchronous messages the special flag to allow for that.
297 // We can force it to do so by incrementing the "DispatchMessageMarkedDispatchWhenWaitingForSyncReply" count.
298 m_connection->incrementDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount();
299
300 // The call to createPluginInternal can potentially cause the plug-in to be destroyed and
301 // thus free the WebProcessConnection object. Protect it.
302 Ref<WebProcessConnection> protect(*this);
303 createPluginInternal(creationParameters, result, wantsWheelEvents, remoteLayerClientID);
304
305 if (!m_connection) {
306 // createPluginInternal caused the connection to go away.
307 return;
308 }
309
310 m_connection->decrementDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount();
311
312 // If someone asked for this plug-in synchronously while it was in the middle of being created then we need perform the
313 // synchronous reply instead of sending the asynchronous reply.
314 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(creationParameters.pluginInstanceID);
315 ASSERT(pluginControllerProxy);
316 if (auto delayedSyncReply = pluginControllerProxy->takeInitializationReply()) {
317 delayedSyncReply(result, wantsWheelEvents, remoteLayerClientID);
318 return;
319 }
320
321 // Otherwise, send the asynchronous results now.
322 if (!result) {
323 m_connection->sendSync(Messages::PluginProxy::DidFailToCreatePlugin(), Messages::PluginProxy::DidFailToCreatePlugin::Reply(), creationParameters.pluginInstanceID);
324 return;
325 }
326
327 m_connection->sendSync(Messages::PluginProxy::DidCreatePlugin(wantsWheelEvents, remoteLayerClientID), Messages::PluginProxy::DidCreatePlugin::Reply(), creationParameters.pluginInstanceID);
328}
329
330} // namespace WebKit
331
332#endif // ENABLE(NETSCAPE_PLUGIN_API)
333