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"
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"
31 bool has_created_instance
= false;
36 ApplicationManager::TestAPI::TestAPI(ApplicationManager
* 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
>());
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
)) {
95 GURL resolved_url
= delegate_
->ResolveMojoURL(mapped_url
);
96 if (ConnectToRunningApplication(originator
, resolved_url
, qualifier
,
97 requestor_url
, &services
,
98 &exposed_services
, filter
)) {
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
))) {
110 if (ConnectToApplicationWithLoader(
111 originator
, requested_gurl
, qualifier
, resolved_url
, requestor_url
,
112 &services
, &exposed_services
, filter
, on_application_end
,
113 GetLoaderForURL(resolved_url
))) {
117 if (ConnectToApplicationWithLoader(
118 originator
, requested_gurl
, qualifier
, resolved_url
, requestor_url
,
119 &services
, &exposed_services
, filter
, on_application_end
,
120 default_loader_
.get())) {
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(
132 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
))) {
136 if (resolved_url
.SchemeIsFile()) {
137 // LocalFetcher uses the network service to infer MIME types from URLs.
138 // Skip this for mojo URLs to avoid recursively loading the network service.
139 if (!network_service_
&& !requested_gurl
.SchemeIs("mojo"))
140 ConnectToService(GURL("mojo:network_service"), &network_service_
);
142 network_service_
.get(), resolved_url
,
143 GetBaseURLAndQuery(resolved_url
, nullptr),
144 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
));
148 if (mapped_url
.SchemeIs("mojo") &&
149 base::CommandLine::ForCurrentProcess()->HasSwitch(
150 switches::kUseUpdater
)) {
151 ConnectToService(GURL("mojo:updater"), &updater_
);
153 mapped_url
, updater_
.get(),
154 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
));
158 if (!url_loader_factory_
)
159 ConnectToService(GURL("mojo:network_service"), &url_loader_factory_
);
161 const NativeApplicationCleanup cleanup
=
162 base::CommandLine::ForCurrentProcess()->HasSwitch(
163 switches::kDontDeleteOnDownload
)
164 ? NativeApplicationCleanup::DONT_DELETE
165 : NativeApplicationCleanup::DELETE
;
167 if (requested_gurl
.SchemeIs("mojo")) {
168 // Use the resolved mojo URL in the request to support origin mapping, etc.
169 mojo::URLRequestPtr
resolved_url_request(mojo::URLRequest::New());
170 resolved_url_request
->url
= resolved_url
.spec();
171 new NetworkFetcher(disable_cache_
, resolved_url_request
.Pass(),
172 url_loader_factory_
.get(),
173 base::Bind(callback
, cleanup
));
177 new NetworkFetcher(disable_cache_
, requested_url
.Pass(),
178 url_loader_factory_
.get(), base::Bind(callback
, cleanup
));
181 bool ApplicationManager::ConnectToRunningApplication(
182 ApplicationInstance
* originator
,
183 const GURL
& resolved_url
,
184 const std::string
& qualifier
,
185 const GURL
& requestor_url
,
186 InterfaceRequest
<ServiceProvider
>* services
,
187 ServiceProviderPtr
* exposed_services
,
188 const CapabilityFilter
& filter
) {
189 GURL application_url
= GetBaseURLAndQuery(resolved_url
, nullptr);
190 ApplicationInstance
* instance
=
191 GetApplicationInstance(Identity(application_url
, qualifier
));
195 instance
->ConnectToClient(originator
, resolved_url
, requestor_url
,
196 services
->Pass(), exposed_services
->Pass(),
201 bool ApplicationManager::ConnectToApplicationWithLoader(
202 ApplicationInstance
* originator
,
203 const GURL
& requested_url
,
204 const std::string
& qualifier
,
205 const GURL
& resolved_url
,
206 const GURL
& requestor_url
,
207 InterfaceRequest
<ServiceProvider
>* services
,
208 ServiceProviderPtr
* exposed_services
,
209 const CapabilityFilter
& filter
,
210 const base::Closure
& on_application_end
,
211 ApplicationLoader
* loader
) {
216 requested_url
.SchemeIs("mojo") ? requested_url
: resolved_url
;
220 RegisterInstance(originator
, app_url
, qualifier
, requestor_url
,
221 services
->Pass(), exposed_services
->Pass(),
222 filter
, on_application_end
));
226 InterfaceRequest
<Application
> ApplicationManager::RegisterInstance(
227 ApplicationInstance
* originator
,
229 const std::string
& qualifier
,
230 const GURL
& requestor_url
,
231 InterfaceRequest
<ServiceProvider
> services
,
232 ServiceProviderPtr exposed_services
,
233 const CapabilityFilter
& filter
,
234 const base::Closure
& on_application_end
) {
235 Identity
app_identity(app_url
, qualifier
);
237 ApplicationPtr application
;
238 InterfaceRequest
<Application
> application_request
= GetProxy(&application
);
239 ApplicationInstance
* instance
= new ApplicationInstance(
240 application
.Pass(), this,
241 originator
? originator
->identity() : Identity(GURL()), app_identity
,
242 filter
, on_application_end
);
243 identity_to_instance_
[app_identity
] = instance
;
244 instance
->InitializeApplication();
245 instance
->ConnectToClient(originator
, app_url
, requestor_url
, services
.Pass(),
246 exposed_services
.Pass(), filter
);
247 return application_request
.Pass();
250 ApplicationInstance
* ApplicationManager::GetApplicationInstance(
251 const Identity
& identity
) const {
252 const auto& instance_it
= identity_to_instance_
.find(identity
);
253 if (instance_it
!= identity_to_instance_
.end())
254 return instance_it
->second
;
258 void ApplicationManager::HandleFetchCallback(
259 ApplicationInstance
* originator
,
260 const GURL
& requested_url
,
261 const std::string
& qualifier
,
262 const GURL
& requestor_url
,
263 InterfaceRequest
<ServiceProvider
> services
,
264 ServiceProviderPtr exposed_services
,
265 const CapabilityFilter
& filter
,
266 const base::Closure
& on_application_end
,
267 NativeApplicationCleanup cleanup
,
268 scoped_ptr
<Fetcher
> fetcher
) {
270 // Network error. Drop |application_request| to tell requestor.
274 GURL redirect_url
= fetcher
->GetRedirectURL();
275 if (!redirect_url
.is_empty()) {
276 // And around we go again... Whee!
277 // TODO(sky): this loses |requested_url|.
278 mojo::URLRequestPtr
request(mojo::URLRequest::New());
279 request
->url
= mojo::String::From(redirect_url
.spec());
280 HttpHeaderPtr header
= HttpHeader::New();
281 header
->name
= "Referer";
282 header
->value
= fetcher
->GetRedirectReferer().spec();
283 request
->headers
.push_back(header
.Pass());
284 ConnectToApplication(originator
, request
.Pass(), qualifier
, requestor_url
,
285 services
.Pass(), exposed_services
.Pass(), filter
,
290 // We already checked if the application was running before we fetched it, but
291 // it might have started while the fetch was outstanding. We don't want to
292 // have two copies of the app running, so check again.
294 // Also, it's possible the original URL was redirected to an app that is
296 if (ConnectToRunningApplication(originator
, requested_url
, qualifier
,
297 requestor_url
, &services
,
298 &exposed_services
, filter
)) {
303 requested_url
.scheme() == "mojo" ? requested_url
: fetcher
->GetURL();
305 InterfaceRequest
<Application
> request(
306 RegisterInstance(originator
, app_url
, qualifier
, requestor_url
,
307 services
.Pass(), exposed_services
.Pass(), filter
,
308 on_application_end
));
310 // For resources that are loaded with content handlers, we group app instances
313 // If the response begins with a #!mojo <content-handler-url>, use it.
314 GURL content_handler_url
;
316 bool enable_multi_process
= base::CommandLine::ForCurrentProcess()->HasSwitch(
317 switches::kEnableMultiprocess
);
319 if (fetcher
->PeekContentHandler(&shebang
, &content_handler_url
)) {
320 URLResponsePtr
response(fetcher
->AsURLResponse(
321 blocking_pool_
, static_cast<int>(shebang
.size())));
323 enable_multi_process
? response
->site
.To
<std::string
>() : std::string();
324 LoadWithContentHandler(originator
, content_handler_url
, requestor_url
, site
,
325 filter
, request
.Pass(), response
.Pass());
329 MimeTypeToURLMap::iterator iter
= mime_type_to_url_
.find(fetcher
->MimeType());
330 if (iter
!= mime_type_to_url_
.end()) {
331 URLResponsePtr
response(fetcher
->AsURLResponse(blocking_pool_
, 0));
333 enable_multi_process
? response
->site
.To
<std::string
>() : std::string();
334 LoadWithContentHandler(originator
, iter
->second
, requestor_url
, site
,
335 filter
, request
.Pass(), response
.Pass());
339 auto alias_iter
= application_package_alias_
.find(app_url
);
340 if (alias_iter
!= application_package_alias_
.end()) {
341 // We replace the qualifier with the one our package alias requested.
342 URLResponsePtr
response(URLResponse::New());
343 response
->url
= String::From(app_url
.spec());
345 std::string qualifier
;
346 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
347 switches::kEnableMultiprocess
)) {
348 // Why can't we use this in single process mode? Because of
349 // base::AtExitManager. If you link in ApplicationRunner into
350 // your code, and then we make initialize multiple copies of the
351 // application, we end up with multiple AtExitManagers and will check on
352 // the second one being created.
354 // Why doesn't that happen when running different apps? Because
355 // your_thing.mojo!base::AtExitManager and
356 // my_thing.mojo!base::AtExitManager are different symbols.
357 qualifier
= alias_iter
->second
.second
;
360 LoadWithContentHandler(originator
, alias_iter
->second
.first
, requestor_url
,
361 qualifier
, filter
, request
.Pass(),
366 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
367 // application. That could either mean looking for the platform-specific dll
368 // header, or looking for some specific mojo signature prepended to the
370 // TODO(vtl): (Maybe this should be done by the factory/runner?)
372 GURL base_resolved_url
= GetBaseURLAndQuery(fetcher
->GetURL(), nullptr);
373 NativeRunnerFactory::Options options
;
374 if (url_to_native_options_
.find(base_resolved_url
) !=
375 url_to_native_options_
.end()) {
376 DVLOG(2) << "Applying stored native options to resolved URL "
377 << fetcher
->GetURL();
378 options
= url_to_native_options_
[base_resolved_url
];
381 // TODO(erg): Have a better way of switching the sandbox on. For now, switch
382 // it on hard coded when we're using some of the sandboxable core services.
383 bool start_sandboxed
= false;
384 if (app_url
== GURL("mojo://core_services/") && qualifier
== "Core")
385 start_sandboxed
= true;
386 else if (app_url
== GURL("mojo://html_viewer/"))
387 start_sandboxed
= true;
389 fetcher
->AsPath(blocking_pool_
,
390 base::Bind(&ApplicationManager::RunNativeApplication
,
391 weak_ptr_factory_
.GetWeakPtr(),
392 base::Passed(request
.Pass()), start_sandboxed
,
393 options
, cleanup
, base::Passed(fetcher
.Pass())));
396 void ApplicationManager::RunNativeApplication(
397 InterfaceRequest
<Application
> application_request
,
398 bool start_sandboxed
,
399 const NativeRunnerFactory::Options
& options
,
400 NativeApplicationCleanup cleanup
,
401 scoped_ptr
<Fetcher
> fetcher
,
402 const base::FilePath
& path
,
404 // We only passed fetcher to keep it alive. Done with it now.
407 DCHECK(application_request
.is_pending());
410 LOG(ERROR
) << "Library not started because library path '" << path
.value()
411 << "' does not exist.";
415 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
416 path
.AsUTF8Unsafe());
417 NativeRunner
* runner
= native_runner_factory_
->Create(options
).release();
418 native_runners_
.push_back(runner
);
419 runner
->Start(path
, start_sandboxed
, cleanup
, application_request
.Pass(),
420 base::Bind(&ApplicationManager::CleanupRunner
,
421 weak_ptr_factory_
.GetWeakPtr(), runner
));
424 void ApplicationManager::RegisterContentHandler(
425 const std::string
& mime_type
,
426 const GURL
& content_handler_url
) {
427 DCHECK(content_handler_url
.is_valid())
428 << "Content handler URL is invalid for mime type " << mime_type
;
429 mime_type_to_url_
[mime_type
] = content_handler_url
;
432 void ApplicationManager::RegisterApplicationPackageAlias(
434 const GURL
& content_handler_package
,
435 const std::string
& qualifier
) {
436 application_package_alias_
[alias
] =
437 std::make_pair(content_handler_package
, qualifier
);
440 void ApplicationManager::LoadWithContentHandler(
441 ApplicationInstance
* originator
,
442 const GURL
& content_handler_url
,
443 const GURL
& requestor_url
,
444 const std::string
& qualifier
,
445 const CapabilityFilter
& filter
,
446 InterfaceRequest
<Application
> application_request
,
447 URLResponsePtr url_response
) {
448 ContentHandlerConnection
* connection
= nullptr;
449 std::pair
<GURL
, std::string
> key(content_handler_url
, qualifier
);
450 // TODO(beng): Figure out the extent to which capability filter should be
451 // factored into handler identity.
452 URLToContentHandlerMap::iterator iter
= url_to_content_handler_
.find(key
);
453 if (iter
!= url_to_content_handler_
.end()) {
454 connection
= iter
->second
;
456 connection
= new ContentHandlerConnection(
457 originator
, this, content_handler_url
, requestor_url
, qualifier
,
459 url_to_content_handler_
[key
] = connection
;
462 connection
->content_handler()->StartApplication(application_request
.Pass(),
463 url_response
.Pass());
466 void ApplicationManager::SetLoaderForURL(scoped_ptr
<ApplicationLoader
> loader
,
468 URLToLoaderMap::iterator it
= url_to_loader_
.find(url
);
469 if (it
!= url_to_loader_
.end())
471 url_to_loader_
[url
] = loader
.release();
474 void ApplicationManager::SetLoaderForScheme(
475 scoped_ptr
<ApplicationLoader
> loader
,
476 const std::string
& scheme
) {
477 SchemeToLoaderMap::iterator it
= scheme_to_loader_
.find(scheme
);
478 if (it
!= scheme_to_loader_
.end())
480 scheme_to_loader_
[scheme
] = loader
.release();
483 void ApplicationManager::SetNativeOptionsForURL(
484 const NativeRunnerFactory::Options
& options
,
486 DCHECK(!url
.has_query()); // Precondition.
487 // Apply mappings and resolution to get the resolved URL.
489 delegate_
->ResolveMojoURL(delegate_
->ResolveMappings(url
));
490 DCHECK(!resolved_url
.has_query()); // Still shouldn't have query.
491 // TODO(vtl): We should probably also remove/disregard the query string (and
492 // maybe canonicalize in other ways).
493 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
494 << " (original URL " << url
<< ")";
495 url_to_native_options_
[resolved_url
] = options
;
498 ApplicationLoader
* ApplicationManager::GetLoaderForURL(const GURL
& url
) {
499 auto url_it
= url_to_loader_
.find(GetBaseURLAndQuery(url
, nullptr));
500 if (url_it
!= url_to_loader_
.end())
501 return url_it
->second
;
502 auto scheme_it
= scheme_to_loader_
.find(url
.scheme());
503 if (scheme_it
!= scheme_to_loader_
.end())
504 return scheme_it
->second
;
508 void ApplicationManager::OnApplicationInstanceError(
509 ApplicationInstance
* instance
) {
510 // Called from ~ApplicationInstance, so we do not need to call Destroy here.
511 const Identity identity
= instance
->identity();
512 base::Closure on_application_end
= instance
->on_application_end();
514 auto it
= identity_to_instance_
.find(identity
);
515 DCHECK(it
!= identity_to_instance_
.end());
517 identity_to_instance_
.erase(it
);
518 if (!on_application_end
.is_null())
519 on_application_end
.Run();
522 void ApplicationManager::OnContentHandlerConnectionClosed(
523 ContentHandlerConnection
* content_handler
) {
524 // Remove the mapping to the content handler.
525 auto it
= url_to_content_handler_
.find(
526 std::make_pair(content_handler
->content_handler_url(),
527 content_handler
->content_handler_qualifier()));
528 DCHECK(it
!= url_to_content_handler_
.end());
529 url_to_content_handler_
.erase(it
);
532 void ApplicationManager::CleanupRunner(NativeRunner
* runner
) {
533 native_runners_
.erase(
534 std::find(native_runners_
.begin(), native_runners_
.end(), runner
));
537 ScopedMessagePipeHandle
ApplicationManager::ConnectToServiceByName(
538 const GURL
& application_url
,
539 const std::string
& interface_name
) {
540 ServiceProviderPtr services
;
541 mojo::URLRequestPtr
request(mojo::URLRequest::New());
542 request
->url
= mojo::String::From(application_url
.spec());
543 ConnectToApplication(nullptr, request
.Pass(), std::string(), GURL(),
544 GetProxy(&services
), nullptr,
545 GetPermissiveCapabilityFilter(), base::Closure());
547 services
->ConnectToService(interface_name
, pipe
.handle1
.Pass());
548 return pipe
.handle0
.Pass();