1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "mojo/application_manager/application_manager.h" 6 7 #include <stdio.h> 8 9 #include "base/bind.h" 10 #include "base/lazy_instance.h" 11 #include "base/logging.h" 12 #include "base/macros.h" 13 #include "base/stl_util.h" 14 #include "mojo/application_manager/application_loader.h" 15 #include "mojo/common/common_type_converters.h" 16 #include "mojo/public/cpp/application/connect.h" 17 #include "mojo/public/interfaces/application/application.mojom.h" 18 #include "mojo/public/interfaces/application/shell.mojom.h" 19 #include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h" 20 21 namespace mojo { 22 23 namespace { 24 // Used by TestAPI. 25 bool has_created_instance = false; 26 27 class StubServiceProvider : public InterfaceImpl<ServiceProvider> { 28 public: 29 ServiceProvider* GetRemoteServiceProvider() { return client(); } 30 31 private: 32 virtual void ConnectToService(const String& service_name, 33 ScopedMessagePipeHandle client_handle) 34 MOJO_OVERRIDE {} 35 }; 36 37 } // namespace 38 39 ApplicationManager::Delegate::~Delegate() {} 40 41 class ApplicationManager::LoadCallbacksImpl 42 : public ApplicationLoader::LoadCallbacks { 43 public: 44 LoadCallbacksImpl(base::WeakPtr<ApplicationManager> manager, 45 const GURL& requested_url, 46 const GURL& requestor_url, 47 ServiceProviderPtr service_provider) 48 : manager_(manager), 49 requested_url_(requested_url), 50 requestor_url_(requestor_url), 51 service_provider_(service_provider.Pass()) {} 52 53 private: 54 virtual ~LoadCallbacksImpl() {} 55 56 // LoadCallbacks implementation 57 virtual ScopedMessagePipeHandle RegisterApplication() OVERRIDE { 58 ScopedMessagePipeHandle shell_handle; 59 if (manager_) { 60 manager_->RegisterLoadedApplication(requested_url_, 61 requestor_url_, 62 service_provider_.Pass(), 63 &shell_handle); 64 } 65 return shell_handle.Pass(); 66 } 67 68 virtual void LoadWithContentHandler(const GURL& content_handler_url, 69 URLResponsePtr url_response) OVERRIDE { 70 if (manager_) { 71 manager_->LoadWithContentHandler(requested_url_, 72 requestor_url_, 73 content_handler_url, 74 url_response.Pass(), 75 service_provider_.Pass()); 76 } 77 } 78 79 base::WeakPtr<ApplicationManager> manager_; 80 GURL requested_url_; 81 GURL requestor_url_; 82 ServiceProviderPtr service_provider_; 83 }; 84 85 class ApplicationManager::ShellImpl : public InterfaceImpl<Shell> { 86 public: 87 ShellImpl(ApplicationManager* manager, const GURL& url) 88 : manager_(manager), url_(url) {} 89 90 virtual ~ShellImpl() {} 91 92 void ConnectToClient(const GURL& requestor_url, 93 ServiceProviderPtr service_provider) { 94 client()->AcceptConnection(String::From(requestor_url), 95 service_provider.Pass()); 96 } 97 98 // ServiceProvider implementation: 99 virtual void ConnectToApplication( 100 const String& app_url, 101 InterfaceRequest<ServiceProvider> in_service_provider) OVERRIDE { 102 ServiceProviderPtr out_service_provider; 103 out_service_provider.Bind(in_service_provider.PassMessagePipe()); 104 manager_->ConnectToApplication( 105 app_url.To<GURL>(), url_, out_service_provider.Pass()); 106 } 107 108 const GURL& url() const { return url_; } 109 110 private: 111 virtual void OnConnectionError() OVERRIDE { 112 manager_->OnShellImplError(this); 113 } 114 115 ApplicationManager* const manager_; 116 const GURL url_; 117 118 DISALLOW_COPY_AND_ASSIGN(ShellImpl); 119 }; 120 121 struct ApplicationManager::ContentHandlerConnection { 122 ContentHandlerConnection(ApplicationManager* manager, 123 const GURL& content_handler_url) { 124 ServiceProviderPtr service_provider; 125 BindToProxy(&service_provider_impl, &service_provider); 126 manager->ConnectToApplication( 127 content_handler_url, GURL(), service_provider.Pass()); 128 mojo::ConnectToService(service_provider_impl.client(), &content_handler); 129 } 130 131 StubServiceProvider service_provider_impl; 132 ContentHandlerPtr content_handler; 133 }; 134 135 // static 136 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager) 137 : manager_(manager) { 138 } 139 140 ApplicationManager::TestAPI::~TestAPI() { 141 } 142 143 bool ApplicationManager::TestAPI::HasCreatedInstance() { 144 return has_created_instance; 145 } 146 147 bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const { 148 return manager_->url_to_shell_impl_.find(url) != 149 manager_->url_to_shell_impl_.end(); 150 } 151 152 ApplicationManager::ApplicationManager() 153 : delegate_(NULL), 154 interceptor_(NULL), 155 weak_ptr_factory_(this) { 156 } 157 158 ApplicationManager::~ApplicationManager() { 159 STLDeleteValues(&url_to_content_handler_); 160 TerminateShellConnections(); 161 STLDeleteValues(&url_to_loader_); 162 STLDeleteValues(&scheme_to_loader_); 163 } 164 165 void ApplicationManager::TerminateShellConnections() { 166 STLDeleteValues(&url_to_shell_impl_); 167 } 168 169 // static 170 ApplicationManager* ApplicationManager::GetInstance() { 171 static base::LazyInstance<ApplicationManager> instance = 172 LAZY_INSTANCE_INITIALIZER; 173 has_created_instance = true; 174 return &instance.Get(); 175 } 176 177 void ApplicationManager::ConnectToApplication( 178 const GURL& url, 179 const GURL& requestor_url, 180 ServiceProviderPtr service_provider) { 181 URLToShellImplMap::const_iterator shell_it = url_to_shell_impl_.find(url); 182 if (shell_it != url_to_shell_impl_.end()) { 183 ConnectToClient( 184 shell_it->second, url, requestor_url, service_provider.Pass()); 185 return; 186 } 187 188 scoped_refptr<LoadCallbacksImpl> callbacks( 189 new LoadCallbacksImpl(weak_ptr_factory_.GetWeakPtr(), 190 url, 191 requestor_url, 192 service_provider.Pass())); 193 GetLoaderForURL(url)->Load(this, url, callbacks); 194 } 195 196 void ApplicationManager::ConnectToClient(ShellImpl* shell_impl, 197 const GURL& url, 198 const GURL& requestor_url, 199 ServiceProviderPtr service_provider) { 200 if (interceptor_) { 201 shell_impl->ConnectToClient( 202 requestor_url, 203 interceptor_->OnConnectToClient(url, service_provider.Pass())); 204 } else { 205 shell_impl->ConnectToClient(requestor_url, service_provider.Pass()); 206 } 207 } 208 209 void ApplicationManager::RegisterLoadedApplication( 210 const GURL& url, 211 const GURL& requestor_url, 212 ServiceProviderPtr service_provider, 213 ScopedMessagePipeHandle* shell_handle) { 214 ShellImpl* shell_impl = NULL; 215 URLToShellImplMap::iterator iter = url_to_shell_impl_.find(url); 216 if (iter != url_to_shell_impl_.end()) { 217 // This can happen because services are loaded asynchronously. So if we get 218 // two requests for the same service close to each other, we might get here 219 // and find that we already have it. 220 shell_impl = iter->second; 221 } else { 222 MessagePipe pipe; 223 URLToArgsMap::const_iterator args_it = url_to_args_.find(url); 224 Array<String> args; 225 if (args_it != url_to_args_.end()) 226 args = Array<String>::From(args_it->second); 227 shell_impl = WeakBindToPipe(new ShellImpl(this, url), pipe.handle1.Pass()); 228 url_to_shell_impl_[url] = shell_impl; 229 *shell_handle = pipe.handle0.Pass(); 230 shell_impl->client()->Initialize(args.Pass()); 231 } 232 233 ConnectToClient(shell_impl, url, requestor_url, service_provider.Pass()); 234 } 235 236 void ApplicationManager::LoadWithContentHandler( 237 const GURL& content_url, 238 const GURL& requestor_url, 239 const GURL& content_handler_url, 240 URLResponsePtr url_response, 241 ServiceProviderPtr service_provider) { 242 ContentHandlerConnection* connection = NULL; 243 URLToContentHandlerMap::iterator iter = 244 url_to_content_handler_.find(content_handler_url); 245 if (iter != url_to_content_handler_.end()) { 246 connection = iter->second; 247 } else { 248 connection = new ContentHandlerConnection(this, content_handler_url); 249 url_to_content_handler_[content_handler_url] = connection; 250 } 251 252 InterfaceRequest<ServiceProvider> spir; 253 spir.Bind(service_provider.PassMessagePipe()); 254 connection->content_handler->OnConnect( 255 content_url.spec(), url_response.Pass(), spir.Pass()); 256 } 257 258 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, 259 const GURL& url) { 260 URLToLoaderMap::iterator it = url_to_loader_.find(url); 261 if (it != url_to_loader_.end()) 262 delete it->second; 263 url_to_loader_[url] = loader.release(); 264 } 265 266 void ApplicationManager::SetLoaderForScheme( 267 scoped_ptr<ApplicationLoader> loader, 268 const std::string& scheme) { 269 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme); 270 if (it != scheme_to_loader_.end()) 271 delete it->second; 272 scheme_to_loader_[scheme] = loader.release(); 273 } 274 275 void ApplicationManager::SetArgsForURL(const std::vector<std::string>& args, 276 const GURL& url) { 277 url_to_args_[url] = args; 278 } 279 280 void ApplicationManager::SetInterceptor(Interceptor* interceptor) { 281 interceptor_ = interceptor; 282 } 283 284 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) { 285 URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url); 286 if (url_it != url_to_loader_.end()) 287 return url_it->second; 288 SchemeToLoaderMap::const_iterator scheme_it = 289 scheme_to_loader_.find(url.scheme()); 290 if (scheme_it != scheme_to_loader_.end()) 291 return scheme_it->second; 292 return default_loader_.get(); 293 } 294 295 void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) { 296 // Called from ~ShellImpl, so we do not need to call Destroy here. 297 const GURL url = shell_impl->url(); 298 URLToShellImplMap::iterator it = url_to_shell_impl_.find(url); 299 DCHECK(it != url_to_shell_impl_.end()); 300 delete it->second; 301 url_to_shell_impl_.erase(it); 302 ApplicationLoader* loader = GetLoaderForURL(url); 303 if (loader) 304 loader->OnApplicationError(this, url); 305 if (delegate_) 306 delegate_->OnApplicationError(url); 307 } 308 309 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName( 310 const GURL& application_url, 311 const std::string& interface_name) { 312 StubServiceProvider* stub_sp = new StubServiceProvider; 313 ServiceProviderPtr spp; 314 BindToProxy(stub_sp, &spp); 315 ConnectToApplication(application_url, GURL(), spp.Pass()); 316 MessagePipe pipe; 317 stub_sp->GetRemoteServiceProvider()->ConnectToService(interface_name, 318 pipe.handle1.Pass()); 319 return pipe.handle0.Pass(); 320 } 321 } // namespace mojo 322