Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blob899439275986e3570271f8d26364aa1a4e24ddd5
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/application_instance.h"
18 #include "mojo/shell/content_handler_connection.h"
19 #include "mojo/shell/fetcher.h"
20 #include "mojo/shell/local_fetcher.h"
21 #include "mojo/shell/network_fetcher.h"
22 #include "mojo/shell/query_util.h"
23 #include "mojo/shell/switches.h"
24 #include "mojo/shell/update_fetcher.h"
26 namespace mojo {
27 namespace shell {
29 namespace {
31 // Used by TestAPI.
32 bool has_created_instance = false;
34 } // namespace
36 // static
37 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
38 : manager_(manager) {
41 ApplicationManager::TestAPI::~TestAPI() {
44 bool ApplicationManager::TestAPI::HasCreatedInstance() {
45 return has_created_instance;
48 bool ApplicationManager::TestAPI::HasRunningInstanceForURL(
49 const GURL& url) const {
50 return manager_->identity_to_instance_.find(Identity(url)) !=
51 manager_->identity_to_instance_.end();
54 ApplicationManager::ApplicationManager(Delegate* delegate)
55 : delegate_(delegate), weak_ptr_factory_(this) {
58 ApplicationManager::~ApplicationManager() {
59 URLToContentHandlerMap url_to_content_handler(url_to_content_handler_);
60 for (auto& pair : url_to_content_handler)
61 pair.second->CloseConnection();
62 TerminateShellConnections();
63 STLDeleteValues(&url_to_loader_);
64 STLDeleteValues(&scheme_to_loader_);
67 void ApplicationManager::TerminateShellConnections() {
68 STLDeleteValues(&identity_to_instance_);
71 void ApplicationManager::ConnectToApplication(
72 ApplicationInstance* originator,
73 mojo::URLRequestPtr requested_url,
74 const std::string& qualifier,
75 const GURL& requestor_url,
76 InterfaceRequest<ServiceProvider> services,
77 ServiceProviderPtr exposed_services,
78 CapabilityFilterPtr filter,
79 const base::Closure& on_application_end) {
80 GURL requested_gurl(requested_url->url.To<std::string>());
81 TRACE_EVENT_INSTANT1(
82 "mojo_shell", "ApplicationManager::ConnectToApplication",
83 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_gurl.spec());
84 DCHECK(requested_gurl.is_valid());
86 // We check both the mapped and resolved urls for existing instances because
87 // external applications can be registered for the unresolved mojo:foo urls.
89 GURL mapped_url = delegate_->ResolveMappings(requested_gurl);
90 if (ConnectToRunningApplication(originator, mapped_url, qualifier,
91 requestor_url, &services,
92 &exposed_services, &filter)) {
93 return;
96 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
97 if (ConnectToRunningApplication(originator, resolved_url, qualifier,
98 requestor_url, &services,
99 &exposed_services, &filter)) {
100 return;
103 // The application is not running, let's compute the parameters.
104 if (ConnectToApplicationWithLoader(
105 originator, requested_gurl, qualifier, mapped_url, requestor_url,
106 &services, &exposed_services, &filter, on_application_end,
107 GetLoaderForURL(mapped_url))) {
108 return;
111 if (ConnectToApplicationWithLoader(
112 originator, requested_gurl, qualifier, resolved_url, requestor_url,
113 &services, &exposed_services, &filter, on_application_end,
114 GetLoaderForURL(resolved_url))) {
115 return;
118 if (ConnectToApplicationWithLoader(
119 originator, requested_gurl, qualifier, resolved_url, requestor_url,
120 &services, &exposed_services, &filter, on_application_end,
121 default_loader_.get())) {
122 return;
125 auto callback = base::Bind(
126 &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
127 originator, requested_gurl, qualifier, requestor_url,
128 base::Passed(services.Pass()), base::Passed(exposed_services.Pass()),
129 base::Passed(filter.Pass()),
130 on_application_end);
132 if (delegate_->CreateFetcher(
133 resolved_url,
134 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
135 return;
138 if (resolved_url.SchemeIsFile()) {
139 new LocalFetcher(
140 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
141 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
142 return;
145 if (mapped_url.SchemeIs("mojo") &&
146 base::CommandLine::ForCurrentProcess()->HasSwitch(
147 switches::kUseUpdater)) {
148 ConnectToService(GURL("mojo:updater"), &updater_);
149 new UpdateFetcher(
150 mapped_url, updater_.get(),
151 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
152 return;
155 if (!url_loader_factory_)
156 ConnectToService(GURL("mojo:network_service"), &url_loader_factory_);
158 const NativeApplicationCleanup cleanup =
159 base::CommandLine::ForCurrentProcess()->HasSwitch(
160 switches::kDontDeleteOnDownload)
161 ? NativeApplicationCleanup::DONT_DELETE
162 : NativeApplicationCleanup::DELETE;
164 if (requested_gurl.SchemeIs("mojo")) {
165 // Use the resolved mojo URL in the request to support origin mapping, etc.
166 mojo::URLRequestPtr resolved_url_request(mojo::URLRequest::New());
167 resolved_url_request->url = resolved_url.spec();
168 new NetworkFetcher(disable_cache_, resolved_url_request.Pass(),
169 url_loader_factory_.get(),
170 base::Bind(callback, cleanup));
171 return;
174 new NetworkFetcher(disable_cache_, requested_url.Pass(),
175 url_loader_factory_.get(), base::Bind(callback, cleanup));
178 bool ApplicationManager::ConnectToRunningApplication(
179 ApplicationInstance* originator,
180 const GURL& resolved_url,
181 const std::string& qualifier,
182 const GURL& requestor_url,
183 InterfaceRequest<ServiceProvider>* services,
184 ServiceProviderPtr* exposed_services,
185 CapabilityFilterPtr* filter) {
186 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
187 ApplicationInstance* instance =
188 GetApplicationInstance(application_url, qualifier);
189 if (!instance)
190 return false;
192 instance->ConnectToClient(originator, resolved_url, requestor_url,
193 services->Pass(), exposed_services->Pass(),
194 filter->Pass());
195 return true;
198 bool ApplicationManager::ConnectToApplicationWithLoader(
199 ApplicationInstance* originator,
200 const GURL& requested_url,
201 const std::string& qualifier,
202 const GURL& resolved_url,
203 const GURL& requestor_url,
204 InterfaceRequest<ServiceProvider>* services,
205 ServiceProviderPtr* exposed_services,
206 CapabilityFilterPtr* filter,
207 const base::Closure& on_application_end,
208 ApplicationLoader* loader) {
209 if (!loader)
210 return false;
212 const GURL app_url =
213 requested_url.SchemeIs("mojo") ? requested_url : resolved_url;
215 loader->Load(
216 resolved_url,
217 RegisterInstance(originator, app_url, qualifier, requestor_url,
218 services->Pass(), exposed_services->Pass(),
219 filter->Pass(), on_application_end));
220 return true;
223 InterfaceRequest<Application> ApplicationManager::RegisterInstance(
224 ApplicationInstance* originator,
225 const GURL& app_url,
226 const std::string& qualifier,
227 const GURL& requestor_url,
228 InterfaceRequest<ServiceProvider> services,
229 ServiceProviderPtr exposed_services,
230 CapabilityFilterPtr filter,
231 const base::Closure& on_application_end) {
232 Identity app_identity(app_url, qualifier);
234 ApplicationPtr application;
235 InterfaceRequest<Application> application_request = GetProxy(&application);
236 ApplicationInstance::CapabilityFilter capability_filter;
237 std::set<std::string> interfaces;
238 interfaces.insert("*");
239 capability_filter["*"] = interfaces;
240 if (!filter.is_null()) {
241 capability_filter =
242 filter->filter.To<ApplicationInstance::CapabilityFilter>();
244 ApplicationInstance* instance = new ApplicationInstance(
245 application.Pass(), this, app_identity, capability_filter,
246 on_application_end);
247 identity_to_instance_[app_identity] = instance;
248 instance->InitializeApplication();
249 instance->ConnectToClient(originator, app_url, requestor_url, services.Pass(),
250 exposed_services.Pass(), filter.Pass());
251 return application_request.Pass();
254 ApplicationInstance* ApplicationManager::GetApplicationInstance(
255 const GURL& url,
256 const std::string& qualifier) {
257 const auto& instance_it =
258 identity_to_instance_.find(Identity(url, qualifier));
259 if (instance_it != identity_to_instance_.end())
260 return instance_it->second;
261 return nullptr;
264 void ApplicationManager::HandleFetchCallback(
265 ApplicationInstance* originator,
266 const GURL& requested_url,
267 const std::string& qualifier,
268 const GURL& requestor_url,
269 InterfaceRequest<ServiceProvider> services,
270 ServiceProviderPtr exposed_services,
271 CapabilityFilterPtr filter,
272 const base::Closure& on_application_end,
273 NativeApplicationCleanup cleanup,
274 scoped_ptr<Fetcher> fetcher) {
275 if (!fetcher) {
276 // Network error. Drop |application_request| to tell requestor.
277 return;
280 GURL redirect_url = fetcher->GetRedirectURL();
281 if (!redirect_url.is_empty()) {
282 // And around we go again... Whee!
283 // TODO(sky): this loses |requested_url|.
284 mojo::URLRequestPtr request(mojo::URLRequest::New());
285 request->url = mojo::String::From(redirect_url.spec());
286 HttpHeaderPtr header = HttpHeader::New();
287 header->name = "Referer";
288 header->value = fetcher->GetRedirectReferer().spec();
289 request->headers.push_back(header.Pass());
290 ConnectToApplication(originator, request.Pass(), qualifier, requestor_url,
291 services.Pass(), exposed_services.Pass(), nullptr,
292 on_application_end);
293 return;
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.
300 // Also, it's possible the original URL was redirected to an app that is
301 // already running.
302 if (ConnectToRunningApplication(originator, requested_url, qualifier,
303 requestor_url, &services,
304 &exposed_services, &filter)) {
305 return;
308 const GURL app_url =
309 requested_url.scheme() == "mojo" ? requested_url : fetcher->GetURL();
311 InterfaceRequest<Application> request(
312 RegisterInstance(originator, app_url, qualifier, requestor_url,
313 services.Pass(), exposed_services.Pass(), filter.Pass(),
314 on_application_end));
316 // For resources that are loaded with content handlers, we group app instances
317 // by site.
319 // If the response begins with a #!mojo <content-handler-url>, use it.
320 GURL content_handler_url;
321 std::string shebang;
322 bool enable_multi_process = base::CommandLine::ForCurrentProcess()->HasSwitch(
323 switches::kEnableMultiprocess);
325 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
326 URLResponsePtr response(fetcher->AsURLResponse(
327 blocking_pool_, static_cast<int>(shebang.size())));
328 std::string site =
329 enable_multi_process ? response->site.To<std::string>() : std::string();
330 LoadWithContentHandler(content_handler_url, requestor_url, site,
331 request.Pass(), response.Pass());
332 return;
335 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
336 if (iter != mime_type_to_url_.end()) {
337 URLResponsePtr response(fetcher->AsURLResponse(blocking_pool_, 0));
338 std::string site =
339 enable_multi_process ? response->site.To<std::string>() : std::string();
340 LoadWithContentHandler(iter->second, requestor_url, site, request.Pass(),
341 response.Pass());
342 return;
345 auto alias_iter = application_package_alias_.find(app_url);
346 if (alias_iter != application_package_alias_.end()) {
347 // We replace the qualifier with the one our package alias requested.
348 URLResponsePtr response(URLResponse::New());
349 response->url = String::From(app_url.spec());
351 std::string qualifier;
352 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
353 switches::kEnableMultiprocess)) {
354 // Why can't we use this in single process mode? Because of
355 // base::AtExitManager. If you link in ApplicationRunner into
356 // your code, and then we make initialize multiple copies of the
357 // application, we end up with multiple AtExitManagers and will check on
358 // the second one being created.
360 // Why doesn't that happen when running different apps? Because
361 // your_thing.mojo!base::AtExitManager and
362 // my_thing.mojo!base::AtExitManager are different symbols.
363 qualifier = alias_iter->second.second;
366 LoadWithContentHandler(alias_iter->second.first, requestor_url, qualifier,
367 request.Pass(), response.Pass());
368 return;
371 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
372 // application. That could either mean looking for the platform-specific dll
373 // header, or looking for some specific mojo signature prepended to the
374 // library.
375 // TODO(vtl): (Maybe this should be done by the factory/runner?)
377 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
378 NativeRunnerFactory::Options options;
379 if (url_to_native_options_.find(base_resolved_url) !=
380 url_to_native_options_.end()) {
381 DVLOG(2) << "Applying stored native options to resolved URL "
382 << fetcher->GetURL();
383 options = url_to_native_options_[base_resolved_url];
386 fetcher->AsPath(
387 blocking_pool_,
388 base::Bind(&ApplicationManager::RunNativeApplication,
389 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
390 options, cleanup, base::Passed(fetcher.Pass())));
393 void ApplicationManager::RunNativeApplication(
394 InterfaceRequest<Application> application_request,
395 const NativeRunnerFactory::Options& options,
396 NativeApplicationCleanup cleanup,
397 scoped_ptr<Fetcher> fetcher,
398 const base::FilePath& path,
399 bool path_exists) {
400 // We only passed fetcher to keep it alive. Done with it now.
401 fetcher.reset();
403 DCHECK(application_request.is_pending());
405 if (!path_exists) {
406 LOG(ERROR) << "Library not started because library path '" << path.value()
407 << "' does not exist.";
408 return;
411 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
412 path.AsUTF8Unsafe());
413 NativeRunner* runner = native_runner_factory_->Create(options).release();
414 native_runners_.push_back(runner);
415 runner->Start(path, cleanup, application_request.Pass(),
416 base::Bind(&ApplicationManager::CleanupRunner,
417 weak_ptr_factory_.GetWeakPtr(), runner));
420 void ApplicationManager::RegisterContentHandler(
421 const std::string& mime_type,
422 const GURL& content_handler_url) {
423 DCHECK(content_handler_url.is_valid())
424 << "Content handler URL is invalid for mime type " << mime_type;
425 mime_type_to_url_[mime_type] = content_handler_url;
428 void ApplicationManager::RegisterApplicationPackageAlias(
429 const GURL& alias,
430 const GURL& content_handler_package,
431 const std::string& qualifier) {
432 application_package_alias_[alias] =
433 std::make_pair(content_handler_package, qualifier);
436 void ApplicationManager::LoadWithContentHandler(
437 const GURL& content_handler_url,
438 const GURL& requestor_url,
439 const std::string& qualifier,
440 InterfaceRequest<Application> application_request,
441 URLResponsePtr url_response) {
442 ContentHandlerConnection* connection = nullptr;
443 std::pair<GURL, std::string> key(content_handler_url, qualifier);
444 URLToContentHandlerMap::iterator iter = url_to_content_handler_.find(key);
445 if (iter != url_to_content_handler_.end()) {
446 connection = iter->second;
447 } else {
448 connection = new ContentHandlerConnection(this, content_handler_url,
449 requestor_url, qualifier);
450 url_to_content_handler_[key] = connection;
453 connection->content_handler()->StartApplication(application_request.Pass(),
454 url_response.Pass());
457 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
458 const GURL& url) {
459 URLToLoaderMap::iterator it = url_to_loader_.find(url);
460 if (it != url_to_loader_.end())
461 delete it->second;
462 url_to_loader_[url] = loader.release();
465 void ApplicationManager::SetLoaderForScheme(
466 scoped_ptr<ApplicationLoader> loader,
467 const std::string& scheme) {
468 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
469 if (it != scheme_to_loader_.end())
470 delete it->second;
471 scheme_to_loader_[scheme] = loader.release();
474 void ApplicationManager::SetNativeOptionsForURL(
475 const NativeRunnerFactory::Options& options,
476 const GURL& url) {
477 DCHECK(!url.has_query()); // Precondition.
478 // Apply mappings and resolution to get the resolved URL.
479 GURL resolved_url =
480 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
481 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
482 // TODO(vtl): We should probably also remove/disregard the query string (and
483 // maybe canonicalize in other ways).
484 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
485 << " (original URL " << url << ")";
486 url_to_native_options_[resolved_url] = options;
489 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
490 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
491 if (url_it != url_to_loader_.end())
492 return url_it->second;
493 auto scheme_it = scheme_to_loader_.find(url.scheme());
494 if (scheme_it != scheme_to_loader_.end())
495 return scheme_it->second;
496 return nullptr;
499 void ApplicationManager::OnApplicationInstanceError(
500 ApplicationInstance* instance) {
501 // Called from ~ApplicationInstance, so we do not need to call Destroy here.
502 const Identity identity = instance->identity();
503 base::Closure on_application_end = instance->on_application_end();
504 // Remove the shell.
505 auto it = identity_to_instance_.find(identity);
506 DCHECK(it != identity_to_instance_.end());
507 delete it->second;
508 identity_to_instance_.erase(it);
509 if (!on_application_end.is_null())
510 on_application_end.Run();
513 void ApplicationManager::OnContentHandlerConnectionClosed(
514 ContentHandlerConnection* content_handler) {
515 // Remove the mapping to the content handler.
516 auto it = url_to_content_handler_.find(
517 std::make_pair(content_handler->content_handler_url(),
518 content_handler->content_handler_qualifier()));
519 DCHECK(it != url_to_content_handler_.end());
520 url_to_content_handler_.erase(it);
523 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
524 native_runners_.erase(
525 std::find(native_runners_.begin(), native_runners_.end(), runner));
528 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
529 const GURL& application_url,
530 const std::string& interface_name) {
531 ServiceProviderPtr services;
532 mojo::URLRequestPtr request(mojo::URLRequest::New());
533 request->url = mojo::String::From(application_url.spec());
534 ConnectToApplication(nullptr, request.Pass(), std::string(), GURL(),
535 GetProxy(&services), nullptr, nullptr, base::Closure());
536 MessagePipe pipe;
537 services->ConnectToService(interface_name, pipe.handle1.Pass());
538 return pipe.handle0.Pass();
541 } // namespace shell
542 } // namespace mojo