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(NETSCAPE_PLUGIN_API)
30
31#include "PluginProcessConnectionManagerMessages.h"
32#include "PluginProcessCreationParameters.h"
33#include "PluginProcessManager.h"
34#include "PluginProcessMessages.h"
35#include "WebContext.h"
36#include "WebCoreArgumentCoders.h"
37#include "WebPluginSiteDataManager.h"
38#include "WebProcessProxy.h"
39#include <WebCore/NotImplemented.h>
40#include <wtf/RunLoop.h>
41
42#if OS(DARWIN)
43#include "MachPort.h"
44#endif
45
46using namespace WebCore;
47
48namespace WebKit {
49
50static const double minimumLifetime = 2 * 60;
51static const double snapshottingMinimumLifetime = 30;
52
53static const double shutdownTimeout = 1 * 60;
54static const double snapshottingShutdownTimeout = 15;
55
56PassRefPtr<PluginProcessProxy> PluginProcessProxy::create(PluginProcessManager* PluginProcessManager, const PluginProcessAttributes& pluginProcessAttributes, uint64_t pluginProcessToken)
57{
58    return adoptRef(new PluginProcessProxy(PluginProcessManager, pluginProcessAttributes, pluginProcessToken));
59}
60
61PluginProcessProxy::PluginProcessProxy(PluginProcessManager* PluginProcessManager, const PluginProcessAttributes& pluginProcessAttributes, uint64_t pluginProcessToken)
62    : m_pluginProcessManager(PluginProcessManager)
63    , m_pluginProcessAttributes(pluginProcessAttributes)
64    , m_pluginProcessToken(pluginProcessToken)
65    , m_numPendingConnectionRequests(0)
66#if PLATFORM(COCOA)
67    , m_modalWindowIsShowing(false)
68    , m_fullscreenWindowIsShowing(false)
69    , m_preFullscreenAppPresentationOptions(0)
70#endif
71{
72    connect();
73}
74
75PluginProcessProxy::~PluginProcessProxy()
76{
77}
78
79void PluginProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
80{
81    launchOptions.processType = ProcessLauncher::PluginProcess;
82    platformGetLaunchOptions(launchOptions, m_pluginProcessAttributes);
83}
84
85// Asks the plug-in process to create a new connection to a web process. The connection identifier will be
86// encoded in the given argument encoder and sent back to the connection of the given web process.
87void PluginProcessProxy::getPluginProcessConnection(PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
88{
89    m_pendingConnectionReplies.append(reply);
90
91    if (state() == State::Launching) {
92        m_numPendingConnectionRequests++;
93        return;
94    }
95
96    // Ask the plug-in process to create a connection. Since the plug-in can be waiting for a synchronous reply
97    // we need to make sure that this message is always processed, even when the plug-in is waiting for a synchronus reply.
98    m_connection->send(Messages::PluginProcess::CreateWebProcessConnection(), 0, IPC::DispatchMessageEvenWhenWaitingForSyncReply);
99}
100
101void PluginProcessProxy::getSitesWithData(WebPluginSiteDataManager* webPluginSiteDataManager, uint64_t callbackID)
102{
103    ASSERT(!m_pendingGetSitesReplies.contains(callbackID));
104    m_pendingGetSitesReplies.set(callbackID, webPluginSiteDataManager);
105
106    if (state() == State::Launching) {
107        m_pendingGetSitesRequests.append(callbackID);
108        return;
109    }
110
111    // Ask the plug-in process for the sites with data.
112    m_connection->send(Messages::PluginProcess::GetSitesWithData(callbackID), 0);
113}
114
115void PluginProcessProxy::clearSiteData(WebPluginSiteDataManager* webPluginSiteDataManager, const Vector<String>& sites, uint64_t flags, uint64_t maxAgeInSeconds, uint64_t callbackID)
116{
117    ASSERT(!m_pendingClearSiteDataReplies.contains(callbackID));
118    m_pendingClearSiteDataReplies.set(callbackID, webPluginSiteDataManager);
119
120    if (state() == State::Launching) {
121        ClearSiteDataRequest request;
122        request.sites = sites;
123        request.flags = flags;
124        request.maxAgeInSeconds = maxAgeInSeconds;
125        request.callbackID = callbackID;
126        m_pendingClearSiteDataRequests.append(request);
127        return;
128    }
129
130    // Ask the plug-in process to clear the site data.
131    m_connection->send(Messages::PluginProcess::ClearSiteData(sites, flags, maxAgeInSeconds, callbackID), 0);
132}
133
134void PluginProcessProxy::pluginProcessCrashedOrFailedToLaunch()
135{
136    // The plug-in process must have crashed or exited, send any pending sync replies we might have.
137    while (!m_pendingConnectionReplies.isEmpty()) {
138        RefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply = m_pendingConnectionReplies.takeFirst();
139
140#if OS(DARWIN)
141        reply->send(IPC::Attachment(0, MACH_MSG_TYPE_MOVE_SEND), false);
142#elif USE(UNIX_DOMAIN_SOCKETS)
143        reply->send(IPC::Attachment(), false);
144#else
145        notImplemented();
146#endif
147    }
148
149    while (!m_pendingGetSitesReplies.isEmpty())
150        didGetSitesWithData(Vector<String>(), m_pendingGetSitesReplies.begin()->key);
151
152    while (!m_pendingClearSiteDataReplies.isEmpty())
153        didClearSiteData(m_pendingClearSiteDataReplies.begin()->key);
154
155    // Tell the plug-in process manager to forget about this plug-in process proxy. This may cause us to be deleted.
156    m_pluginProcessManager->removePluginProcessProxy(this);
157}
158
159void PluginProcessProxy::didClose(IPC::Connection*)
160{
161#if PLATFORM(COCOA)
162    if (m_modalWindowIsShowing)
163        endModal();
164
165    if (m_fullscreenWindowIsShowing)
166        exitFullscreen();
167#endif
168
169    const Vector<WebContext*>& contexts = WebContext::allContexts();
170    for (size_t i = 0; i < contexts.size(); ++i)
171        contexts[i]->sendToAllProcesses(Messages::PluginProcessConnectionManager::PluginProcessCrashed(m_pluginProcessToken));
172
173    // This will cause us to be deleted.
174    pluginProcessCrashedOrFailedToLaunch();
175}
176
177void PluginProcessProxy::didReceiveInvalidMessage(IPC::Connection*, IPC::StringReference, IPC::StringReference)
178{
179}
180
181void PluginProcessProxy::didFinishLaunching(ProcessLauncher*, IPC::Connection::Identifier connectionIdentifier)
182{
183    ASSERT(!m_connection);
184
185    if (IPC::Connection::identifierIsNull(connectionIdentifier)) {
186        pluginProcessCrashedOrFailedToLaunch();
187        return;
188    }
189
190    m_connection = IPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main());
191#if PLATFORM(MAC)
192    m_connection->setShouldCloseConnectionOnMachExceptions();
193#endif
194
195    m_connection->open();
196
197    PluginProcessCreationParameters parameters;
198    parameters.processType = m_pluginProcessAttributes.processType;
199    if (parameters.processType == PluginProcessTypeSnapshot) {
200        parameters.minimumLifetime = snapshottingMinimumLifetime;
201        parameters.terminationTimeout = snapshottingShutdownTimeout;
202    } else {
203        parameters.minimumLifetime = minimumLifetime;
204        parameters.terminationTimeout = shutdownTimeout;
205    }
206    platformInitializePluginProcess(parameters);
207
208    // Initialize the plug-in host process.
209    m_connection->send(Messages::PluginProcess::InitializePluginProcess(parameters), 0);
210
211#if PLATFORM(COCOA)
212    m_connection->send(Messages::PluginProcess::SetQOS(pluginProcessLatencyQOS(), pluginProcessThroughputQOS()), 0);
213#endif
214
215    // Send all our pending requests.
216    for (size_t i = 0; i < m_pendingGetSitesRequests.size(); ++i)
217        m_connection->send(Messages::PluginProcess::GetSitesWithData(m_pendingGetSitesRequests[i]), 0);
218    m_pendingGetSitesRequests.clear();
219
220    for (size_t i = 0; i < m_pendingClearSiteDataRequests.size(); ++i) {
221        const ClearSiteDataRequest& request = m_pendingClearSiteDataRequests[i];
222        m_connection->send(Messages::PluginProcess::ClearSiteData(request.sites, request.flags, request.maxAgeInSeconds, request.callbackID), 0);
223    }
224    m_pendingClearSiteDataRequests.clear();
225
226    for (unsigned i = 0; i < m_numPendingConnectionRequests; ++i)
227        m_connection->send(Messages::PluginProcess::CreateWebProcessConnection(), 0);
228
229    m_numPendingConnectionRequests = 0;
230
231#if PLATFORM(COCOA)
232    if (WebContext::processSuppressionIsEnabledForAllContexts())
233        setProcessSuppressionEnabled(true);
234#endif
235}
236
237void PluginProcessProxy::didCreateWebProcessConnection(const IPC::Attachment& connectionIdentifier, bool supportsAsynchronousPluginInitialization)
238{
239    ASSERT(!m_pendingConnectionReplies.isEmpty());
240
241    // Grab the first pending connection reply.
242    RefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply = m_pendingConnectionReplies.takeFirst();
243
244#if OS(DARWIN)
245    reply->send(IPC::Attachment(connectionIdentifier.port(), MACH_MSG_TYPE_MOVE_SEND), supportsAsynchronousPluginInitialization);
246#elif USE(UNIX_DOMAIN_SOCKETS)
247    reply->send(connectionIdentifier, supportsAsynchronousPluginInitialization);
248#else
249    notImplemented();
250#endif
251}
252
253void PluginProcessProxy::didGetSitesWithData(const Vector<String>& sites, uint64_t callbackID)
254{
255    RefPtr<WebPluginSiteDataManager> webPluginSiteDataManager = m_pendingGetSitesReplies.take(callbackID);
256    ASSERT(webPluginSiteDataManager);
257
258    webPluginSiteDataManager->didGetSitesWithDataForSinglePlugin(sites, callbackID);
259}
260
261void PluginProcessProxy::didClearSiteData(uint64_t callbackID)
262{
263    RefPtr<WebPluginSiteDataManager> webPluginSiteDataManager = m_pendingClearSiteDataReplies.take(callbackID);
264    ASSERT(webPluginSiteDataManager);
265
266    webPluginSiteDataManager->didClearSiteDataForSinglePlugin(callbackID);
267}
268
269} // namespace WebKit
270
271#endif // ENABLE(NETSCAPE_PLUGIN_API)
272