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 "PluginProcessProxy.h"
28
29#if ENABLE(PLUGIN_PROCESS)
30
31#include "PluginProcessCreationParameters.h"
32#include "PluginProcessManager.h"
33#include "PluginProcessMessages.h"
34#include "RunLoop.h"
35#include "WebCoreArgumentCoders.h"
36#include "WebPluginSiteDataManager.h"
37#include "WebProcessProxy.h"
38
39#if PLATFORM(MAC)
40#include "MachPort.h"
41#endif
42
43namespace WebKit {
44
45PassOwnPtr<PluginProcessProxy> PluginProcessProxy::create(PluginProcessManager* PluginProcessManager, const PluginInfoStore::Plugin& pluginInfo)
46{
47    return adoptPtr(new PluginProcessProxy(PluginProcessManager, pluginInfo));
48}
49
50PluginProcessProxy::PluginProcessProxy(PluginProcessManager* PluginProcessManager, const PluginInfoStore::Plugin& pluginInfo)
51    : m_pluginProcessManager(PluginProcessManager)
52    , m_pluginInfo(pluginInfo)
53    , m_numPendingConnectionRequests(0)
54#if PLATFORM(MAC)
55    , m_modalWindowIsShowing(false)
56    , m_fullscreenWindowIsShowing(false)
57    , m_preFullscreenAppPresentationOptions(0)
58#endif
59{
60    ProcessLauncher::LaunchOptions launchOptions;
61    launchOptions.processType = ProcessLauncher::PluginProcess;
62#if PLATFORM(MAC)
63    launchOptions.architecture = pluginInfo.pluginArchitecture;
64    launchOptions.executableHeap = PluginProcessProxy::pluginNeedsExecutableHeap(pluginInfo);
65#endif
66
67    m_processLauncher = ProcessLauncher::create(this, launchOptions);
68}
69
70PluginProcessProxy::~PluginProcessProxy()
71{
72}
73
74// Asks the plug-in process to create a new connection to a web process. The connection identifier will be
75// encoded in the given argument encoder and sent back to the connection of the given web process.
76void PluginProcessProxy::getPluginProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
77{
78    m_pendingConnectionReplies.append(reply);
79
80    if (m_processLauncher->isLaunching()) {
81        m_numPendingConnectionRequests++;
82        return;
83    }
84
85    // Ask the plug-in process to create a connection. Since the plug-in can be waiting for a synchronous reply
86    // we need to make sure that this message is always processed, even when the plug-in is waiting for a synchronus reply.
87    m_connection->send(Messages::PluginProcess::CreateWebProcessConnection(), 0, CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply);
88}
89
90void PluginProcessProxy::getSitesWithData(WebPluginSiteDataManager* webPluginSiteDataManager, uint64_t callbackID)
91{
92    ASSERT(!m_pendingGetSitesReplies.contains(callbackID));
93    m_pendingGetSitesReplies.set(callbackID, webPluginSiteDataManager);
94
95    if (m_processLauncher->isLaunching()) {
96        m_pendingGetSitesRequests.append(callbackID);
97        return;
98    }
99
100    // Ask the plug-in process for the sites with data.
101    m_connection->send(Messages::PluginProcess::GetSitesWithData(callbackID), 0);
102}
103
104void PluginProcessProxy::clearSiteData(WebPluginSiteDataManager* webPluginSiteDataManager, const Vector<String>& sites, uint64_t flags, uint64_t maxAgeInSeconds, uint64_t callbackID)
105{
106    ASSERT(!m_pendingClearSiteDataReplies.contains(callbackID));
107    m_pendingClearSiteDataReplies.set(callbackID, webPluginSiteDataManager);
108
109    if (m_processLauncher->isLaunching()) {
110        ClearSiteDataRequest request;
111        request.sites = sites;
112        request.flags = flags;
113        request.maxAgeInSeconds = maxAgeInSeconds;
114        request.callbackID = callbackID;
115        m_pendingClearSiteDataRequests.append(request);
116        return;
117    }
118
119    // Ask the plug-in process to clear the site data.
120    m_connection->send(Messages::PluginProcess::ClearSiteData(sites, flags, maxAgeInSeconds, callbackID), 0);
121}
122
123void PluginProcessProxy::terminate()
124{
125     m_processLauncher->terminateProcess();
126}
127
128void PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch()
129{
130    // The plug-in process must have crashed or exited, send any pending sync replies we might have.
131    while (!m_pendingConnectionReplies.isEmpty()) {
132        RefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply = m_pendingConnectionReplies.takeFirst();
133
134#if PLATFORM(MAC)
135        reply->send(CoreIPC::MachPort(0, MACH_MSG_TYPE_MOVE_SEND));
136#else
137        // FIXME: Implement.
138        ASSERT_NOT_REACHED();
139#endif
140    }
141
142    while (!m_pendingGetSitesReplies.isEmpty())
143        didGetSitesWithData(Vector<String>(), m_pendingGetSitesReplies.begin()->first);
144
145    while (!m_pendingClearSiteDataReplies.isEmpty())
146        didClearSiteData(m_pendingClearSiteDataReplies.begin()->first);
147
148    // Tell the plug-in process manager to forget about this plug-in process proxy.
149    m_pluginProcessManager->removePluginProcessProxy(this);
150    delete this;
151}
152
153void PluginProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)
154{
155    didReceivePluginProcessProxyMessage(connection, messageID, arguments);
156}
157
158void PluginProcessProxy::didClose(CoreIPC::Connection*)
159{
160#if PLATFORM(MAC)
161    if (m_modalWindowIsShowing)
162        endModal();
163
164    if (m_fullscreenWindowIsShowing)
165        exitFullscreen();
166#endif
167
168    pluginProcessCrashedOrFailedToLaunch();
169}
170
171void PluginProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID)
172{
173}
174
175void PluginProcessProxy::syncMessageSendTimedOut(CoreIPC::Connection*)
176{
177}
178
179void PluginProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
180{
181    ASSERT(!m_connection);
182
183    if (!connectionIdentifier) {
184        pluginProcessCrashedOrFailedToLaunch();
185        return;
186    }
187
188    m_connection = CoreIPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main());
189    m_connection->open();
190
191    PluginProcessCreationParameters parameters;
192
193    parameters.pluginPath = m_pluginInfo.path;
194
195    platformInitializePluginProcess(parameters);
196
197    // Initialize the plug-in host process.
198    m_connection->send(Messages::PluginProcess::InitializePluginProcess(parameters), 0);
199
200    // Send all our pending requests.
201    for (size_t i = 0; i < m_pendingGetSitesRequests.size(); ++i)
202        m_connection->send(Messages::PluginProcess::GetSitesWithData(m_pendingGetSitesRequests[i]), 0);
203    m_pendingGetSitesRequests.clear();
204
205    for (size_t i = 0; i < m_pendingClearSiteDataRequests.size(); ++i) {
206        const ClearSiteDataRequest& request = m_pendingClearSiteDataRequests[i];
207        m_connection->send(Messages::PluginProcess::ClearSiteData(request.sites, request.flags, request.maxAgeInSeconds, request.callbackID), 0);
208    }
209    m_pendingClearSiteDataRequests.clear();
210
211    for (unsigned i = 0; i < m_numPendingConnectionRequests; ++i)
212        m_connection->send(Messages::PluginProcess::CreateWebProcessConnection(), 0);
213
214    m_numPendingConnectionRequests = 0;
215}
216
217#if PLATFORM(MAC)
218void PluginProcessProxy::didCreateWebProcessConnection(const CoreIPC::MachPort& machPort)
219{
220    ASSERT(!m_pendingConnectionReplies.isEmpty());
221
222    // Grab the first pending connection reply.
223    RefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply = m_pendingConnectionReplies.takeFirst();
224
225    reply->send(CoreIPC::MachPort(machPort.port(), MACH_MSG_TYPE_MOVE_SEND));
226}
227#endif
228
229void PluginProcessProxy::didGetSitesWithData(const Vector<String>& sites, uint64_t callbackID)
230{
231    RefPtr<WebPluginSiteDataManager> webPluginSiteDataManager = m_pendingGetSitesReplies.take(callbackID);
232    ASSERT(webPluginSiteDataManager);
233
234    webPluginSiteDataManager->didGetSitesWithDataForSinglePlugin(sites, callbackID);
235}
236
237void PluginProcessProxy::didClearSiteData(uint64_t callbackID)
238{
239    RefPtr<WebPluginSiteDataManager> webPluginSiteDataManager = m_pendingClearSiteDataReplies.take(callbackID);
240    ASSERT(webPluginSiteDataManager);
241
242    webPluginSiteDataManager->didClearSiteDataForSinglePlugin(callbackID);
243}
244
245} // namespace WebKit
246
247#endif // ENABLE(PLUGIN_PROCESS)
248