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/fetcher.h"
18 #include "mojo/shell/local_fetcher.h"
19 #include "mojo/shell/network_fetcher.h"
20 #include "mojo/shell/query_util.h"
21 #include "mojo/shell/shell_impl.h"
22 #include "mojo/shell/switches.h"
23 #include "mojo/shell/update_fetcher.h"
31 bool has_created_instance
= false;
35 class ApplicationManager::ContentHandlerConnection
: public ErrorHandler
{
37 ContentHandlerConnection(ApplicationManager
* manager
,
38 const GURL
& content_handler_url
,
39 const GURL
& requestor_url
,
40 const std::string
& qualifier
)
42 content_handler_url_(content_handler_url
),
43 content_handler_qualifier_(qualifier
) {
44 ServiceProviderPtr services
;
45 mojo::URLRequestPtr
request(mojo::URLRequest::New());
46 request
->url
= mojo::String::From(content_handler_url
.spec());
47 manager
->ConnectToApplicationInternal(
48 request
.Pass(), qualifier
, requestor_url
, GetProxy(&services
),
49 nullptr, base::Closure());
51 content_handler_
.Bind(
52 InterfacePtrInfo
<ContentHandler
>(pipe
.handle0
.Pass(), 0u));
53 services
->ConnectToService(ContentHandler::Name_
, pipe
.handle1
.Pass());
54 content_handler_
.set_error_handler(this);
57 ContentHandler
* content_handler() { return content_handler_
.get(); }
59 GURL
content_handler_url() { return content_handler_url_
; }
60 std::string
content_handler_qualifier() { return content_handler_qualifier_
; }
63 // ErrorHandler implementation:
64 void OnConnectionError() override
{ manager_
->OnContentHandlerError(this); }
66 ApplicationManager
* manager_
;
67 GURL content_handler_url_
;
68 std::string content_handler_qualifier_
;
69 ContentHandlerPtr content_handler_
;
71 DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection
);
75 ApplicationManager::TestAPI::TestAPI(ApplicationManager
* manager
)
79 ApplicationManager::TestAPI::~TestAPI() {
82 bool ApplicationManager::TestAPI::HasCreatedInstance() {
83 return has_created_instance
;
86 bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL
& url
) const {
87 return manager_
->identity_to_shell_impl_
.find(Identity(url
)) !=
88 manager_
->identity_to_shell_impl_
.end();
91 ApplicationManager::ApplicationManager(Delegate
* delegate
)
92 : delegate_(delegate
), weak_ptr_factory_(this) {
95 ApplicationManager::~ApplicationManager() {
96 STLDeleteValues(&url_to_content_handler_
);
97 TerminateShellConnections();
98 STLDeleteValues(&url_to_loader_
);
99 STLDeleteValues(&scheme_to_loader_
);
102 void ApplicationManager::TerminateShellConnections() {
103 STLDeleteValues(&identity_to_shell_impl_
);
106 void ApplicationManager::ConnectToApplication(
107 mojo::URLRequestPtr requested_url
,
108 const GURL
& requestor_url
,
109 InterfaceRequest
<ServiceProvider
> services
,
110 ServiceProviderPtr exposed_services
,
111 const base::Closure
& on_application_end
) {
112 ConnectToApplicationInternal(
113 requested_url
.Pass(), std::string(), requestor_url
, services
.Pass(),
114 exposed_services
.Pass(), on_application_end
);
117 void ApplicationManager::ConnectToApplicationInternal(
118 mojo::URLRequestPtr requested_url
,
119 const std::string
& qualifier
,
120 const GURL
& requestor_url
,
121 InterfaceRequest
<ServiceProvider
> services
,
122 ServiceProviderPtr exposed_services
,
123 const base::Closure
& on_application_end
) {
124 GURL
requested_gurl(requested_url
->url
.To
<std::string
>());
125 TRACE_EVENT_INSTANT1(
126 "mojo_shell", "ApplicationManager::ConnectToApplication",
127 TRACE_EVENT_SCOPE_THREAD
, "requested_url", requested_gurl
.spec());
128 DCHECK(requested_gurl
.is_valid());
130 // We check both the mapped and resolved urls for existing shell_impls because
131 // external applications can be registered for the unresolved mojo:foo urls.
133 GURL mapped_url
= delegate_
->ResolveMappings(requested_gurl
);
134 if (ConnectToRunningApplication(mapped_url
, qualifier
, requestor_url
,
135 &services
, &exposed_services
)) {
139 GURL resolved_url
= delegate_
->ResolveMojoURL(mapped_url
);
140 if (ConnectToRunningApplication(resolved_url
, qualifier
, requestor_url
,
141 &services
, &exposed_services
)) {
145 // The application is not running, let's compute the parameters.
146 if (ConnectToApplicationWithLoader(
147 requested_gurl
, qualifier
, mapped_url
, requestor_url
, &services
,
148 &exposed_services
, on_application_end
, GetLoaderForURL(mapped_url
))) {
152 if (ConnectToApplicationWithLoader(
153 requested_gurl
, qualifier
, resolved_url
, requestor_url
, &services
,
154 &exposed_services
, on_application_end
,
155 GetLoaderForURL(resolved_url
))) {
159 if (ConnectToApplicationWithLoader(
160 requested_gurl
, qualifier
, resolved_url
, requestor_url
, &services
,
161 &exposed_services
, on_application_end
, default_loader_
.get())) {
165 auto callback
= base::Bind(
166 &ApplicationManager::HandleFetchCallback
, weak_ptr_factory_
.GetWeakPtr(),
167 requested_gurl
, qualifier
, requestor_url
, base::Passed(services
.Pass()),
168 base::Passed(exposed_services
.Pass()), on_application_end
);
170 if (delegate_
->CreateFetcher(
172 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
))) {
176 if (resolved_url
.SchemeIsFile()) {
178 resolved_url
, GetBaseURLAndQuery(resolved_url
, nullptr),
179 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
));
183 if (mapped_url
.SchemeIs("mojo") &&
184 base::CommandLine::ForCurrentProcess()->HasSwitch(
185 switches::kUseUpdater
)) {
186 ConnectToService(GURL("mojo:updater"), &updater_
);
188 mapped_url
, updater_
.get(),
189 base::Bind(callback
, NativeApplicationCleanup::DONT_DELETE
));
193 if (!url_loader_factory_
)
194 ConnectToService(GURL("mojo:network_service"), &url_loader_factory_
);
196 const NativeApplicationCleanup cleanup
=
197 base::CommandLine::ForCurrentProcess()->HasSwitch(
198 switches::kDontDeleteOnDownload
)
199 ? NativeApplicationCleanup::DONT_DELETE
200 : NativeApplicationCleanup::DELETE
;
202 if (requested_gurl
.SchemeIs("mojo")) {
203 // Use the resolved mojo URL in the request to support origin mapping, etc.
204 mojo::URLRequestPtr
resolved_url_request(mojo::URLRequest::New());
205 resolved_url_request
->url
= resolved_url
.spec();
206 new NetworkFetcher(disable_cache_
, resolved_url_request
.Pass(),
207 url_loader_factory_
.get(),
208 base::Bind(callback
, cleanup
));
212 new NetworkFetcher(disable_cache_
, requested_url
.Pass(),
213 url_loader_factory_
.get(), base::Bind(callback
, cleanup
));
216 bool ApplicationManager::ConnectToRunningApplication(
217 const GURL
& resolved_url
,
218 const std::string
& qualifier
,
219 const GURL
& requestor_url
,
220 InterfaceRequest
<ServiceProvider
>* services
,
221 ServiceProviderPtr
* exposed_services
) {
222 GURL application_url
= GetBaseURLAndQuery(resolved_url
, nullptr);
223 ShellImpl
* shell_impl
= GetShellImpl(application_url
, qualifier
);
227 ConnectToClient(shell_impl
, resolved_url
, requestor_url
, services
->Pass(),
228 exposed_services
->Pass());
232 bool ApplicationManager::ConnectToApplicationWithLoader(
233 const GURL
& requested_url
,
234 const std::string
& qualifier
,
235 const GURL
& resolved_url
,
236 const GURL
& requestor_url
,
237 InterfaceRequest
<ServiceProvider
>* services
,
238 ServiceProviderPtr
* exposed_services
,
239 const base::Closure
& on_application_end
,
240 ApplicationLoader
* loader
) {
245 requested_url
.SchemeIs("mojo") ? requested_url
: resolved_url
;
249 RegisterShell(app_url
, qualifier
, requestor_url
, services
->Pass(),
250 exposed_services
->Pass(), on_application_end
));
254 InterfaceRequest
<Application
> ApplicationManager::RegisterShell(
256 const std::string
& qualifier
,
257 const GURL
& requestor_url
,
258 InterfaceRequest
<ServiceProvider
> services
,
259 ServiceProviderPtr exposed_services
,
260 const base::Closure
& on_application_end
) {
261 Identity
app_identity(app_url
, qualifier
);
263 ApplicationPtr application
;
264 InterfaceRequest
<Application
> application_request
= GetProxy(&application
);
266 new ShellImpl(application
.Pass(), this, app_identity
, on_application_end
);
267 identity_to_shell_impl_
[app_identity
] = shell
;
268 shell
->InitializeApplication();
269 ConnectToClient(shell
, app_url
, requestor_url
, services
.Pass(),
270 exposed_services
.Pass());
271 return application_request
.Pass();
274 ShellImpl
* ApplicationManager::GetShellImpl(const GURL
& url
,
275 const std::string
& qualifier
) {
276 const auto& shell_it
= identity_to_shell_impl_
.find(Identity(url
, qualifier
));
277 if (shell_it
!= identity_to_shell_impl_
.end())
278 return shell_it
->second
;
282 void ApplicationManager::ConnectToClient(
283 ShellImpl
* shell_impl
,
284 const GURL
& resolved_url
,
285 const GURL
& requestor_url
,
286 InterfaceRequest
<ServiceProvider
> services
,
287 ServiceProviderPtr exposed_services
) {
288 shell_impl
->ConnectToClient(resolved_url
, requestor_url
, services
.Pass(),
289 exposed_services
.Pass());
292 void ApplicationManager::HandleFetchCallback(
293 const GURL
& requested_url
,
294 const std::string
& qualifier
,
295 const GURL
& requestor_url
,
296 InterfaceRequest
<ServiceProvider
> services
,
297 ServiceProviderPtr exposed_services
,
298 const base::Closure
& on_application_end
,
299 NativeApplicationCleanup cleanup
,
300 scoped_ptr
<Fetcher
> fetcher
) {
302 // Network error. Drop |application_request| to tell requestor.
306 GURL redirect_url
= fetcher
->GetRedirectURL();
307 if (!redirect_url
.is_empty()) {
308 // And around we go again... Whee!
309 // TODO(sky): this loses |requested_url|.
310 mojo::URLRequestPtr
request(mojo::URLRequest::New());
311 request
->url
= mojo::String::From(redirect_url
.spec());
312 HttpHeaderPtr header
= HttpHeader::New();
313 header
->name
= "Referer";
314 header
->value
= fetcher
->GetRedirectReferer().spec();
315 request
->headers
.push_back(header
.Pass());
316 ConnectToApplicationInternal(request
.Pass(), qualifier
, requestor_url
,
317 services
.Pass(), exposed_services
.Pass(),
322 // We already checked if the application was running before we fetched it, but
323 // it might have started while the fetch was outstanding. We don't want to
324 // have two copies of the app running, so check again.
326 // Also, it's possible the original URL was redirected to an app that is
328 if (ConnectToRunningApplication(requested_url
, qualifier
, requestor_url
,
329 &services
, &exposed_services
)) {
334 requested_url
.scheme() == "mojo" ? requested_url
: fetcher
->GetURL();
336 InterfaceRequest
<Application
> request(
337 RegisterShell(app_url
, qualifier
, requestor_url
, services
.Pass(),
338 exposed_services
.Pass(), on_application_end
));
340 // If the response begins with a #!mojo <content-handler-url>, use it.
341 GURL content_handler_url
;
343 if (fetcher
->PeekContentHandler(&shebang
, &content_handler_url
)) {
344 LoadWithContentHandler(
345 content_handler_url
, requestor_url
, qualifier
, request
.Pass(),
346 fetcher
->AsURLResponse(blocking_pool_
,
347 static_cast<int>(shebang
.size())));
351 MimeTypeToURLMap::iterator iter
= mime_type_to_url_
.find(fetcher
->MimeType());
352 if (iter
!= mime_type_to_url_
.end()) {
353 LoadWithContentHandler(iter
->second
, requestor_url
, qualifier
,
355 fetcher
->AsURLResponse(blocking_pool_
, 0));
359 auto alias_iter
= application_package_alias_
.find(app_url
);
360 if (alias_iter
!= application_package_alias_
.end()) {
361 // We replace the qualifier with the one our package alias requested.
362 URLResponsePtr
response(URLResponse::New());
363 response
->url
= String::From(app_url
.spec());
365 std::string qualifier
;
366 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
367 switches::kEnableMultiprocess
)) {
368 // Why can't we use this in single process mode? Because of
369 // base::AtExitManager. If you link in ApplicationRunner into
370 // your code, and then we make initialize multiple copies of the
371 // application, we end up with multiple AtExitManagers and will check on
372 // the second one being created.
374 // Why doesn't that happen when running different apps? Because
375 // your_thing.mojo!base::AtExitManager and
376 // my_thing.mojo!base::AtExitManager are different symbols.
377 qualifier
= alias_iter
->second
.second
;
380 LoadWithContentHandler(alias_iter
->second
.first
, requestor_url
, qualifier
,
381 request
.Pass(), response
.Pass());
385 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
386 // application. That could either mean looking for the platform-specific dll
387 // header, or looking for some specific mojo signature prepended to the
389 // TODO(vtl): (Maybe this should be done by the factory/runner?)
391 GURL base_resolved_url
= GetBaseURLAndQuery(fetcher
->GetURL(), nullptr);
392 NativeRunnerFactory::Options options
;
393 if (url_to_native_options_
.find(base_resolved_url
) !=
394 url_to_native_options_
.end()) {
395 DVLOG(2) << "Applying stored native options to resolved URL "
396 << fetcher
->GetURL();
397 options
= url_to_native_options_
[base_resolved_url
];
402 base::Bind(&ApplicationManager::RunNativeApplication
,
403 weak_ptr_factory_
.GetWeakPtr(), base::Passed(request
.Pass()),
404 options
, cleanup
, base::Passed(fetcher
.Pass())));
407 void ApplicationManager::RunNativeApplication(
408 InterfaceRequest
<Application
> application_request
,
409 const NativeRunnerFactory::Options
& options
,
410 NativeApplicationCleanup cleanup
,
411 scoped_ptr
<Fetcher
> fetcher
,
412 const base::FilePath
& path
,
414 // We only passed fetcher to keep it alive. Done with it now.
417 DCHECK(application_request
.is_pending());
420 LOG(ERROR
) << "Library not started because library path '" << path
.value()
421 << "' does not exist.";
425 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
426 path
.AsUTF8Unsafe());
427 NativeRunner
* runner
= native_runner_factory_
->Create(options
).release();
428 native_runners_
.push_back(runner
);
429 runner
->Start(path
, cleanup
, application_request
.Pass(),
430 base::Bind(&ApplicationManager::CleanupRunner
,
431 weak_ptr_factory_
.GetWeakPtr(), runner
));
434 void ApplicationManager::RegisterContentHandler(
435 const std::string
& mime_type
,
436 const GURL
& content_handler_url
) {
437 DCHECK(content_handler_url
.is_valid())
438 << "Content handler URL is invalid for mime type " << mime_type
;
439 mime_type_to_url_
[mime_type
] = content_handler_url
;
442 void ApplicationManager::RegisterApplicationPackageAlias(
444 const GURL
& content_handler_package
,
445 const std::string
& qualifier
) {
446 application_package_alias_
[alias
] =
447 std::make_pair(content_handler_package
, qualifier
);
450 void ApplicationManager::LoadWithContentHandler(
451 const GURL
& content_handler_url
,
452 const GURL
& requestor_url
,
453 const std::string
& qualifier
,
454 InterfaceRequest
<Application
> application_request
,
455 URLResponsePtr url_response
) {
456 ContentHandlerConnection
* connection
= nullptr;
457 std::pair
<GURL
, std::string
> key(content_handler_url
, qualifier
);
458 URLToContentHandlerMap::iterator iter
= url_to_content_handler_
.find(key
);
459 if (iter
!= url_to_content_handler_
.end()) {
460 connection
= iter
->second
;
462 connection
= new ContentHandlerConnection(this, content_handler_url
,
463 requestor_url
, qualifier
);
464 url_to_content_handler_
[key
] = connection
;
467 connection
->content_handler()->StartApplication(application_request
.Pass(),
468 url_response
.Pass());
471 void ApplicationManager::SetLoaderForURL(scoped_ptr
<ApplicationLoader
> loader
,
473 URLToLoaderMap::iterator it
= url_to_loader_
.find(url
);
474 if (it
!= url_to_loader_
.end())
476 url_to_loader_
[url
] = loader
.release();
479 void ApplicationManager::SetLoaderForScheme(
480 scoped_ptr
<ApplicationLoader
> loader
,
481 const std::string
& scheme
) {
482 SchemeToLoaderMap::iterator it
= scheme_to_loader_
.find(scheme
);
483 if (it
!= scheme_to_loader_
.end())
485 scheme_to_loader_
[scheme
] = loader
.release();
488 void ApplicationManager::SetNativeOptionsForURL(
489 const NativeRunnerFactory::Options
& options
,
491 DCHECK(!url
.has_query()); // Precondition.
492 // Apply mappings and resolution to get the resolved URL.
494 delegate_
->ResolveMojoURL(delegate_
->ResolveMappings(url
));
495 DCHECK(!resolved_url
.has_query()); // Still shouldn't have query.
496 // TODO(vtl): We should probably also remove/disregard the query string (and
497 // maybe canonicalize in other ways).
498 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
499 << " (original URL " << url
<< ")";
500 url_to_native_options_
[resolved_url
] = options
;
503 ApplicationLoader
* ApplicationManager::GetLoaderForURL(const GURL
& url
) {
504 auto url_it
= url_to_loader_
.find(GetBaseURLAndQuery(url
, nullptr));
505 if (url_it
!= url_to_loader_
.end())
506 return url_it
->second
;
507 auto scheme_it
= scheme_to_loader_
.find(url
.scheme());
508 if (scheme_it
!= scheme_to_loader_
.end())
509 return scheme_it
->second
;
513 void ApplicationManager::OnShellImplError(ShellImpl
* shell_impl
) {
514 // Called from ~ShellImpl, so we do not need to call Destroy here.
515 const Identity identity
= shell_impl
->identity();
516 base::Closure on_application_end
= shell_impl
->on_application_end();
518 auto it
= identity_to_shell_impl_
.find(identity
);
519 DCHECK(it
!= identity_to_shell_impl_
.end());
521 identity_to_shell_impl_
.erase(it
);
522 if (!on_application_end
.is_null())
523 on_application_end
.Run();
526 void ApplicationManager::OnContentHandlerError(
527 ContentHandlerConnection
* content_handler
) {
528 // Remove the mapping to the content handler.
529 auto it
= url_to_content_handler_
.find(
530 std::make_pair(content_handler
->content_handler_url(),
531 content_handler
->content_handler_qualifier()));
532 DCHECK(it
!= url_to_content_handler_
.end());
534 url_to_content_handler_
.erase(it
);
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(request
.Pass(), GURL(), GetProxy(&services
), nullptr,
546 services
->ConnectToService(interface_name
, pipe
.handle1
.Pass());
547 return pipe
.handle0
.Pass();
550 void ApplicationManager::CleanupRunner(NativeRunner
* runner
) {
551 native_runners_
.erase(
552 std::find(native_runners_
.begin(), native_runners_
.end(), runner
));