Update V8 to version 4.7.19.
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blobacafadb715f5655e574ab9c73ae8e476bab6f293
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 void OnEmptyOnConnectCallback(uint32_t content_handler_id) {}
35 } // namespace
37 // static
38 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
39 : manager_(manager) {
42 ApplicationManager::TestAPI::~TestAPI() {
45 bool ApplicationManager::TestAPI::HasCreatedInstance() {
46 return has_created_instance;
49 bool ApplicationManager::TestAPI::HasRunningInstanceForURL(
50 const GURL& url) const {
51 return manager_->identity_to_instance_.find(Identity(url)) !=
52 manager_->identity_to_instance_.end();
55 ApplicationManager::ApplicationManager(Delegate* delegate)
56 : delegate_(delegate),
57 disable_cache_(false),
58 content_handler_id_counter_(0u),
59 weak_ptr_factory_(this) {}
61 ApplicationManager::~ApplicationManager() {
62 URLToContentHandlerMap url_to_content_handler(url_to_content_handler_);
63 for (auto& pair : url_to_content_handler)
64 pair.second->CloseConnection();
65 TerminateShellConnections();
66 STLDeleteValues(&url_to_loader_);
67 STLDeleteValues(&scheme_to_loader_);
70 void ApplicationManager::TerminateShellConnections() {
71 STLDeleteValues(&identity_to_instance_);
74 void ApplicationManager::ConnectToApplication(
75 ApplicationInstance* originator,
76 mojo::URLRequestPtr requested_url,
77 const std::string& qualifier,
78 const GURL& requestor_url,
79 InterfaceRequest<ServiceProvider> services,
80 ServiceProviderPtr exposed_services,
81 const CapabilityFilter& filter,
82 const base::Closure& on_application_end,
83 const Shell::ConnectToApplicationCallback& connect_callback) {
84 GURL requested_gurl(requested_url->url.To<std::string>());
85 TRACE_EVENT_INSTANT1(
86 "mojo_shell", "ApplicationManager::ConnectToApplication",
87 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_gurl.spec());
88 DCHECK(requested_gurl.is_valid());
90 // We check both the mapped and resolved urls for existing instances because
91 // external applications can be registered for the unresolved mojo:foo urls.
93 GURL mapped_url = delegate_->ResolveMappings(requested_gurl);
94 if (ConnectToRunningApplication(originator, mapped_url, qualifier,
95 requestor_url, &services, &exposed_services,
96 filter, connect_callback)) {
97 return;
100 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
101 if (ConnectToRunningApplication(originator, resolved_url, qualifier,
102 requestor_url, &services, &exposed_services,
103 filter, connect_callback)) {
104 return;
107 // The application is not running, let's compute the parameters.
108 if (ConnectToApplicationWithLoader(
109 originator, requested_gurl, qualifier, mapped_url, requestor_url,
110 &services, &exposed_services, filter, on_application_end,
111 connect_callback, GetLoaderForURL(mapped_url))) {
112 return;
115 if (ConnectToApplicationWithLoader(
116 originator, requested_gurl, qualifier, resolved_url, requestor_url,
117 &services, &exposed_services, filter, on_application_end,
118 connect_callback, GetLoaderForURL(resolved_url))) {
119 return;
122 if (ConnectToApplicationWithLoader(
123 originator, requested_gurl, qualifier, resolved_url, requestor_url,
124 &services, &exposed_services, filter, on_application_end,
125 connect_callback, default_loader_.get())) {
126 return;
129 auto callback = base::Bind(
130 &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
131 originator, requested_gurl, qualifier, requestor_url,
132 base::Passed(services.Pass()), base::Passed(exposed_services.Pass()),
133 filter, on_application_end, connect_callback);
135 if (delegate_->CreateFetcher(
136 resolved_url,
137 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
138 return;
141 if (resolved_url.SchemeIsFile()) {
142 // LocalFetcher uses the network service to infer MIME types from URLs.
143 // Skip this for mojo URLs to avoid recursively loading the network service.
144 if (!network_service_ && !requested_gurl.SchemeIs("mojo"))
145 ConnectToService(GURL("mojo:network_service"), &network_service_);
146 new LocalFetcher(
147 network_service_.get(), resolved_url,
148 GetBaseURLAndQuery(resolved_url, nullptr),
149 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
150 return;
153 if (mapped_url.SchemeIs("mojo") &&
154 base::CommandLine::ForCurrentProcess()->HasSwitch(
155 switches::kUseUpdater)) {
156 ConnectToService(GURL("mojo:updater"), &updater_);
157 new UpdateFetcher(
158 mapped_url, updater_.get(),
159 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
160 return;
163 if (!url_loader_factory_)
164 ConnectToService(GURL("mojo:network_service"), &url_loader_factory_);
166 const NativeApplicationCleanup cleanup =
167 base::CommandLine::ForCurrentProcess()->HasSwitch(
168 switches::kDontDeleteOnDownload)
169 ? NativeApplicationCleanup::DONT_DELETE
170 : NativeApplicationCleanup::DELETE;
172 if (requested_gurl.SchemeIs("mojo")) {
173 // Use the resolved mojo URL in the request to support origin mapping, etc.
174 mojo::URLRequestPtr resolved_url_request(mojo::URLRequest::New());
175 resolved_url_request->url = resolved_url.spec();
176 new NetworkFetcher(disable_cache_, resolved_url_request.Pass(),
177 url_loader_factory_.get(),
178 base::Bind(callback, cleanup));
179 return;
182 new NetworkFetcher(disable_cache_, requested_url.Pass(),
183 url_loader_factory_.get(), base::Bind(callback, cleanup));
186 bool ApplicationManager::ConnectToRunningApplication(
187 ApplicationInstance* originator,
188 const GURL& resolved_url,
189 const std::string& qualifier,
190 const GURL& requestor_url,
191 InterfaceRequest<ServiceProvider>* services,
192 ServiceProviderPtr* exposed_services,
193 const CapabilityFilter& filter,
194 const Shell::ConnectToApplicationCallback& connect_callback) {
195 ApplicationInstance* instance =
196 GetApplicationInstance(Identity(resolved_url, qualifier));
197 if (!instance)
198 return false;
200 instance->ConnectToClient(originator, resolved_url, requestor_url,
201 services->Pass(), exposed_services->Pass(), filter,
202 connect_callback);
203 return true;
206 bool ApplicationManager::ConnectToApplicationWithLoader(
207 ApplicationInstance* originator,
208 const GURL& requested_url,
209 const std::string& qualifier,
210 const GURL& resolved_url,
211 const GURL& requestor_url,
212 InterfaceRequest<ServiceProvider>* services,
213 ServiceProviderPtr* exposed_services,
214 const CapabilityFilter& filter,
215 const base::Closure& on_application_end,
216 const Shell::ConnectToApplicationCallback& connect_callback,
217 ApplicationLoader* loader) {
218 if (!loader)
219 return false;
221 const GURL app_url =
222 requested_url.SchemeIs("mojo") ? requested_url : resolved_url;
224 loader->Load(
225 resolved_url,
226 RegisterInstance(originator, app_url, qualifier, requestor_url,
227 services->Pass(), exposed_services->Pass(), filter,
228 on_application_end, connect_callback, nullptr));
229 return true;
232 InterfaceRequest<Application> ApplicationManager::RegisterInstance(
233 ApplicationInstance* originator,
234 const GURL& app_url,
235 const std::string& qualifier,
236 const GURL& requestor_url,
237 InterfaceRequest<ServiceProvider> services,
238 ServiceProviderPtr exposed_services,
239 const CapabilityFilter& filter,
240 const base::Closure& on_application_end,
241 const Shell::ConnectToApplicationCallback& connect_callback,
242 ApplicationInstance** resulting_instance) {
243 Identity app_identity(app_url, qualifier);
245 ApplicationPtr application;
246 InterfaceRequest<Application> application_request = GetProxy(&application);
247 ApplicationInstance* instance = new ApplicationInstance(
248 application.Pass(), this,
249 originator ? originator->identity() : Identity(GURL()), app_identity,
250 filter, Shell::kInvalidContentHandlerID, on_application_end);
251 DCHECK(identity_to_instance_.find(app_identity) ==
252 identity_to_instance_.end());
253 identity_to_instance_[app_identity] = instance;
254 instance->InitializeApplication();
255 instance->ConnectToClient(originator, app_url, requestor_url, services.Pass(),
256 exposed_services.Pass(), filter, connect_callback);
257 if (resulting_instance)
258 *resulting_instance = instance;
259 return application_request.Pass();
262 ApplicationInstance* ApplicationManager::GetApplicationInstance(
263 const Identity& identity) const {
264 const auto& instance_it = identity_to_instance_.find(identity);
265 if (instance_it != identity_to_instance_.end())
266 return instance_it->second;
267 return nullptr;
270 void ApplicationManager::HandleFetchCallback(
271 ApplicationInstance* originator,
272 const GURL& requested_url,
273 const std::string& qualifier,
274 const GURL& requestor_url,
275 InterfaceRequest<ServiceProvider> services,
276 ServiceProviderPtr exposed_services,
277 const CapabilityFilter& filter,
278 const base::Closure& on_application_end,
279 const Shell::ConnectToApplicationCallback& connect_callback,
280 NativeApplicationCleanup cleanup,
281 scoped_ptr<Fetcher> fetcher) {
282 if (!fetcher) {
283 // Network error. Drop |application_request| to tell requestor.
284 connect_callback.Run(Shell::kInvalidContentHandlerID);
285 return;
288 GURL redirect_url = fetcher->GetRedirectURL();
289 if (!redirect_url.is_empty()) {
290 // And around we go again... Whee!
291 // TODO(sky): this loses |requested_url|.
292 mojo::URLRequestPtr request(mojo::URLRequest::New());
293 request->url = mojo::String::From(redirect_url.spec());
294 HttpHeaderPtr header = HttpHeader::New();
295 header->name = "Referer";
296 header->value = fetcher->GetRedirectReferer().spec();
297 request->headers.push_back(header.Pass());
298 ConnectToApplication(originator, request.Pass(), qualifier, requestor_url,
299 services.Pass(), exposed_services.Pass(), filter,
300 on_application_end, connect_callback);
301 return;
304 // We already checked if the application was running before we fetched it, but
305 // it might have started while the fetch was outstanding. We don't want to
306 // have two copies of the app running, so check again.
308 // Also, it's possible the original URL was redirected to an app that is
309 // already running.
310 if (ConnectToRunningApplication(originator, requested_url, qualifier,
311 requestor_url, &services, &exposed_services,
312 filter, connect_callback)) {
313 return;
316 const GURL app_url =
317 requested_url.scheme() == "mojo" ? requested_url : fetcher->GetURL();
319 ApplicationInstance* app = nullptr;
320 InterfaceRequest<Application> request(
321 RegisterInstance(originator, app_url, qualifier, requestor_url,
322 services.Pass(), exposed_services.Pass(), filter,
323 on_application_end, EmptyConnectCallback(), &app));
325 // For resources that are loaded with content handlers, we group app instances
326 // by site.
328 // If the response begins with a #!mojo <content-handler-url>, use it.
329 GURL content_handler_url;
330 std::string shebang;
331 bool enable_multi_process = base::CommandLine::ForCurrentProcess()->HasSwitch(
332 switches::kEnableMultiprocess);
334 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
335 URLResponsePtr response(fetcher->AsURLResponse(
336 blocking_pool_, static_cast<int>(shebang.size())));
337 std::string site =
338 enable_multi_process ? response->site.To<std::string>() : std::string();
339 LoadWithContentHandler(originator, content_handler_url, requestor_url, site,
340 filter, connect_callback, app, request.Pass(),
341 response.Pass());
342 return;
345 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
346 if (iter != mime_type_to_url_.end()) {
347 URLResponsePtr response(fetcher->AsURLResponse(blocking_pool_, 0));
348 std::string site =
349 enable_multi_process ? response->site.To<std::string>() : std::string();
350 LoadWithContentHandler(originator, iter->second, requestor_url, site,
351 filter, connect_callback, app, request.Pass(),
352 response.Pass());
353 return;
356 auto alias_iter = application_package_alias_.find(app_url);
357 if (alias_iter != application_package_alias_.end()) {
358 // We replace the qualifier with the one our package alias requested.
359 URLResponsePtr response(URLResponse::New());
360 response->url = String::From(app_url.spec());
362 std::string qualifier;
363 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
364 switches::kEnableMultiprocess)) {
365 // Why can't we use this in single process mode? Because of
366 // base::AtExitManager. If you link in ApplicationRunner into
367 // your code, and then we make initialize multiple copies of the
368 // application, we end up with multiple AtExitManagers and will check on
369 // the second one being created.
371 // Why doesn't that happen when running different apps? Because
372 // your_thing.mojo!base::AtExitManager and
373 // my_thing.mojo!base::AtExitManager are different symbols.
374 qualifier = alias_iter->second.second;
377 LoadWithContentHandler(originator, alias_iter->second.first, requestor_url,
378 qualifier, filter, connect_callback, app,
379 request.Pass(), response.Pass());
380 return;
383 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
384 // application. That could either mean looking for the platform-specific dll
385 // header, or looking for some specific mojo signature prepended to the
386 // library.
387 // TODO(vtl): (Maybe this should be done by the factory/runner?)
389 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
390 NativeRunnerFactory::Options options;
391 if (url_to_native_options_.find(base_resolved_url) !=
392 url_to_native_options_.end()) {
393 DVLOG(2) << "Applying stored native options to resolved URL "
394 << fetcher->GetURL();
395 options = url_to_native_options_[base_resolved_url];
398 // TODO(erg): Have a better way of switching the sandbox on. For now, switch
399 // it on hard coded when we're using some of the sandboxable core services.
400 bool start_sandboxed = false;
401 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
402 switches::kMojoNoSandbox)) {
403 if (app_url == GURL("mojo://core_services/") && qualifier == "Core")
404 start_sandboxed = true;
405 else if (app_url == GURL("mojo://html_viewer/"))
406 start_sandboxed = true;
409 connect_callback.Run(Shell::kInvalidContentHandlerID);
411 fetcher->AsPath(blocking_pool_,
412 base::Bind(&ApplicationManager::RunNativeApplication,
413 weak_ptr_factory_.GetWeakPtr(),
414 base::Passed(request.Pass()), start_sandboxed,
415 options, cleanup, base::Passed(fetcher.Pass())));
418 void ApplicationManager::RunNativeApplication(
419 InterfaceRequest<Application> application_request,
420 bool start_sandboxed,
421 const NativeRunnerFactory::Options& options,
422 NativeApplicationCleanup cleanup,
423 scoped_ptr<Fetcher> fetcher,
424 const base::FilePath& path,
425 bool path_exists) {
426 // We only passed fetcher to keep it alive. Done with it now.
427 fetcher.reset();
429 DCHECK(application_request.is_pending());
431 if (!path_exists) {
432 LOG(ERROR) << "Library not started because library path '" << path.value()
433 << "' does not exist.";
434 return;
437 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
438 path.AsUTF8Unsafe());
439 NativeRunner* runner = native_runner_factory_->Create(options).release();
440 native_runners_.push_back(runner);
441 runner->Start(path, start_sandboxed, cleanup, application_request.Pass(),
442 base::Bind(&ApplicationManager::CleanupRunner,
443 weak_ptr_factory_.GetWeakPtr(), runner));
446 void ApplicationManager::RegisterContentHandler(
447 const std::string& mime_type,
448 const GURL& content_handler_url) {
449 DCHECK(content_handler_url.is_valid())
450 << "Content handler URL is invalid for mime type " << mime_type;
451 mime_type_to_url_[mime_type] = content_handler_url;
454 void ApplicationManager::RegisterApplicationPackageAlias(
455 const GURL& alias,
456 const GURL& content_handler_package,
457 const std::string& qualifier) {
458 application_package_alias_[alias] =
459 std::make_pair(content_handler_package, qualifier);
462 void ApplicationManager::LoadWithContentHandler(
463 ApplicationInstance* originator,
464 const GURL& content_handler_url,
465 const GURL& requestor_url,
466 const std::string& qualifier,
467 const CapabilityFilter& filter,
468 const Shell::ConnectToApplicationCallback& connect_callback,
469 ApplicationInstance* app,
470 InterfaceRequest<Application> application_request,
471 URLResponsePtr url_response) {
472 ContentHandlerConnection* connection = nullptr;
473 std::pair<GURL, std::string> key(content_handler_url, qualifier);
474 // TODO(beng): Figure out the extent to which capability filter should be
475 // factored into handler identity.
476 URLToContentHandlerMap::iterator iter = url_to_content_handler_.find(key);
477 if (iter != url_to_content_handler_.end()) {
478 connection = iter->second;
479 } else {
480 connection = new ContentHandlerConnection(
481 originator, this, content_handler_url, requestor_url, qualifier, filter,
482 ++content_handler_id_counter_);
483 url_to_content_handler_[key] = connection;
486 app->set_requesting_content_handler_id(connection->id());
487 connection->content_handler()->StartApplication(application_request.Pass(),
488 url_response.Pass());
489 connect_callback.Run(connection->id());
492 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
493 const GURL& url) {
494 URLToLoaderMap::iterator it = url_to_loader_.find(url);
495 if (it != url_to_loader_.end())
496 delete it->second;
497 url_to_loader_[url] = loader.release();
500 void ApplicationManager::SetLoaderForScheme(
501 scoped_ptr<ApplicationLoader> loader,
502 const std::string& scheme) {
503 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
504 if (it != scheme_to_loader_.end())
505 delete it->second;
506 scheme_to_loader_[scheme] = loader.release();
509 void ApplicationManager::SetNativeOptionsForURL(
510 const NativeRunnerFactory::Options& options,
511 const GURL& url) {
512 DCHECK(!url.has_query()); // Precondition.
513 // Apply mappings and resolution to get the resolved URL.
514 GURL resolved_url =
515 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
516 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
517 // TODO(vtl): We should probably also remove/disregard the query string (and
518 // maybe canonicalize in other ways).
519 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
520 << " (original URL " << url << ")";
521 url_to_native_options_[resolved_url] = options;
524 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
525 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
526 if (url_it != url_to_loader_.end())
527 return url_it->second;
528 auto scheme_it = scheme_to_loader_.find(url.scheme());
529 if (scheme_it != scheme_to_loader_.end())
530 return scheme_it->second;
531 return nullptr;
534 void ApplicationManager::OnApplicationInstanceError(
535 ApplicationInstance* instance) {
536 // Called from ~ApplicationInstance, so we do not need to call Destroy here.
537 const Identity identity = instance->identity();
538 base::Closure on_application_end = instance->on_application_end();
539 // Remove the shell.
540 auto it = identity_to_instance_.find(identity);
541 DCHECK(it != identity_to_instance_.end());
542 delete it->second;
543 identity_to_instance_.erase(it);
544 if (!on_application_end.is_null())
545 on_application_end.Run();
548 void ApplicationManager::OnContentHandlerConnectionClosed(
549 ContentHandlerConnection* content_handler) {
550 // Remove the mapping to the content handler.
551 auto it = url_to_content_handler_.find(
552 std::make_pair(content_handler->content_handler_url(),
553 content_handler->content_handler_qualifier()));
554 DCHECK(it != url_to_content_handler_.end());
555 url_to_content_handler_.erase(it);
558 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
559 native_runners_.erase(
560 std::find(native_runners_.begin(), native_runners_.end(), runner));
563 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
564 const GURL& application_url,
565 const std::string& interface_name) {
566 ServiceProviderPtr services;
567 mojo::URLRequestPtr request(mojo::URLRequest::New());
568 request->url = mojo::String::From(application_url.spec());
569 ConnectToApplication(nullptr, request.Pass(), std::string(), GURL(),
570 GetProxy(&services), nullptr,
571 GetPermissiveCapabilityFilter(), base::Closure(),
572 EmptyConnectCallback());
573 MessagePipe pipe;
574 services->ConnectToService(interface_name, pipe.handle1.Pass());
575 return pipe.handle0.Pass();
578 Shell::ConnectToApplicationCallback EmptyConnectCallback() {
579 return base::Bind(&OnEmptyOnConnectCallback);
582 } // namespace shell
583 } // namespace mojo