| // Copyright 2022 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "fuchsia_web/webinstance_host/fuchsia_web_debug_proxy.h" | 
 |  | 
 | #include <lib/fidl/cpp/binding.h> | 
 |  | 
 | #include "base/check.h" | 
 | #include "base/containers/flat_set.h" | 
 | #include "base/containers/unique_ptr_adapters.h" | 
 | #include "base/fuchsia/fuchsia_logging.h" | 
 | #include "base/logging.h" | 
 |  | 
 | namespace { | 
 |  | 
 | // Proxies notifications from a DevToolsPerContextListener channel connected | 
 | // to a web container, to the specified set of DevToolsListeners. | 
 | class PerContextListenerProxy final | 
 |     : public fuchsia::web::DevToolsPerContextListener { | 
 |  public: | 
 |   PerContextListenerProxy( | 
 |       fidl::InterfaceRequest<fuchsia::web::DevToolsPerContextListener> request, | 
 |       const fidl::InterfacePtrSet<fuchsia::web::DevToolsListener>& clients) | 
 |       : binding_(this, std::move(request)) { | 
 |     // Notify clients of the newly-available per-Context DevTools. | 
 |     for (const auto& client : clients.ptrs()) { | 
 |       fidl::InterfaceHandle<fuchsia::web::DevToolsPerContextListener> handle; | 
 |       (*client)->OnContextDevToolsAvailable(handle.NewRequest()); | 
 |       auto ptr = std::make_unique< | 
 |           fidl::InterfacePtr<fuchsia::web::DevToolsPerContextListener>>( | 
 |           handle.Bind()); | 
 |  | 
 |       // If the client disconnects then remove it. If no clients remain then | 
 |       // delete this proxy. | 
 |       ptr->set_error_handler([this, client = ptr.get()](zx_status_t status) { | 
 |         ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status); | 
 |         clients_.erase(client); | 
 |         if (clients_.empty()) | 
 |           delete this; | 
 |       }); | 
 |  | 
 |       clients_.insert(std::move(ptr)); | 
 |     } | 
 |  | 
 |     // Delete this proxy if the instance goes-away. | 
 |     binding_.set_error_handler([this](zx_status_t status) { | 
 |       ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status); | 
 |       delete this; | 
 |     }); | 
 |   } | 
 |   ~PerContextListenerProxy() override = default; | 
 |  | 
 |   // fuchsia::web::DevToolsPerContextListener implementation. | 
 |   void OnHttpPortOpen(uint16_t port) override { | 
 |     for (const auto& client : clients_) { | 
 |       (*client)->OnHttpPortOpen(port); | 
 |     } | 
 |   } | 
 |  | 
 |  private: | 
 |   fidl::Binding<fuchsia::web::DevToolsPerContextListener> binding_; | 
 |  | 
 |   // Connections to clients to which to propagate updates. | 
 |   base::flat_set<std::unique_ptr<fidl::InterfacePtr< | 
 |                      fuchsia::web::DevToolsPerContextListener>>, | 
 |                  base::UniquePtrComparator> | 
 |       clients_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | FuchsiaWebDebugProxy::FuchsiaWebDebugProxy() = default; | 
 | FuchsiaWebDebugProxy::~FuchsiaWebDebugProxy() = default; | 
 |  | 
 | void FuchsiaWebDebugProxy::EnableDevTools( | 
 |     fidl::InterfaceHandle<fuchsia::web::DevToolsListener> listener, | 
 |     EnableDevToolsCallback callback) { | 
 |   // Add the listener to the active set, but do not inform it of any debuggable | 
 |   // instances that already exist, since callers are expected to only start | 
 |   // creating new instances after the callback has returned. | 
 |   devtools_listeners_.AddInterfacePtr(listener.Bind()); | 
 |  | 
 |   // If there were statically-configured fuchsia.web.Debug instances configured | 
 |   // then they should be connected-to here, and |callback()| invoked only | 
 |   // when each static instance' |EnableDevTools()| call had completed, or | 
 |   // failed. | 
 |   callback(); | 
 | } | 
 |  | 
 | void FuchsiaWebDebugProxy::RegisterInstance( | 
 |     fidl::InterfaceHandle<fuchsia::web::Debug> debug) { | 
 |   DCHECK(has_clients()); | 
 |  | 
 |   // Create a new DevToolsListener binding, and connect it to the instance. | 
 |   fidl::InterfaceHandle<fuchsia::web::DevToolsListener> handle; | 
 |   instance_bindings_.AddBinding(this, handle.NewRequest()); | 
 |   debug.Bind()->EnableDevTools(std::move(handle), {}); | 
 | } | 
 |  | 
 | void FuchsiaWebDebugProxy::OnContextDevToolsAvailable( | 
 |     fidl::InterfaceRequest<fuchsia::web::DevToolsPerContextListener> request) { | 
 |   // Create a proxy to propagate notifications for this new per-Context | 
 |   // listener out to each of the clients. | 
 |   new PerContextListenerProxy(std::move(request), devtools_listeners_); | 
 | } |