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