// 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 "mojo/application_manager/application_manager.h" #include #include "base/bind.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" #include "base/stl_util.h" #include "mojo/application_manager/application_loader.h" #include "mojo/common/common_type_converters.h" #include "mojo/public/cpp/application/connect.h" #include "mojo/public/interfaces/application/application.mojom.h" #include "mojo/public/interfaces/application/shell.mojom.h" #include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h" namespace mojo { namespace { // Used by TestAPI. bool has_created_instance = false; class StubServiceProvider : public InterfaceImpl { public: ServiceProvider* GetRemoteServiceProvider() { return client(); } private: virtual void ConnectToService(const String& service_name, ScopedMessagePipeHandle client_handle) MOJO_OVERRIDE {} }; } // namespace ApplicationManager::Delegate::~Delegate() {} class ApplicationManager::LoadCallbacksImpl : public ApplicationLoader::LoadCallbacks { public: LoadCallbacksImpl(base::WeakPtr manager, const GURL& requested_url, const GURL& requestor_url, ServiceProviderPtr service_provider) : manager_(manager), requested_url_(requested_url), requestor_url_(requestor_url), service_provider_(service_provider.Pass()) {} private: virtual ~LoadCallbacksImpl() {} // LoadCallbacks implementation virtual ScopedMessagePipeHandle RegisterApplication() OVERRIDE { ScopedMessagePipeHandle shell_handle; if (manager_) { manager_->RegisterLoadedApplication(requested_url_, requestor_url_, service_provider_.Pass(), &shell_handle); } return shell_handle.Pass(); } virtual void LoadWithContentHandler(const GURL& content_handler_url, URLResponsePtr url_response) OVERRIDE { if (manager_) { manager_->LoadWithContentHandler(requested_url_, requestor_url_, content_handler_url, url_response.Pass(), service_provider_.Pass()); } } base::WeakPtr manager_; GURL requested_url_; GURL requestor_url_; ServiceProviderPtr service_provider_; }; class ApplicationManager::ShellImpl : public InterfaceImpl { public: ShellImpl(ApplicationManager* manager, const GURL& url) : manager_(manager), url_(url) {} virtual ~ShellImpl() {} void ConnectToClient(const GURL& requestor_url, ServiceProviderPtr service_provider) { client()->AcceptConnection(String::From(requestor_url), service_provider.Pass()); } // ServiceProvider implementation: virtual void ConnectToApplication( const String& app_url, InterfaceRequest in_service_provider) OVERRIDE { ServiceProviderPtr out_service_provider; out_service_provider.Bind(in_service_provider.PassMessagePipe()); manager_->ConnectToApplication( app_url.To(), url_, out_service_provider.Pass()); } const GURL& url() const { return url_; } private: virtual void OnConnectionError() OVERRIDE { manager_->OnShellImplError(this); } ApplicationManager* const manager_; const GURL url_; DISALLOW_COPY_AND_ASSIGN(ShellImpl); }; struct ApplicationManager::ContentHandlerConnection { ContentHandlerConnection(ApplicationManager* manager, const GURL& content_handler_url) { ServiceProviderPtr service_provider; BindToProxy(&service_provider_impl, &service_provider); manager->ConnectToApplication( content_handler_url, GURL(), service_provider.Pass()); mojo::ConnectToService(service_provider_impl.client(), &content_handler); } StubServiceProvider service_provider_impl; ContentHandlerPtr content_handler; }; // 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_(NULL), interceptor_(NULL), 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_); } // static ApplicationManager* ApplicationManager::GetInstance() { static base::LazyInstance instance = LAZY_INSTANCE_INITIALIZER; has_created_instance = true; return &instance.Get(); } void ApplicationManager::ConnectToApplication( const GURL& url, const GURL& requestor_url, ServiceProviderPtr service_provider) { URLToShellImplMap::const_iterator shell_it = url_to_shell_impl_.find(url); if (shell_it != url_to_shell_impl_.end()) { ConnectToClient( shell_it->second, url, requestor_url, service_provider.Pass()); return; } scoped_refptr callbacks( new LoadCallbacksImpl(weak_ptr_factory_.GetWeakPtr(), url, requestor_url, service_provider.Pass())); GetLoaderForURL(url)->Load(this, url, callbacks); } void ApplicationManager::ConnectToClient(ShellImpl* shell_impl, const GURL& url, const GURL& requestor_url, ServiceProviderPtr service_provider) { if (interceptor_) { shell_impl->ConnectToClient( requestor_url, interceptor_->OnConnectToClient(url, service_provider.Pass())); } else { shell_impl->ConnectToClient(requestor_url, service_provider.Pass()); } } void ApplicationManager::RegisterLoadedApplication( const GURL& url, const GURL& requestor_url, ServiceProviderPtr service_provider, ScopedMessagePipeHandle* shell_handle) { ShellImpl* shell_impl = NULL; URLToShellImplMap::iterator iter = url_to_shell_impl_.find(url); if (iter != url_to_shell_impl_.end()) { // This can happen because services are loaded asynchronously. So if we get // two requests for the same service close to each other, we might get here // and find that we already have it. shell_impl = iter->second; } else { MessagePipe pipe; URLToArgsMap::const_iterator args_it = url_to_args_.find(url); Array args; if (args_it != url_to_args_.end()) args = Array::From(args_it->second); shell_impl = WeakBindToPipe(new ShellImpl(this, url), pipe.handle1.Pass()); url_to_shell_impl_[url] = shell_impl; *shell_handle = pipe.handle0.Pass(); shell_impl->client()->Initialize(args.Pass()); } ConnectToClient(shell_impl, url, requestor_url, service_provider.Pass()); } void ApplicationManager::LoadWithContentHandler( const GURL& content_url, const GURL& requestor_url, const GURL& content_handler_url, URLResponsePtr url_response, ServiceProviderPtr service_provider) { 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; } InterfaceRequest spir; spir.Bind(service_provider.PassMessagePipe()); connection->content_handler->OnConnect( content_url.spec(), url_response.Pass(), spir.Pass()); } void ApplicationManager::SetLoaderForURL(scoped_ptr 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 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& args, const GURL& url) { url_to_args_[url] = args; } void ApplicationManager::SetInterceptor(Interceptor* interceptor) { interceptor_ = interceptor; } ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) { URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url); if (url_it != url_to_loader_.end()) return url_it->second; SchemeToLoaderMap::const_iterator scheme_it = scheme_to_loader_.find(url.scheme()); if (scheme_it != scheme_to_loader_.end()) return scheme_it->second; return default_loader_.get(); } void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) { // Called from ~ShellImpl, so we do not need to call Destroy here. const GURL url = shell_impl->url(); 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(url); if (loader) loader->OnApplicationError(this, url); if (delegate_) delegate_->OnApplicationError(url); } ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName( const GURL& application_url, const std::string& interface_name) { StubServiceProvider* stub_sp = new StubServiceProvider; ServiceProviderPtr spp; BindToProxy(stub_sp, &spp); ConnectToApplication(application_url, GURL(), spp.Pass()); MessagePipe pipe; stub_sp->GetRemoteServiceProvider()->ConnectToService(interface_name, pipe.handle1.Pass()); return pipe.handle0.Pass(); } } // namespace mojo