1/*
2 * Copyright (C) 2012-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 "AuxiliaryProcessProxy.h"
28
29#include "AuxiliaryProcessMessages.h"
30#include <wtf/RunLoop.h>
31
32namespace WebKit {
33
34AuxiliaryProcessProxy::AuxiliaryProcessProxy(bool alwaysRunsAtBackgroundPriority)
35 : m_alwaysRunsAtBackgroundPriority(alwaysRunsAtBackgroundPriority)
36{
37}
38
39AuxiliaryProcessProxy::~AuxiliaryProcessProxy()
40{
41 if (m_connection)
42 m_connection->invalidate();
43
44 if (m_processLauncher) {
45 m_processLauncher->invalidate();
46 m_processLauncher = nullptr;
47 }
48}
49
50void AuxiliaryProcessProxy::getLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions)
51{
52 launchOptions.processIdentifier = m_processIdentifier;
53
54 if (const char* userDirectorySuffix = getenv("DIRHELPER_USER_DIR_SUFFIX"))
55 launchOptions.extraInitializationData.add("user-directory-suffix"_s, userDirectorySuffix);
56
57 if (m_alwaysRunsAtBackgroundPriority)
58 launchOptions.extraInitializationData.add("always-runs-at-background-priority"_s, "true");
59
60#if ENABLE(DEVELOPER_MODE) && (PLATFORM(GTK) || PLATFORM(WPE))
61 const char* varname;
62 switch (launchOptions.processType) {
63 case ProcessLauncher::ProcessType::Web:
64 varname = "WEB_PROCESS_CMD_PREFIX";
65 break;
66#if ENABLE(NETSCAPE_PLUGIN_API)
67 case ProcessLauncher::ProcessType::Plugin64:
68 case ProcessLauncher::ProcessType::Plugin32:
69 varname = "PLUGIN_PROCESS_CMD_PREFIX";
70 break;
71#endif
72 case ProcessLauncher::ProcessType::Network:
73 varname = "NETWORK_PROCESS_CMD_PREFIX";
74 break;
75 }
76 const char* processCmdPrefix = getenv(varname);
77 if (processCmdPrefix && *processCmdPrefix)
78 launchOptions.processCmdPrefix = String::fromUTF8(processCmdPrefix);
79#endif // ENABLE(DEVELOPER_MODE) && (PLATFORM(GTK) || PLATFORM(WPE))
80
81 platformGetLaunchOptions(launchOptions);
82}
83
84void AuxiliaryProcessProxy::connect()
85{
86 ASSERT(!m_processLauncher);
87 ProcessLauncher::LaunchOptions launchOptions;
88 getLaunchOptions(launchOptions);
89 m_processLauncher = ProcessLauncher::create(this, launchOptions);
90}
91
92void AuxiliaryProcessProxy::terminate()
93{
94#if PLATFORM(COCOA)
95 if (m_connection && m_connection->kill())
96 return;
97#endif
98
99 // FIXME: We should really merge process launching into IPC connection creation and get rid of the process launcher.
100 if (m_processLauncher)
101 m_processLauncher->terminateProcess();
102}
103
104AuxiliaryProcessProxy::State AuxiliaryProcessProxy::state() const
105{
106 if (m_processLauncher && m_processLauncher->isLaunching())
107 return AuxiliaryProcessProxy::State::Launching;
108
109 if (!m_connection)
110 return AuxiliaryProcessProxy::State::Terminated;
111
112 return AuxiliaryProcessProxy::State::Running;
113}
114
115bool AuxiliaryProcessProxy::sendMessage(std::unique_ptr<IPC::Encoder> encoder, OptionSet<IPC::SendOption> sendOptions)
116{
117 switch (state()) {
118 case State::Launching:
119 // If we're waiting for the child process to launch, we need to stash away the messages so we can send them once we have a connection.
120 m_pendingMessages.append(std::make_pair(WTFMove(encoder), sendOptions));
121 return true;
122
123 case State::Running:
124 return connection()->sendMessage(WTFMove(encoder), sendOptions);
125
126 case State::Terminated:
127 return false;
128 }
129
130 return false;
131}
132
133void AuxiliaryProcessProxy::addMessageReceiver(IPC::StringReference messageReceiverName, IPC::MessageReceiver& messageReceiver)
134{
135 m_messageReceiverMap.addMessageReceiver(messageReceiverName, messageReceiver);
136}
137
138void AuxiliaryProcessProxy::addMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID, IPC::MessageReceiver& messageReceiver)
139{
140 m_messageReceiverMap.addMessageReceiver(messageReceiverName, destinationID, messageReceiver);
141}
142
143void AuxiliaryProcessProxy::removeMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID)
144{
145 m_messageReceiverMap.removeMessageReceiver(messageReceiverName, destinationID);
146}
147
148void AuxiliaryProcessProxy::removeMessageReceiver(IPC::StringReference messageReceiverName)
149{
150 m_messageReceiverMap.removeMessageReceiver(messageReceiverName);
151}
152
153bool AuxiliaryProcessProxy::dispatchMessage(IPC::Connection& connection, IPC::Decoder& decoder)
154{
155 return m_messageReceiverMap.dispatchMessage(connection, decoder);
156}
157
158bool AuxiliaryProcessProxy::dispatchSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, std::unique_ptr<IPC::Encoder>& replyEncoder)
159{
160 return m_messageReceiverMap.dispatchSyncMessage(connection, decoder, replyEncoder);
161}
162
163void AuxiliaryProcessProxy::didFinishLaunching(ProcessLauncher*, IPC::Connection::Identifier connectionIdentifier)
164{
165 ASSERT(!m_connection);
166
167 if (!IPC::Connection::identifierIsValid(connectionIdentifier))
168 return;
169
170 m_connection = IPC::Connection::createServerConnection(connectionIdentifier, *this);
171
172 connectionWillOpen(*m_connection);
173 m_connection->open();
174
175 for (size_t i = 0; i < m_pendingMessages.size(); ++i) {
176 std::unique_ptr<IPC::Encoder> message = WTFMove(m_pendingMessages[i].first);
177 OptionSet<IPC::SendOption> sendOptions = m_pendingMessages[i].second;
178 m_connection->sendMessage(WTFMove(message), sendOptions);
179 }
180
181 m_pendingMessages.clear();
182}
183
184void AuxiliaryProcessProxy::shutDownProcess()
185{
186 switch (state()) {
187 case State::Launching:
188 m_processLauncher->invalidate();
189 m_processLauncher = nullptr;
190 break;
191 case State::Running:
192#if PLATFORM(IOS_FAMILY)
193 // On iOS deploy a watchdog in the UI process, since the child process may be suspended.
194 // If 30s is insufficient for any outstanding activity to complete cleanly, then it will be killed.
195 ASSERT(m_connection);
196 m_connection->terminateSoon(30_s);
197#endif
198 break;
199 case State::Terminated:
200 return;
201 }
202
203 if (!m_connection)
204 return;
205
206 processWillShutDown(*m_connection);
207
208 if (canSendMessage())
209 send(Messages::AuxiliaryProcess::ShutDown(), 0);
210
211 m_connection->invalidate();
212 m_connection = nullptr;
213}
214
215void AuxiliaryProcessProxy::setProcessSuppressionEnabled(bool processSuppressionEnabled)
216{
217#if PLATFORM(COCOA)
218 if (state() != State::Running)
219 return;
220
221 connection()->send(Messages::AuxiliaryProcess::SetProcessSuppressionEnabled(processSuppressionEnabled), 0);
222#else
223 UNUSED_PARAM(processSuppressionEnabled);
224#endif
225}
226
227void AuxiliaryProcessProxy::connectionWillOpen(IPC::Connection&)
228{
229}
230
231} // namespace WebKit
232