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/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"
32 bool has_created_instance
= false;
37 ApplicationManager::TestAPI::TestAPI(ApplicationManager
* 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
>());
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
)) {
96 GURL resolved_url
= delegate_
->ResolveMojoURL(mapped_url
);
97 if (ConnectToRunningApplication(originator
, resolved_url
, qualifier
,
98 requestor_url
, &services
,
99 &exposed_services
, &filter
)) {
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
))) {
111 if (ConnectToApplicationWithLoader(
112 originator
, requested_gurl
, qualifier
, resolved_url
, requestor_url
,
113 &services
, &exposed_services
, &filter
, on_application_end
,
114 GetLoaderForURL(resolved_url
))) {
118 if (ConnectToApplicationWithLoader(
119 originator
, requested_gurl
, qualifier
, resolved_url
, requestor_url
,
120 &services
, &exposed_services
, &filter
, on_application_end
,
121 default_loader_
.get())) {
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()),
132 if (delegate_
->CreateFetcher(
134 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
))) {
138 if (resolved_url
.SchemeIsFile()) {
140 resolved_url
, GetBaseURLAndQuery(resolved_url
, nullptr),
141 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
));
145 if (mapped_url
.SchemeIs("mojo") &&
146 base::CommandLine::ForCurrentProcess()->HasSwitch(
147 switches::kUseUpdater
)) {
148 ConnectToService(GURL("mojo:updater"), &updater_
);
150 mapped_url
, updater_
.get(),
151 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
));
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
));
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
);
192 instance
->ConnectToClient(originator
, resolved_url
, requestor_url
,
193 services
->Pass(), exposed_services
->Pass(),
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
) {
213 requested_url
.SchemeIs("mojo") ? requested_url
: resolved_url
;
217 RegisterInstance(originator
, app_url
, qualifier
, requestor_url
,
218 services
->Pass(), exposed_services
->Pass(),
219 filter
->Pass(), on_application_end
));
223 InterfaceRequest
<Application
> ApplicationManager::RegisterInstance(
224 ApplicationInstance
* originator
,
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()) {
242 filter
->filter
.To
<ApplicationInstance::CapabilityFilter
>();
244 ApplicationInstance
* instance
= new ApplicationInstance(
245 application
.Pass(), this, app_identity
, capability_filter
,
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(
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
;
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
) {
276 // Network error. Drop |application_request| to tell requestor.
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,
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
302 if (ConnectToRunningApplication(originator
, requested_url
, qualifier
,
303 requestor_url
, &services
,
304 &exposed_services
, &filter
)) {
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
319 // If the response begins with a #!mojo <content-handler-url>, use it.
320 GURL content_handler_url
;
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())));
329 enable_multi_process
? response
->site
.To
<std::string
>() : std::string();
330 LoadWithContentHandler(content_handler_url
, requestor_url
, site
,
331 request
.Pass(), response
.Pass());
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));
339 enable_multi_process
? response
->site
.To
<std::string
>() : std::string();
340 LoadWithContentHandler(iter
->second
, requestor_url
, site
, request
.Pass(),
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());
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
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
];
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
,
400 // We only passed fetcher to keep it alive. Done with it now.
403 DCHECK(application_request
.is_pending());
406 LOG(ERROR
) << "Library not started because library path '" << path
.value()
407 << "' does not exist.";
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(
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
;
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
,
459 URLToLoaderMap::iterator it
= url_to_loader_
.find(url
);
460 if (it
!= url_to_loader_
.end())
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())
471 scheme_to_loader_
[scheme
] = loader
.release();
474 void ApplicationManager::SetNativeOptionsForURL(
475 const NativeRunnerFactory::Options
& options
,
477 DCHECK(!url
.has_query()); // Precondition.
478 // Apply mappings and resolution to get the 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
;
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();
505 auto it
= identity_to_instance_
.find(identity
);
506 DCHECK(it
!= identity_to_instance_
.end());
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());
537 services
->ConnectToService(interface_name
, pipe
.handle1
.Pass());
538 return pipe
.handle0
.Pass();