[MBI] Create legacy IPC channel per AgentSchedulingGroup.

This CL introduces separate legacy IPC channels per AgentSchedulingGroup
in "MBI mode". This channel is then used to associate channel-associated
interfaces with, and to send legacy IPC messages.

In addition it does the following:
1. Remove MaybeAssociatedRemote/Receiver, as the ASG's mojo interfaces
   are now always channel-associated.
2. ASG and ASGH implement IPC::Listener to handle legacy IPC message on
   the new channel.
3. Rename SetUpMojo/ResetMojo to SetUpIPC/ResetIPC.
4. `should_associate_` flag is replaced with an enum to make its meaning
   clearer.


Bug: 1111231
Change-Id: I1d850aae48b4a900ab0aef24ac8a58b00cbcb218
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2535954
Commit-Queue: Tal Pressman <talp@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828175}
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index ce284fb..436e17a 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -261,6 +261,8 @@
   RWH_CLOSE_PORTAL = 233,
   MSDH_INVALID_STREAM_TYPE = 234,
   RFH_CREATE_CHILD_FRAME_TOKENS_NOT_FOUND = 235,
+  ASGH_ASSOCIATED_INTERFACE_REQUEST = 236,
+  ASGH_RECEIVED_CONTROL_MESSAGE = 237,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.cc b/content/browser/renderer_host/agent_scheduling_group_host.cc
index 8fe6b45..492dfe5c 100644
--- a/content/browser/renderer_host/agent_scheduling_group_host.cc
+++ b/content/browser/renderer_host/agent_scheduling_group_host.cc
@@ -9,18 +9,21 @@
 #include "base/no_destructor.h"
 #include "base/stl_util.h"
 #include "base/supports_user_data.h"
+#include "content/browser/bad_message.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/common/agent_scheduling_group.mojom.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/state_transitions.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_features.h"
+#include "ipc/ipc_channel_mojo.h"
 #include "ipc/ipc_message.h"
 
 namespace content {
 
 namespace {
 
+using ::IPC::ChannelMojo;
 using ::IPC::ChannelProxy;
 using ::IPC::Listener;
 using ::mojo::AssociatedReceiver;
@@ -52,47 +55,6 @@
 
 }  // namespace
 
-// MaybeAssociatedRemote:
-AgentSchedulingGroupHost::MaybeAssociatedRemote::MaybeAssociatedRemote(
-    bool should_associate) {
-  if (should_associate) {
-    remote_ = AssociatedRemote<mojom::AgentSchedulingGroup>();
-  } else {
-    remote_ = Remote<mojom::AgentSchedulingGroup>();
-  }
-}
-
-AgentSchedulingGroupHost::MaybeAssociatedRemote::~MaybeAssociatedRemote() =
-    default;
-
-PendingReceiver<mojom::AgentSchedulingGroup>
-AgentSchedulingGroupHost::MaybeAssociatedRemote::BindNewPipeAndPassReceiver() {
-  return absl::get<Remote<mojom::AgentSchedulingGroup>>(remote_)
-      .BindNewPipeAndPassReceiver();
-}
-
-PendingAssociatedReceiver<mojom::AgentSchedulingGroup>
-AgentSchedulingGroupHost::MaybeAssociatedRemote::
-    BindNewEndpointAndPassReceiver() {
-  return absl::get<AssociatedRemote<mojom::AgentSchedulingGroup>>(remote_)
-      .BindNewEndpointAndPassReceiver();
-}
-
-void AgentSchedulingGroupHost::MaybeAssociatedRemote::reset() {
-  absl::visit([](auto& remote) { remote.reset(); }, remote_);
-}
-
-bool AgentSchedulingGroupHost::MaybeAssociatedRemote::is_bound() {
-  return absl::visit([](auto& remote) { return remote.is_bound(); }, remote_);
-}
-
-mojom::AgentSchedulingGroup*
-AgentSchedulingGroupHost::MaybeAssociatedRemote::get() {
-  return absl::visit([](auto& r) { return r.get(); }, remote_);
-}
-
-// AgentSchedulingGroupHost:
-
 // static
 AgentSchedulingGroupHost* AgentSchedulingGroupHost::Get(
     const SiteInstance& instance,
@@ -111,26 +73,21 @@
 }
 
 AgentSchedulingGroupHost::AgentSchedulingGroupHost(RenderProcessHost& process)
-    : AgentSchedulingGroupHost(
-          process,
-          !base::FeatureList::IsEnabled(
-              features::kMbiDetachAgentSchedulingGroupFromChannel)) {}
-
-AgentSchedulingGroupHost::AgentSchedulingGroupHost(RenderProcessHost& process,
-                                                   bool should_associate)
     : process_(process),
-      should_associate_(should_associate),
-      receiver_(this),
-      mojo_remote_(should_associate) {
+      association_mode_(base::FeatureList::IsEnabled(
+                            features::kMbiDetachAgentSchedulingGroupFromChannel)
+                            ? IPCAssociationMode::kUnassociated
+                            : IPCAssociationMode::kAssociatedWithProcess),
+      receiver_(this) {
   process_.AddObserver(this);
 
   // The RenderProcessHost's channel and other mojo interfaces are bound by the
-  // time this class is constructed, so we eagerly initialize this class's mojos
+  // time this class is constructed, so we eagerly initialize this class's IPC
   // so they have the same bind lifetime as those of the RenderProcessHost.
   // Furthermore, when the RenderProcessHost's channel and mojo interfaces get
   // reset and reinitialized, we'll be notified so that we can reset and
   // reinitialize ours as well.
-  SetUpMojo();
+  SetUpIPC();
 }
 
 // DO NOT USE |process_| HERE! At this point it (or at least parts of it) is no
@@ -148,17 +105,17 @@
   // We mirror the RenderProcessHost flow here by resetting our mojos, and
   // reinitializing them once the process's IPC::ChannelProxy and renderer
   // interface are reinitialized.
-  ResetMojo();
+  ResetIPC();
 
   // RenderProcessHostImpl will attempt to call this method later if it has not
-  // already been called. We call it now since `SetUpMojo()` relies on it being
-  // called, thus setting up the IPC ChannelProxy and mojom::Renderer interface.
+  // already been called. We call it now since `SetUpIPC()` relies on it being
+  // called, thus setting up the IPC channel and mojom::Renderer interface.
   process_.EnableSendQueue();
 
   // We call this so that we can immediately queue IPC and mojo messages on the
   // new channel/interfaces that are bound for the next renderer process, should
   // one eventually be spun up.
-  SetUpMojo();
+  SetUpIPC();
 }
 
 void AgentSchedulingGroupHost::RenderProcessHostDestroyed(
@@ -176,6 +133,37 @@
   SetState(LifecycleState::kRenderProcessHostDestroyed);
 }
 
+bool AgentSchedulingGroupHost::OnMessageReceived(const IPC::Message& message) {
+  if (message.routing_id() == MSG_ROUTING_CONTROL) {
+    bad_message::ReceivedBadMessage(&process_,
+                                    bad_message::ASGH_RECEIVED_CONTROL_MESSAGE);
+    return false;
+  }
+
+  auto* listener = GetListener(message.routing_id());
+  if (!listener)
+    return false;
+
+  return listener->OnMessageReceived(message);
+}
+
+void AgentSchedulingGroupHost::OnBadMessageReceived(
+    const IPC::Message& message) {
+  // If a bad message is received, it should be treated the same as a bad
+  // message on the renderer-wide channel (i.e., kill the renderer).
+  return process_.OnBadMessageReceived(message);
+}
+
+void AgentSchedulingGroupHost::OnAssociatedInterfaceRequest(
+    const std::string& interface_name,
+    mojo::ScopedInterfaceEndpointHandle handle) {
+  // There shouldn't be any interfaces requested this way - process-wide
+  // interfaces should be requested via the process-wide channel, and
+  // ASG-related interfaces should go through `RouteProvider`.
+  bad_message::ReceivedBadMessage(
+      &process_, bad_message::ASGH_ASSOCIATED_INTERFACE_REQUEST);
+}
+
 RenderProcessHost* AgentSchedulingGroupHost::GetProcess() {
   // TODO(crbug.com/1111231): Make the condition below hold.
   // Currently the DCHECK doesn't hold, since RenderViewHostImpl outlives
@@ -202,12 +190,30 @@
 
 ChannelProxy* AgentSchedulingGroupHost::GetChannel() {
   DCHECK_EQ(state_, LifecycleState::kBound);
-  return process_.GetChannel();
+
+  if (association_mode_ == IPCAssociationMode::kAssociatedWithProcess)
+    return process_.GetChannel();
+
+  DCHECK(channel_);
+  return channel_.get();
 }
 
 bool AgentSchedulingGroupHost::Send(IPC::Message* message) {
   DCHECK_EQ(state_, LifecycleState::kBound);
-  return process_.Send(message);
+
+  std::unique_ptr<IPC::Message> msg(message);
+
+  if (association_mode_ == IPCAssociationMode::kAssociatedWithProcess)
+    return process_.Send(msg.release());
+
+  // This DCHECK is too idealistic for now - messages that are handled by
+  // filters are sent as control messages since they are intercepted before
+  // routing. It is put here as documentation for now, since this code would not
+  // be reached until we activate `kUnassociated`.
+  DCHECK_NE(message->routing_id(), MSG_ROUTING_CONTROL);
+
+  DCHECK(channel_);
+  return channel_->Send(msg.release());
 }
 
 void AgentSchedulingGroupHost::AddRoute(int32_t routing_id,
@@ -246,10 +252,11 @@
     int32_t routing_id,
     mojom::AgentSchedulingGroup::DestroyViewCallback callback) {
   DCHECK_EQ(state_, LifecycleState::kBound);
-  if (mojo_remote_.is_bound())
+  if (mojo_remote_.is_bound()) {
     mojo_remote_.get()->DestroyView(routing_id, std::move(callback));
-  else
+  } else {
     std::move(callback).Run();
+  }
 }
 
 void AgentSchedulingGroupHost::CreateFrameProxy(
@@ -288,36 +295,85 @@
     listener->OnAssociatedInterfaceRequest(name, receiver.PassHandle());
 }
 
-void AgentSchedulingGroupHost::ResetMojo() {
+void AgentSchedulingGroupHost::ResetIPC() {
   DCHECK_EQ(state_, LifecycleState::kRenderProcessExited);
   receiver_.reset();
   mojo_remote_.reset();
   remote_route_provider_.reset();
   route_provider_receiver_.reset();
   associated_interface_provider_receivers_.Clear();
+  channel_ = nullptr;
 }
 
-void AgentSchedulingGroupHost::SetUpMojo() {
+void AgentSchedulingGroupHost::SetUpIPC() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // The RenderProcessHostImpl's renderer interface must be initialized at this
-  // at this point. We don't DCHECK |process_.IsInitializedAndNotDead()| here
-  // because we may end up here after the render process has died but before
-  // RenderProcessHostImpl::Init() is called. Therefore, the process can
-  // accept IPCs that will be queued for the next render process if one is spun
-  // up.
-  DCHECK(process_.GetRendererInterface());
-
   DCHECK(state_ == LifecycleState::kNewborn ||
          state_ == LifecycleState::kRenderProcessExited);
 
-  if (should_associate_) {
+  // The RenderProcessHostImpl's renderer interface must be initialized at this
+  // point. We don't DCHECK |process_.IsInitializedAndNotDead()| here because
+  // we may end up here after the renderer process has died but before
+  // RenderProcessHostImpl::Init() is called. Therefore, the process can accept
+  // IPCs that will be queued for the next renderer process if one is spun up.
+  DCHECK(process_.GetRendererInterface());
+
+  DCHECK(!channel_);
+  DCHECK(!mojo_remote_.is_bound());
+  DCHECK(!receiver_.is_bound());
+  DCHECK(!remote_route_provider_.is_bound());
+  DCHECK(!route_provider_receiver_.is_bound());
+
+  // After this function returns, all of `this`'s associated mojo interfaces
+  // need to be bound, and associated "properly" - in `kUnassociated` mode that
+  // means they are associated with the ASG's channel, and in
+  // `kAssociatedWithProcess` mode with the process-global channel. This
+  // initialization is done in a number of steps:
+  // 1. If we're in `kUnassociated` mode, create an IPC Channel (i.e.,
+  //    initialize `channel_`). After this, regardless of which mode we're in,
+  //    the ASGH would have a channel.
+  // 2. Initialize `mojo_remote_`. In `kAssociatedWithProcess` mode, this can be
+  //    done via the `mojom::Renderer` interface, but in `kUnassociated` mode
+  //    this *has* to be done via the just-created channel (so the interface is
+  //    associated with the correct pipe).
+  // 3. All the ASGH's other associated interfaces can now be initialized via
+  //    `mojo_remote_`, and will be transitively associated with the appropriate
+  //    IPC channel/pipe.
+  if (association_mode_ == IPCAssociationMode::kAssociatedWithProcess) {
     process_.GetRendererInterface()->CreateAssociatedAgentSchedulingGroup(
         mojo_remote_.BindNewEndpointAndPassReceiver());
   } else {
+    auto io_task_runner = GetIOThreadTaskRunner({});
+
+    // Empty interface endpoint to pass pipes more easily.
+    PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap;
+
     process_.GetRendererInterface()->CreateAgentSchedulingGroup(
-        mojo_remote_.BindNewPipeAndPassReceiver());
+        bootstrap.InitWithNewPipeAndPassReceiver());
+
+    auto channel_factory = ChannelMojo::CreateServerFactory(
+        bootstrap.PassPipe(), /*ipc_task_runner=*/io_task_runner,
+        /*proxy_task_runner=*/base::ThreadTaskRunnerHandle::Get());
+
+    // TODO(crbug.com/1111231): Android WebViews (that support synchronous
+    // compositing) send sync messages from the browser to the renderer, and
+    // therefore need a `SyncChannel`. However, we don't plan to support
+    // WebViews at this stage, so a plain `ChannelProxy` is fine for now.
+    channel_ = ChannelProxy::Create(
+        std::move(channel_factory), /*listener=*/this,
+        /*ipc_task_runner=*/io_task_runner,
+        /*listener_task_runner=*/base::ThreadTaskRunnerHandle::Get());
+
+    // TODO(crbug.com/1111231): Add necessary filters.
+    // Most of the filters currently installed on the process-wide channel are:
+    // 1. "Process-bound", that is, they do not handle messages sent using ASG,
+    // 2. Pepper/NaCl-related, that are going away, and are not supported, or
+    // 3. Related to Android WebViews, which are not currently supported.
+
+    channel_->GetRemoteAssociatedInterface(&mojo_remote_);
   }
 
+  DCHECK(mojo_remote_.is_bound());
+
   mojo_remote_.get()->BindAssociatedInterfaces(
       receiver_.BindNewEndpointAndPassRemote(),
       route_provider_receiver_.BindNewEndpointAndPassRemote(),
diff --git a/content/browser/renderer_host/agent_scheduling_group_host.h b/content/browser/renderer_host/agent_scheduling_group_host.h
index 8efdc7a..db4e4d71 100644
--- a/content/browser/renderer_host/agent_scheduling_group_host.h
+++ b/content/browser/renderer_host/agent_scheduling_group_host.h
@@ -14,17 +14,16 @@
 #include "content/common/renderer.mojom-forward.h"
 #include "content/common/state_transitions.h"
 #include "content/public/browser/render_process_host_observer.h"
+#include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/mojom/associated_interfaces/associated_interfaces.mojom.h"
 
 namespace IPC {
 class ChannelProxy;
-class Listener;
 class Message;
 }  // namespace IPC
 
@@ -43,6 +42,7 @@
 // RenderProcessHost.
 class CONTENT_EXPORT AgentSchedulingGroupHost
     : public RenderProcessHostObserver,
+      public IPC::Listener,
       public mojom::AgentSchedulingGroupHost,
       public mojom::RouteProvider,
       public blink::mojom::AssociatedInterfaceProvider {
@@ -54,7 +54,6 @@
   static AgentSchedulingGroupHost* Get(const SiteInstance& instance,
                                        RenderProcessHost& process);
 
-  // Utility ctor, forwarding to the main ctor below.
   // Should not be called explicitly. Use `Get()` instead.
   explicit AgentSchedulingGroupHost(RenderProcessHost& process);
   ~AgentSchedulingGroupHost() override;
@@ -106,41 +105,12 @@
   friend StateTransitions<LifecycleState>;
   friend std::ostream& operator<<(std::ostream& os, LifecycleState state);
 
-  // `MaybeAssociatedRemote` is a temporary helper class that allows us to
-  // switch between using an associated and non-associated remote. This behavior
-  // is controlled by the `kMbiDetachAgentSchedulingGroupFromChannel` feature
-  // flag. Associated remotes are associated with the IPC channel (transitively,
-  // via the `Renderer` interface), thus preserving cross-agent scheduling group
-  // message order. Non-associated interfaces are independent from each other
-  // and do not preserve message order between agent scheduling groups.
-  // TODO(crbug.com/1111231): Remove this once we can remove the flag.
-  class MaybeAssociatedRemote {
-   public:
-    explicit MaybeAssociatedRemote(bool should_associate);
-    ~MaybeAssociatedRemote();
-
-    mojo::PendingReceiver<mojom::AgentSchedulingGroup>
-    BindNewPipeAndPassReceiver() WARN_UNUSED_RESULT;
-    mojo::PendingAssociatedReceiver<mojom::AgentSchedulingGroup>
-    BindNewEndpointAndPassReceiver() WARN_UNUSED_RESULT;
-
-    void reset();
-    bool is_bound();
-    mojom::AgentSchedulingGroup* get();
-
-   private:
-    absl::variant<mojo::Remote<mojom::AgentSchedulingGroup>,
-                  mojo::AssociatedRemote<mojom::AgentSchedulingGroup>>
-        remote_;
-  };
-
-  // Main constructor.
-  // |should_associate| determines whether the `AgentSchedulingGroupHost` and
-  // `AgentSchedulingGroup` mojos should be associated with the `Renderer` or
-  // not. If they are, message order will be preserved across the entire
-  // process. If not, ordering will only be preserved inside an
-  // `AgentSchedulingGroup`.
-  AgentSchedulingGroupHost(RenderProcessHost& process, bool should_associate);
+  // IPC::Listener
+  bool OnMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnAssociatedInterfaceRequest(
+      const std::string& interface_name,
+      mojo::ScopedInterfaceEndpointHandle handle) override;
 
   // mojom::RouteProvider
   void GetRoute(
@@ -159,8 +129,8 @@
                            const ChildProcessTerminationInfo& info) override;
   void RenderProcessHostDestroyed(RenderProcessHost* host) override;
 
-  void ResetMojo();
-  void SetUpMojo();
+  void ResetIPC();
+  void SetUpIPC();
 
   void SetState(LifecycleState state);
 
@@ -169,19 +139,34 @@
   // The RenderProcessHost this AgentSchedulingGroup is assigned to.
   RenderProcessHost& process_;
 
-  const bool should_associate_;
+  enum class IPCAssociationMode {
+    // In this mode, the AgentSchedulingGroup will use the process-wide legacy
+    // IPC channel for communication with the renderer process and to associate
+    // its interfaces with.
+    kAssociatedWithProcess = 0,
+
+    // In this mode, each AgentSchedulingGroup will have its own legacy IPC
+    // channel for communication with the renderer process and to associate its
+    // interfaces with.
+    kUnassociated = 1,
+  };
+  const IPCAssociationMode association_mode_;
+
+  // This AgentSchedulingGroup's legacy IPC channel. Will only be used in
+  // `kUnassociated` mode.
+  std::unique_ptr<IPC::ChannelProxy> channel_;
 
   // Map of registered IPC listeners.
   base::IDMap<IPC::Listener*> listener_map_;
 
+  // Remote stub of `mojom::AgentSchedulingGroup`, used for sending calls to the
+  // (renderer-side) `AgentSchedulingGroup`.
+  mojo::AssociatedRemote<mojom::AgentSchedulingGroup> mojo_remote_;
+
   // Implementation of `mojom::AgentSchedulingGroupHost`, used for responding to
   // calls from the (renderer-side) `AgentSchedulingGroup`.
   mojo::AssociatedReceiver<mojom::AgentSchedulingGroupHost> receiver_;
 
-  // Remote stub of `mojom::AgentSchedulingGroup`, used for sending calls to the
-  // (renderer-side) `AgentSchedulingGroup`.
-  MaybeAssociatedRemote mojo_remote_;
-
   // The `mojom::RouteProvider` mojo pair to setup
   // `blink::AssociatedInterfaceProvider` routes between this and the
   // renderer-side `AgentSchedulingGroup`.
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index ecb1d23..70ab352 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -6,6 +6,7 @@
 
 import "content/common/agent_scheduling_group.mojom";
 import "content/common/native_types.mojom";
+import "ipc/ipc.mojom";
 import "mojo/public/mojom/base/generic_pending_receiver.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "services/network/public/mojom/network_types.mojom";
@@ -58,22 +59,24 @@
 // This should be used for implementing browser-to-renderer control messages
 // which need to retain FIFO with respect to legacy IPC messages.
 interface Renderer {
-  // Tells the renderer to create a new AgentSchedulingGroup, that will
-  // listen to messages sent by the host via the pending
-  // |agent_scheduling_group|. This will create "independent" agent scheduling
-  // groups that are not associated with the IPC channel, which will not
-  // guarantee any order across agent scheduling groups.
+  // Tells the renderer to create a new AgentSchedulingGroup, and initialize its
+  // legacy IPC Channel using the pipe provided by `bootstrap`. The channel will
+  // later be used to bind the mojom::AgentSchedulingGroup receiver, that
+  // listens to messages sent by the host.
+  // This will create an "independent" agent scheduling group that is not
+  // associated with the process-wide or other groups' IPC channels, meaning
+  // there is no guaranteed ordering between them.
   CreateAgentSchedulingGroup(
-    pending_receiver<AgentSchedulingGroup> agent_scheduling_group
-  );
+    pending_receiver<IPC.mojom.ChannelBootstrap> bootstrap);
 
   // Tells the renderer to create a new AgentSchedulingGroup, that will
   // listen to messages sent by the host via the pending
-  // |agent_scheduling_group|. This will create a channel-associated interface
-  // that will preserve message ordering across agent scheduling groups.
+  // |agent_scheduling_group|.
+  // This will create an agent scheduling group that is associated with the
+  // process-wide IPC channel, preserving message ordering across different
+  // agent scheduling groups.
   CreateAssociatedAgentSchedulingGroup(
-    pending_associated_receiver<AgentSchedulingGroup> agent_scheduling_group
-  );
+    pending_associated_receiver<AgentSchedulingGroup> agent_scheduling_group);
 
   // Tells the renderer that the network type has changed so that
   // navigator.onLine and navigator.connection can be updated.
diff --git a/content/renderer/agent_scheduling_group.cc b/content/renderer/agent_scheduling_group.cc
index a253662..73f3252 100644
--- a/content/renderer/agent_scheduling_group.cc
+++ b/content/renderer/agent_scheduling_group.cc
@@ -6,16 +6,22 @@
 
 #include "base/feature_list.h"
 #include "base/util/type_safety/pass_key.h"
+#include "content/common/agent_scheduling_group.mojom.h"
 #include "content/public/common/content_features.h"
 #include "content/renderer/compositor/compositor_dependencies.h"
 #include "content/renderer/render_frame_proxy.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/render_view_impl.h"
+#include "ipc/ipc_channel_mojo.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_sync_channel.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 
 namespace content {
 
+using ::IPC::ChannelMojo;
 using ::IPC::Listener;
+using ::IPC::SyncChannel;
 using ::mojo::AssociatedReceiver;
 using ::mojo::AssociatedRemote;
 using ::mojo::PendingAssociatedReceiver;
@@ -35,53 +41,49 @@
 
 }  // namespace
 
-// MaybeAssociatedReceiver:
-AgentSchedulingGroup::MaybeAssociatedReceiver::MaybeAssociatedReceiver(
-    AgentSchedulingGroup& impl,
-    PendingReceiver<mojom::AgentSchedulingGroup> receiver,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : receiver_(absl::in_place_type<Receiver<mojom::AgentSchedulingGroup>>,
-                &impl,
-                std::move(receiver),
-                task_runner) {}
-
-AgentSchedulingGroup::MaybeAssociatedReceiver::MaybeAssociatedReceiver(
-    AgentSchedulingGroup& impl,
-    PendingAssociatedReceiver<mojom::AgentSchedulingGroup> receiver,
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : receiver_(
-          absl::in_place_type<AssociatedReceiver<mojom::AgentSchedulingGroup>>,
-          &impl,
-          std::move(receiver),
-          task_runner) {}
-
-AgentSchedulingGroup::MaybeAssociatedReceiver::~MaybeAssociatedReceiver() =
-    default;
-
 // AgentSchedulingGroup:
 AgentSchedulingGroup::AgentSchedulingGroup(
     RenderThread& render_thread,
-    PendingReceiver<mojom::AgentSchedulingGroup> receiver)
-    : agent_group_scheduler_(
+    mojo::PendingReceiver<IPC::mojom::ChannelBootstrap> bootstrap)
+    : association_mode_(IPCAssociationMode::kUnassociated),
+      agent_group_scheduler_(
           blink::scheduler::WebThreadScheduler::MainThreadScheduler()
               ->CreateAgentGroupScheduler()),
       render_thread_(render_thread),
-      receiver_(*this,
-                std::move(receiver),
-                agent_group_scheduler_->DefaultTaskRunner()) {
+      // `receiver_` will be bound by `OnAssociatedInterfaceRequest()`.
+      receiver_(this) {
   DCHECK(agent_group_scheduler_);
   DCHECK(base::FeatureList::IsEnabled(
       features::kMbiDetachAgentSchedulingGroupFromChannel));
+
+  channel_ = SyncChannel::Create(
+      /*listener=*/this, /*ipc_task_runner=*/render_thread_.GetIOTaskRunner(),
+      /*listener_task_runner=*/agent_group_scheduler_->DefaultTaskRunner(),
+      render_thread_.GetShutdownEvent());
+
+  // TODO(crbug.com/1111231): Add necessary filters.
+  // Currently, the renderer process has these filters:
+  // 1. `UnfreezableMessageFilter` - in the process of being removed,
+  // 2. `PnaclTranslationResourceHost` - NaCl is going away, and
+  // 3. `AutomationMessageFilter` - needs to be handled somehow.
+
+  channel_->Init(
+      ChannelMojo::CreateClientFactory(
+          bootstrap.PassPipe(),
+          /*ipc_task_runner=*/render_thread_.GetIOTaskRunner(),
+          /*proxy_task_runner=*/agent_group_scheduler_->DefaultTaskRunner()),
+      /*create_pipe_now=*/true);
 }
 
 AgentSchedulingGroup::AgentSchedulingGroup(
     RenderThread& render_thread,
     PendingAssociatedReceiver<mojom::AgentSchedulingGroup> receiver)
-    : agent_group_scheduler_(
+    : association_mode_(IPCAssociationMode::kAssociatedWithProcess),
+      agent_group_scheduler_(
           blink::scheduler::WebThreadScheduler::MainThreadScheduler()
               ->CreateAgentGroupScheduler()),
       render_thread_(render_thread),
-      receiver_(*this,
+      receiver_(this,
                 std::move(receiver),
                 agent_group_scheduler_->DefaultTaskRunner()) {
   DCHECK(agent_group_scheduler_);
@@ -91,12 +93,49 @@
 
 AgentSchedulingGroup::~AgentSchedulingGroup() = default;
 
-// IPC messages to be forwarded to the `RenderThread`, for now. In the future
-// they will be handled directly by the `AgentSchedulingGroup`.
+bool AgentSchedulingGroup::OnMessageReceived(const IPC::Message& message) {
+  DCHECK_NE(message.routing_id(), MSG_ROUTING_CONTROL);
+
+  auto* listener = GetListener(message.routing_id());
+  if (!listener)
+    return false;
+
+  return listener->OnMessageReceived(message);
+}
+
+void AgentSchedulingGroup::OnBadMessageReceived(const IPC::Message& message) {
+  // Not strictly required, since we don't currently do anything with bad
+  // messages in the renderer, but if we ever do then this will "just work".
+  return ToImpl(render_thread_).OnBadMessageReceived(message);
+}
+
+void AgentSchedulingGroup::OnAssociatedInterfaceRequest(
+    const std::string& interface_name,
+    mojo::ScopedInterfaceEndpointHandle handle) {
+  // The ASG's channel should only be used to bootstrap the ASG mojo interface.
+  DCHECK_EQ(interface_name, mojom::AgentSchedulingGroup::Name_);
+  DCHECK(!receiver_.is_bound());
+
+  PendingAssociatedReceiver<mojom::AgentSchedulingGroup> pending_receiver(
+      std::move(handle));
+  receiver_.Bind(std::move(pending_receiver),
+                 agent_group_scheduler_->DefaultTaskRunner());
+}
+
 bool AgentSchedulingGroup::Send(IPC::Message* message) {
-  // TODO(crbug.com/1111231): For some reason, changing this to use
-  // render_thread_ causes trybots to time out (not specific tests).
-  return RenderThread::Get()->Send(message);
+  std::unique_ptr<IPC::Message> msg(message);
+
+  if (association_mode_ == IPCAssociationMode::kAssociatedWithProcess)
+    return render_thread_.Send(msg.release());
+
+  // This DCHECK is too idealistic for now - messages that are handled by
+  // filters are sent control messages since they are intercepted before
+  // routing. It is put here as documentation for now, since this code would not
+  // be reached until we activate `kUnassociated`.
+  DCHECK_NE(message->routing_id(), MSG_ROUTING_CONTROL);
+
+  DCHECK(channel_);
+  return channel_->Send(msg.release());
 }
 
 void AgentSchedulingGroup::AddRoute(int32_t routing_id, Listener* listener) {
diff --git a/content/renderer/agent_scheduling_group.h b/content/renderer/agent_scheduling_group.h
index 90ed0f5..0ac011db 100644
--- a/content/renderer/agent_scheduling_group.h
+++ b/content/renderer/agent_scheduling_group.h
@@ -9,18 +9,19 @@
 #include "content/common/agent_scheduling_group.mojom.h"
 #include "content/common/associated_interfaces.mojom.h"
 #include "content/common/content_export.h"
+#include "ipc/ipc.mojom.h"
+#include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/mojom/associated_interfaces/associated_interfaces.mojom.h"
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
 
 namespace IPC {
-class Listener;
 class Message;
+class SyncChannel;
 }  // namespace IPC
 
 namespace content {
@@ -33,22 +34,21 @@
 // to obtain ordering guarantees between different Mojo (associated) interfaces
 // and legacy IPC messages.
 class CONTENT_EXPORT AgentSchedulingGroup
-    : public mojom::AgentSchedulingGroup,
+    : public IPC::Listener,
+      public mojom::AgentSchedulingGroup,
       public mojom::RouteProvider,
       public blink::mojom::AssociatedInterfaceProvider {
  public:
   AgentSchedulingGroup(
       RenderThread& render_thread,
-      mojo::PendingReceiver<mojom::AgentSchedulingGroup> receiver);
+      mojo::PendingReceiver<IPC::mojom::ChannelBootstrap> bootstrap);
   AgentSchedulingGroup(
       RenderThread& render_thread,
       mojo::PendingAssociatedReceiver<mojom::AgentSchedulingGroup> receiver);
   ~AgentSchedulingGroup() override;
 
   AgentSchedulingGroup(const AgentSchedulingGroup&) = delete;
-  AgentSchedulingGroup(const AgentSchedulingGroup&&) = delete;
   AgentSchedulingGroup& operator=(const AgentSchedulingGroup&) = delete;
-  AgentSchedulingGroup& operator=(const AgentSchedulingGroup&&) = delete;
 
   bool Send(IPC::Message* message);
   void AddRoute(int32_t routing_id, IPC::Listener* listener);
@@ -62,32 +62,12 @@
   }
 
  private:
-  // `MaybeAssociatedReceiver` is a temporary helper class that allows us to
-  // switch between using an associated and non-associated receiver. This
-  // behavior is controlled by the `kMbiDetachAgentSchedulingGroupFromChannel`
-  // feature flag. Associated receivers are associated with the IPC channel
-  // (transitively, via the `Renderer` interface), thus preserving cross-agent
-  // scheduling group message order. Non-associated receivers are independent
-  // from each other and do not preserve message order between agent scheduling
-  // groups.
-  // TODO(crbug.com/1111231): Remove these once we can remove the flag.
-  class MaybeAssociatedReceiver {
-   public:
-    MaybeAssociatedReceiver(
-        AgentSchedulingGroup& impl,
-        mojo::PendingReceiver<mojom::AgentSchedulingGroup> receiver,
-        scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-    MaybeAssociatedReceiver(
-        AgentSchedulingGroup& impl,
-        mojo::PendingAssociatedReceiver<mojom::AgentSchedulingGroup> receiver,
-        scoped_refptr<base::SingleThreadTaskRunner> task_runner);
-    ~MaybeAssociatedReceiver();
-
-   private:
-    absl::variant<mojo::Receiver<mojom::AgentSchedulingGroup>,
-                  mojo::AssociatedReceiver<mojom::AgentSchedulingGroup>>
-        receiver_;
-  };
+  // IPC::Listener:
+  bool OnMessageReceived(const IPC::Message& message) override;
+  void OnBadMessageReceived(const IPC::Message& message) override;
+  void OnAssociatedInterfaceRequest(
+      const std::string& interface_name,
+      mojo::ScopedInterfaceEndpointHandle handle) override;
 
   // mojom::AgentSchedulingGroup:
   void CreateView(mojom::CreateViewParamsPtr params) override;
@@ -122,6 +102,23 @@
 
   IPC::Listener* GetListener(int32_t routing_id);
 
+  enum class IPCAssociationMode {
+    // In this mode, the AgentSchedulingGroup will use the process-wide legacy
+    // IPC channel for communication with the renderer process and to associate
+    // its interfaces with.
+    kAssociatedWithProcess = 0,
+
+    // In this mode, each AgentSchedulingGroup will have its own legacy IPC
+    // channel for communication with the renderer process and to associate its
+    // interfaces with.
+    kUnassociated = 1,
+  };
+  const IPCAssociationMode association_mode_;
+
+  // This AgentSchedulingGroup's legacy IPC channel. Will only be used in
+  // `kUnassociated` mode.
+  std::unique_ptr<IPC::SyncChannel> channel_;
+
   // Map of registered IPC listeners.
   base::IDMap<IPC::Listener*> listener_map_;
 
@@ -133,7 +130,7 @@
 
   // Implementation of `mojom::AgentSchedulingGroup`, used for responding to
   // calls from the (browser-side) `AgentSchedulingGroupHost`.
-  MaybeAssociatedReceiver receiver_;
+  mojo::AssociatedReceiver<mojom::AgentSchedulingGroup> receiver_;
 
   // Remote stub of mojom::AgentSchedulingGroupHost, used for sending calls to
   // the (browser-side) AgentSchedulingGroupHost.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 5a43139..b0dc8414 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1642,9 +1642,9 @@
 }
 
 void RenderThreadImpl::CreateAgentSchedulingGroup(
-    mojo::PendingReceiver<mojom::AgentSchedulingGroup> agent_scheduling_group) {
-  agent_scheduling_groups_.emplace(std::make_unique<AgentSchedulingGroup>(
-      *this, std::move(agent_scheduling_group)));
+    mojo::PendingReceiver<IPC::mojom::ChannelBootstrap> bootstrap) {
+  agent_scheduling_groups_.emplace(
+      std::make_unique<AgentSchedulingGroup>(*this, std::move(bootstrap)));
 }
 
 void RenderThreadImpl::CreateAssociatedAgentSchedulingGroup(
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 989f770..a195f88f 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -439,8 +439,7 @@
 
   // mojom::Renderer:
   void CreateAgentSchedulingGroup(
-      mojo::PendingReceiver<mojom::AgentSchedulingGroup> agent_scheduling_group)
-      override;
+      mojo::PendingReceiver<IPC::mojom::ChannelBootstrap> bootstrap) override;
   void CreateAssociatedAgentSchedulingGroup(
       mojo::PendingAssociatedReceiver<mojom::AgentSchedulingGroup>
           agent_scheduling_group) override;
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 8d12bb7..7bd5fb0 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6625,6 +6625,8 @@
   <int value="233" label="RWH_CLOSE_PORTAL"/>
   <int value="234" label="MSDH_INVALID_STREAM_TYPE"/>
   <int value="235" label="RFH_CREATE_CHILD_FRAME_TOKENS_NOT_FOUND"/>
+  <int value="236" label="ASGH_ASSOCIATED_INTERFACE_REQUEST"/>
+  <int value="237" label="ASGH_RECEIVED_CONTROL_MESSAGE"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">