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