Adds Embed() variant to ViewManagerService that takes ViewManagerClient

This is for situations where an app wants to directly supply the
ViewManagerClient implementation.

R=msw@chromium.org

Review URL: https://codereview.chromium.org/905083002
diff --git a/mojo/services/view_manager/public/cpp/lib/view.cc b/mojo/services/view_manager/public/cpp/lib/view.cc
index 4a70072..1e8b2b0 100644
--- a/mojo/services/view_manager/public/cpp/lib/view.cc
+++ b/mojo/services/view_manager/public/cpp/lib/view.cc
@@ -379,6 +379,10 @@
       ->Embed(url, id_, services.Pass(), exposed_services.Pass());
 }
 
+void View::Embed(ViewManagerClientPtr client) {
+  static_cast<ViewManagerClientImpl*>(manager_)->Embed(id_, client.Pass());
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // View, protected:
 
diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc
index 3fffac2..574d300 100644
--- a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc
+++ b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc
@@ -204,8 +204,13 @@
                                   InterfaceRequest<ServiceProvider> services,
                                   ServiceProviderPtr exposed_services) {
   DCHECK(service_);
-  service_->Embed(url, view_id, services.Pass(), exposed_services.Pass(),
-                  ActionCompletedCallback());
+  service_->EmbedUrl(url, view_id, services.Pass(), exposed_services.Pass(),
+                     ActionCompletedCallback());
+}
+
+void ViewManagerClientImpl::Embed(Id view_id, ViewManagerClientPtr client) {
+  DCHECK(service_);
+  service_->Embed(view_id, client.Pass(), ActionCompletedCallback());
 }
 
 void ViewManagerClientImpl::AddView(View* view) {
diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h
index b3dd1fd..c3a2ad5 100644
--- a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h
+++ b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h
@@ -64,6 +64,7 @@
              Id view_id,
              InterfaceRequest<ServiceProvider> services,
              ServiceProviderPtr exposed_services);
+  void Embed(Id view_id, ViewManagerClientPtr client);
 
   void set_change_acked_callback(const base::Callback<void(void)>& callback) {
     change_acked_callback_ = callback;
diff --git a/mojo/services/view_manager/public/cpp/view.h b/mojo/services/view_manager/public/cpp/view.h
index 07bc5b0..d888ef0 100644
--- a/mojo/services/view_manager/public/cpp/view.h
+++ b/mojo/services/view_manager/public/cpp/view.h
@@ -122,11 +122,12 @@
   // Focus.
   void SetFocus();
 
-  // Embedding.
+  // Embedding. See view_manager.mojom for details.
   void Embed(const String& url);
   void Embed(const String& url,
              InterfaceRequest<ServiceProvider> services,
              ServiceProviderPtr exposed_services);
+  void Embed(ViewManagerClientPtr client);
 
  protected:
   // This class is subclassed only by test classes that provide a public ctor.
diff --git a/mojo/services/view_manager/public/interfaces/view_manager.mojom b/mojo/services/view_manager/public/interfaces/view_manager.mojom
index 961b783..86560ba 100644
--- a/mojo/services/view_manager/public/interfaces/view_manager.mojom
+++ b/mojo/services/view_manager/public/interfaces/view_manager.mojom
@@ -105,10 +105,16 @@
   // Shows the surface in the specified view.
   SetViewSurfaceId(uint32 view_id, SurfaceId surface_id) => (bool success);
 
-  // Embeds the app for |url| in the specified view. More specifically this
-  // creates a new connection to the specified url, expecting to get a
-  // ViewManagerClient and configures it with the root view |view|. Fails
-  // if |view| was not created by this connection.
+  // A connection may grant access to a view from another connection by way of
+  // the embed functions. There are two variants of this call:
+  //
+  // . EmbedUrl: the ViewManager connects to the app at the supplied url and
+  //   asks it for a ViewManagerClient.
+  // . With the second variant a ViewManagerClient is directly supplied.
+  //
+  // In both cases the new ViewManagerClient is configured with a root of
+  // |view_id|. The call fails if the originating connection did not create
+  // the view identified by |view_id|.
   //
   // A view may only be a root of one connection at a time. Subsequent calls to
   // Embed() for the same view result in the view being removed from the
@@ -129,10 +135,11 @@
   // if a different app is subsequently embedded at |view_id| the
   // ServiceProvider connections to its client in the embedded app and any
   // services it provided are not broken and continue to be valid.
-  Embed(string url,
-        uint32 view_id,
-        ServiceProvider&? services,
-        ServiceProvider? exposed_services) => (bool success);
+  EmbedUrl(string url,
+           uint32 view_id,
+           ServiceProvider&? services,
+           ServiceProvider? exposed_services) => (bool success);
+  Embed(uint32 view_id, ViewManagerClient client) => (bool success);
 };
 
 // Changes to views are not sent to the connection that originated the
diff --git a/services/view_manager/connection_manager.cc b/services/view_manager/connection_manager.cc
index ce2bb9c..8e06304 100644
--- a/services/view_manager/connection_manager.cc
+++ b/services/view_manager/connection_manager.cc
@@ -182,6 +182,25 @@
   OnConnectionMessagedClient(client_connection->service()->id());
 }
 
+void ConnectionManager::EmbedAtView(mojo::ConnectionSpecificId creator_id,
+                                    const ViewId& view_id,
+                                    mojo::ViewManagerClientPtr client) {
+  std::string creator_url;
+  ConnectionMap::const_iterator it = connection_map_.find(creator_id);
+  if (it != connection_map_.end())
+    creator_url = it->second->service()->url();
+
+  mojo::ViewManagerServicePtr service_ptr;
+  ClientConnection* client_connection =
+      delegate_->CreateClientConnectionForEmbedAtView(
+          this, GetProxy(&service_ptr), creator_id, creator_url, view_id,
+          client.Pass());
+  AddConnection(client_connection);
+  client_connection->service()->Init(client_connection->client(),
+                                     service_ptr.Pass(), nullptr, nullptr);
+  OnConnectionMessagedClient(client_connection->service()->id());
+}
+
 ViewManagerServiceImpl* ConnectionManager::GetConnection(
     ConnectionSpecificId connection_id) {
   ConnectionMap::iterator i = connection_map_.find(connection_id);
diff --git a/services/view_manager/connection_manager.h b/services/view_manager/connection_manager.h
index 0515236..cb61806 100644
--- a/services/view_manager/connection_manager.h
+++ b/services/view_manager/connection_manager.h
@@ -82,6 +82,9 @@
                    const ViewId& view_id,
                    mojo::InterfaceRequest<mojo::ServiceProvider> services,
                    mojo::ServiceProviderPtr exposed_services);
+  void EmbedAtView(mojo::ConnectionSpecificId creator_id,
+                   const ViewId& view_id,
+                   mojo::ViewManagerClientPtr client);
 
   // Returns the connection by id.
   ViewManagerServiceImpl* GetConnection(
diff --git a/services/view_manager/connection_manager_delegate.h b/services/view_manager/connection_manager_delegate.h
index 652f513..fb4612f 100644
--- a/services/view_manager/connection_manager_delegate.h
+++ b/services/view_manager/connection_manager_delegate.h
@@ -33,6 +33,13 @@
       const std::string& creator_url,
       const std::string& url,
       const ViewId& root_id) = 0;
+  virtual ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const ViewId& root_id,
+      mojo::ViewManagerClientPtr view_manager_client) = 0;
 
  protected:
   virtual ~ConnectionManagerDelegate() {}
diff --git a/services/view_manager/view_manager_app.cc b/services/view_manager/view_manager_app.cc
index 42548a7..29bd799 100644
--- a/services/view_manager/view_manager_app.cc
+++ b/services/view_manager/view_manager_app.cc
@@ -72,6 +72,20 @@
                                      service_request.Pass(), client.Pass());
 }
 
+ClientConnection* ViewManagerApp::CreateClientConnectionForEmbedAtView(
+    ConnectionManager* connection_manager,
+    mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+    mojo::ConnectionSpecificId creator_id,
+    const std::string& creator_url,
+    const ViewId& root_id,
+    mojo::ViewManagerClientPtr view_manager_client) {
+  scoped_ptr<ViewManagerServiceImpl> service(new ViewManagerServiceImpl(
+      connection_manager, creator_id, creator_url, std::string(), root_id));
+  return new DefaultClientConnection(service.Pass(), connection_manager,
+                                     service_request.Pass(),
+                                     view_manager_client.Pass());
+}
+
 void ViewManagerApp::Create(ApplicationConnection* connection,
                             InterfaceRequest<ViewManagerService> request) {
   if (connection_manager_->has_window_manager_client_connection()) {
diff --git a/services/view_manager/view_manager_app.h b/services/view_manager/view_manager_app.h
index 7d42f24..8ef6f3e 100644
--- a/services/view_manager/view_manager_app.h
+++ b/services/view_manager/view_manager_app.h
@@ -44,6 +44,13 @@
       const std::string& creator_url,
       const std::string& url,
       const ViewId& root_id) override;
+  ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const ViewId& root_id,
+      mojo::ViewManagerClientPtr view_manager_client) override;
 
   // mojo::InterfaceFactory<mojo::ViewManagerService>:
   void Create(
diff --git a/services/view_manager/view_manager_service_apptest.cc b/services/view_manager/view_manager_service_apptest.cc
index 2389291..ccd97c7 100644
--- a/services/view_manager/view_manager_service_apptest.cc
+++ b/services/view_manager/view_manager_service_apptest.cc
@@ -80,11 +80,24 @@
   return result == ERROR_CODE_NONE;
 }
 
-bool Embed(ViewManagerService* vm, Id root_id) {
+bool EmbedUrl(ViewManagerService* vm, Id root_id) {
   bool result = false;
   base::RunLoop run_loop;
   {
-    vm->Embed("mojo:view_manager_service_apptests", root_id, nullptr, nullptr,
+    vm->EmbedUrl("mojo:view_manager_service_apptests", root_id, nullptr,
+                 nullptr, base::Bind(&BoolResultCallback, &run_loop, &result));
+  }
+  run_loop.Run();
+  return result;
+}
+
+bool Embed(ViewManagerService* vm,
+           Id root_id,
+           mojo::ViewManagerClientPtr client) {
+  bool result = false;
+  base::RunLoop run_loop;
+  {
+    vm->Embed(root_id, client.Pass(),
               base::Bind(&BoolResultCallback, &run_loop, &result));
   }
   run_loop.Run();
@@ -417,7 +430,7 @@
   scoped_ptr<ViewManagerClientImpl> EstablishConnectionViaEmbed(
       ViewManagerService* owner,
       Id root_id) {
-    if (!Embed(owner, root_id)) {
+    if (!EmbedUrl(owner, root_id)) {
       ADD_FAILURE() << "Embed() failed";
       return nullptr;
     }
@@ -1321,7 +1334,6 @@
 }
 
 TEST_F(ViewManagerServiceAppTest, SetViewProperty) {
-  // Create 1 and 2 in the first connection and parent both to the root.
   ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
 
   ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
@@ -1450,6 +1462,19 @@
   EXPECT_FALSE(HasClonedView(views));
 }
 
+// Verifies Embed() works when supplying a ViewManagerClient.
+TEST_F(ViewManagerServiceAppTest, EmbedSupplyingViewManagerClient) {
+  ASSERT_TRUE(CreateView(vm1(), BuildViewId(1, 1)));
+
+  ViewManagerClientImpl client2;
+  mojo::ViewManagerClientPtr client2_ptr;
+  mojo::Binding<ViewManagerClient> client2_binding(&client2, &client2_ptr);
+  ASSERT_TRUE(Embed(vm1(), BuildViewId(1, 1), client2_ptr.Pass()));
+  client2.WaitForOnEmbed();
+  EXPECT_EQ("OnEmbed creator=mojo:window_manager",
+            SingleChangeToDescription(*client2.tracker()->changes()));
+}
+
 // TODO(sky): need to better track changes to initial connection. For example,
 // that SetBounsdViews/AddView and the like don't result in messages to the
 // originating connection.
diff --git a/services/view_manager/view_manager_service_impl.cc b/services/view_manager/view_manager_service_impl.cc
index 7dc4d3b..0c8a583 100644
--- a/services/view_manager/view_manager_service_impl.cc
+++ b/services/view_manager/view_manager_service_impl.cc
@@ -140,30 +140,26 @@
   return true;
 }
 
-bool ViewManagerServiceImpl::Embed(const std::string& url,
-                                   const ViewId& view_id,
-                                   InterfaceRequest<ServiceProvider> services,
-                                   ServiceProviderPtr exposed_services) {
-  const ServerView* view = GetView(view_id);
-  if (!view || !access_policy_->CanEmbed(view))
+bool ViewManagerServiceImpl::EmbedUrl(
+    const std::string& url,
+    const ViewId& view_id,
+    InterfaceRequest<ServiceProvider> services,
+    ServiceProviderPtr exposed_services) {
+  if (!PrepareForEmbed(view_id))
     return false;
-
-  // Only allow a node to be the root for one connection.
-  ViewManagerServiceImpl* existing_owner =
-      connection_manager_->GetConnectionWithRoot(view_id);
-
-  ConnectionManager::ScopedChange change(this, connection_manager_, true);
-  RemoveChildrenAsPartOfEmbed(view_id);
-  if (existing_owner) {
-    // Never message the originating connection.
-    connection_manager_->OnConnectionMessagedClient(id_);
-    existing_owner->RemoveRoot();
-  }
   connection_manager_->EmbedAtView(id_, url, view_id, services.Pass(),
                                    exposed_services.Pass());
   return true;
 }
 
+bool ViewManagerServiceImpl::Embed(const ViewId& view_id,
+                                   mojo::ViewManagerClientPtr client) {
+  if (!client.get() || !PrepareForEmbed(view_id))
+    return false;
+  connection_manager_->EmbedAtView(id_, view_id, client.Pass());
+  return true;
+}
+
 void ViewManagerServiceImpl::ProcessViewBoundsChanged(
     const ServerView* view,
     const gfx::Rect& old_bounds,
@@ -476,6 +472,25 @@
   }
 }
 
+bool ViewManagerServiceImpl::PrepareForEmbed(const ViewId& view_id) {
+  const ServerView* view = GetView(view_id);
+  if (!view || !access_policy_->CanEmbed(view))
+    return false;
+
+  // Only allow a node to be the root for one connection.
+  ViewManagerServiceImpl* existing_owner =
+      connection_manager_->GetConnectionWithRoot(view_id);
+
+  ConnectionManager::ScopedChange change(this, connection_manager_, true);
+  RemoveChildrenAsPartOfEmbed(view_id);
+  if (existing_owner) {
+    // Never message the originating connection.
+    connection_manager_->OnConnectionMessagedClient(id_);
+    existing_owner->RemoveRoot();
+  }
+  return true;
+}
+
 void ViewManagerServiceImpl::CreateView(
     Id transport_view_id,
     const Callback<void(mojo::ErrorCode)>& callback) {
@@ -595,14 +610,21 @@
   callback.Run(success);
 }
 
-void ViewManagerServiceImpl::Embed(const String& url,
-                                   Id transport_view_id,
-                                   InterfaceRequest<ServiceProvider> services,
-                                   ServiceProviderPtr exposed_services,
-                                   const Callback<void(bool)>& callback) {
-  callback.Run(Embed(url.To<std::string>(),
-                     ViewIdFromTransportId(transport_view_id), services.Pass(),
-                     exposed_services.Pass()));
+void ViewManagerServiceImpl::EmbedUrl(
+    const String& url,
+    Id transport_view_id,
+    InterfaceRequest<ServiceProvider> services,
+    ServiceProviderPtr exposed_services,
+    const Callback<void(bool)>& callback) {
+  callback.Run(EmbedUrl(url.To<std::string>(),
+                        ViewIdFromTransportId(transport_view_id),
+                        services.Pass(), exposed_services.Pass()));
+}
+
+void ViewManagerServiceImpl::Embed(mojo::Id transport_view_id,
+                                   mojo::ViewManagerClientPtr client,
+                                   const mojo::Callback<void(bool)>& callback) {
+  callback.Run(Embed(ViewIdFromTransportId(transport_view_id), client.Pass()));
 }
 
 bool ViewManagerServiceImpl::IsRootForAccessPolicy(const ViewId& id) const {
diff --git a/services/view_manager/view_manager_service_impl.h b/services/view_manager/view_manager_service_impl.h
index 2008d13..a315178 100644
--- a/services/view_manager/view_manager_service_impl.h
+++ b/services/view_manager/view_manager_service_impl.h
@@ -80,10 +80,11 @@
   bool AddView(const ViewId& parent_id, const ViewId& child_id);
   std::vector<const ServerView*> GetViewTree(const ViewId& view_id) const;
   bool SetViewVisibility(const ViewId& view_id, bool visible);
-  bool Embed(const std::string& url,
-             const ViewId& view_id,
-             mojo::InterfaceRequest<mojo::ServiceProvider> services,
-             mojo::ServiceProviderPtr exposed_services);
+  bool EmbedUrl(const std::string& url,
+                const ViewId& view_id,
+                mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                mojo::ServiceProviderPtr exposed_services);
+  bool Embed(const ViewId& view_id, mojo::ViewManagerClientPtr client);
 
   // The following methods are invoked after the corresponding change has been
   // processed. They do the appropriate bookkeeping and update the client as
@@ -167,6 +168,8 @@
   // Deletes all Views we own.
   void DestroyViews();
 
+  bool PrepareForEmbed(const ViewId& view_id);
+
   // ViewManagerService:
   void CreateView(
       mojo::Id transport_view_id,
@@ -199,10 +202,13 @@
                        const mojo::String& name,
                        mojo::Array<uint8_t> value,
                        const mojo::Callback<void(bool)>& callback) override;
-  void Embed(const mojo::String& url,
-             mojo::Id view_id,
-             mojo::InterfaceRequest<mojo::ServiceProvider> services,
-             mojo::ServiceProviderPtr exposed_services,
+  void EmbedUrl(const mojo::String& url,
+                mojo::Id transport_view_id,
+                mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                mojo::ServiceProviderPtr exposed_services,
+                const mojo::Callback<void(bool)>& callback) override;
+  void Embed(mojo::Id transport_view_id,
+             mojo::ViewManagerClientPtr client,
              const mojo::Callback<void(bool)>& callback) override;
 
   // AccessPolicyDelegate:
@@ -224,6 +230,8 @@
   mojo::ConnectionSpecificId creator_id_;
 
   // The URL of the app that embedded the app this connection was created for.
+  // NOTE: this is empty if the connection was created by way of directly
+  // supplying the ViewManagerClient.
   const std::string creator_url_;
 
   mojo::ViewManagerClient* client_;
diff --git a/services/view_manager/view_manager_service_unittest.cc b/services/view_manager/view_manager_service_unittest.cc
index b684fa5..7e95ff0 100644
--- a/services/view_manager/view_manager_service_unittest.cc
+++ b/services/view_manager/view_manager_service_unittest.cc
@@ -152,6 +152,16 @@
     last_connection_ = new TestClientConnection(service.Pass());
     return last_connection_;
   }
+  ClientConnection* CreateClientConnectionForEmbedAtView(
+      ConnectionManager* connection_manager,
+      mojo::InterfaceRequest<mojo::ViewManagerService> service_request,
+      mojo::ConnectionSpecificId creator_id,
+      const std::string& creator_url,
+      const ViewId& root_id,
+      mojo::ViewManagerClientPtr client) override {
+    NOTIMPLEMENTED();
+    return nullptr;
+  }
 
   TestClientConnection* last_connection_;
 
@@ -279,7 +289,8 @@
   EXPECT_TRUE(test->wm_connection()->SetViewVisibility(*embed_view_id, true));
   EXPECT_TRUE(test->wm_connection()->AddView(*(test->wm_connection()->root()),
                                              *embed_view_id));
-  test->wm_connection()->Embed(std::string(), *embed_view_id, nullptr, nullptr);
+  test->wm_connection()->EmbedUrl(std::string(), *embed_view_id, nullptr,
+                                  nullptr);
   ViewManagerServiceImpl* connection1 =
       test->connection_manager()->GetConnectionWithRoot(*embed_view_id);
   ASSERT_TRUE(connection1 != nullptr);
@@ -415,7 +426,7 @@
   EXPECT_TRUE(wm_connection()->SetViewVisibility(embed_view_id, true));
   EXPECT_TRUE(
       wm_connection()->AddView(*(wm_connection()->root()), embed_view_id));
-  wm_connection()->Embed(std::string(), embed_view_id, nullptr, nullptr);
+  wm_connection()->EmbedUrl(std::string(), embed_view_id, nullptr, nullptr);
   ViewManagerServiceImpl* connection1 =
       connection_manager()->GetConnectionWithRoot(embed_view_id);
   ASSERT_TRUE(connection1 != nullptr);