| OLD | NEW |
| (Empty) |
| 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/shell/application_manager/application_manager.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/macros.h" | |
| 11 #include "base/stl_util.h" | |
| 12 #include "base/strings/string_util.h" | |
| 13 #include "base/trace_event/trace_event.h" | |
| 14 #include "mojo/public/cpp/bindings/binding.h" | |
| 15 #include "mojo/public/cpp/bindings/error_handler.h" | |
| 16 #include "mojo/shell/application_manager/fetcher.h" | |
| 17 #include "mojo/shell/application_manager/local_fetcher.h" | |
| 18 #include "mojo/shell/application_manager/network_fetcher.h" | |
| 19 #include "mojo/shell/application_manager/query_util.h" | |
| 20 #include "mojo/shell/application_manager/shell_impl.h" | |
| 21 #include "mojo/shell/switches.h" | |
| 22 #include "third_party/mojo_services/src/content_handler/public/interfaces/conten
t_handler.mojom.h" | |
| 23 | |
| 24 namespace mojo { | |
| 25 namespace shell { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // Used by TestAPI. | |
| 30 bool has_created_instance = false; | |
| 31 | |
| 32 } // namespace | |
| 33 | |
| 34 class ApplicationManager::ContentHandlerConnection : public ErrorHandler { | |
| 35 public: | |
| 36 ContentHandlerConnection(ApplicationManager* manager, | |
| 37 const GURL& content_handler_url) | |
| 38 : manager_(manager), content_handler_url_(content_handler_url) { | |
| 39 ServiceProviderPtr services; | |
| 40 manager->ConnectToApplication(content_handler_url, GURL(), | |
| 41 GetProxy(&services), nullptr, | |
| 42 base::Closure()); | |
| 43 MessagePipe pipe; | |
| 44 content_handler_.Bind(pipe.handle0.Pass()); | |
| 45 services->ConnectToService(ContentHandler::Name_, pipe.handle1.Pass()); | |
| 46 content_handler_.set_error_handler(this); | |
| 47 } | |
| 48 | |
| 49 ContentHandler* content_handler() { return content_handler_.get(); } | |
| 50 | |
| 51 GURL content_handler_url() { return content_handler_url_; } | |
| 52 | |
| 53 private: | |
| 54 // ErrorHandler implementation: | |
| 55 void OnConnectionError() override { manager_->OnContentHandlerError(this); } | |
| 56 | |
| 57 ApplicationManager* manager_; | |
| 58 GURL content_handler_url_; | |
| 59 ContentHandlerPtr content_handler_; | |
| 60 | |
| 61 DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection); | |
| 62 }; | |
| 63 | |
| 64 // static | |
| 65 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager) | |
| 66 : manager_(manager) { | |
| 67 } | |
| 68 | |
| 69 ApplicationManager::TestAPI::~TestAPI() { | |
| 70 } | |
| 71 | |
| 72 bool ApplicationManager::TestAPI::HasCreatedInstance() { | |
| 73 return has_created_instance; | |
| 74 } | |
| 75 | |
| 76 bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const { | |
| 77 return manager_->identity_to_shell_impl_.find(Identity(url)) != | |
| 78 manager_->identity_to_shell_impl_.end(); | |
| 79 } | |
| 80 | |
| 81 ApplicationManager::ApplicationManager(Delegate* delegate) | |
| 82 : delegate_(delegate), weak_ptr_factory_(this) { | |
| 83 } | |
| 84 | |
| 85 ApplicationManager::~ApplicationManager() { | |
| 86 STLDeleteValues(&url_to_content_handler_); | |
| 87 TerminateShellConnections(); | |
| 88 STLDeleteValues(&url_to_loader_); | |
| 89 STLDeleteValues(&scheme_to_loader_); | |
| 90 } | |
| 91 | |
| 92 void ApplicationManager::TerminateShellConnections() { | |
| 93 STLDeleteValues(&identity_to_shell_impl_); | |
| 94 } | |
| 95 | |
| 96 void ApplicationManager::ConnectToApplication( | |
| 97 const GURL& requested_url, | |
| 98 const GURL& requestor_url, | |
| 99 InterfaceRequest<ServiceProvider> services, | |
| 100 ServiceProviderPtr exposed_services, | |
| 101 const base::Closure& on_application_end) { | |
| 102 ConnectToApplicationWithParameters( | |
| 103 requested_url, requestor_url, services.Pass(), exposed_services.Pass(), | |
| 104 on_application_end, std::vector<std::string>()); | |
| 105 } | |
| 106 | |
| 107 void ApplicationManager::ConnectToApplicationWithParameters( | |
| 108 const GURL& requested_url, | |
| 109 const GURL& requestor_url, | |
| 110 InterfaceRequest<ServiceProvider> services, | |
| 111 ServiceProviderPtr exposed_services, | |
| 112 const base::Closure& on_application_end, | |
| 113 const std::vector<std::string>& pre_redirect_parameters) { | |
| 114 TRACE_EVENT_INSTANT1( | |
| 115 "mojo_shell", "ApplicationManager::ConnectToApplicationWithParameters", | |
| 116 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_url.spec()); | |
| 117 DCHECK(requested_url.is_valid()); | |
| 118 | |
| 119 // We check both the mapped and resolved urls for existing shell_impls because | |
| 120 // external applications can be registered for the unresolved mojo:foo urls. | |
| 121 | |
| 122 GURL mapped_url = delegate_->ResolveMappings(requested_url); | |
| 123 if (ConnectToRunningApplication(mapped_url, requestor_url, &services, | |
| 124 &exposed_services)) { | |
| 125 return; | |
| 126 } | |
| 127 | |
| 128 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url); | |
| 129 if (ConnectToRunningApplication(resolved_url, requestor_url, &services, | |
| 130 &exposed_services)) { | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 // The application is not running, let's compute the parameters. | |
| 135 if (ConnectToApplicationWithLoader(mapped_url, requestor_url, &services, | |
| 136 &exposed_services, on_application_end, | |
| 137 pre_redirect_parameters, | |
| 138 GetLoaderForURL(mapped_url))) { | |
| 139 return; | |
| 140 } | |
| 141 | |
| 142 if (ConnectToApplicationWithLoader(resolved_url, requestor_url, &services, | |
| 143 &exposed_services, on_application_end, | |
| 144 pre_redirect_parameters, | |
| 145 GetLoaderForURL(resolved_url))) { | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 if (ConnectToApplicationWithLoader( | |
| 150 resolved_url, requestor_url, &services, &exposed_services, | |
| 151 on_application_end, pre_redirect_parameters, default_loader_.get())) { | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 auto callback = base::Bind(&ApplicationManager::HandleFetchCallback, | |
| 156 weak_ptr_factory_.GetWeakPtr(), requestor_url, | |
| 157 base::Passed(services.Pass()), | |
| 158 base::Passed(exposed_services.Pass()), | |
| 159 on_application_end, pre_redirect_parameters); | |
| 160 | |
| 161 if (resolved_url.SchemeIsFile()) { | |
| 162 new LocalFetcher( | |
| 163 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr), | |
| 164 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE)); | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 if (!network_service_) | |
| 169 ConnectToService(GURL("mojo:network_service"), &network_service_); | |
| 170 | |
| 171 const NativeApplicationCleanup cleanup = | |
| 172 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 173 switches::kDontDeleteOnDownload) | |
| 174 ? NativeApplicationCleanup::DONT_DELETE | |
| 175 : NativeApplicationCleanup::DELETE; | |
| 176 | |
| 177 new NetworkFetcher(disable_cache_, resolved_url, network_service_.get(), | |
| 178 base::Bind(callback, cleanup)); | |
| 179 } | |
| 180 | |
| 181 bool ApplicationManager::ConnectToRunningApplication( | |
| 182 const GURL& resolved_url, | |
| 183 const GURL& requestor_url, | |
| 184 InterfaceRequest<ServiceProvider>* services, | |
| 185 ServiceProviderPtr* exposed_services) { | |
| 186 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr); | |
| 187 ShellImpl* shell_impl = GetShellImpl(application_url); | |
| 188 if (!shell_impl) | |
| 189 return false; | |
| 190 | |
| 191 ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(), | |
| 192 exposed_services->Pass()); | |
| 193 return true; | |
| 194 } | |
| 195 | |
| 196 bool ApplicationManager::ConnectToApplicationWithLoader( | |
| 197 const GURL& resolved_url, | |
| 198 const GURL& requestor_url, | |
| 199 InterfaceRequest<ServiceProvider>* services, | |
| 200 ServiceProviderPtr* exposed_services, | |
| 201 const base::Closure& on_application_end, | |
| 202 const std::vector<std::string>& parameters, | |
| 203 ApplicationLoader* loader) { | |
| 204 if (!loader) | |
| 205 return false; | |
| 206 | |
| 207 loader->Load( | |
| 208 resolved_url, | |
| 209 RegisterShell(resolved_url, requestor_url, services->Pass(), | |
| 210 exposed_services->Pass(), on_application_end, parameters)); | |
| 211 return true; | |
| 212 } | |
| 213 | |
| 214 InterfaceRequest<Application> ApplicationManager::RegisterShell( | |
| 215 const GURL& resolved_url, | |
| 216 const GURL& requestor_url, | |
| 217 InterfaceRequest<ServiceProvider> services, | |
| 218 ServiceProviderPtr exposed_services, | |
| 219 const base::Closure& on_application_end, | |
| 220 const std::vector<std::string>& parameters) { | |
| 221 Identity app_identity(resolved_url); | |
| 222 | |
| 223 ApplicationPtr application; | |
| 224 InterfaceRequest<Application> application_request = GetProxy(&application); | |
| 225 ShellImpl* shell = | |
| 226 new ShellImpl(application.Pass(), this, app_identity, on_application_end); | |
| 227 identity_to_shell_impl_[app_identity] = shell; | |
| 228 shell->InitializeApplication(Array<String>::From(parameters)); | |
| 229 ConnectToClient(shell, resolved_url, requestor_url, services.Pass(), | |
| 230 exposed_services.Pass()); | |
| 231 return application_request.Pass(); | |
| 232 } | |
| 233 | |
| 234 ShellImpl* ApplicationManager::GetShellImpl(const GURL& url) { | |
| 235 const auto& shell_it = identity_to_shell_impl_.find(Identity(url)); | |
| 236 if (shell_it != identity_to_shell_impl_.end()) | |
| 237 return shell_it->second; | |
| 238 return nullptr; | |
| 239 } | |
| 240 | |
| 241 void ApplicationManager::ConnectToClient( | |
| 242 ShellImpl* shell_impl, | |
| 243 const GURL& resolved_url, | |
| 244 const GURL& requestor_url, | |
| 245 InterfaceRequest<ServiceProvider> services, | |
| 246 ServiceProviderPtr exposed_services) { | |
| 247 shell_impl->ConnectToClient(resolved_url, requestor_url, services.Pass(), | |
| 248 exposed_services.Pass()); | |
| 249 } | |
| 250 | |
| 251 void ApplicationManager::HandleFetchCallback( | |
| 252 const GURL& requestor_url, | |
| 253 InterfaceRequest<ServiceProvider> services, | |
| 254 ServiceProviderPtr exposed_services, | |
| 255 const base::Closure& on_application_end, | |
| 256 const std::vector<std::string>& parameters, | |
| 257 NativeApplicationCleanup cleanup, | |
| 258 scoped_ptr<Fetcher> fetcher) { | |
| 259 if (!fetcher) { | |
| 260 // Network error. Drop |application_request| to tell requestor. | |
| 261 return; | |
| 262 } | |
| 263 | |
| 264 GURL redirect_url = fetcher->GetRedirectURL(); | |
| 265 if (!redirect_url.is_empty()) { | |
| 266 // And around we go again... Whee! | |
| 267 ConnectToApplicationWithParameters(redirect_url, requestor_url, | |
| 268 services.Pass(), exposed_services.Pass(), | |
| 269 on_application_end, parameters); | |
| 270 return; | |
| 271 } | |
| 272 | |
| 273 // We already checked if the application was running before we fetched it, but | |
| 274 // it might have started while the fetch was outstanding. We don't want to | |
| 275 // have two copies of the app running, so check again. | |
| 276 // | |
| 277 // Also, it's possible the original URL was redirected to an app that is | |
| 278 // already running. | |
| 279 if (ConnectToRunningApplication(fetcher->GetURL(), requestor_url, &services, | |
| 280 &exposed_services)) { | |
| 281 return; | |
| 282 } | |
| 283 | |
| 284 InterfaceRequest<Application> request( | |
| 285 RegisterShell(fetcher->GetURL(), requestor_url, services.Pass(), | |
| 286 exposed_services.Pass(), on_application_end, parameters)); | |
| 287 | |
| 288 // If the response begins with a #!mojo <content-handler-url>, use it. | |
| 289 GURL content_handler_url; | |
| 290 std::string shebang; | |
| 291 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) { | |
| 292 LoadWithContentHandler( | |
| 293 content_handler_url, request.Pass(), | |
| 294 fetcher->AsURLResponse(blocking_pool_, | |
| 295 static_cast<int>(shebang.size()))); | |
| 296 return; | |
| 297 } | |
| 298 | |
| 299 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType()); | |
| 300 if (iter != mime_type_to_url_.end()) { | |
| 301 LoadWithContentHandler(iter->second, request.Pass(), | |
| 302 fetcher->AsURLResponse(blocking_pool_, 0)); | |
| 303 return; | |
| 304 } | |
| 305 | |
| 306 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo | |
| 307 // application. That could either mean looking for the platform-specific dll | |
| 308 // header, or looking for some specific mojo signature prepended to the | |
| 309 // library. | |
| 310 // TODO(vtl): (Maybe this should be done by the factory/runner?) | |
| 311 | |
| 312 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr); | |
| 313 NativeRunnerFactory::Options options; | |
| 314 if (url_to_native_options_.find(base_resolved_url) != | |
| 315 url_to_native_options_.end()) { | |
| 316 DVLOG(2) << "Applying stored native options to resolved URL " | |
| 317 << fetcher->GetURL(); | |
| 318 options = url_to_native_options_[base_resolved_url]; | |
| 319 } | |
| 320 | |
| 321 fetcher->AsPath( | |
| 322 blocking_pool_, | |
| 323 base::Bind(&ApplicationManager::RunNativeApplication, | |
| 324 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()), | |
| 325 options, cleanup, base::Passed(fetcher.Pass()))); | |
| 326 } | |
| 327 | |
| 328 void ApplicationManager::RunNativeApplication( | |
| 329 InterfaceRequest<Application> application_request, | |
| 330 const NativeRunnerFactory::Options& options, | |
| 331 NativeApplicationCleanup cleanup, | |
| 332 scoped_ptr<Fetcher> fetcher, | |
| 333 const base::FilePath& path, | |
| 334 bool path_exists) { | |
| 335 // We only passed fetcher to keep it alive. Done with it now. | |
| 336 fetcher.reset(); | |
| 337 | |
| 338 DCHECK(application_request.is_pending()); | |
| 339 | |
| 340 if (!path_exists) { | |
| 341 LOG(ERROR) << "Library not started because library path '" << path.value() | |
| 342 << "' does not exist."; | |
| 343 return; | |
| 344 } | |
| 345 | |
| 346 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path", | |
| 347 path.AsUTF8Unsafe()); | |
| 348 NativeRunner* runner = native_runner_factory_->Create(options).release(); | |
| 349 native_runners_.push_back(runner); | |
| 350 runner->Start(path, cleanup, application_request.Pass(), | |
| 351 base::Bind(&ApplicationManager::CleanupRunner, | |
| 352 weak_ptr_factory_.GetWeakPtr(), runner)); | |
| 353 } | |
| 354 | |
| 355 void ApplicationManager::RegisterContentHandler( | |
| 356 const std::string& mime_type, | |
| 357 const GURL& content_handler_url) { | |
| 358 DCHECK(content_handler_url.is_valid()) | |
| 359 << "Content handler URL is invalid for mime type " << mime_type; | |
| 360 mime_type_to_url_[mime_type] = content_handler_url; | |
| 361 } | |
| 362 | |
| 363 void ApplicationManager::LoadWithContentHandler( | |
| 364 const GURL& content_handler_url, | |
| 365 InterfaceRequest<Application> application_request, | |
| 366 URLResponsePtr url_response) { | |
| 367 ContentHandlerConnection* connection = nullptr; | |
| 368 URLToContentHandlerMap::iterator iter = | |
| 369 url_to_content_handler_.find(content_handler_url); | |
| 370 if (iter != url_to_content_handler_.end()) { | |
| 371 connection = iter->second; | |
| 372 } else { | |
| 373 connection = new ContentHandlerConnection(this, content_handler_url); | |
| 374 url_to_content_handler_[content_handler_url] = connection; | |
| 375 } | |
| 376 | |
| 377 connection->content_handler()->StartApplication(application_request.Pass(), | |
| 378 url_response.Pass()); | |
| 379 } | |
| 380 | |
| 381 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader, | |
| 382 const GURL& url) { | |
| 383 URLToLoaderMap::iterator it = url_to_loader_.find(url); | |
| 384 if (it != url_to_loader_.end()) | |
| 385 delete it->second; | |
| 386 url_to_loader_[url] = loader.release(); | |
| 387 } | |
| 388 | |
| 389 void ApplicationManager::SetLoaderForScheme( | |
| 390 scoped_ptr<ApplicationLoader> loader, | |
| 391 const std::string& scheme) { | |
| 392 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme); | |
| 393 if (it != scheme_to_loader_.end()) | |
| 394 delete it->second; | |
| 395 scheme_to_loader_[scheme] = loader.release(); | |
| 396 } | |
| 397 | |
| 398 void ApplicationManager::SetNativeOptionsForURL( | |
| 399 const NativeRunnerFactory::Options& options, | |
| 400 const GURL& url) { | |
| 401 DCHECK(!url.has_query()); // Precondition. | |
| 402 // Apply mappings and resolution to get the resolved URL. | |
| 403 GURL resolved_url = | |
| 404 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url)); | |
| 405 DCHECK(!resolved_url.has_query()); // Still shouldn't have query. | |
| 406 // TODO(vtl): We should probably also remove/disregard the query string (and | |
| 407 // maybe canonicalize in other ways). | |
| 408 DVLOG(2) << "Storing native options for resolved URL " << resolved_url | |
| 409 << " (original URL " << url << ")"; | |
| 410 url_to_native_options_[resolved_url] = options; | |
| 411 } | |
| 412 | |
| 413 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) { | |
| 414 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr)); | |
| 415 if (url_it != url_to_loader_.end()) | |
| 416 return url_it->second; | |
| 417 auto scheme_it = scheme_to_loader_.find(url.scheme()); | |
| 418 if (scheme_it != scheme_to_loader_.end()) | |
| 419 return scheme_it->second; | |
| 420 return nullptr; | |
| 421 } | |
| 422 | |
| 423 void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) { | |
| 424 // Called from ~ShellImpl, so we do not need to call Destroy here. | |
| 425 const Identity identity = shell_impl->identity(); | |
| 426 base::Closure on_application_end = shell_impl->on_application_end(); | |
| 427 // Remove the shell. | |
| 428 auto it = identity_to_shell_impl_.find(identity); | |
| 429 DCHECK(it != identity_to_shell_impl_.end()); | |
| 430 delete it->second; | |
| 431 identity_to_shell_impl_.erase(it); | |
| 432 if (!on_application_end.is_null()) | |
| 433 on_application_end.Run(); | |
| 434 } | |
| 435 | |
| 436 void ApplicationManager::OnContentHandlerError( | |
| 437 ContentHandlerConnection* content_handler) { | |
| 438 // Remove the mapping to the content handler. | |
| 439 auto it = | |
| 440 url_to_content_handler_.find(content_handler->content_handler_url()); | |
| 441 DCHECK(it != url_to_content_handler_.end()); | |
| 442 delete it->second; | |
| 443 url_to_content_handler_.erase(it); | |
| 444 } | |
| 445 | |
| 446 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName( | |
| 447 const GURL& application_url, | |
| 448 const std::string& interface_name) { | |
| 449 ServiceProviderPtr services; | |
| 450 ConnectToApplication(application_url, GURL(), GetProxy(&services), nullptr, | |
| 451 base::Closure()); | |
| 452 MessagePipe pipe; | |
| 453 services->ConnectToService(interface_name, pipe.handle1.Pass()); | |
| 454 return pipe.handle0.Pass(); | |
| 455 } | |
| 456 | |
| 457 void ApplicationManager::CleanupRunner(NativeRunner* runner) { | |
| 458 native_runners_.erase( | |
| 459 std::find(native_runners_.begin(), native_runners_.end(), runner)); | |
| 460 } | |
| 461 | |
| 462 } // namespace shell | |
| 463 } // namespace mojo | |
| OLD | NEW |