[iOS] Replace "node assistance" terminology in WebKit with "focused element"
[WebKit-https.git] / Source / WebKit / UIProcess / ChildProcessProxy.cpp
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 "ChildProcessProxy.h"
28
29 #include "ChildProcessMessages.h"
30 #include <wtf/RunLoop.h>
31
32 namespace WebKit {
33
34 ChildProcessProxy::ChildProcessProxy(bool alwaysRunsAtBackgroundPriority)
35     : m_alwaysRunsAtBackgroundPriority(alwaysRunsAtBackgroundPriority)
36 {
37 }
38
39 ChildProcessProxy::~ChildProcessProxy()
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 ChildProcessProxy::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 ChildProcessProxy::connect()
85 {
86     ASSERT(!m_processLauncher);
87     ProcessLauncher::LaunchOptions launchOptions;
88     getLaunchOptions(launchOptions);
89     m_processLauncher = ProcessLauncher::create(this, launchOptions);
90 }
91
92 void ChildProcessProxy::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 ChildProcessProxy::State ChildProcessProxy::state() const
105 {
106     if (m_processLauncher && m_processLauncher->isLaunching())
107         return ChildProcessProxy::State::Launching;
108
109     if (!m_connection)
110         return ChildProcessProxy::State::Terminated;
111
112     return ChildProcessProxy::State::Running;
113 }
114
115 bool ChildProcessProxy::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 ChildProcessProxy::addMessageReceiver(IPC::StringReference messageReceiverName, IPC::MessageReceiver& messageReceiver)
134 {
135     m_messageReceiverMap.addMessageReceiver(messageReceiverName, messageReceiver);
136 }
137
138 void ChildProcessProxy::addMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID, IPC::MessageReceiver& messageReceiver)
139 {
140     m_messageReceiverMap.addMessageReceiver(messageReceiverName, destinationID, messageReceiver);
141 }
142
143 void ChildProcessProxy::removeMessageReceiver(IPC::StringReference messageReceiverName, uint64_t destinationID)
144 {
145     m_messageReceiverMap.removeMessageReceiver(messageReceiverName, destinationID);
146 }
147
148 void ChildProcessProxy::removeMessageReceiver(IPC::StringReference messageReceiverName)
149 {
150     m_messageReceiverMap.removeMessageReceiver(messageReceiverName);
151 }
152
153 bool ChildProcessProxy::dispatchMessage(IPC::Connection& connection, IPC::Decoder& decoder)
154 {
155     return m_messageReceiverMap.dispatchMessage(connection, decoder);
156 }
157
158 bool ChildProcessProxy::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 ChildProcessProxy::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 ChildProcessProxy::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::ChildProcess::ShutDown(), 0);
210
211     m_connection->invalidate();
212     m_connection = nullptr;
213 }
214
215 void ChildProcessProxy::setProcessSuppressionEnabled(bool processSuppressionEnabled)
216 {
217 #if PLATFORM(COCOA)
218     if (state() != State::Running)
219         return;
220
221     connection()->send(Messages::ChildProcess::SetProcessSuppressionEnabled(processSuppressionEnabled), 0);
222 #else
223     UNUSED_PARAM(processSuppressionEnabled);
224 #endif
225 }
226
227 void ChildProcessProxy::connectionWillOpen(IPC::Connection&)
228 {
229 }
230
231 } // namespace WebKit