Allow external applications to register for mojo: urls

BUG=
R=aa@chromium.org

Review URL: https://codereview.chromium.org/861293002
diff --git a/shell/application_manager/application_manager.cc b/shell/application_manager/application_manager.cc
index ff386e0..8032d0f 100644
--- a/shell/application_manager/application_manager.cc
+++ b/shell/application_manager/application_manager.cc
@@ -107,6 +107,24 @@
     ServiceProviderPtr exposed_services) {
   DCHECK(requested_url.is_valid());
   GURL mapped_url = delegate_->ResolveMappings(requested_url);
+
+  // We check both the mapped and resolved urls for existing shell_impls because
+  // external applications can be registered for the unresolved mojo:foo urls.
+  ShellImpl* shell_impl = GetShellImpl(mapped_url);
+  if (shell_impl) {
+    ConnectToClient(shell_impl, mapped_url, requestor_url, services.Pass(),
+                    exposed_services.Pass());
+    return;
+  }
+
+  GURL resolved_url = delegate_->ResolveURL(mapped_url);
+  shell_impl = GetShellImpl(resolved_url);
+  if (shell_impl) {
+    ConnectToClient(shell_impl, resolved_url, requestor_url, services.Pass(),
+                    exposed_services.Pass());
+    return;
+  }
+
   ApplicationLoader* loader =
       GetLoaderForURL(mapped_url, DONT_INCLUDE_DEFAULT_LOADER);
   if (loader) {
@@ -115,7 +133,6 @@
     return;
   }
 
-  GURL resolved_url = delegate_->ResolveURL(mapped_url);
   loader = GetLoaderForURL(resolved_url, INCLUDE_DEFAULT_LOADER);
   if (loader) {
     ConnectToApplicationImpl(requested_url, resolved_url, requestor_url,
@@ -134,26 +151,26 @@
     InterfaceRequest<ServiceProvider> services,
     ServiceProviderPtr exposed_services,
     ApplicationLoader* loader) {
-  ShellImpl* shell = nullptr;
-  URLToShellImplMap::const_iterator shell_it =
-      url_to_shell_impl_.find(resolved_url);
-  if (shell_it != url_to_shell_impl_.end()) {
-    shell = shell_it->second;
-  } else {
-    ShellPtr shell_ptr;
-    shell =
-        new ShellImpl(GetProxy(&shell_ptr), this, requested_url, resolved_url);
-    url_to_shell_impl_[resolved_url] = shell;
-    shell->client()->Initialize(GetArgsForURL(requested_url));
+  ShellPtr shell_ptr;
+  ShellImpl* shell =
+      new ShellImpl(GetProxy(&shell_ptr), this, requested_url, resolved_url);
+  url_to_shell_impl_[resolved_url] = shell;
+  shell->client()->Initialize(GetArgsForURL(requested_url));
 
-    loader->Load(this, resolved_url, shell_ptr.Pass(),
-                 base::Bind(&ApplicationManager::LoadWithContentHandler,
-                            weak_ptr_factory_.GetWeakPtr()));
-  }
+  loader->Load(this, resolved_url, shell_ptr.Pass(),
+               base::Bind(&ApplicationManager::LoadWithContentHandler,
+                          weak_ptr_factory_.GetWeakPtr()));
   ConnectToClient(shell, resolved_url, requestor_url, services.Pass(),
                   exposed_services.Pass());
 }
 
+ShellImpl* ApplicationManager::GetShellImpl(const GURL& url) {
+  const auto& shell_it = url_to_shell_impl_.find(url);
+  if (shell_it != url_to_shell_impl_.end())
+    return shell_it->second;
+  return nullptr;
+}
+
 void ApplicationManager::ConnectToClient(
     ShellImpl* shell_impl,
     const GURL& url,
diff --git a/shell/application_manager/application_manager.h b/shell/application_manager/application_manager.h
index f5b63c5..02fc308 100644
--- a/shell/application_manager/application_manager.h
+++ b/shell/application_manager/application_manager.h
@@ -116,6 +116,8 @@
                                 ServiceProviderPtr exposed_services,
                                 ApplicationLoader* loader);
 
+  ShellImpl* GetShellImpl(const GURL& url);
+
   void ConnectToClient(ShellImpl* shell_impl,
                        const GURL& url,
                        const GURL& requestor_url,
diff --git a/shell/application_manager/application_manager_unittest.cc b/shell/application_manager/application_manager_unittest.cc
index f3b0503..b8da53b 100644
--- a/shell/application_manager/application_manager_unittest.cc
+++ b/shell/application_manager/application_manager_unittest.cc
@@ -415,6 +415,34 @@
   std::map<GURL, GURL> mappings_;
 };
 
+class TestExternal : public ApplicationDelegate {
+ public:
+  TestExternal()
+      : initialize_called_(false),
+        configure_incoming_connection_called_(false) {}
+
+  virtual void Initialize(ApplicationImpl* app) override {
+    initialize_called_ = true;
+    base::MessageLoop::current()->Quit();
+  }
+
+  virtual bool ConfigureIncomingConnection(
+      ApplicationConnection* connection) override {
+    configure_incoming_connection_called_ = true;
+    base::MessageLoop::current()->Quit();
+    return true;
+  }
+
+  bool initialize_called() const { return initialize_called_; }
+  bool configure_incoming_connection_called() const {
+    return configure_incoming_connection_called_;
+  }
+
+ private:
+  bool initialize_called_;
+  bool configure_incoming_connection_called_;
+};
+
 }  // namespace
 
 class ApplicationManagerTest : public testing::Test {
@@ -696,4 +724,18 @@
   custom_loader->set_context(nullptr);
 }
 
+TEST_F(ApplicationManagerTest, ExternalApp) {
+  MessagePipe shell_pipe;
+  TestExternal external;
+  ApplicationImpl app(&external, shell_pipe.handle0.Pass());
+  application_manager_->RegisterExternalApplication(
+      GURL("mojo:test"), shell_pipe.handle1.Pass());
+  loop_.Run();
+  EXPECT_TRUE(external.initialize_called());
+  application_manager_->ConnectToServiceByName(
+      GURL("mojo:test"), std::string());
+  loop_.Run();
+  EXPECT_TRUE(external.configure_incoming_connection_called());
+};
+
 }  // namespace mojo