Move application_manager from /mojo to /shell.
application_manager is an implementation detail of the shell.
BUG=430984
R=jamesr@chromium.org
Review URL: https://codereview.chromium.org/814273005
diff --git a/shell/BUILD.gn b/shell/BUILD.gn
index d1bba63..63fcde4 100644
--- a/shell/BUILD.gn
+++ b/shell/BUILD.gn
@@ -35,6 +35,7 @@
deps = [
":external_application_unittests",
":mojo_shell_tests",
+ "//shell/application_manager:mojo_application_manager_unittests",
]
}
@@ -71,8 +72,8 @@
deps += [
":jni_headers",
- "//mojo/application_manager:application_manager",
"//services/native_viewport:lib",
+ "//shell/application_manager",
"//ui/gl",
]
}
@@ -178,13 +179,13 @@
"//base/third_party/dynamic_annotations",
"//base:base_static",
"//mojo/application",
- "//mojo/application_manager",
"//mojo/common",
"//mojo/common:tracing_impl",
"//mojo/edk/system",
"//mojo/public/cpp/bindings",
"//mojo/public/interfaces/application",
"//mojo/services/network/public/interfaces",
+ "//shell/application_manager",
"//shell/domain_socket",
"//services/tracing:bindings",
"//url",
@@ -424,12 +425,12 @@
"//testing/gtest",
"//net:test_support",
"//url",
- "//mojo/application_manager",
"//mojo/common",
"//mojo/edk/system",
"//mojo/environment:chromium",
"//mojo/public/cpp/bindings",
"//services/test_service:bindings",
+ "//shell/application_manager",
]
datadeps = [
@@ -458,8 +459,8 @@
":init",
":lib",
"//base",
- "//mojo/application_manager",
"//mojo/edk/system",
+ "//shell/application_manager",
]
}
@@ -479,10 +480,10 @@
"//testing/gtest",
"//url",
"//mojo/application",
- "//mojo/application_manager",
"//mojo/common",
"//mojo/edk/system",
"//mojo/environment:chromium",
+ "//shell/application_manager",
"//shell/domain_socket",
"//shell/domain_socket:tests",
]
diff --git a/shell/android/android_handler_loader.h b/shell/android/android_handler_loader.h
index 19e7fa6..9360574 100644
--- a/shell/android/android_handler_loader.h
+++ b/shell/android/android_handler_loader.h
@@ -8,9 +8,9 @@
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
-#include "mojo/application_manager/application_loader.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "shell/android/android_handler.h"
+#include "shell/application_manager/application_loader.h"
namespace mojo {
namespace shell {
diff --git a/shell/android/background_application_loader.cc b/shell/android/background_application_loader.cc
index 8e6d0f4..0f72c3c 100644
--- a/shell/android/background_application_loader.cc
+++ b/shell/android/background_application_loader.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "base/run_loop.h"
-#include "mojo/application_manager/application_manager.h"
+#include "shell/application_manager/application_manager.h"
namespace mojo {
diff --git a/shell/android/background_application_loader.h b/shell/android/background_application_loader.h
index 5321d97..4650ed6 100644
--- a/shell/android/background_application_loader.h
+++ b/shell/android/background_application_loader.h
@@ -10,7 +10,7 @@
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/simple_thread.h"
-#include "mojo/application_manager/application_loader.h"
+#include "shell/application_manager/application_loader.h"
namespace mojo {
diff --git a/shell/android/mojo_main.cc b/shell/android/mojo_main.cc
index 06581ae..502a340 100644
--- a/shell/android/mojo_main.cc
+++ b/shell/android/mojo_main.cc
@@ -20,12 +20,12 @@
#include "base/run_loop.h"
#include "base/threading/simple_thread.h"
#include "jni/MojoMain_jni.h"
-#include "mojo/application_manager/application_loader.h"
#include "mojo/common/message_pump_mojo.h"
#include "shell/android/android_handler_loader.h"
#include "shell/android/background_application_loader.h"
#include "shell/android/native_viewport_application_loader.h"
#include "shell/android/ui_application_loader_android.h"
+#include "shell/application_manager/application_loader.h"
#include "shell/command_line_util.h"
#include "shell/context.h"
#include "shell/init.h"
diff --git a/shell/android/native_viewport_application_loader.h b/shell/android/native_viewport_application_loader.h
index ecaca49..f5d53ea 100644
--- a/shell/android/native_viewport_application_loader.h
+++ b/shell/android/native_viewport_application_loader.h
@@ -5,12 +5,12 @@
#ifndef MOJO_SHELL_ANDROID_NATIVE_VIEWPORT_APPLICATION_LOADER_H_
#define MOJO_SHELL_ANDROID_NATIVE_VIEWPORT_APPLICATION_LOADER_H_
-#include "mojo/application_manager/application_loader.h"
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/interface_factory.h"
#include "mojo/services/gpu/public/interfaces/gpu.mojom.h"
#include "mojo/services/native_viewport/public/interfaces/native_viewport.mojom.h"
#include "services/gles2/gpu_impl.h"
+#include "shell/application_manager/application_loader.h"
namespace mojo {
diff --git a/shell/android/ui_application_loader_android.cc b/shell/android/ui_application_loader_android.cc
index ca4ff8b..dca237c 100644
--- a/shell/android/ui_application_loader_android.cc
+++ b/shell/android/ui_application_loader_android.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
-#include "mojo/application_manager/application_manager.h"
+#include "shell/application_manager/application_manager.h"
namespace mojo {
diff --git a/shell/android/ui_application_loader_android.h b/shell/android/ui_application_loader_android.h
index 7de34c7..26ac13b 100644
--- a/shell/android/ui_application_loader_android.h
+++ b/shell/android/ui_application_loader_android.h
@@ -7,7 +7,7 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
-#include "mojo/application_manager/application_loader.h"
+#include "shell/application_manager/application_loader.h"
namespace base {
class MessageLoop;
diff --git a/shell/application_manager/BUILD.gn b/shell/application_manager/BUILD.gn
new file mode 100644
index 0000000..c6828d7
--- /dev/null
+++ b/shell/application_manager/BUILD.gn
@@ -0,0 +1,62 @@
+# Copyright 2014 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+component("application_manager") {
+ output_name = "mojo_application_manager"
+ sources = [
+ "application_loader.cc",
+ "application_loader.h",
+ "application_manager.cc",
+ "application_manager.h",
+ "application_manager_export.h",
+ "shell_impl.cc",
+ "shell_impl.h",
+ ]
+
+ defines = [ "MOJO_APPLICATION_MANAGER_IMPLEMENTATION" ]
+
+ public_deps = [
+ "//base",
+ "//mojo/common",
+ "//mojo/public/interfaces/application:application",
+ "//mojo/services/network/public/interfaces",
+ "//url",
+ ]
+ deps = [
+ "//base/third_party/dynamic_annotations",
+ "//url",
+ "//mojo/edk/system",
+ "//mojo/environment:chromium",
+ "//mojo/public/cpp/application",
+ "//mojo/public/cpp/bindings",
+ "//mojo/services/content_handler/public/interfaces",
+ ]
+}
+
+test("mojo_application_manager_unittests") {
+ sources = [
+ "application_manager_unittest.cc",
+ ]
+
+ deps = [
+ ":application_manager",
+ ":test_bindings",
+ "//base",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/edk/test:run_all_unittests",
+ "//mojo/environment:chromium",
+ "//mojo/public/cpp/bindings",
+ "//testing/gtest",
+ "//url",
+ ]
+}
+
+mojom("test_bindings") {
+ sources = [
+ "test.mojom",
+ ]
+}
diff --git a/shell/application_manager/application_loader.cc b/shell/application_manager/application_loader.cc
new file mode 100644
index 0000000..ea653a1
--- /dev/null
+++ b/shell/application_manager/application_loader.cc
@@ -0,0 +1,26 @@
+// Copyright 2014 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 "shell/application_manager/application_loader.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+
+namespace mojo {
+
+namespace {
+
+void NotReached(const GURL& url,
+ ScopedMessagePipeHandle shell_handle,
+ URLResponsePtr response) {
+ NOTREACHED();
+}
+
+} // namespace
+
+ApplicationLoader::LoadCallback ApplicationLoader::SimpleLoadCallback() {
+ return base::Bind(&NotReached);
+}
+
+} // namespace mojo
diff --git a/shell/application_manager/application_loader.h b/shell/application_manager/application_loader.h
new file mode 100644
index 0000000..7dffe0a
--- /dev/null
+++ b/shell/application_manager/application_loader.h
@@ -0,0 +1,58 @@
+// Copyright 2014 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 SHELL_APPLICATION_MANAGER_APPLICATION_LOADER_H_
+#define SHELL_APPLICATION_MANAGER_APPLICATION_LOADER_H_
+
+#include "base/callback.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/network/public/interfaces/url_loader.mojom.h"
+#include "shell/application_manager/application_manager_export.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class ApplicationManager;
+
+// Interface to allowing loading behavior to be established for schemes,
+// specific urls or as the default.
+// A ApplicationLoader is responsible to using whatever mechanism is appropriate
+// to load the application at url.
+// The handle to the shell is passed to that application so it can bind it to
+// a Shell instance. This will give the Application a way to connect to other
+// apps and services.
+class MOJO_APPLICATION_MANAGER_EXPORT ApplicationLoader {
+ public:
+ typedef base::Callback<
+ void(const GURL&, ScopedMessagePipeHandle, URLResponsePtr)> LoadCallback;
+ virtual ~ApplicationLoader() {}
+
+ // Returns a callback that will should never be called.
+ static LoadCallback SimpleLoadCallback();
+
+ // Load the application named |url|. Applications can be loaded two ways:
+ //
+ // 1. |url| can refer directly to a Mojo application. In this case,
+ // shell_handle should be used to implement the mojo.Application interface.
+ //
+ // 2. |url| can refer to some content that can be handled by some other Mojo
+ // application. In this case, call callbacks and specify the URL of the
+ // application that should handle the content. The specified application
+ // must implement the mojo.ContentHandler interface.
+ virtual void Load(ApplicationManager* application_manager,
+ const GURL& url,
+ ScopedMessagePipeHandle shell_handle,
+ LoadCallback callback) = 0;
+
+ // Called when the Application exits.
+ virtual void OnApplicationError(ApplicationManager* manager,
+ const GURL& url) = 0;
+
+ protected:
+ ApplicationLoader() {}
+};
+
+} // namespace mojo
+
+#endif // SHELL_APPLICATION_MANAGER_APPLICATION_LOADER_H_
diff --git a/shell/application_manager/application_manager.cc b/shell/application_manager/application_manager.cc
new file mode 100644
index 0000000..2ead72e
--- /dev/null
+++ b/shell/application_manager/application_manager.cc
@@ -0,0 +1,275 @@
+// Copyright 2014 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 "shell/application_manager/application_manager.h"
+
+#include <stdio.h>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/content_handler/public/interfaces/content_handler.mojom.h"
+
+namespace mojo {
+
+namespace {
+// Used by TestAPI.
+bool has_created_instance = false;
+
+class StubServiceProvider : public InterfaceImpl<ServiceProvider> {
+ public:
+ ServiceProvider* GetRemoteServiceProvider() { return client(); }
+
+ private:
+ void ConnectToService(const String& service_name,
+ ScopedMessagePipeHandle client_handle) override {}
+};
+
+} // namespace
+
+
+ApplicationManager::Delegate::~Delegate() {
+}
+
+void ApplicationManager::Delegate::OnApplicationError(const GURL& url) {
+ LOG(ERROR) << "Communication error with application: " << url.spec();
+}
+
+GURL ApplicationManager::Delegate::ResolveURL(const GURL& url) {
+ return url;
+}
+
+class ApplicationManager::ContentHandlerConnection : public ErrorHandler {
+ public:
+ ContentHandlerConnection(ApplicationManager* manager,
+ const GURL& content_handler_url)
+ : manager_(manager), content_handler_url_(content_handler_url) {
+ ServiceProviderPtr services;
+ manager->ConnectToApplication(content_handler_url, GURL(),
+ GetProxy(&services), nullptr);
+ mojo::ConnectToService(services.get(), &content_handler_);
+ content_handler_.set_error_handler(this);
+ }
+
+ ContentHandler* content_handler() { return content_handler_.get(); }
+
+ GURL content_handler_url() { return content_handler_url_; }
+
+ private:
+ // ErrorHandler implementation:
+ void OnConnectionError() override { manager_->OnContentHandlerError(this); }
+
+ ApplicationManager* manager_;
+ GURL content_handler_url_;
+ ContentHandlerPtr content_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
+};
+
+// static
+ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
+ : manager_(manager) {
+}
+
+ApplicationManager::TestAPI::~TestAPI() {
+}
+
+bool ApplicationManager::TestAPI::HasCreatedInstance() {
+ return has_created_instance;
+}
+
+bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
+ return manager_->url_to_shell_impl_.find(url) !=
+ manager_->url_to_shell_impl_.end();
+}
+
+ApplicationManager::ApplicationManager(Delegate* delegate)
+ : delegate_(delegate),
+ weak_ptr_factory_(this) {
+}
+
+ApplicationManager::~ApplicationManager() {
+ STLDeleteValues(&url_to_content_handler_);
+ TerminateShellConnections();
+ STLDeleteValues(&url_to_loader_);
+ STLDeleteValues(&scheme_to_loader_);
+}
+
+void ApplicationManager::TerminateShellConnections() {
+ STLDeleteValues(&url_to_shell_impl_);
+}
+
+void ApplicationManager::ConnectToApplication(
+ const GURL& requested_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ DCHECK(requested_url.is_valid());
+ ApplicationLoader* loader = GetLoaderForURL(requested_url,
+ DONT_INCLUDE_DEFAULT_LOADER);
+ if (loader) {
+ ConnectToApplicationImpl(requested_url, requested_url, requestor_url,
+ services.Pass(), exposed_services.Pass(), loader);
+ return;
+ }
+
+ GURL resolved_url = delegate_->ResolveURL(requested_url);
+ loader = GetLoaderForURL(resolved_url, INCLUDE_DEFAULT_LOADER);
+ if (loader) {
+ ConnectToApplicationImpl(requested_url, resolved_url, requestor_url,
+ services.Pass(), exposed_services.Pass(), loader);
+ return;
+ }
+
+ LOG(WARNING) << "Could not find loader to load application: "
+ << requested_url.spec();
+}
+
+void ApplicationManager::ConnectToApplicationImpl(
+ const GURL& requested_url,
+ const GURL& resolved_url,
+ const GURL& requestor_url,
+ 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 {
+ MessagePipe pipe;
+ shell =
+ new ShellImpl(pipe.handle0.Pass(), this, requested_url, resolved_url);
+ url_to_shell_impl_[resolved_url] = shell;
+ shell->client()->Initialize(GetArgsForURL(requested_url));
+
+ loader->Load(this, resolved_url, pipe.handle1.Pass(),
+ base::Bind(&ApplicationManager::LoadWithContentHandler,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ ConnectToClient(shell, resolved_url, requestor_url, services.Pass(),
+ exposed_services.Pass());
+}
+
+void ApplicationManager::ConnectToClient(
+ ShellImpl* shell_impl,
+ const GURL& url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ shell_impl->ConnectToClient(requestor_url, services.Pass(),
+ exposed_services.Pass());
+}
+
+void ApplicationManager::RegisterExternalApplication(
+ const GURL& url,
+ ScopedMessagePipeHandle shell_handle) {
+ ShellImpl* shell_impl = new ShellImpl(shell_handle.Pass(), this, url, url);
+ url_to_shell_impl_[url] = shell_impl;
+ shell_impl->client()->Initialize(GetArgsForURL(url));
+}
+
+void ApplicationManager::LoadWithContentHandler(
+ const GURL& content_handler_url,
+ ScopedMessagePipeHandle shell_handle,
+ URLResponsePtr url_response) {
+ ContentHandlerConnection* connection = NULL;
+ URLToContentHandlerMap::iterator iter =
+ url_to_content_handler_.find(content_handler_url);
+ if (iter != url_to_content_handler_.end()) {
+ connection = iter->second;
+ } else {
+ connection = new ContentHandlerConnection(this, content_handler_url);
+ url_to_content_handler_[content_handler_url] = connection;
+ }
+
+ connection->content_handler()->StartApplication(
+ MakeProxy<Shell>(shell_handle.Pass()), url_response.Pass());
+}
+
+void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
+ const GURL& url) {
+ URLToLoaderMap::iterator it = url_to_loader_.find(url);
+ if (it != url_to_loader_.end())
+ delete it->second;
+ url_to_loader_[url] = loader.release();
+}
+
+void ApplicationManager::SetLoaderForScheme(
+ scoped_ptr<ApplicationLoader> loader,
+ const std::string& scheme) {
+ SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
+ if (it != scheme_to_loader_.end())
+ delete it->second;
+ scheme_to_loader_[scheme] = loader.release();
+}
+
+void ApplicationManager::SetArgsForURL(const std::vector<std::string>& args,
+ const GURL& url) {
+ url_to_args_[url] = args;
+}
+
+ApplicationLoader* ApplicationManager::GetLoaderForURL(
+ const GURL& url, IncludeDefaultLoader include_default_loader) {
+ auto url_it = url_to_loader_.find(url);
+ if (url_it != url_to_loader_.end())
+ return url_it->second;
+ auto scheme_it = scheme_to_loader_.find(url.scheme());
+ if (scheme_it != scheme_to_loader_.end())
+ return scheme_it->second;
+ if (include_default_loader == INCLUDE_DEFAULT_LOADER)
+ return default_loader_.get();
+ return NULL;
+}
+
+void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
+ // Called from ~ShellImpl, so we do not need to call Destroy here.
+ const GURL url = shell_impl->url();
+ const GURL requested_url = shell_impl->requested_url();
+ // Remove the shell.
+ URLToShellImplMap::iterator it = url_to_shell_impl_.find(url);
+ DCHECK(it != url_to_shell_impl_.end());
+ delete it->second;
+ url_to_shell_impl_.erase(it);
+ ApplicationLoader* loader = GetLoaderForURL(requested_url,
+ INCLUDE_DEFAULT_LOADER);
+ if (loader)
+ loader->OnApplicationError(this, url);
+ delegate_->OnApplicationError(requested_url);
+}
+
+void ApplicationManager::OnContentHandlerError(
+ ContentHandlerConnection* content_handler) {
+ // Remove the mapping to the content handler.
+ auto it =
+ url_to_content_handler_.find(content_handler->content_handler_url());
+ DCHECK(it != url_to_content_handler_.end());
+ delete it->second;
+ url_to_content_handler_.erase(it);
+}
+
+ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
+ const GURL& application_url,
+ const std::string& interface_name) {
+ ServiceProviderPtr services;
+ ConnectToApplication(application_url, GURL(), GetProxy(&services), nullptr);
+ MessagePipe pipe;
+ services->ConnectToService(interface_name, pipe.handle1.Pass());
+ return pipe.handle0.Pass();
+}
+
+Array<String> ApplicationManager::GetArgsForURL(const GURL& url) {
+ URLToArgsMap::const_iterator args_it = url_to_args_.find(url);
+ if (args_it != url_to_args_.end())
+ return Array<String>::From(args_it->second);
+ return Array<String>();
+}
+} // namespace mojo
diff --git a/shell/application_manager/application_manager.h b/shell/application_manager/application_manager.h
new file mode 100644
index 0000000..c420e51
--- /dev/null
+++ b/shell/application_manager/application_manager.h
@@ -0,0 +1,156 @@
+// Copyright 2014 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 SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
+#define SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "shell/application_manager/application_loader.h"
+#include "shell/application_manager/application_manager_export.h"
+#include "shell/application_manager/shell_impl.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class MOJO_APPLICATION_MANAGER_EXPORT ApplicationManager {
+ public:
+ class MOJO_APPLICATION_MANAGER_EXPORT Delegate {
+ public:
+ virtual ~Delegate();
+ // Send when the Application holding the handle on the other end of the
+ // Shell pipe goes away.
+ virtual void OnApplicationError(const GURL& url);
+ virtual GURL ResolveURL(const GURL& url);
+ };
+
+ // API for testing.
+ class MOJO_APPLICATION_MANAGER_EXPORT TestAPI {
+ public:
+ explicit TestAPI(ApplicationManager* manager);
+ ~TestAPI();
+
+ // Returns true if the shared instance has been created.
+ static bool HasCreatedInstance();
+ // Returns true if there is a ShellImpl for this URL.
+ bool HasFactoryForURL(const GURL& url) const;
+
+ private:
+ ApplicationManager* manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAPI);
+ };
+
+ explicit ApplicationManager(Delegate* delegate);
+ ~ApplicationManager();
+
+ // Loads a service if necessary and establishes a new client connection.
+ void ConnectToApplication(const GURL& application_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services);
+
+ template <typename Interface>
+ inline void ConnectToService(const GURL& application_url,
+ InterfacePtr<Interface>* ptr) {
+ ScopedMessagePipeHandle service_handle =
+ ConnectToServiceByName(application_url, Interface::Name_);
+ ptr->Bind(service_handle.Pass());
+ }
+
+ ScopedMessagePipeHandle ConnectToServiceByName(
+ const GURL& application_url,
+ const std::string& interface_name);
+
+ void RegisterExternalApplication(const GURL& application_url,
+ ScopedMessagePipeHandle shell);
+
+ // Sets the default Loader to be used if not overridden by SetLoaderForURL()
+ // or SetLoaderForScheme().
+ void set_default_loader(scoped_ptr<ApplicationLoader> loader) {
+ default_loader_ = loader.Pass();
+ }
+ // Sets a Loader to be used for a specific url.
+ void SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, const GURL& url);
+ // Sets a Loader to be used for a specific url scheme.
+ void SetLoaderForScheme(scoped_ptr<ApplicationLoader> loader,
+ const std::string& scheme);
+ // These strings will be passed to the Initialize() method when an
+ // Application is instantiated.
+ void SetArgsForURL(const std::vector<std::string>& args, const GURL& url);
+
+ // Destroys all Shell-ends of connections established with Applications.
+ // Applications connected by this ApplicationManager will observe pipe errors
+ // and have a chance to shutdown.
+ void TerminateShellConnections();
+
+ // Removes a ShellImpl when it encounters an error.
+ void OnShellImplError(ShellImpl* shell_impl);
+
+ private:
+ enum IncludeDefaultLoader {
+ INCLUDE_DEFAULT_LOADER,
+ DONT_INCLUDE_DEFAULT_LOADER,
+ };
+
+ class ContentHandlerConnection;
+
+ typedef std::map<std::string, ApplicationLoader*> SchemeToLoaderMap;
+ typedef std::map<GURL, ApplicationLoader*> URLToLoaderMap;
+ typedef std::map<GURL, ShellImpl*> URLToShellImplMap;
+ typedef std::map<GURL, ContentHandlerConnection*> URLToContentHandlerMap;
+ typedef std::map<GURL, std::vector<std::string> > URLToArgsMap;
+
+ void ConnectToApplicationImpl(const GURL& requested_url,
+ const GURL& resolved_url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services,
+ ApplicationLoader* loader);
+
+ void ConnectToClient(ShellImpl* shell_impl,
+ const GURL& url,
+ const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services);
+
+ void LoadWithContentHandler(const GURL& content_handler_url,
+ ScopedMessagePipeHandle shell_handle,
+ URLResponsePtr url_response);
+
+ // Return the appropriate loader for |url|. This can return NULL if there is
+ // no default loader configured.
+ ApplicationLoader* GetLoaderForURL(const GURL& url,
+ IncludeDefaultLoader fallback);
+
+ // Removes a ContentHandler when it encounters an error.
+ void OnContentHandlerError(ContentHandlerConnection* content_handler);
+
+ // Returns the arguments for the given url.
+ Array<String> GetArgsForURL(const GURL& url);
+
+ Delegate* delegate_;
+ // Loader management.
+ URLToLoaderMap url_to_loader_;
+ SchemeToLoaderMap scheme_to_loader_;
+ scoped_ptr<ApplicationLoader> default_loader_;
+
+ URLToShellImplMap url_to_shell_impl_;
+ URLToContentHandlerMap url_to_content_handler_;
+ URLToArgsMap url_to_args_;
+
+ base::WeakPtrFactory<ApplicationManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ApplicationManager);
+};
+
+} // namespace mojo
+
+#endif // SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_H_
diff --git a/shell/application_manager/application_manager_export.h b/shell/application_manager/application_manager_export.h
new file mode 100644
index 0000000..7b3f357
--- /dev/null
+++ b/shell/application_manager/application_manager_export.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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 SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_
+#define SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_APPLICATION_MANAGER_IMPLEMENTATION)
+#define MOJO_APPLICATION_MANAGER_EXPORT __declspec(dllexport)
+#else
+#define MOJO_APPLICATION_MANAGER_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_APPLICATION_MANAGER_IMPLEMENTATION)
+#define MOJO_APPLICATION_MANAGER_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_APPLICATION_MANAGER_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_APPLICATION_MANAGER_EXPORT
+#endif
+
+#endif // SHELL_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_
diff --git a/shell/application_manager/application_manager_unittest.cc b/shell/application_manager/application_manager_unittest.cc
new file mode 100644
index 0000000..f1a09cd
--- /dev/null
+++ b/shell/application_manager/application_manager_unittest.cc
@@ -0,0 +1,673 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "shell/application_manager/application_loader.h"
+#include "shell/application_manager/application_manager.h"
+#include "shell/application_manager/test.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kTestURLString[] = "test:testService";
+const char kTestAURLString[] = "test:TestA";
+const char kTestBURLString[] = "test:TestB";
+
+struct TestContext {
+ TestContext() : num_impls(0), num_loader_deletes(0) {}
+ std::string last_test_string;
+ int num_impls;
+ int num_loader_deletes;
+};
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+ QuitMessageLoopErrorHandler() {}
+ ~QuitMessageLoopErrorHandler() override {}
+
+ // |ErrorHandler| implementation:
+ void OnConnectionError() override {
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+class TestServiceImpl : public InterfaceImpl<TestService> {
+ public:
+ explicit TestServiceImpl(TestContext* context) : context_(context) {
+ ++context_->num_impls;
+ }
+
+ ~TestServiceImpl() override { --context_->num_impls; }
+
+ void OnConnectionError() override {
+ if (!base::MessageLoop::current()->is_running())
+ return;
+ base::MessageLoop::current()->Quit();
+ InterfaceImpl::OnConnectionError();
+ }
+
+ // TestService implementation:
+ void Test(const String& test_string) override {
+ context_->last_test_string = test_string;
+ client()->AckTest();
+ }
+
+ private:
+ TestContext* context_;
+};
+
+class TestClientImpl : public TestClient {
+ public:
+ explicit TestClientImpl(TestServicePtr service)
+ : service_(service.Pass()), quit_after_ack_(false) {
+ service_.set_client(this);
+ }
+
+ ~TestClientImpl() override { service_.reset(); }
+
+ void AckTest() override {
+ if (quit_after_ack_)
+ base::MessageLoop::current()->Quit();
+ }
+
+ void Test(const std::string& test_string) {
+ quit_after_ack_ = true;
+ service_->Test(test_string);
+ }
+
+ private:
+ TestServicePtr service_;
+ bool quit_after_ack_;
+ DISALLOW_COPY_AND_ASSIGN(TestClientImpl);
+};
+
+class TestApplicationLoader : public ApplicationLoader,
+ public ApplicationDelegate,
+ public InterfaceFactory<TestService> {
+ public:
+ TestApplicationLoader() : context_(NULL), num_loads_(0) {}
+
+ ~TestApplicationLoader() override {
+ if (context_)
+ ++context_->num_loader_deletes;
+ test_app_.reset(NULL);
+ }
+
+ void set_context(TestContext* context) { context_ = context; }
+ int num_loads() const { return num_loads_; }
+ const std::vector<std::string>& GetArgs() const { return test_app_->args(); }
+
+ private:
+ // ApplicationLoader implementation.
+ void Load(ApplicationManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle shell_handle,
+ LoadCallback callback) override {
+ ++num_loads_;
+ test_app_.reset(new ApplicationImpl(this, shell_handle.Pass()));
+ }
+
+ void OnApplicationError(ApplicationManager* manager,
+ const GURL& url) override {}
+
+ // ApplicationDelegate implementation.
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ connection->AddService(this);
+ return true;
+ }
+
+ // InterfaceFactory implementation.
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestService> request) override {
+ BindToRequest(new TestServiceImpl(context_), &request);
+ }
+
+ scoped_ptr<ApplicationImpl> test_app_;
+ TestContext* context_;
+ int num_loads_;
+ DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+class TesterContext {
+ public:
+ explicit TesterContext(base::MessageLoop* loop)
+ : num_b_calls_(0),
+ num_c_calls_(0),
+ num_a_deletes_(0),
+ num_b_deletes_(0),
+ num_c_deletes_(0),
+ tester_called_quit_(false),
+ a_called_quit_(false),
+ loop_(loop) {}
+
+ void IncrementNumBCalls() {
+ base::AutoLock lock(lock_);
+ num_b_calls_++;
+ }
+
+ void IncrementNumCCalls() {
+ base::AutoLock lock(lock_);
+ num_c_calls_++;
+ }
+
+ void IncrementNumADeletes() {
+ base::AutoLock lock(lock_);
+ num_a_deletes_++;
+ }
+
+ void IncrementNumBDeletes() {
+ base::AutoLock lock(lock_);
+ num_b_deletes_++;
+ }
+
+ void IncrementNumCDeletes() {
+ base::AutoLock lock(lock_);
+ num_c_deletes_++;
+ }
+
+ void set_tester_called_quit() {
+ base::AutoLock lock(lock_);
+ tester_called_quit_ = true;
+ }
+
+ void set_a_called_quit() {
+ base::AutoLock lock(lock_);
+ a_called_quit_ = true;
+ }
+
+ int num_b_calls() {
+ base::AutoLock lock(lock_);
+ return num_b_calls_;
+ }
+ int num_c_calls() {
+ base::AutoLock lock(lock_);
+ return num_c_calls_;
+ }
+ int num_a_deletes() {
+ base::AutoLock lock(lock_);
+ return num_a_deletes_;
+ }
+ int num_b_deletes() {
+ base::AutoLock lock(lock_);
+ return num_b_deletes_;
+ }
+ int num_c_deletes() {
+ base::AutoLock lock(lock_);
+ return num_c_deletes_;
+ }
+ bool tester_called_quit() {
+ base::AutoLock lock(lock_);
+ return tester_called_quit_;
+ }
+ bool a_called_quit() {
+ base::AutoLock lock(lock_);
+ return a_called_quit_;
+ }
+
+ void QuitSoon() {
+ loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+
+ private:
+ // lock_ protects all members except for loop_ which must be unchanged for the
+ // lifetime of this class.
+ base::Lock lock_;
+ int num_b_calls_;
+ int num_c_calls_;
+ int num_a_deletes_;
+ int num_b_deletes_;
+ int num_c_deletes_;
+ bool tester_called_quit_;
+ bool a_called_quit_;
+
+ base::MessageLoop* loop_;
+};
+
+// Used to test that the requestor url will be correctly passed.
+class TestAImpl : public TestA {
+ public:
+ TestAImpl(ApplicationConnection* connection,
+ TesterContext* test_context,
+ InterfaceRequest<TestA> request)
+ : test_context_(test_context),
+ binding_(this, request.Pass()) {
+ connection->ConnectToApplication(kTestBURLString)->ConnectToService(&b_);
+ }
+ ~TestAImpl() override {
+ test_context_->IncrementNumADeletes();
+ if (base::MessageLoop::current()->is_running())
+ Quit();
+ }
+
+ private:
+ void CallB() override {
+ b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+ }
+
+ void CallCFromB() override {
+ b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+ }
+
+ void Quit() {
+ base::MessageLoop::current()->Quit();
+ test_context_->set_a_called_quit();
+ test_context_->QuitSoon();
+ }
+
+ TesterContext* test_context_;
+ TestBPtr b_;
+ Binding<TestA> binding_;
+};
+
+class TestBImpl : public InterfaceImpl<TestB> {
+ public:
+ TestBImpl(ApplicationConnection* connection, TesterContext* test_context)
+ : test_context_(test_context) {
+ connection->ConnectToService(&c_);
+ }
+
+ ~TestBImpl() override {
+ test_context_->IncrementNumBDeletes();
+ if (base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->Quit();
+ test_context_->QuitSoon();
+ }
+
+ private:
+ void B(const mojo::Callback<void()>& callback) override {
+ test_context_->IncrementNumBCalls();
+ callback.Run();
+ }
+
+ void CallC(const mojo::Callback<void()>& callback) override {
+ test_context_->IncrementNumBCalls();
+ c_->C(callback);
+ }
+
+ TesterContext* test_context_;
+ TestCPtr c_;
+};
+
+class TestCImpl : public InterfaceImpl<TestC> {
+ public:
+ TestCImpl(ApplicationConnection* connection, TesterContext* test_context)
+ : test_context_(test_context) {}
+
+ ~TestCImpl() override { test_context_->IncrementNumCDeletes(); }
+
+ private:
+ void C(const mojo::Callback<void()>& callback) override {
+ test_context_->IncrementNumCCalls();
+ callback.Run();
+ }
+ TesterContext* test_context_;
+};
+
+class Tester : public ApplicationDelegate,
+ public ApplicationLoader,
+ public InterfaceFactory<TestA>,
+ public InterfaceFactory<TestB>,
+ public InterfaceFactory<TestC> {
+ public:
+ Tester(TesterContext* context, const std::string& requestor_url)
+ : context_(context), requestor_url_(requestor_url) {}
+ ~Tester() override {}
+
+ private:
+ void Load(ApplicationManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle shell_handle,
+ LoadCallback callback) override {
+ app_.reset(new ApplicationImpl(this, shell_handle.Pass()));
+ }
+
+ void OnApplicationError(ApplicationManager* manager,
+ const GURL& url) override {}
+
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ if (!requestor_url_.empty() &&
+ requestor_url_ != connection->GetRemoteApplicationURL()) {
+ context_->set_tester_called_quit();
+ context_->QuitSoon();
+ base::MessageLoop::current()->Quit();
+ return false;
+ }
+ // If we're coming from A, then add B, otherwise A.
+ if (connection->GetRemoteApplicationURL() == kTestAURLString)
+ connection->AddService<TestB>(this);
+ else
+ connection->AddService<TestA>(this);
+ return true;
+ }
+
+ bool ConfigureOutgoingConnection(ApplicationConnection* connection) override {
+ // If we're connecting to B, then add C.
+ if (connection->GetRemoteApplicationURL() == kTestBURLString)
+ connection->AddService<TestC>(this);
+ return true;
+ }
+
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestA> request) override {
+ a_bindings_.push_back(new TestAImpl(connection, context_, request.Pass()));
+ }
+
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestB> request) override {
+ BindToRequest(new TestBImpl(connection, context_), &request);
+ }
+
+ void Create(ApplicationConnection* connection,
+ InterfaceRequest<TestC> request) override {
+ BindToRequest(new TestCImpl(connection, context_), &request);
+ }
+
+ TesterContext* context_;
+ scoped_ptr<ApplicationImpl> app_;
+ std::string requestor_url_;
+ ScopedVector<TestAImpl> a_bindings_;
+};
+
+class TestDelegate : public ApplicationManager::Delegate {
+ public:
+ void AddMapping(const GURL& from, const GURL& to) {
+ mappings_[from] = to;
+ }
+
+ // ApplicationManager::Delegate
+ virtual GURL ResolveURL(const GURL& url) override {
+ auto it = mappings_.find(url);
+ if (it != mappings_.end())
+ return it->second;
+ return url;
+ }
+
+ virtual void OnApplicationError(const GURL& url) override {
+ }
+
+ private:
+ std::map<GURL, GURL> mappings_;
+};
+
+} // namespace
+
+class ApplicationManagerTest : public testing::Test {
+ public:
+ ApplicationManagerTest() : tester_context_(&loop_) {}
+
+ ~ApplicationManagerTest() override {}
+
+ void SetUp() override {
+ application_manager_.reset(new ApplicationManager(&test_delegate_));
+ test_loader_ = new TestApplicationLoader;
+ test_loader_->set_context(&context_);
+ application_manager_->set_default_loader(
+ scoped_ptr<ApplicationLoader>(test_loader_));
+
+ TestServicePtr service_proxy;
+ application_manager_->ConnectToService(GURL(kTestURLString),
+ &service_proxy);
+ test_client_.reset(new TestClientImpl(service_proxy.Pass()));
+ }
+
+ void TearDown() override {
+ test_client_.reset(NULL);
+ application_manager_.reset(NULL);
+ }
+
+ void AddLoaderForURL(const GURL& url, const std::string& requestor_url) {
+ application_manager_->SetLoaderForURL(
+ make_scoped_ptr(new Tester(&tester_context_, requestor_url)), url);
+ }
+
+ bool HasFactoryForTestURL() {
+ ApplicationManager::TestAPI manager_test_api(application_manager_.get());
+ return manager_test_api.HasFactoryForURL(GURL(kTestURLString));
+ }
+
+ protected:
+ base::ShadowingAtExitManager at_exit_;
+ TestDelegate test_delegate_;
+ TestApplicationLoader* test_loader_;
+ TesterContext tester_context_;
+ TestContext context_;
+ base::MessageLoop loop_;
+ scoped_ptr<TestClientImpl> test_client_;
+ scoped_ptr<ApplicationManager> application_manager_;
+ DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest);
+};
+
+TEST_F(ApplicationManagerTest, Basic) {
+ test_client_->Test("test");
+ loop_.Run();
+ EXPECT_EQ(std::string("test"), context_.last_test_string);
+}
+
+// Confirm that no arguments are sent to an application by default.
+TEST_F(ApplicationManagerTest, NoArgs) {
+ ApplicationManager am(&test_delegate_);
+ GURL test_url("test:test");
+ TestApplicationLoader* loader = new TestApplicationLoader;
+ loader->set_context(&context_);
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url);
+ TestServicePtr test_service;
+ am.ConnectToService(test_url, &test_service);
+ TestClientImpl test_client(test_service.Pass());
+ test_client.Test("test");
+ loop_.Run();
+ std::vector<std::string> app_args = loader->GetArgs();
+ EXPECT_EQ(0U, app_args.size());
+}
+
+// Confirm that arguments are sent to an application.
+TEST_F(ApplicationManagerTest, Args) {
+ ApplicationManager am(&test_delegate_);
+ GURL test_url("test:test");
+ std::vector<std::string> args;
+ args.push_back("test_arg1");
+ args.push_back("test_arg2");
+ am.SetArgsForURL(args, test_url);
+ TestApplicationLoader* loader = new TestApplicationLoader;
+ loader->set_context(&context_);
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url);
+ TestServicePtr test_service;
+ am.ConnectToService(test_url, &test_service);
+ TestClientImpl test_client(test_service.Pass());
+ test_client.Test("test");
+ loop_.Run();
+ std::vector<std::string> app_args = loader->GetArgs();
+ ASSERT_EQ(args.size(), app_args.size());
+ EXPECT_EQ(args[0], app_args[0]);
+ EXPECT_EQ(args[1], app_args[1]);
+}
+
+TEST_F(ApplicationManagerTest, ClientError) {
+ test_client_->Test("test");
+ EXPECT_TRUE(HasFactoryForTestURL());
+ loop_.Run();
+ EXPECT_EQ(1, context_.num_impls);
+ test_client_.reset(NULL);
+ loop_.Run();
+ EXPECT_EQ(0, context_.num_impls);
+ EXPECT_TRUE(HasFactoryForTestURL());
+}
+
+TEST_F(ApplicationManagerTest, Deletes) {
+ {
+ ApplicationManager am(&test_delegate_);
+ TestApplicationLoader* default_loader = new TestApplicationLoader;
+ default_loader->set_context(&context_);
+ TestApplicationLoader* url_loader1 = new TestApplicationLoader;
+ TestApplicationLoader* url_loader2 = new TestApplicationLoader;
+ url_loader1->set_context(&context_);
+ url_loader2->set_context(&context_);
+ TestApplicationLoader* scheme_loader1 = new TestApplicationLoader;
+ TestApplicationLoader* scheme_loader2 = new TestApplicationLoader;
+ scheme_loader1->set_context(&context_);
+ scheme_loader2->set_context(&context_);
+ am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader));
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1),
+ GURL("test:test1"));
+ am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2),
+ GURL("test:test1"));
+ am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader1),
+ "test");
+ am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader2),
+ "test");
+ }
+ EXPECT_EQ(5, context_.num_loader_deletes);
+}
+
+// Confirm that both urls and schemes can have their loaders explicitly set.
+TEST_F(ApplicationManagerTest, SetLoaders) {
+ TestApplicationLoader* default_loader = new TestApplicationLoader;
+ TestApplicationLoader* url_loader = new TestApplicationLoader;
+ TestApplicationLoader* scheme_loader = new TestApplicationLoader;
+ application_manager_->set_default_loader(
+ scoped_ptr<ApplicationLoader>(default_loader));
+ application_manager_->SetLoaderForURL(
+ scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1"));
+ application_manager_->SetLoaderForScheme(
+ scoped_ptr<ApplicationLoader>(scheme_loader), "test");
+
+ // test::test1 should go to url_loader.
+ TestServicePtr test_service;
+ application_manager_->ConnectToService(GURL("test:test1"), &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(0, scheme_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // test::test2 should go to scheme loader.
+ application_manager_->ConnectToService(GURL("test:test2"), &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // http::test1 should go to default loader.
+ application_manager_->ConnectToService(GURL("http:test1"), &test_service);
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+ EXPECT_EQ(1, default_loader->num_loads());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads.
+TEST_F(ApplicationManagerTest, ACallB) {
+ // Any url can load a.
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // Only a can load b.
+ AddLoaderForURL(GURL(kTestBURLString), kTestAURLString);
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+ a->CallB();
+ loop_.Run();
+ EXPECT_EQ(1, tester_context_.num_b_calls());
+ EXPECT_TRUE(tester_context_.a_called_quit());
+}
+
+// A calls B which calls C.
+TEST_F(ApplicationManagerTest, BCallC) {
+ // Any url can load a.
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // Only a can load b.
+ AddLoaderForURL(GURL(kTestBURLString), kTestAURLString);
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+ a->CallCFromB();
+ loop_.Run();
+
+ EXPECT_EQ(1, tester_context_.num_b_calls());
+ EXPECT_EQ(1, tester_context_.num_c_calls());
+ EXPECT_TRUE(tester_context_.a_called_quit());
+}
+
+// Confirm that a service impl will be deleted if the app that connected to
+// it goes away.
+TEST_F(ApplicationManagerTest, BDeleted) {
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+ AddLoaderForURL(GURL(kTestBURLString), std::string());
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+
+ a->CallB();
+ loop_.Run();
+
+ // Kills the a app.
+ application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(),
+ GURL(kTestAURLString));
+ loop_.Run();
+
+ EXPECT_EQ(1, tester_context_.num_b_deletes());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads, and that it can be rejected.
+TEST_F(ApplicationManagerTest, ANoLoadB) {
+ // Any url can load a.
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // Only c can load b, so this will fail.
+ AddLoaderForURL(GURL(kTestBURLString), "test:TestC");
+
+ TestAPtr a;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &a);
+ a->CallB();
+ loop_.Run();
+ EXPECT_EQ(0, tester_context_.num_b_calls());
+
+ EXPECT_FALSE(tester_context_.a_called_quit());
+ EXPECT_TRUE(tester_context_.tester_called_quit());
+}
+
+TEST_F(ApplicationManagerTest, NoServiceNoLoad) {
+ AddLoaderForURL(GURL(kTestAURLString), std::string());
+
+ // There is no TestC service implementation registered with
+ // ApplicationManager, so this cannot succeed (but also shouldn't crash).
+ TestCPtr c;
+ application_manager_->ConnectToService(GURL(kTestAURLString), &c);
+ QuitMessageLoopErrorHandler quitter;
+ c.set_error_handler(&quitter);
+
+ loop_.Run();
+ EXPECT_TRUE(c.encountered_error());
+}
+
+TEST_F(ApplicationManagerTest, MappedURLsShouldNotCauseDuplicateLoad) {
+ test_delegate_.AddMapping(GURL("foo:foo2"), GURL("foo:foo"));
+ // 1 because ApplicationManagerTest connects once at startup.
+ EXPECT_EQ(1, test_loader_->num_loads());
+
+ TestServicePtr test_service;
+ application_manager_->ConnectToService(GURL("foo:foo"), &test_service);
+ EXPECT_EQ(2, test_loader_->num_loads());
+
+ TestServicePtr test_service2;
+ application_manager_->ConnectToService(GURL("foo:foo2"), &test_service2);
+ EXPECT_EQ(2, test_loader_->num_loads());
+
+ TestServicePtr test_service3;
+ application_manager_->ConnectToService(GURL("bar:bar"), &test_service2);
+ EXPECT_EQ(3, test_loader_->num_loads());
+}
+
+} // namespace mojo
diff --git a/shell/application_manager/shell_impl.cc b/shell/application_manager/shell_impl.cc
new file mode 100644
index 0000000..0d2b59d
--- /dev/null
+++ b/shell/application_manager/shell_impl.cc
@@ -0,0 +1,66 @@
+// Copyright 2015 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 "shell/application_manager/shell_impl.h"
+
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/content_handler/public/interfaces/content_handler.mojom.h"
+#include "shell/application_manager/application_manager.h"
+
+namespace mojo {
+
+ShellImpl::ShellImpl(ScopedMessagePipeHandle handle,
+ ApplicationManager* manager,
+ const GURL& requested_url,
+ const GURL& url)
+ : ShellImpl(manager, requested_url, url) {
+ binding_.Bind(handle.Pass());
+}
+
+ShellImpl::ShellImpl(ShellPtr* ptr,
+ ApplicationManager* manager,
+ const GURL& requested_url,
+ const GURL& url)
+ : ShellImpl(manager, requested_url, url) {
+ binding_.Bind(ptr);
+}
+
+ShellImpl::~ShellImpl() {
+}
+
+void ShellImpl::ConnectToClient(const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ client()->AcceptConnection(String::From(requestor_url), services.Pass(),
+ exposed_services.Pass());
+}
+
+ShellImpl::ShellImpl(ApplicationManager* manager,
+ const GURL& requested_url,
+ const GURL& url)
+ : manager_(manager),
+ requested_url_(requested_url),
+ url_(url),
+ binding_(this) {
+ binding_.set_error_handler(this);
+}
+
+// Shell implementation:
+void ShellImpl::ConnectToApplication(const String& app_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) {
+ GURL app_gurl(app_url);
+ if (!app_gurl.is_valid()) {
+ LOG(ERROR) << "Error: invalid URL: " << app_url;
+ return;
+ }
+ manager_->ConnectToApplication(app_gurl, url_, services.Pass(),
+ exposed_services.Pass());
+}
+
+void ShellImpl::OnConnectionError() {
+ manager_->OnShellImplError(this);
+}
+
+} // namespace mojo
diff --git a/shell/application_manager/shell_impl.h b/shell/application_manager/shell_impl.h
new file mode 100644
index 0000000..ef4de83
--- /dev/null
+++ b/shell/application_manager/shell_impl.h
@@ -0,0 +1,62 @@
+// Copyright 2015 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 SHELL_APPLICATION_MANAGER_SHELL_IMPL_H_
+#define SHELL_APPLICATION_MANAGER_SHELL_IMPL_H_
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/application/application.mojom.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+class ApplicationManager;
+
+class ShellImpl : public Shell, public ErrorHandler {
+ public:
+ ShellImpl(ScopedMessagePipeHandle handle,
+ ApplicationManager* manager,
+ const GURL& requested_url,
+ const GURL& url);
+
+ ShellImpl(ShellPtr* ptr,
+ ApplicationManager* manager,
+ const GURL& requested_url,
+ const GURL& url);
+
+ ~ShellImpl() override;
+
+ void ConnectToClient(const GURL& requestor_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services);
+
+ Application* client() { return binding_.client(); }
+ const GURL& url() const { return url_; }
+ const GURL& requested_url() const { return requested_url_; }
+
+ private:
+ ShellImpl(ApplicationManager* manager,
+ const GURL& requested_url,
+ const GURL& url);
+
+ // Shell implementation:
+ void ConnectToApplication(const String& app_url,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) override;
+
+ // ErrorHandler implementation:
+ void OnConnectionError() override;
+
+ ApplicationManager* const manager_;
+ const GURL requested_url_;
+ const GURL url_;
+ Binding<Shell> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellImpl);
+};
+
+} // namespace mojo
+
+#endif // SHELL_APPLICATION_MANAGER_SHELL_IMPL_H_
diff --git a/shell/application_manager/test.mojom b/shell/application_manager/test.mojom
new file mode 100644
index 0000000..2912eec
--- /dev/null
+++ b/shell/application_manager/test.mojom
@@ -0,0 +1,28 @@
+// Copyright 2014 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.
+
+module mojo;
+
+[Client=TestClient]
+interface TestService {
+ Test(string test_string);
+};
+
+interface TestClient {
+ AckTest();
+};
+
+interface TestA {
+ CallB();
+ CallCFromB();
+};
+
+interface TestB {
+ B() => ();
+ CallC() => ();
+};
+
+interface TestC {
+ C() => ();
+};
diff --git a/shell/context.cc b/shell/context.cc
index 76a9076..d3a8613 100644
--- a/shell/context.cc
+++ b/shell/context.cc
@@ -16,8 +16,6 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
-#include "mojo/application_manager/application_loader.h"
-#include "mojo/application_manager/application_manager.h"
#include "mojo/common/tracing_impl.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/simple_platform_support.h"
@@ -25,6 +23,8 @@
#include "mojo/public/cpp/application/application_delegate.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "services/tracing/tracing.mojom.h"
+#include "shell/application_manager/application_loader.h"
+#include "shell/application_manager/application_manager.h"
#include "shell/dynamic_application_loader.h"
#include "shell/external_application_listener.h"
#include "shell/in_process_dynamic_service_runner.h"
diff --git a/shell/context.h b/shell/context.h
index 605719a..ab30f14 100644
--- a/shell/context.h
+++ b/shell/context.h
@@ -8,7 +8,7 @@
#include <string>
#include "base/macros.h"
-#include "mojo/application_manager/application_manager.h"
+#include "shell/application_manager/application_manager.h"
#include "shell/mojo_url_resolver.h"
#include "shell/task_runners.h"
diff --git a/shell/dynamic_application_loader.h b/shell/dynamic_application_loader.h
index e0c553a..7780b78 100644
--- a/shell/dynamic_application_loader.h
+++ b/shell/dynamic_application_loader.h
@@ -11,9 +11,9 @@
#include "base/macros.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
-#include "mojo/application_manager/application_loader.h"
#include "mojo/public/cpp/system/core.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
+#include "shell/application_manager/application_loader.h"
#include "shell/dynamic_service_runner.h"
#include "url/gurl.h"
diff --git a/shell/external_application_listener_unittest.cc b/shell/external_application_listener_unittest.cc
index f8e7316..ebc1a65 100644
--- a/shell/external_application_listener_unittest.cc
+++ b/shell/external_application_listener_unittest.cc
@@ -8,12 +8,12 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread.h"
-#include "mojo/application_manager/application_loader.h"
-#include "mojo/application_manager/application_manager.h"
#include "mojo/common/common_type_converters.h"
#include "mojo/public/interfaces/application/application.mojom.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "shell/application_manager/application_loader.h"
+#include "shell/application_manager/application_manager.h"
#include "shell/domain_socket/net_errors.h"
#include "shell/domain_socket/test_completion_callback.h"
#include "shell/domain_socket/unix_domain_client_socket_posix.h"
diff --git a/shell/shell_test_helper.h b/shell/shell_test_helper.h
index 5d6cb5e..4a29818 100644
--- a/shell/shell_test_helper.h
+++ b/shell/shell_test_helper.h
@@ -8,7 +8,7 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
-#include "mojo/application_manager/application_loader.h"
+#include "shell/application_manager/application_loader.h"
#include "shell/context.h"
class GURL;