gn: 'platform_impl' should be a group instead of component (since it has no code...
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blob39b133fa76e610fd3b260c906f6a65714bfe387c
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.
5 #include "mojo/shell/application_manager.h"
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/application/public/interfaces/content_handler.mojom.h"
15 #include "mojo/public/cpp/bindings/binding.h"
16 #include "mojo/public/cpp/bindings/error_handler.h"
17 #include "mojo/shell/fetcher.h"
18 #include "mojo/shell/local_fetcher.h"
19 #include "mojo/shell/network_fetcher.h"
20 #include "mojo/shell/query_util.h"
21 #include "mojo/shell/shell_impl.h"
22 #include "mojo/shell/switches.h"
24 namespace mojo {
25 namespace shell {
27 namespace {
29 // Used by TestAPI.
30 bool has_created_instance = false;
32 } // namespace
34 class ApplicationManager::ContentHandlerConnection : public ErrorHandler {
35 public:
36 ContentHandlerConnection(ApplicationManager* manager,
37 const GURL& content_handler_url,
38 const GURL& requestor_url,
39 const std::string& qualifier)
40 : manager_(manager),
41 content_handler_url_(content_handler_url),
42 content_handler_qualifier_(qualifier) {
43 ServiceProviderPtr services;
44 mojo::URLRequestPtr request(mojo::URLRequest::New());
45 request->url = mojo::String::From(content_handler_url.spec());
46 manager->ConnectToApplicationInternal(
47 request.Pass(), qualifier, requestor_url, GetProxy(&services),
48 nullptr, base::Closure());
49 MessagePipe pipe;
50 content_handler_.Bind(
51 InterfacePtrInfo<ContentHandler>(pipe.handle0.Pass(), 0u));
52 services->ConnectToService(ContentHandler::Name_, pipe.handle1.Pass());
53 content_handler_.set_error_handler(this);
56 ContentHandler* content_handler() { return content_handler_.get(); }
58 GURL content_handler_url() { return content_handler_url_; }
59 std::string content_handler_qualifier() { return content_handler_qualifier_; }
61 private:
62 // ErrorHandler implementation:
63 void OnConnectionError() override { manager_->OnContentHandlerError(this); }
65 ApplicationManager* manager_;
66 GURL content_handler_url_;
67 std::string content_handler_qualifier_;
68 ContentHandlerPtr content_handler_;
70 DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
73 // static
74 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
75 : manager_(manager) {
78 ApplicationManager::TestAPI::~TestAPI() {
81 bool ApplicationManager::TestAPI::HasCreatedInstance() {
82 return has_created_instance;
85 bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
86 return manager_->identity_to_shell_impl_.find(Identity(url)) !=
87 manager_->identity_to_shell_impl_.end();
90 ApplicationManager::ApplicationManager(Delegate* delegate)
91 : delegate_(delegate), weak_ptr_factory_(this) {
94 ApplicationManager::~ApplicationManager() {
95 STLDeleteValues(&url_to_content_handler_);
96 TerminateShellConnections();
97 STLDeleteValues(&url_to_loader_);
98 STLDeleteValues(&scheme_to_loader_);
101 void ApplicationManager::TerminateShellConnections() {
102 STLDeleteValues(&identity_to_shell_impl_);
105 void ApplicationManager::ConnectToApplication(
106 mojo::URLRequestPtr requested_url,
107 const GURL& requestor_url,
108 InterfaceRequest<ServiceProvider> services,
109 ServiceProviderPtr exposed_services,
110 const base::Closure& on_application_end) {
111 ConnectToApplicationInternal(
112 requested_url.Pass(), std::string(), requestor_url, services.Pass(),
113 exposed_services.Pass(), on_application_end);
116 void ApplicationManager::ConnectToApplicationInternal(
117 mojo::URLRequestPtr requested_url,
118 const std::string& qualifier,
119 const GURL& requestor_url,
120 InterfaceRequest<ServiceProvider> services,
121 ServiceProviderPtr exposed_services,
122 const base::Closure& on_application_end) {
123 GURL requested_gurl(requested_url->url.To<std::string>());
124 TRACE_EVENT_INSTANT1(
125 "mojo_shell", "ApplicationManager::ConnectToApplication",
126 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_gurl.spec());
127 DCHECK(requested_gurl.is_valid());
129 // We check both the mapped and resolved urls for existing shell_impls because
130 // external applications can be registered for the unresolved mojo:foo urls.
132 GURL mapped_url = delegate_->ResolveMappings(requested_gurl);
133 if (ConnectToRunningApplication(mapped_url, qualifier, requestor_url,
134 &services, &exposed_services)) {
135 return;
138 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
139 if (ConnectToRunningApplication(resolved_url, qualifier, requestor_url,
140 &services, &exposed_services)) {
141 return;
144 // The application is not running, let's compute the parameters.
145 if (ConnectToApplicationWithLoader(
146 requested_gurl, qualifier, mapped_url, requestor_url, &services,
147 &exposed_services, on_application_end, GetLoaderForURL(mapped_url))) {
148 return;
151 if (ConnectToApplicationWithLoader(
152 requested_gurl, qualifier, resolved_url, requestor_url, &services,
153 &exposed_services, on_application_end,
154 GetLoaderForURL(resolved_url))) {
155 return;
158 if (ConnectToApplicationWithLoader(
159 requested_gurl, qualifier, resolved_url, requestor_url, &services,
160 &exposed_services, on_application_end, default_loader_.get())) {
161 return;
164 auto callback = base::Bind(
165 &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
166 requested_gurl, qualifier, requestor_url, base::Passed(services.Pass()),
167 base::Passed(exposed_services.Pass()), on_application_end);
169 if (delegate_->CreateFetcher(
170 resolved_url,
171 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
172 return;
175 if (resolved_url.SchemeIsFile()) {
176 new LocalFetcher(
177 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
178 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
179 return;
182 if (!url_loader_factory_)
183 ConnectToService(GURL("mojo:network_service"), &url_loader_factory_);
185 const NativeApplicationCleanup cleanup =
186 base::CommandLine::ForCurrentProcess()->HasSwitch(
187 switches::kDontDeleteOnDownload)
188 ? NativeApplicationCleanup::DONT_DELETE
189 : NativeApplicationCleanup::DELETE;
191 if (requested_gurl.SchemeIs("mojo")) {
192 // Use the resolved mojo URL in the request to support origin mapping, etc.
193 mojo::URLRequestPtr resolved_url_request(mojo::URLRequest::New());
194 resolved_url_request->url = resolved_url.spec();
195 new NetworkFetcher(disable_cache_, resolved_url_request.Pass(),
196 url_loader_factory_.get(),
197 base::Bind(callback, cleanup));
198 return;
201 new NetworkFetcher(disable_cache_, requested_url.Pass(),
202 url_loader_factory_.get(), base::Bind(callback, cleanup));
205 bool ApplicationManager::ConnectToRunningApplication(
206 const GURL& resolved_url,
207 const std::string& qualifier,
208 const GURL& requestor_url,
209 InterfaceRequest<ServiceProvider>* services,
210 ServiceProviderPtr* exposed_services) {
211 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
212 ShellImpl* shell_impl = GetShellImpl(application_url, qualifier);
213 if (!shell_impl)
214 return false;
216 ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(),
217 exposed_services->Pass());
218 return true;
221 bool ApplicationManager::ConnectToApplicationWithLoader(
222 const GURL& requested_url,
223 const std::string& qualifier,
224 const GURL& resolved_url,
225 const GURL& requestor_url,
226 InterfaceRequest<ServiceProvider>* services,
227 ServiceProviderPtr* exposed_services,
228 const base::Closure& on_application_end,
229 ApplicationLoader* loader) {
230 if (!loader)
231 return false;
233 const GURL app_url =
234 requested_url.SchemeIs("mojo") ? requested_url : resolved_url;
236 loader->Load(
237 resolved_url,
238 RegisterShell(app_url, qualifier, requestor_url, services->Pass(),
239 exposed_services->Pass(), on_application_end));
240 return true;
243 InterfaceRequest<Application> ApplicationManager::RegisterShell(
244 const GURL& app_url,
245 const std::string& qualifier,
246 const GURL& requestor_url,
247 InterfaceRequest<ServiceProvider> services,
248 ServiceProviderPtr exposed_services,
249 const base::Closure& on_application_end) {
250 Identity app_identity(app_url, qualifier);
252 ApplicationPtr application;
253 InterfaceRequest<Application> application_request = GetProxy(&application);
254 ShellImpl* shell =
255 new ShellImpl(application.Pass(), this, app_identity, on_application_end);
256 identity_to_shell_impl_[app_identity] = shell;
257 shell->InitializeApplication();
258 ConnectToClient(shell, app_url, requestor_url, services.Pass(),
259 exposed_services.Pass());
260 return application_request.Pass();
263 ShellImpl* ApplicationManager::GetShellImpl(const GURL& url,
264 const std::string& qualifier) {
265 const auto& shell_it = identity_to_shell_impl_.find(Identity(url, qualifier));
266 if (shell_it != identity_to_shell_impl_.end())
267 return shell_it->second;
268 return nullptr;
271 void ApplicationManager::ConnectToClient(
272 ShellImpl* shell_impl,
273 const GURL& resolved_url,
274 const GURL& requestor_url,
275 InterfaceRequest<ServiceProvider> services,
276 ServiceProviderPtr exposed_services) {
277 shell_impl->ConnectToClient(resolved_url, requestor_url, services.Pass(),
278 exposed_services.Pass());
281 void ApplicationManager::HandleFetchCallback(
282 const GURL& requested_url,
283 const std::string& qualifier,
284 const GURL& requestor_url,
285 InterfaceRequest<ServiceProvider> services,
286 ServiceProviderPtr exposed_services,
287 const base::Closure& on_application_end,
288 NativeApplicationCleanup cleanup,
289 scoped_ptr<Fetcher> fetcher) {
290 if (!fetcher) {
291 // Network error. Drop |application_request| to tell requestor.
292 return;
295 GURL redirect_url = fetcher->GetRedirectURL();
296 if (!redirect_url.is_empty()) {
297 // And around we go again... Whee!
298 // TODO(sky): this loses |requested_url|.
299 mojo::URLRequestPtr request(mojo::URLRequest::New());
300 request->url = mojo::String::From(redirect_url.spec());
301 HttpHeaderPtr header = HttpHeader::New();
302 header->name = "Referer";
303 header->value = fetcher->GetRedirectReferer().spec();
304 request->headers.push_back(header.Pass());
305 ConnectToApplicationInternal(request.Pass(), qualifier, requestor_url,
306 services.Pass(), exposed_services.Pass(),
307 on_application_end);
308 return;
311 // We already checked if the application was running before we fetched it, but
312 // it might have started while the fetch was outstanding. We don't want to
313 // have two copies of the app running, so check again.
315 // Also, it's possible the original URL was redirected to an app that is
316 // already running.
317 if (ConnectToRunningApplication(requested_url, qualifier, requestor_url,
318 &services, &exposed_services)) {
319 return;
322 const GURL app_url =
323 requested_url.scheme() == "mojo" ? requested_url : fetcher->GetURL();
325 InterfaceRequest<Application> request(
326 RegisterShell(app_url, qualifier, requestor_url, services.Pass(),
327 exposed_services.Pass(), on_application_end));
329 // If the response begins with a #!mojo <content-handler-url>, use it.
330 GURL content_handler_url;
331 std::string shebang;
332 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
333 LoadWithContentHandler(
334 content_handler_url, requestor_url, qualifier, request.Pass(),
335 fetcher->AsURLResponse(blocking_pool_,
336 static_cast<int>(shebang.size())));
337 return;
340 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
341 if (iter != mime_type_to_url_.end()) {
342 LoadWithContentHandler(iter->second, requestor_url, qualifier,
343 request.Pass(),
344 fetcher->AsURLResponse(blocking_pool_, 0));
345 return;
348 auto alias_iter = application_package_alias_.find(app_url);
349 if (alias_iter != application_package_alias_.end()) {
350 // We replace the qualifier with the one our package alias requested.
351 URLResponsePtr response(URLResponse::New());
352 response->url = String::From(app_url.spec());
354 std::string qualifier;
355 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
356 switches::kEnableMultiprocess)) {
357 // Why can't we use this in single process mode? Because of
358 // base::AtExitManager. If you link in ApplicationRunner into
359 // your code, and then we make initialize multiple copies of the
360 // application, we end up with multiple AtExitManagers and will check on
361 // the second one being created.
363 // Why doesn't that happen when running different apps? Because
364 // your_thing.mojo!base::AtExitManager and
365 // my_thing.mojo!base::AtExitManager are different symbols.
366 qualifier = alias_iter->second.second;
369 LoadWithContentHandler(alias_iter->second.first, requestor_url, qualifier,
370 request.Pass(), response.Pass());
371 return;
374 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
375 // application. That could either mean looking for the platform-specific dll
376 // header, or looking for some specific mojo signature prepended to the
377 // library.
378 // TODO(vtl): (Maybe this should be done by the factory/runner?)
380 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
381 NativeRunnerFactory::Options options;
382 if (url_to_native_options_.find(base_resolved_url) !=
383 url_to_native_options_.end()) {
384 DVLOG(2) << "Applying stored native options to resolved URL "
385 << fetcher->GetURL();
386 options = url_to_native_options_[base_resolved_url];
389 fetcher->AsPath(
390 blocking_pool_,
391 base::Bind(&ApplicationManager::RunNativeApplication,
392 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
393 options, cleanup, base::Passed(fetcher.Pass())));
396 void ApplicationManager::RunNativeApplication(
397 InterfaceRequest<Application> application_request,
398 const NativeRunnerFactory::Options& options,
399 NativeApplicationCleanup cleanup,
400 scoped_ptr<Fetcher> fetcher,
401 const base::FilePath& path,
402 bool path_exists) {
403 // We only passed fetcher to keep it alive. Done with it now.
404 fetcher.reset();
406 DCHECK(application_request.is_pending());
408 if (!path_exists) {
409 LOG(ERROR) << "Library not started because library path '" << path.value()
410 << "' does not exist.";
411 return;
414 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
415 path.AsUTF8Unsafe());
416 NativeRunner* runner = native_runner_factory_->Create(options).release();
417 native_runners_.push_back(runner);
418 runner->Start(path, cleanup, application_request.Pass(),
419 base::Bind(&ApplicationManager::CleanupRunner,
420 weak_ptr_factory_.GetWeakPtr(), runner));
423 void ApplicationManager::RegisterContentHandler(
424 const std::string& mime_type,
425 const GURL& content_handler_url) {
426 DCHECK(content_handler_url.is_valid())
427 << "Content handler URL is invalid for mime type " << mime_type;
428 mime_type_to_url_[mime_type] = content_handler_url;
431 void ApplicationManager::RegisterApplicationPackageAlias(
432 const GURL& alias,
433 const GURL& content_handler_package,
434 const std::string& qualifier) {
435 application_package_alias_[alias] =
436 std::make_pair(content_handler_package, qualifier);
439 void ApplicationManager::LoadWithContentHandler(
440 const GURL& content_handler_url,
441 const GURL& requestor_url,
442 const std::string& qualifier,
443 InterfaceRequest<Application> application_request,
444 URLResponsePtr url_response) {
445 ContentHandlerConnection* connection = nullptr;
446 std::pair<GURL, std::string> key(content_handler_url, qualifier);
447 URLToContentHandlerMap::iterator iter = url_to_content_handler_.find(key);
448 if (iter != url_to_content_handler_.end()) {
449 connection = iter->second;
450 } else {
451 connection = new ContentHandlerConnection(this, content_handler_url,
452 requestor_url, qualifier);
453 url_to_content_handler_[key] = connection;
456 connection->content_handler()->StartApplication(application_request.Pass(),
457 url_response.Pass());
460 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
461 const GURL& url) {
462 URLToLoaderMap::iterator it = url_to_loader_.find(url);
463 if (it != url_to_loader_.end())
464 delete it->second;
465 url_to_loader_[url] = loader.release();
468 void ApplicationManager::SetLoaderForScheme(
469 scoped_ptr<ApplicationLoader> loader,
470 const std::string& scheme) {
471 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
472 if (it != scheme_to_loader_.end())
473 delete it->second;
474 scheme_to_loader_[scheme] = loader.release();
477 void ApplicationManager::SetNativeOptionsForURL(
478 const NativeRunnerFactory::Options& options,
479 const GURL& url) {
480 DCHECK(!url.has_query()); // Precondition.
481 // Apply mappings and resolution to get the resolved URL.
482 GURL resolved_url =
483 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
484 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
485 // TODO(vtl): We should probably also remove/disregard the query string (and
486 // maybe canonicalize in other ways).
487 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
488 << " (original URL " << url << ")";
489 url_to_native_options_[resolved_url] = options;
492 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
493 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
494 if (url_it != url_to_loader_.end())
495 return url_it->second;
496 auto scheme_it = scheme_to_loader_.find(url.scheme());
497 if (scheme_it != scheme_to_loader_.end())
498 return scheme_it->second;
499 return nullptr;
502 void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
503 // Called from ~ShellImpl, so we do not need to call Destroy here.
504 const Identity identity = shell_impl->identity();
505 base::Closure on_application_end = shell_impl->on_application_end();
506 // Remove the shell.
507 auto it = identity_to_shell_impl_.find(identity);
508 DCHECK(it != identity_to_shell_impl_.end());
509 delete it->second;
510 identity_to_shell_impl_.erase(it);
511 if (!on_application_end.is_null())
512 on_application_end.Run();
515 void ApplicationManager::OnContentHandlerError(
516 ContentHandlerConnection* content_handler) {
517 // Remove the mapping to the content handler.
518 auto it = url_to_content_handler_.find(
519 std::make_pair(content_handler->content_handler_url(),
520 content_handler->content_handler_qualifier()));
521 DCHECK(it != url_to_content_handler_.end());
522 delete it->second;
523 url_to_content_handler_.erase(it);
526 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
527 const GURL& application_url,
528 const std::string& interface_name) {
529 ServiceProviderPtr services;
530 mojo::URLRequestPtr request(mojo::URLRequest::New());
531 request->url = mojo::String::From(application_url.spec());
532 ConnectToApplication(request.Pass(), GURL(), GetProxy(&services), nullptr,
533 base::Closure());
534 MessagePipe pipe;
535 services->ConnectToService(interface_name, pipe.handle1.Pass());
536 return pipe.handle0.Pass();
539 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
540 native_runners_.erase(
541 std::find(native_runners_.begin(), native_runners_.end(), runner));
544 } // namespace shell
545 } // namespace mojo