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