1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #include "content/browser/renderer_host/agent_scheduling_group_host.h"
5
6 #include <memory>
7
8 #include "base/feature_list.h"
9 #include "base/no_destructor.h"
10 #include "base/stl_util.h"
11 #include "base/supports_user_data.h"
12 #include "content/browser/renderer_host/render_process_host_impl.h"
13 #include "content/common/agent_scheduling_group.mojom.h"
14 #include "content/common/renderer.mojom.h"
15 #include "content/common/state_transitions.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/common/content_features.h"
18 #include "ipc/ipc_message.h"
19
20 namespace content {
21
22 namespace {
23
24 using ::IPC::ChannelProxy;
25 using ::IPC::Listener;
26 using ::mojo::AssociatedReceiver;
27 using ::mojo::AssociatedRemote;
28 using ::mojo::PendingAssociatedReceiver;
29 using ::mojo::PendingAssociatedRemote;
30 using ::mojo::PendingReceiver;
31 using ::mojo::PendingRemote;
32 using ::mojo::Receiver;
33 using ::mojo::Remote;
34
35 static constexpr char kAgentGroupHostDataKey[] =
36 "AgentSchedulingGroupHostUserDataKey";
37
38 class AgentGroupHostUserData : public base::SupportsUserData::Data {
39 public:
AgentGroupHostUserData(std::unique_ptr<AgentSchedulingGroupHost> agent_group)40 explicit AgentGroupHostUserData(
41 std::unique_ptr<AgentSchedulingGroupHost> agent_group)
42 : agent_group_(std::move(agent_group)) {
43 DCHECK(agent_group_);
44 }
45 ~AgentGroupHostUserData() override = default;
46
agent_group()47 AgentSchedulingGroupHost* agent_group() { return agent_group_.get(); }
48
49 private:
50 std::unique_ptr<AgentSchedulingGroupHost> agent_group_;
51 };
52
53 } // namespace
54
55 // MaybeAssociatedReceiver:
MaybeAssociatedReceiver(AgentSchedulingGroupHost & host,bool should_associate)56 AgentSchedulingGroupHost::MaybeAssociatedReceiver::MaybeAssociatedReceiver(
57 AgentSchedulingGroupHost& host,
58 bool should_associate) {
59 if (should_associate) {
60 receiver_or_monostate_
61 .emplace<AssociatedReceiver<mojom::AgentSchedulingGroupHost>>(&host);
62 receiver_ = &absl::get<AssociatedReceiver<mojom::AgentSchedulingGroupHost>>(
63 receiver_or_monostate_);
64 } else {
65 receiver_or_monostate_.emplace<Receiver<mojom::AgentSchedulingGroupHost>>(
66 &host);
67 receiver_ = &absl::get<Receiver<mojom::AgentSchedulingGroupHost>>(
68 receiver_or_monostate_);
69 }
70 }
71
72 AgentSchedulingGroupHost::MaybeAssociatedReceiver::~MaybeAssociatedReceiver() =
73 default;
74
75 PendingRemote<mojom::AgentSchedulingGroupHost>
BindNewPipeAndPassRemote()76 AgentSchedulingGroupHost::MaybeAssociatedReceiver::BindNewPipeAndPassRemote() {
77 return absl::get<Receiver<mojom::AgentSchedulingGroupHost>*>(receiver_)
78 ->BindNewPipeAndPassRemote();
79 }
80
81 PendingAssociatedRemote<mojom::AgentSchedulingGroupHost>
82 AgentSchedulingGroupHost::MaybeAssociatedReceiver::
BindNewEndpointAndPassRemote()83 BindNewEndpointAndPassRemote() {
84 return absl::get<AssociatedReceiver<mojom::AgentSchedulingGroupHost>*>(
85 receiver_)
86 ->BindNewEndpointAndPassRemote();
87 }
88
reset()89 void AgentSchedulingGroupHost::MaybeAssociatedReceiver::reset() {
90 absl::visit([](auto* r) { r->reset(); }, receiver_);
91 }
92
is_bound()93 bool AgentSchedulingGroupHost::MaybeAssociatedReceiver::is_bound() {
94 return absl::visit([](auto* r) { return r->is_bound(); }, receiver_);
95 }
96
97 // MaybeAssociatedRemote:
MaybeAssociatedRemote(bool should_associate)98 AgentSchedulingGroupHost::MaybeAssociatedRemote::MaybeAssociatedRemote(
99 bool should_associate) {
100 if (should_associate) {
101 remote_ = AssociatedRemote<mojom::AgentSchedulingGroup>();
102 } else {
103 remote_ = Remote<mojom::AgentSchedulingGroup>();
104 }
105 }
106
107 AgentSchedulingGroupHost::MaybeAssociatedRemote::~MaybeAssociatedRemote() =
108 default;
109
110 PendingReceiver<mojom::AgentSchedulingGroup>
BindNewPipeAndPassReceiver()111 AgentSchedulingGroupHost::MaybeAssociatedRemote::BindNewPipeAndPassReceiver() {
112 return absl::get<Remote<mojom::AgentSchedulingGroup>>(remote_)
113 .BindNewPipeAndPassReceiver();
114 }
115
116 PendingAssociatedReceiver<mojom::AgentSchedulingGroup>
117 AgentSchedulingGroupHost::MaybeAssociatedRemote::
BindNewEndpointAndPassReceiver()118 BindNewEndpointAndPassReceiver() {
119 return absl::get<AssociatedRemote<mojom::AgentSchedulingGroup>>(remote_)
120 .BindNewEndpointAndPassReceiver();
121 }
122
reset()123 void AgentSchedulingGroupHost::MaybeAssociatedRemote::reset() {
124 absl::visit([](auto& remote) { remote.reset(); }, remote_);
125 }
126
is_bound()127 bool AgentSchedulingGroupHost::MaybeAssociatedRemote::is_bound() {
128 return absl::visit([](auto& remote) { return remote.is_bound(); }, remote_);
129 }
130
131 mojom::AgentSchedulingGroup*
get()132 AgentSchedulingGroupHost::MaybeAssociatedRemote::get() {
133 return absl::visit([](auto& r) { return r.get(); }, remote_);
134 }
135
136 // AgentSchedulingGroupHost:
137
138 // static
Get(const SiteInstance & instance,RenderProcessHost & process)139 AgentSchedulingGroupHost* AgentSchedulingGroupHost::Get(
140 const SiteInstance& instance,
141 RenderProcessHost& process) {
142 AgentGroupHostUserData* data = static_cast<AgentGroupHostUserData*>(
143 process.GetUserData(kAgentGroupHostDataKey));
144 if (data != nullptr)
145 return data->agent_group();
146
147 auto agent_group_data = std::make_unique<AgentGroupHostUserData>(
148 std::make_unique<AgentSchedulingGroupHost>(process));
149 AgentSchedulingGroupHost* agent_group = agent_group_data->agent_group();
150 process.SetUserData(kAgentGroupHostDataKey, std::move(agent_group_data));
151
152 return agent_group;
153 }
154
AgentSchedulingGroupHost(RenderProcessHost & process)155 AgentSchedulingGroupHost::AgentSchedulingGroupHost(RenderProcessHost& process)
156 : AgentSchedulingGroupHost(
157 process,
158 !base::FeatureList::IsEnabled(
159 features::kMbiDetachAgentSchedulingGroupFromChannel)) {}
160
AgentSchedulingGroupHost(RenderProcessHost & process,bool should_associate)161 AgentSchedulingGroupHost::AgentSchedulingGroupHost(RenderProcessHost& process,
162 bool should_associate)
163 : process_(process),
164 should_associate_(should_associate),
165 receiver_(*this, should_associate),
166 mojo_remote_(should_associate) {
167 process_.AddObserver(this);
168
169 // The RenderProcessHost's channel and other mojo interfaces are initialized
170 // by the time this class is constructed, so we eagerly initialize this
171 // class's mojos so they have the same bind lifetime as those of the
172 // RenderProcessHost. Furthermore, when the RenderProcessHost's channel and
173 // mojo interfaces get reset and reinitialized, we'll be notified so that we
174 // can reset and reinitialize ours as well.
175 SetUpMojoIfNeeded();
176 }
177
178 // DO NOT USE |process_| HERE! At this point it (or at least parts of it) is no
179 // longer valid.
~AgentSchedulingGroupHost()180 AgentSchedulingGroupHost::~AgentSchedulingGroupHost() {
181 DCHECK_EQ(state_, LifecycleState::kRenderProcessHostDestroyed);
182 }
183
RenderProcessExited(RenderProcessHost * host,const ChildProcessTerminationInfo & info)184 void AgentSchedulingGroupHost::RenderProcessExited(
185 RenderProcessHost* host,
186 const ChildProcessTerminationInfo& info) {
187 SetState(LifecycleState::kRenderProcessExited);
188 DCHECK_EQ(host, &process_);
189
190 // We mirror the RenderProcessHost flow here by resetting our mojos, and
191 // reinitializing them once the process's IPC::ChannelProxy and renderer
192 // interface are reinitialized.
193 ResetMojo();
194
195 // RenderProcessHostImpl will attempt to call this method later if it has not
196 // already been called. We call it now since `SetUpMojoIfNeeded()` relies on
197 // it being called, thus setting up the IPC channel and mojom::Renderer
198 // interface.
199 process_.EnableSendQueue();
200
201 // We call this so that we can immediately queue IPC and mojo messages on the
202 // new channel/interfaces that are bound for the next renderer process, should
203 // one eventually be spun up.
204 SetUpMojoIfNeeded();
205 }
206
RenderProcessHostDestroyed(RenderProcessHost * host)207 void AgentSchedulingGroupHost::RenderProcessHostDestroyed(
208 RenderProcessHost* host) {
209 if (RenderProcessHost::run_renderer_in_process()) {
210 // In single process mode, RenderProcessExited call is sometimes omitted.
211 if (state_ != LifecycleState::kBound) {
212 RenderProcessExited(host, ChildProcessTerminationInfo());
213 }
214 }
215 DCHECK_EQ(state_, LifecycleState::kBound);
216
217 DCHECK_EQ(host, &process_);
218 process_.RemoveObserver(this);
219 SetState(LifecycleState::kRenderProcessHostDestroyed);
220 }
221
GetProcess()222 RenderProcessHost* AgentSchedulingGroupHost::GetProcess() {
223 // TODO(crbug.com/1111231): Make the condition below hold.
224 // Currently the DCHECK doesn't hold, since RenderViewHostImpl outlives
225 // its associated AgentSchedulingGroupHost, and the dtor queries the
226 // associated RenderProcessHost to remove itself from the
227 // PerProcessRenderViewHostSet and RemoveObserver() itself.
228 // DCHECK_NE(state_, LifecycleState::kRenderProcessHostDestroyed);
229 return &process_;
230 }
231
Init()232 bool AgentSchedulingGroupHost::Init() {
233 // If we are about to initialize the RenderProcessHost, it is expected that
234 // `RenderProcessHost::InitializeChannelProxy()` has already been called, and
235 // thus its IPC::ChannelProxy and renderer interface are usable, as are our
236 // own mojos. This is because the lifetime of our mojos should match the
237 // lifetime of the RenderProcessHost's IPC::ChannelProxy and renderer
238 // interfaces.
239 DCHECK(process_.GetRendererInterface());
240 DCHECK(mojo_remote_.is_bound());
241 DCHECK_EQ(state_, LifecycleState::kBound);
242
243 return process_.Init();
244 }
245
GetChannel()246 ChannelProxy* AgentSchedulingGroupHost::GetChannel() {
247 DCHECK_EQ(state_, LifecycleState::kBound);
248 return process_.GetChannel();
249 }
250
Send(IPC::Message * message)251 bool AgentSchedulingGroupHost::Send(IPC::Message* message) {
252 DCHECK_EQ(state_, LifecycleState::kBound);
253 return process_.Send(message);
254 }
255
AddRoute(int32_t routing_id,Listener * listener)256 void AgentSchedulingGroupHost::AddRoute(int32_t routing_id,
257 Listener* listener) {
258 DCHECK_EQ(state_, LifecycleState::kBound);
259 DCHECK(!listener_map_.Lookup(routing_id));
260 listener_map_.AddWithID(listener, routing_id);
261 process_.AddRoute(routing_id, listener);
262 }
263
RemoveRoute(int32_t routing_id)264 void AgentSchedulingGroupHost::RemoveRoute(int32_t routing_id) {
265 DCHECK_EQ(state_, LifecycleState::kBound);
266 listener_map_.Remove(routing_id);
267 process_.RemoveRoute(routing_id);
268 }
GetRemoteRouteProvider()269 mojom::RouteProvider* AgentSchedulingGroupHost::GetRemoteRouteProvider() {
270 DCHECK_EQ(state_, LifecycleState::kBound);
271 return remote_route_provider_.get();
272 }
273
CreateFrame(mojom::CreateFrameParamsPtr params)274 void AgentSchedulingGroupHost::CreateFrame(mojom::CreateFrameParamsPtr params) {
275 DCHECK_EQ(state_, LifecycleState::kBound);
276 DCHECK(process_.IsInitializedAndNotDead());
277 DCHECK(mojo_remote_.is_bound());
278 mojo_remote_.get()->CreateFrame(std::move(params));
279 }
280
CreateView(mojom::CreateViewParamsPtr params)281 void AgentSchedulingGroupHost::CreateView(mojom::CreateViewParamsPtr params) {
282 DCHECK_EQ(state_, LifecycleState::kBound);
283 DCHECK(process_.IsInitializedAndNotDead());
284 DCHECK(mojo_remote_.is_bound());
285 mojo_remote_.get()->CreateView(std::move(params));
286 }
287
DestroyView(int32_t routing_id,mojom::AgentSchedulingGroup::DestroyViewCallback callback)288 void AgentSchedulingGroupHost::DestroyView(
289 int32_t routing_id,
290 mojom::AgentSchedulingGroup::DestroyViewCallback callback) {
291 DCHECK_EQ(state_, LifecycleState::kBound);
292 if (mojo_remote_.is_bound())
293 mojo_remote_.get()->DestroyView(routing_id, std::move(callback));
294 else
295 std::move(callback).Run();
296 }
297
CreateFrameProxy(int32_t routing_id,int32_t render_view_routing_id,const base::Optional<base::UnguessableToken> & opener_frame_token,int32_t parent_routing_id,const FrameReplicationState & replicated_state,const base::UnguessableToken & frame_token,const base::UnguessableToken & devtools_frame_token)298 void AgentSchedulingGroupHost::CreateFrameProxy(
299 int32_t routing_id,
300 int32_t render_view_routing_id,
301 const base::Optional<base::UnguessableToken>& opener_frame_token,
302 int32_t parent_routing_id,
303 const FrameReplicationState& replicated_state,
304 const base::UnguessableToken& frame_token,
305 const base::UnguessableToken& devtools_frame_token) {
306 DCHECK_EQ(state_, LifecycleState::kBound);
307 mojo_remote_.get()->CreateFrameProxy(
308 routing_id, render_view_routing_id, opener_frame_token, parent_routing_id,
309 replicated_state, frame_token, devtools_frame_token);
310 }
311
GetRoute(int32_t routing_id,mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider> receiver)312 void AgentSchedulingGroupHost::GetRoute(
313 int32_t routing_id,
314 mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider>
315 receiver) {
316 DCHECK_EQ(state_, LifecycleState::kBound);
317 DCHECK(receiver.is_valid());
318 associated_interface_provider_receivers_.Add(this, std::move(receiver),
319 routing_id);
320 }
321
GetAssociatedInterface(const std::string & name,mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface> receiver)322 void AgentSchedulingGroupHost::GetAssociatedInterface(
323 const std::string& name,
324 mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface>
325 receiver) {
326 DCHECK_EQ(state_, LifecycleState::kBound);
327 int32_t routing_id =
328 associated_interface_provider_receivers_.current_context();
329
330 if (auto* listener = GetListener(routing_id))
331 listener->OnAssociatedInterfaceRequest(name, receiver.PassHandle());
332 }
333
ResetMojo()334 void AgentSchedulingGroupHost::ResetMojo() {
335 DCHECK_EQ(state_, LifecycleState::kRenderProcessExited);
336 receiver_.reset();
337 mojo_remote_.reset();
338 remote_route_provider_.reset();
339 route_provider_receiver_.reset();
340 associated_interface_provider_receivers_.Clear();
341 }
342
SetUpMojoIfNeeded()343 void AgentSchedulingGroupHost::SetUpMojoIfNeeded() {
344 DCHECK_CURRENTLY_ON(BrowserThread::UI);
345 // We don't DCHECK |process_.IsInitializedAndNotDead()| here because we may
346 // end up here after the render process has died but before the
347 // RenderProcessHostImpl is re-initialized (and thus not considered dead
348 // anymore).
349
350 // The RenderProcessHostImpl's renderer interface must be initialized at this
351 // at this point.
352 DCHECK(process_.GetRendererInterface());
353
354 // Make sure that the bind state of all of this class's mojos are equivalent.
355 if (state_ == LifecycleState::kBound) {
356 DCHECK(mojo_remote_.is_bound());
357 DCHECK(receiver_.is_bound());
358 DCHECK(remote_route_provider_.is_bound());
359 DCHECK(route_provider_receiver_.is_bound());
360 return;
361 }
362
363 DCHECK(!mojo_remote_.is_bound());
364 DCHECK(!receiver_.is_bound());
365 DCHECK(!remote_route_provider_.is_bound());
366 DCHECK(!route_provider_receiver_.is_bound());
367
368 DCHECK(state_ == LifecycleState::kNewborn ||
369 state_ == LifecycleState::kRenderProcessExited);
370
371 if (should_associate_) {
372 process_.GetRendererInterface()->CreateAssociatedAgentSchedulingGroup(
373 receiver_.BindNewEndpointAndPassRemote(),
374 mojo_remote_.BindNewEndpointAndPassReceiver());
375 } else {
376 process_.GetRendererInterface()->CreateAgentSchedulingGroup(
377 receiver_.BindNewPipeAndPassRemote(),
378 mojo_remote_.BindNewPipeAndPassReceiver());
379 }
380
381 mojo_remote_.get()->BindAssociatedRouteProvider(
382 route_provider_receiver_.BindNewEndpointAndPassRemote(),
383 remote_route_provider_.BindNewEndpointAndPassReceiver());
384 SetState(LifecycleState::kBound);
385 }
386
SetState(AgentSchedulingGroupHost::LifecycleState state)387 void AgentSchedulingGroupHost::SetState(
388 AgentSchedulingGroupHost::LifecycleState state) {
389 static const base::NoDestructor<StateTransitions<LifecycleState>> transitions(
390 StateTransitions<LifecycleState>({
391 {LifecycleState::kNewborn, {LifecycleState::kBound}},
392 {LifecycleState::kBound,
393 {LifecycleState::kRenderProcessExited,
394 // Note: kRenderProcessHostDestroyed is only reached through kBound
395 // state. Upon kRenderProcessExited, we immediately setup a
396 // unclaimed mojo endpoint to be consumed by the next
397 // renderer process.
398 LifecycleState::kRenderProcessHostDestroyed}},
399 {LifecycleState::kRenderProcessExited, {LifecycleState::kBound}},
400 }));
401
402 DCHECK_STATE_TRANSITION(transitions, state_, state);
403 state_ = state;
404 }
405
operator <<(std::ostream & os,AgentSchedulingGroupHost::LifecycleState state)406 std::ostream& operator<<(std::ostream& os,
407 AgentSchedulingGroupHost::LifecycleState state) {
408 switch (state) {
409 case AgentSchedulingGroupHost::LifecycleState::kNewborn:
410 os << "Newborn";
411 break;
412 case AgentSchedulingGroupHost::LifecycleState::kBound:
413 os << "Bound";
414 break;
415 case AgentSchedulingGroupHost::LifecycleState::kRenderProcessExited:
416 os << "RenderProcessExited";
417 break;
418 case AgentSchedulingGroupHost::LifecycleState::kRenderProcessHostDestroyed:
419 os << "RenderProcessHostDestroyed";
420 break;
421 default:
422 os << "<invalid value: " << static_cast<int>(state) << ">";
423 }
424 return os;
425 }
426
GetListener(int32_t routing_id)427 Listener* AgentSchedulingGroupHost::GetListener(int32_t routing_id) {
428 DCHECK_NE(routing_id, MSG_ROUTING_CONTROL);
429
430 return listener_map_.Lookup(routing_id);
431 }
432
433 } // namespace content
434