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 | |
32 | namespace WebKit { |
33 | |
34 | AuxiliaryProcessProxy::AuxiliaryProcessProxy(bool alwaysRunsAtBackgroundPriority) |
35 | : m_alwaysRunsAtBackgroundPriority(alwaysRunsAtBackgroundPriority) |
36 | { |
37 | } |
38 | |
39 | AuxiliaryProcessProxy::~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 | |
50 | void 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 | |
84 | void AuxiliaryProcessProxy::connect() |
85 | { |
86 | ASSERT(!m_processLauncher); |
87 | ProcessLauncher::LaunchOptions launchOptions; |
88 | getLaunchOptions(launchOptions); |
89 | m_processLauncher = ProcessLauncher::create(this, launchOptions); |
90 | } |
91 | |
92 | void 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 | |
104 | AuxiliaryProcessProxy::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 | |
115 | bool 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 | |
133 | void AuxiliaryProcessProxy::addMessageReceiver(IPC::StringReference messageReceiverName, IPC::MessageReceiver& messageReceiver) |
134 | { |
135 | m_messageReceiverMap.addMessageReceiver(messageReceiverName, messageReceiver); |
136 | } |
137 | |
138 | void AuxiliaryProcessProxy::addMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID, IPC::MessageReceiver& messageReceiver) |
139 | { |
140 | m_messageReceiverMap.addMessageReceiver(messageReceiverName, destinationID, messageReceiver); |
141 | } |
142 | |
143 | void AuxiliaryProcessProxy::removeMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID) |
144 | { |
145 | m_messageReceiverMap.removeMessageReceiver(messageReceiverName, destinationID); |
146 | } |
147 | |
148 | void AuxiliaryProcessProxy::removeMessageReceiver(IPC::StringReference messageReceiverName) |
149 | { |
150 | m_messageReceiverMap.removeMessageReceiver(messageReceiverName); |
151 | } |
152 | |
153 | bool AuxiliaryProcessProxy::dispatchMessage(IPC::Connection& connection, IPC::Decoder& decoder) |
154 | { |
155 | return m_messageReceiverMap.dispatchMessage(connection, decoder); |
156 | } |
157 | |
158 | bool 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 | |
163 | void 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 | |
184 | void 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 | |
215 | void 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 | |
227 | void AuxiliaryProcessProxy::connectionWillOpen(IPC::Connection&) |
228 | { |
229 | } |
230 | |
231 | } // namespace WebKit |
232 | |