Remove temporary logging added for debugging a racing.
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blobf71990b6a37775ae8357cde9e61aec4d5173649b
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/shell/application_instance.h"
17 #include "mojo/shell/content_handler_connection.h"
18 #include "mojo/shell/fetcher.h"
19 #include "mojo/shell/local_fetcher.h"
20 #include "mojo/shell/network_fetcher.h"
21 #include "mojo/shell/query_util.h"
22 #include "mojo/shell/switches.h"
23 #include "mojo/shell/update_fetcher.h"
25 namespace mojo {
26 namespace shell {
28 namespace {
30 // Used by TestAPI.
31 bool has_created_instance = false;
33 } // namespace
35 // static
36 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
37 : manager_(manager) {
40 ApplicationManager::TestAPI::~TestAPI() {
43 bool ApplicationManager::TestAPI::HasCreatedInstance() {
44 return has_created_instance;
47 bool ApplicationManager::TestAPI::HasRunningInstanceForURL(
48 const GURL& url) const {
49 return manager_->identity_to_instance_.find(Identity(url)) !=
50 manager_->identity_to_instance_.end();
53 ApplicationManager::ApplicationManager(Delegate* delegate)
54 : delegate_(delegate), weak_ptr_factory_(this) {
57 ApplicationManager::~ApplicationManager() {
58 URLToContentHandlerMap url_to_content_handler(url_to_content_handler_);
59 for (auto& pair : url_to_content_handler)
60 pair.second->CloseConnection();
61 TerminateShellConnections();
62 STLDeleteValues(&url_to_loader_);
63 STLDeleteValues(&scheme_to_loader_);
66 void ApplicationManager::TerminateShellConnections() {
67 STLDeleteValues(&identity_to_instance_);
70 void ApplicationManager::ConnectToApplication(
71 ApplicationInstance* originator,
72 mojo::URLRequestPtr requested_url,
73 const std::string& qualifier,
74 const GURL& requestor_url,
75 InterfaceRequest<ServiceProvider> services,
76 ServiceProviderPtr exposed_services,
77 const CapabilityFilter& filter,
78 const base::Closure& on_application_end) {
79 GURL requested_gurl(requested_url->url.To<std::string>());
80 TRACE_EVENT_INSTANT1(
81 "mojo_shell", "ApplicationManager::ConnectToApplication",
82 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_gurl.spec());
83 DCHECK(requested_gurl.is_valid());
85 // We check both the mapped and resolved urls for existing instances because
86 // external applications can be registered for the unresolved mojo:foo urls.
88 GURL mapped_url = delegate_->ResolveMappings(requested_gurl);
89 if (ConnectToRunningApplication(originator, mapped_url, qualifier,
90 requestor_url, &services,
91 &exposed_services, filter)) {
92 return;
95 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
96 if (ConnectToRunningApplication(originator, resolved_url, qualifier,
97 requestor_url, &services,
98 &exposed_services, filter)) {
99 return;
102 // The application is not running, let's compute the parameters.
103 if (ConnectToApplicationWithLoader(
104 originator, requested_gurl, qualifier, mapped_url, requestor_url,
105 &services, &exposed_services, filter, on_application_end,
106 GetLoaderForURL(mapped_url))) {
107 return;
110 if (ConnectToApplicationWithLoader(
111 originator, requested_gurl, qualifier, resolved_url, requestor_url,
112 &services, &exposed_services, filter, on_application_end,
113 GetLoaderForURL(resolved_url))) {
114 return;
117 if (ConnectToApplicationWithLoader(
118 originator, requested_gurl, qualifier, resolved_url, requestor_url,
119 &services, &exposed_services, filter, on_application_end,
120 default_loader_.get())) {
121 return;
124 auto callback = base::Bind(
125 &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
126 originator, requested_gurl, qualifier, requestor_url,
127 base::Passed(services.Pass()), base::Passed(exposed_services.Pass()),
128 filter, on_application_end);
130 if (delegate_->CreateFetcher(
131 resolved_url,
132 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
133 return;
136 if (resolved_url.SchemeIsFile()) {
137 new LocalFetcher(
138 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
139 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
140 return;
143 if (mapped_url.SchemeIs("mojo") &&
144 base::CommandLine::ForCurrentProcess()->HasSwitch(
145 switches::kUseUpdater)) {
146 ConnectToService(GURL("mojo:updater"), &updater_);
147 new UpdateFetcher(
148 mapped_url, updater_.get(),
149 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
150 return;
153 if (!url_loader_factory_)
154 ConnectToService(GURL("mojo:network_service"), &url_loader_factory_);
156 const NativeApplicationCleanup cleanup =
157 base::CommandLine::ForCurrentProcess()->HasSwitch(
158 switches::kDontDeleteOnDownload)
159 ? NativeApplicationCleanup::DONT_DELETE
160 : NativeApplicationCleanup::DELETE;
162 if (requested_gurl.SchemeIs("mojo")) {
163 // Use the resolved mojo URL in the request to support origin mapping, etc.
164 mojo::URLRequestPtr resolved_url_request(mojo::URLRequest::New());
165 resolved_url_request->url = resolved_url.spec();
166 new NetworkFetcher(disable_cache_, resolved_url_request.Pass(),
167 url_loader_factory_.get(),
168 base::Bind(callback, cleanup));
169 return;
172 new NetworkFetcher(disable_cache_, requested_url.Pass(),
173 url_loader_factory_.get(), base::Bind(callback, cleanup));
176 bool ApplicationManager::ConnectToRunningApplication(
177 ApplicationInstance* originator,
178 const GURL& resolved_url,
179 const std::string& qualifier,
180 const GURL& requestor_url,
181 InterfaceRequest<ServiceProvider>* services,
182 ServiceProviderPtr* exposed_services,
183 const CapabilityFilter& filter) {
184 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
185 ApplicationInstance* instance =
186 GetApplicationInstance(Identity(application_url, qualifier));
187 if (!instance)
188 return false;
190 instance->ConnectToClient(originator, resolved_url, requestor_url,
191 services->Pass(), exposed_services->Pass(),
192 filter);
193 return true;
196 bool ApplicationManager::ConnectToApplicationWithLoader(
197 ApplicationInstance* originator,
198 const GURL& requested_url,
199 const std::string& qualifier,
200 const GURL& resolved_url,
201 const GURL& requestor_url,
202 InterfaceRequest<ServiceProvider>* services,
203 ServiceProviderPtr* exposed_services,
204 const CapabilityFilter& filter,
205 const base::Closure& on_application_end,
206 ApplicationLoader* loader) {
207 if (!loader)
208 return false;
210 const GURL app_url =
211 requested_url.SchemeIs("mojo") ? requested_url : resolved_url;
213 loader->Load(
214 resolved_url,
215 RegisterInstance(originator, app_url, qualifier, requestor_url,
216 services->Pass(), exposed_services->Pass(),
217 filter, on_application_end));
218 return true;
221 InterfaceRequest<Application> ApplicationManager::RegisterInstance(
222 ApplicationInstance* originator,
223 const GURL& app_url,
224 const std::string& qualifier,
225 const GURL& requestor_url,
226 InterfaceRequest<ServiceProvider> services,
227 ServiceProviderPtr exposed_services,
228 const CapabilityFilter& filter,
229 const base::Closure& on_application_end) {
230 Identity app_identity(app_url, qualifier);
232 ApplicationPtr application;
233 InterfaceRequest<Application> application_request = GetProxy(&application);
234 ApplicationInstance* instance = new ApplicationInstance(
235 application.Pass(), this,
236 originator ? originator->identity() : Identity(GURL()), app_identity,
237 filter, on_application_end);
238 identity_to_instance_[app_identity] = instance;
239 instance->InitializeApplication();
240 instance->ConnectToClient(originator, app_url, requestor_url, services.Pass(),
241 exposed_services.Pass(), filter);
242 return application_request.Pass();
245 ApplicationInstance* ApplicationManager::GetApplicationInstance(
246 const Identity& identity) const {
247 const auto& instance_it = identity_to_instance_.find(identity);
248 if (instance_it != identity_to_instance_.end())
249 return instance_it->second;
250 return nullptr;
253 void ApplicationManager::HandleFetchCallback(
254 ApplicationInstance* originator,
255 const GURL& requested_url,
256 const std::string& qualifier,
257 const GURL& requestor_url,
258 InterfaceRequest<ServiceProvider> services,
259 ServiceProviderPtr exposed_services,
260 const CapabilityFilter& filter,
261 const base::Closure& on_application_end,
262 NativeApplicationCleanup cleanup,
263 scoped_ptr<Fetcher> fetcher) {
264 if (!fetcher) {
265 // Network error. Drop |application_request| to tell requestor.
266 return;
269 GURL redirect_url = fetcher->GetRedirectURL();
270 if (!redirect_url.is_empty()) {
271 // And around we go again... Whee!
272 // TODO(sky): this loses |requested_url|.
273 mojo::URLRequestPtr request(mojo::URLRequest::New());
274 request->url = mojo::String::From(redirect_url.spec());
275 HttpHeaderPtr header = HttpHeader::New();
276 header->name = "Referer";
277 header->value = fetcher->GetRedirectReferer().spec();
278 request->headers.push_back(header.Pass());
279 ConnectToApplication(originator, request.Pass(), qualifier, requestor_url,
280 services.Pass(), exposed_services.Pass(), filter,
281 on_application_end);
282 return;
285 // We already checked if the application was running before we fetched it, but
286 // it might have started while the fetch was outstanding. We don't want to
287 // have two copies of the app running, so check again.
289 // Also, it's possible the original URL was redirected to an app that is
290 // already running.
291 if (ConnectToRunningApplication(originator, requested_url, qualifier,
292 requestor_url, &services,
293 &exposed_services, filter)) {
294 return;
297 const GURL app_url =
298 requested_url.scheme() == "mojo" ? requested_url : fetcher->GetURL();
300 InterfaceRequest<Application> request(
301 RegisterInstance(originator, app_url, qualifier, requestor_url,
302 services.Pass(), exposed_services.Pass(), filter,
303 on_application_end));
305 // For resources that are loaded with content handlers, we group app instances
306 // by site.
308 // If the response begins with a #!mojo <content-handler-url>, use it.
309 GURL content_handler_url;
310 std::string shebang;
311 bool enable_multi_process = base::CommandLine::ForCurrentProcess()->HasSwitch(
312 switches::kEnableMultiprocess);
314 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
315 URLResponsePtr response(fetcher->AsURLResponse(
316 blocking_pool_, static_cast<int>(shebang.size())));
317 std::string site =
318 enable_multi_process ? response->site.To<std::string>() : std::string();
319 LoadWithContentHandler(originator, content_handler_url, requestor_url, site,
320 filter, request.Pass(), response.Pass());
321 return;
324 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
325 if (iter != mime_type_to_url_.end()) {
326 URLResponsePtr response(fetcher->AsURLResponse(blocking_pool_, 0));
327 std::string site =
328 enable_multi_process ? response->site.To<std::string>() : std::string();
329 LoadWithContentHandler(originator, iter->second, requestor_url, site,
330 filter, request.Pass(), response.Pass());
331 return;
334 auto alias_iter = application_package_alias_.find(app_url);
335 if (alias_iter != application_package_alias_.end()) {
336 // We replace the qualifier with the one our package alias requested.
337 URLResponsePtr response(URLResponse::New());
338 response->url = String::From(app_url.spec());
340 std::string qualifier;
341 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
342 switches::kEnableMultiprocess)) {
343 // Why can't we use this in single process mode? Because of
344 // base::AtExitManager. If you link in ApplicationRunner into
345 // your code, and then we make initialize multiple copies of the
346 // application, we end up with multiple AtExitManagers and will check on
347 // the second one being created.
349 // Why doesn't that happen when running different apps? Because
350 // your_thing.mojo!base::AtExitManager and
351 // my_thing.mojo!base::AtExitManager are different symbols.
352 qualifier = alias_iter->second.second;
355 LoadWithContentHandler(originator, alias_iter->second.first, requestor_url,
356 qualifier, filter, request.Pass(),
357 response.Pass());
358 return;
361 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
362 // application. That could either mean looking for the platform-specific dll
363 // header, or looking for some specific mojo signature prepended to the
364 // library.
365 // TODO(vtl): (Maybe this should be done by the factory/runner?)
367 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
368 NativeRunnerFactory::Options options;
369 if (url_to_native_options_.find(base_resolved_url) !=
370 url_to_native_options_.end()) {
371 DVLOG(2) << "Applying stored native options to resolved URL "
372 << fetcher->GetURL();
373 options = url_to_native_options_[base_resolved_url];
376 // TODO(erg): Have a better way of switching the sandbox on. For now, switch
377 // it on hard coded when we're using some of the sandboxable core services.
378 bool start_sandboxed = false;
379 if (app_url == GURL("mojo://core_services/") && qualifier == "Sandboxed Core")
380 start_sandboxed = true;
382 fetcher->AsPath(blocking_pool_,
383 base::Bind(&ApplicationManager::RunNativeApplication,
384 weak_ptr_factory_.GetWeakPtr(),
385 base::Passed(request.Pass()), start_sandboxed,
386 options, cleanup, base::Passed(fetcher.Pass())));
389 void ApplicationManager::RunNativeApplication(
390 InterfaceRequest<Application> application_request,
391 bool start_sandboxed,
392 const NativeRunnerFactory::Options& options,
393 NativeApplicationCleanup cleanup,
394 scoped_ptr<Fetcher> fetcher,
395 const base::FilePath& path,
396 bool path_exists) {
397 // We only passed fetcher to keep it alive. Done with it now.
398 fetcher.reset();
400 DCHECK(application_request.is_pending());
402 if (!path_exists) {
403 LOG(ERROR) << "Library not started because library path '" << path.value()
404 << "' does not exist.";
405 return;
408 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
409 path.AsUTF8Unsafe());
410 NativeRunner* runner = native_runner_factory_->Create(options).release();
411 native_runners_.push_back(runner);
412 runner->Start(path, start_sandboxed, cleanup, application_request.Pass(),
413 base::Bind(&ApplicationManager::CleanupRunner,
414 weak_ptr_factory_.GetWeakPtr(), runner));
417 void ApplicationManager::RegisterContentHandler(
418 const std::string& mime_type,
419 const GURL& content_handler_url) {
420 DCHECK(content_handler_url.is_valid())
421 << "Content handler URL is invalid for mime type " << mime_type;
422 mime_type_to_url_[mime_type] = content_handler_url;
425 void ApplicationManager::RegisterApplicationPackageAlias(
426 const GURL& alias,
427 const GURL& content_handler_package,
428 const std::string& qualifier) {
429 application_package_alias_[alias] =
430 std::make_pair(content_handler_package, qualifier);
433 void ApplicationManager::LoadWithContentHandler(
434 ApplicationInstance* originator,
435 const GURL& content_handler_url,
436 const GURL& requestor_url,
437 const std::string& qualifier,
438 const CapabilityFilter& filter,
439 InterfaceRequest<Application> application_request,
440 URLResponsePtr url_response) {
441 ContentHandlerConnection* connection = nullptr;
442 std::pair<GURL, std::string> key(content_handler_url, qualifier);
443 // TODO(beng): Figure out the extent to which capability filter should be
444 // factored into handler identity.
445 URLToContentHandlerMap::iterator iter = url_to_content_handler_.find(key);
446 if (iter != url_to_content_handler_.end()) {
447 connection = iter->second;
448 } else {
449 connection = new ContentHandlerConnection(
450 originator, this, content_handler_url, requestor_url, qualifier,
451 filter);
452 url_to_content_handler_[key] = connection;
455 connection->content_handler()->StartApplication(application_request.Pass(),
456 url_response.Pass());
459 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
460 const GURL& url) {
461 URLToLoaderMap::iterator it = url_to_loader_.find(url);
462 if (it != url_to_loader_.end())
463 delete it->second;
464 url_to_loader_[url] = loader.release();
467 void ApplicationManager::SetLoaderForScheme(
468 scoped_ptr<ApplicationLoader> loader,
469 const std::string& scheme) {
470 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
471 if (it != scheme_to_loader_.end())
472 delete it->second;
473 scheme_to_loader_[scheme] = loader.release();
476 void ApplicationManager::SetNativeOptionsForURL(
477 const NativeRunnerFactory::Options& options,
478 const GURL& url) {
479 DCHECK(!url.has_query()); // Precondition.
480 // Apply mappings and resolution to get the resolved URL.
481 GURL resolved_url =
482 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
483 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
484 // TODO(vtl): We should probably also remove/disregard the query string (and
485 // maybe canonicalize in other ways).
486 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
487 << " (original URL " << url << ")";
488 url_to_native_options_[resolved_url] = options;
491 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
492 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
493 if (url_it != url_to_loader_.end())
494 return url_it->second;
495 auto scheme_it = scheme_to_loader_.find(url.scheme());
496 if (scheme_it != scheme_to_loader_.end())
497 return scheme_it->second;
498 return nullptr;
501 void ApplicationManager::OnApplicationInstanceError(
502 ApplicationInstance* instance) {
503 // Called from ~ApplicationInstance, so we do not need to call Destroy here.
504 const Identity identity = instance->identity();
505 base::Closure on_application_end = instance->on_application_end();
506 // Remove the shell.
507 auto it = identity_to_instance_.find(identity);
508 DCHECK(it != identity_to_instance_.end());
509 delete it->second;
510 identity_to_instance_.erase(it);
511 if (!on_application_end.is_null())
512 on_application_end.Run();
515 void ApplicationManager::OnContentHandlerConnectionClosed(
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 url_to_content_handler_.erase(it);
525 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
526 native_runners_.erase(
527 std::find(native_runners_.begin(), native_runners_.end(), runner));
530 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
531 const GURL& application_url,
532 const std::string& interface_name) {
533 ServiceProviderPtr services;
534 mojo::URLRequestPtr request(mojo::URLRequest::New());
535 request->url = mojo::String::From(application_url.spec());
536 ConnectToApplication(nullptr, request.Pass(), std::string(), GURL(),
537 GetProxy(&services), nullptr,
538 GetPermissiveCapabilityFilter(), base::Closure());
539 MessagePipe pipe;
540 services->ConnectToService(interface_name, pipe.handle1.Pass());
541 return pipe.handle0.Pass();
544 } // namespace shell
545 } // namespace mojo