[chromecast] Define BindingsManager interface, implement for Fuchsia.

Defines the abstract BindingsManager API, which generically defines
the interface used by bindings modules to communicate scripts and
and MessagePorts with the CastRunner.

Implements BindingsManagerFuchsia, which bridges the BindingsManager
and chromium.cast.ApiBindings FIDL interfaces.

Add BindingsManagerFuchsia to Fuchsia's gn_all metatarget, so that
it's discoverable by the Chromium build waterfall.

Bug: 953958
Change-Id: I21fd818096d47e0c5d67dc0fb10a6dcf284800a5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1613965
Commit-Queue: Kevin Marshall <kmarshall@chromium.org>
Reviewed-by: Luke Halliwell <halliwell@chromium.org>
Reviewed-by: Ken Rockot <rockot@google.com>
Reviewed-by: Sean Topping <seantopping@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661137}
diff --git a/chromecast/bindings/BUILD.gn b/chromecast/bindings/BUILD.gn
index a5ae0cd..c7e2b79 100644
--- a/chromecast/bindings/BUILD.gn
+++ b/chromecast/bindings/BUILD.gn
@@ -2,8 +2,41 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/fuchsia/rules.gni")
+
 source_set("named_message_port_connector_resources") {
   data = [
     "named_message_port_connector.js",
   ]
 }
+
+source_set("bindings_manager") {
+  sources = [
+    "bindings_manager.cc",
+    "bindings_manager.h",
+  ]
+  deps = [
+    "//base",
+  ]
+  public_deps = [
+    "//mojo/public/cpp/bindings",
+  ]
+}
+
+source_set("bindings_manager_fuchsia") {
+  sources = [
+    "bindings_manager_fuchsia.cc",
+    "bindings_manager_fuchsia.h",
+  ]
+
+  deps = [
+    "//base",
+    "//fuchsia:cast_fidl",
+    "//fuchsia/base",
+    "//fuchsia/base:message_port",
+  ]
+
+  public_deps = [
+    ":bindings_manager",
+  ]
+}
diff --git a/chromecast/bindings/DEPS b/chromecast/bindings/DEPS
new file mode 100644
index 0000000..8078e6a
--- /dev/null
+++ b/chromecast/bindings/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+fuchsia",
+  "+mojo/public",
+]
diff --git a/chromecast/bindings/bindings_manager.cc b/chromecast/bindings/bindings_manager.cc
new file mode 100644
index 0000000..35131b7d
--- /dev/null
+++ b/chromecast/bindings/bindings_manager.cc
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/bindings/bindings_manager.h"
+
+#include <utility>
+
+namespace chromecast {
+namespace bindings {
+
+BindingsManager::BindingsManager() = default;
+
+BindingsManager::~BindingsManager() {
+  DCHECK(port_handlers_.empty());
+}
+
+void BindingsManager::RegisterPortHandler(base::StringPiece port_name,
+                                          PortConnectedHandler handler) {
+  auto result = port_handlers_.try_emplace(port_name, std::move(handler));
+  DCHECK(result.second);
+}
+
+void BindingsManager::UnregisterPortHandler(base::StringPiece port_name) {
+  size_t deleted = port_handlers_.erase(port_name);
+  DCHECK_EQ(deleted, 1u);
+}
+
+void BindingsManager::OnPortConnected(base::StringPiece port_name,
+                                      mojo::ScopedMessagePipeHandle port) {
+  auto handler = port_handlers_.find(port_name);
+  if (handler == port_handlers_.end()) {
+    LOG(ERROR) << "No handler found for port " << port_name << ".";
+    return;
+  }
+
+  handler->second.Run(std::move(port));
+}
+
+}  // namespace bindings
+}  // namespace chromecast
diff --git a/chromecast/bindings/bindings_manager.h b/chromecast/bindings/bindings_manager.h
new file mode 100644
index 0000000..8009cc2
--- /dev/null
+++ b/chromecast/bindings/bindings_manager.h
@@ -0,0 +1,63 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BINDINGS_BINDINGS_MANAGER_H_
+#define CHROMECAST_BINDINGS_BINDINGS_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/strings/string_piece.h"
+#include "mojo/public/cpp/system/message_pipe.h"
+
+namespace chromecast {
+namespace bindings {
+
+// Injects Cast Platform API scripts into pages' scripting context and
+// establishes bidirectional communication with them across the JS/native
+// boundary.
+class BindingsManager {
+ public:
+  using PortConnectedHandler =
+      base::RepeatingCallback<void(mojo::ScopedMessagePipeHandle)>;
+
+  BindingsManager();
+
+  // All handlers must be Unregistered() before |this| is destroyed.
+  virtual ~BindingsManager();
+
+  // Registers a |handler| which will receive MessagePorts originating from
+  // the frame's web content. |port_name| is an alphanumeric string that is
+  // consistent across JS and native code.
+  void RegisterPortHandler(base::StringPiece port_name,
+                           PortConnectedHandler handler);
+
+  // Unregisters a previously registered handler.
+  // The owner of BindingsManager is responsible for ensuring that all handlers
+  // are unregistered before |this| is deleted.
+  void UnregisterPortHandler(base::StringPiece port_name);
+
+  // Registers a |binding_script| for injection in the frame.
+  // Replaces registered bindings with the same |binding_name|.
+  virtual void AddBinding(base::StringPiece binding_name,
+                          base::StringPiece binding_script) = 0;
+
+ protected:
+  // Called by platform-specific subclasses when the underlying transport has
+  // delivered a port.
+  void OnPortConnected(base::StringPiece port_name,
+                       mojo::ScopedMessagePipeHandle port);
+
+ private:
+  base::flat_map<std::string, PortConnectedHandler> port_handlers_;
+
+  DISALLOW_COPY_AND_ASSIGN(BindingsManager);
+};
+
+}  // namespace bindings
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BINDINGS_BINDINGS_MANAGER_H_
diff --git a/chromecast/bindings/bindings_manager_fuchsia.cc b/chromecast/bindings/bindings_manager_fuchsia.cc
new file mode 100644
index 0000000..fe88955
--- /dev/null
+++ b/chromecast/bindings/bindings_manager_fuchsia.cc
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/bindings/bindings_manager_fuchsia.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/logging.h"
+#include "fuchsia/base/mem_buffer_util.h"
+#include "fuchsia/base/message_port.h"
+
+namespace chromecast {
+namespace bindings {
+
+BindingsManagerFuchsia::BindingsManagerFuchsia() = default;
+
+BindingsManagerFuchsia::~BindingsManagerFuchsia() = default;
+
+void BindingsManagerFuchsia::AddBinding(base::StringPiece binding_name,
+                                        base::StringPiece binding_script) {
+  bindings_[binding_name.as_string()] =
+      cr_fuchsia::MemBufferFromString(binding_script);
+}
+
+void BindingsManagerFuchsia::GetAll(GetAllCallback callback) {
+  // Build a list of binding scripts and send it to the client.
+  std::vector<chromium::cast::ApiBinding> bindings_vector;
+  for (auto& bindings_name_and_buffer : bindings_) {
+    chromium::cast::ApiBinding binding_cloned;
+    zx_status_t status;
+    status = bindings_name_and_buffer.second.Clone(
+        binding_cloned.mutable_before_load_script());
+    ZX_CHECK(status == ZX_OK, status) << "vmo::clone";
+    bindings_vector.emplace_back(std::move(binding_cloned));
+  }
+  callback(std::move(bindings_vector));
+}
+
+void BindingsManagerFuchsia::Connect(
+    std::string port_name,
+    fidl::InterfaceHandle<::fuchsia::web::MessagePort> message_port) {
+  OnPortConnected(port_name,
+                  cr_fuchsia::MessagePortFromFidl(std::move(message_port)));
+}
+
+}  // namespace bindings
+}  // namespace chromecast
diff --git a/chromecast/bindings/bindings_manager_fuchsia.h b/chromecast/bindings/bindings_manager_fuchsia.h
new file mode 100644
index 0000000..0f90e3d6
--- /dev/null
+++ b/chromecast/bindings/bindings_manager_fuchsia.h
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_BINDINGS_BINDINGS_MANAGER_FUCHSIA_H_
+#define CHROMECAST_BINDINGS_BINDINGS_MANAGER_FUCHSIA_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "chromecast/bindings/bindings_manager.h"
+#include "fuchsia/fidl/chromium/cast/cpp/fidl.h"
+
+namespace chromecast {
+namespace bindings {
+
+// Implements the BindingsManager as a ApiBindings FIDL service.
+class BindingsManagerFuchsia : public chromium::cast::ApiBindings,
+                               public BindingsManager {
+ public:
+  BindingsManagerFuchsia();
+  ~BindingsManagerFuchsia() override;
+
+  // BindingsManager implementation:
+  void AddBinding(base::StringPiece binding_name,
+                  base::StringPiece binding_script) override;
+
+ protected:
+  // chromium::cast::ApiBindings implementation:
+  void GetAll(GetAllCallback callback) override;
+  void Connect(
+      std::string port_name,
+      fidl::InterfaceHandle<::fuchsia::web::MessagePort> message_port) override;
+
+ private:
+  // Stores all bindings, keyed on the string-based IDs provided by the
+  // ApiBindings interface.
+  std::map<std::string, fuchsia::mem::Buffer> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(BindingsManagerFuchsia);
+};
+
+}  // namespace bindings
+}  // namespace chromecast
+
+#endif  // CHROMECAST_BINDINGS_BINDINGS_MANAGER_FUCHSIA_H_
diff --git a/fuchsia/BUILD.gn b/fuchsia/BUILD.gn
index d56a8c74..1aa6d36 100644
--- a/fuchsia/BUILD.gn
+++ b/fuchsia/BUILD.gn
@@ -145,5 +145,6 @@
     "runners:cast_runner_browsertests",
     "runners:cast_runner_integration_tests",
     "runners:web_runner",
+    "//chromecast/bindings:bindings_manager_fuchsia",
   ]
 }
diff --git a/fuchsia/fidl/cast/api_bindings.fidl b/fuchsia/fidl/cast/api_bindings.fidl
index 05d36ee..a44d0dc 100644
--- a/fuchsia/fidl/cast/api_bindings.fidl
+++ b/fuchsia/fidl/cast/api_bindings.fidl
@@ -16,7 +16,7 @@
 
     /// Should be invoked when a connecting a named MessagePort to a native
     /// bindings backend.
-    Connect(string api_name, fuchsia.web.MessagePort message_port);
+    Connect(string port_name, fuchsia.web.MessagePort message_port);
 };
 
 table ApiBinding {