Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blob3f51008dbddf2018edbd4b7147cd33f1c42d07fe
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/public/cpp/bindings/binding.h"
15 #include "mojo/public/cpp/bindings/error_handler.h"
16 #include "mojo/shell/fetcher.h"
17 #include "mojo/shell/local_fetcher.h"
18 #include "mojo/shell/network_fetcher.h"
19 #include "mojo/shell/query_util.h"
20 #include "mojo/shell/shell_impl.h"
21 #include "mojo/shell/switches.h"
22 #include "third_party/mojo_services/src/content_handler/public/interfaces/content_handler.mojom.h"
24 namespace mojo {
25 namespace shell {
27 namespace {
29 // Used by TestAPI.
30 bool has_created_instance = false;
32 } // namespace
34 class ApplicationManager::ContentHandlerConnection : public ErrorHandler {
35 public:
36 ContentHandlerConnection(ApplicationManager* manager,
37 const GURL& content_handler_url)
38 : manager_(manager), content_handler_url_(content_handler_url) {
39 ServiceProviderPtr services;
40 manager->ConnectToApplication(content_handler_url, GURL(),
41 GetProxy(&services), nullptr,
42 base::Closure());
43 MessagePipe pipe;
44 content_handler_.Bind(pipe.handle0.Pass());
45 services->ConnectToService(ContentHandler::Name_, pipe.handle1.Pass());
46 content_handler_.set_error_handler(this);
49 ContentHandler* content_handler() { return content_handler_.get(); }
51 GURL content_handler_url() { return content_handler_url_; }
53 private:
54 // ErrorHandler implementation:
55 void OnConnectionError() override { manager_->OnContentHandlerError(this); }
57 ApplicationManager* manager_;
58 GURL content_handler_url_;
59 ContentHandlerPtr content_handler_;
61 DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
64 // static
65 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
66 : manager_(manager) {
69 ApplicationManager::TestAPI::~TestAPI() {
72 bool ApplicationManager::TestAPI::HasCreatedInstance() {
73 return has_created_instance;
76 bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
77 return manager_->identity_to_shell_impl_.find(Identity(url)) !=
78 manager_->identity_to_shell_impl_.end();
81 ApplicationManager::ApplicationManager(Delegate* delegate)
82 : delegate_(delegate), weak_ptr_factory_(this) {
85 ApplicationManager::~ApplicationManager() {
86 STLDeleteValues(&url_to_content_handler_);
87 TerminateShellConnections();
88 STLDeleteValues(&url_to_loader_);
89 STLDeleteValues(&scheme_to_loader_);
92 void ApplicationManager::TerminateShellConnections() {
93 STLDeleteValues(&identity_to_shell_impl_);
96 void ApplicationManager::ConnectToApplication(
97 const GURL& requested_url,
98 const GURL& requestor_url,
99 InterfaceRequest<ServiceProvider> services,
100 ServiceProviderPtr exposed_services,
101 const base::Closure& on_application_end) {
102 ConnectToApplicationWithParameters(
103 requested_url, requestor_url, services.Pass(), exposed_services.Pass(),
104 on_application_end, std::vector<std::string>());
107 void ApplicationManager::ConnectToApplicationWithParameters(
108 const GURL& requested_url,
109 const GURL& requestor_url,
110 InterfaceRequest<ServiceProvider> services,
111 ServiceProviderPtr exposed_services,
112 const base::Closure& on_application_end,
113 const std::vector<std::string>& pre_redirect_parameters) {
114 TRACE_EVENT_INSTANT1(
115 "mojo_shell", "ApplicationManager::ConnectToApplicationWithParameters",
116 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_url.spec());
117 DCHECK(requested_url.is_valid());
119 // We check both the mapped and resolved urls for existing shell_impls because
120 // external applications can be registered for the unresolved mojo:foo urls.
122 GURL mapped_url = delegate_->ResolveMappings(requested_url);
123 if (ConnectToRunningApplication(mapped_url, requestor_url, &services,
124 &exposed_services)) {
125 return;
128 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
129 if (ConnectToRunningApplication(resolved_url, requestor_url, &services,
130 &exposed_services)) {
131 return;
134 // The application is not running, let's compute the parameters.
135 if (ConnectToApplicationWithLoader(
136 requested_url, mapped_url, requestor_url, &services,
137 &exposed_services, on_application_end, pre_redirect_parameters,
138 GetLoaderForURL(mapped_url))) {
139 return;
142 if (ConnectToApplicationWithLoader(
143 requested_url, resolved_url, requestor_url, &services,
144 &exposed_services, on_application_end, pre_redirect_parameters,
145 GetLoaderForURL(resolved_url))) {
146 return;
149 if (ConnectToApplicationWithLoader(
150 requested_url, resolved_url, requestor_url, &services,
151 &exposed_services, on_application_end, pre_redirect_parameters,
152 default_loader_.get())) {
153 return;
156 auto callback = base::Bind(&ApplicationManager::HandleFetchCallback,
157 weak_ptr_factory_.GetWeakPtr(), requested_url,
158 requestor_url, base::Passed(services.Pass()),
159 base::Passed(exposed_services.Pass()),
160 on_application_end, pre_redirect_parameters);
162 if (resolved_url.SchemeIsFile()) {
163 new LocalFetcher(
164 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
165 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
166 return;
169 if (!network_service_)
170 ConnectToService(GURL("mojo:network_service"), &network_service_);
172 const NativeApplicationCleanup cleanup =
173 base::CommandLine::ForCurrentProcess()->HasSwitch(
174 switches::kDontDeleteOnDownload)
175 ? NativeApplicationCleanup::DONT_DELETE
176 : NativeApplicationCleanup::DELETE;
178 new NetworkFetcher(disable_cache_, resolved_url, network_service_.get(),
179 base::Bind(callback, cleanup));
182 bool ApplicationManager::ConnectToRunningApplication(
183 const GURL& resolved_url,
184 const GURL& requestor_url,
185 InterfaceRequest<ServiceProvider>* services,
186 ServiceProviderPtr* exposed_services) {
187 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
188 ShellImpl* shell_impl = GetShellImpl(application_url);
189 if (!shell_impl)
190 return false;
192 ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(),
193 exposed_services->Pass());
194 return true;
197 bool ApplicationManager::ConnectToApplicationWithLoader(
198 const GURL& requested_url,
199 const GURL& resolved_url,
200 const GURL& requestor_url,
201 InterfaceRequest<ServiceProvider>* services,
202 ServiceProviderPtr* exposed_services,
203 const base::Closure& on_application_end,
204 const std::vector<std::string>& parameters,
205 ApplicationLoader* loader) {
206 if (!loader)
207 return false;
209 const GURL app_url =
210 requested_url.scheme() == "mojo" ? requested_url : resolved_url;
212 loader->Load(
213 resolved_url,
214 RegisterShell(app_url, requestor_url, services->Pass(),
215 exposed_services->Pass(), on_application_end, parameters));
216 return true;
219 InterfaceRequest<Application> ApplicationManager::RegisterShell(
220 const GURL& app_url,
221 const GURL& requestor_url,
222 InterfaceRequest<ServiceProvider> services,
223 ServiceProviderPtr exposed_services,
224 const base::Closure& on_application_end,
225 const std::vector<std::string>& parameters) {
226 Identity app_identity(app_url);
228 ApplicationPtr application;
229 InterfaceRequest<Application> application_request = GetProxy(&application);
230 ShellImpl* shell =
231 new ShellImpl(application.Pass(), this, app_identity, on_application_end);
232 identity_to_shell_impl_[app_identity] = shell;
233 shell->InitializeApplication(Array<String>::From(parameters));
234 ConnectToClient(shell, app_url, requestor_url, services.Pass(),
235 exposed_services.Pass());
236 return application_request.Pass();
239 ShellImpl* ApplicationManager::GetShellImpl(const GURL& url) {
240 const auto& shell_it = identity_to_shell_impl_.find(Identity(url));
241 if (shell_it != identity_to_shell_impl_.end())
242 return shell_it->second;
243 return nullptr;
246 void ApplicationManager::ConnectToClient(
247 ShellImpl* shell_impl,
248 const GURL& resolved_url,
249 const GURL& requestor_url,
250 InterfaceRequest<ServiceProvider> services,
251 ServiceProviderPtr exposed_services) {
252 shell_impl->ConnectToClient(resolved_url, requestor_url, services.Pass(),
253 exposed_services.Pass());
256 void ApplicationManager::HandleFetchCallback(
257 const GURL& requested_url,
258 const GURL& requestor_url,
259 InterfaceRequest<ServiceProvider> services,
260 ServiceProviderPtr exposed_services,
261 const base::Closure& on_application_end,
262 const std::vector<std::string>& parameters,
263 NativeApplicationCleanup cleanup,
264 scoped_ptr<Fetcher> fetcher) {
265 if (!fetcher) {
266 // Network error. Drop |application_request| to tell requestor.
267 return;
270 GURL redirect_url = fetcher->GetRedirectURL();
271 if (!redirect_url.is_empty()) {
272 // And around we go again... Whee!
273 // TODO(sky): this loses |requested_url|.
274 ConnectToApplicationWithParameters(redirect_url, requestor_url,
275 services.Pass(), exposed_services.Pass(),
276 on_application_end, parameters);
277 return;
280 // We already checked if the application was running before we fetched it, but
281 // it might have started while the fetch was outstanding. We don't want to
282 // have two copies of the app running, so check again.
284 // Also, it's possible the original URL was redirected to an app that is
285 // already running.
286 if (ConnectToRunningApplication(requested_url, requestor_url, &services,
287 &exposed_services)) {
288 return;
291 const GURL app_url =
292 requested_url.scheme() == "mojo" ? requested_url : fetcher->GetURL();
294 InterfaceRequest<Application> request(
295 RegisterShell(app_url, requestor_url, services.Pass(),
296 exposed_services.Pass(), on_application_end, parameters));
298 // If the response begins with a #!mojo <content-handler-url>, use it.
299 GURL content_handler_url;
300 std::string shebang;
301 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
302 LoadWithContentHandler(
303 content_handler_url, request.Pass(),
304 fetcher->AsURLResponse(blocking_pool_,
305 static_cast<int>(shebang.size())));
306 return;
309 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
310 if (iter != mime_type_to_url_.end()) {
311 LoadWithContentHandler(iter->second, request.Pass(),
312 fetcher->AsURLResponse(blocking_pool_, 0));
313 return;
316 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
317 // application. That could either mean looking for the platform-specific dll
318 // header, or looking for some specific mojo signature prepended to the
319 // library.
320 // TODO(vtl): (Maybe this should be done by the factory/runner?)
322 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
323 NativeRunnerFactory::Options options;
324 if (url_to_native_options_.find(base_resolved_url) !=
325 url_to_native_options_.end()) {
326 DVLOG(2) << "Applying stored native options to resolved URL "
327 << fetcher->GetURL();
328 options = url_to_native_options_[base_resolved_url];
331 fetcher->AsPath(
332 blocking_pool_,
333 base::Bind(&ApplicationManager::RunNativeApplication,
334 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
335 options, cleanup, base::Passed(fetcher.Pass())));
338 void ApplicationManager::RunNativeApplication(
339 InterfaceRequest<Application> application_request,
340 const NativeRunnerFactory::Options& options,
341 NativeApplicationCleanup cleanup,
342 scoped_ptr<Fetcher> fetcher,
343 const base::FilePath& path,
344 bool path_exists) {
345 // We only passed fetcher to keep it alive. Done with it now.
346 fetcher.reset();
348 DCHECK(application_request.is_pending());
350 if (!path_exists) {
351 LOG(ERROR) << "Library not started because library path '" << path.value()
352 << "' does not exist.";
353 return;
356 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
357 path.AsUTF8Unsafe());
358 NativeRunner* runner = native_runner_factory_->Create(options).release();
359 native_runners_.push_back(runner);
360 runner->Start(path, cleanup, application_request.Pass(),
361 base::Bind(&ApplicationManager::CleanupRunner,
362 weak_ptr_factory_.GetWeakPtr(), runner));
365 void ApplicationManager::RegisterContentHandler(
366 const std::string& mime_type,
367 const GURL& content_handler_url) {
368 DCHECK(content_handler_url.is_valid())
369 << "Content handler URL is invalid for mime type " << mime_type;
370 mime_type_to_url_[mime_type] = content_handler_url;
373 void ApplicationManager::LoadWithContentHandler(
374 const GURL& content_handler_url,
375 InterfaceRequest<Application> application_request,
376 URLResponsePtr url_response) {
377 ContentHandlerConnection* connection = nullptr;
378 URLToContentHandlerMap::iterator iter =
379 url_to_content_handler_.find(content_handler_url);
380 if (iter != url_to_content_handler_.end()) {
381 connection = iter->second;
382 } else {
383 connection = new ContentHandlerConnection(this, content_handler_url);
384 url_to_content_handler_[content_handler_url] = connection;
387 connection->content_handler()->StartApplication(application_request.Pass(),
388 url_response.Pass());
391 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
392 const GURL& url) {
393 URLToLoaderMap::iterator it = url_to_loader_.find(url);
394 if (it != url_to_loader_.end())
395 delete it->second;
396 url_to_loader_[url] = loader.release();
399 void ApplicationManager::SetLoaderForScheme(
400 scoped_ptr<ApplicationLoader> loader,
401 const std::string& scheme) {
402 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
403 if (it != scheme_to_loader_.end())
404 delete it->second;
405 scheme_to_loader_[scheme] = loader.release();
408 void ApplicationManager::SetNativeOptionsForURL(
409 const NativeRunnerFactory::Options& options,
410 const GURL& url) {
411 DCHECK(!url.has_query()); // Precondition.
412 // Apply mappings and resolution to get the resolved URL.
413 GURL resolved_url =
414 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
415 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
416 // TODO(vtl): We should probably also remove/disregard the query string (and
417 // maybe canonicalize in other ways).
418 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
419 << " (original URL " << url << ")";
420 url_to_native_options_[resolved_url] = options;
423 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
424 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
425 if (url_it != url_to_loader_.end())
426 return url_it->second;
427 auto scheme_it = scheme_to_loader_.find(url.scheme());
428 if (scheme_it != scheme_to_loader_.end())
429 return scheme_it->second;
430 return nullptr;
433 void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
434 // Called from ~ShellImpl, so we do not need to call Destroy here.
435 const Identity identity = shell_impl->identity();
436 base::Closure on_application_end = shell_impl->on_application_end();
437 // Remove the shell.
438 auto it = identity_to_shell_impl_.find(identity);
439 DCHECK(it != identity_to_shell_impl_.end());
440 delete it->second;
441 identity_to_shell_impl_.erase(it);
442 if (!on_application_end.is_null())
443 on_application_end.Run();
446 void ApplicationManager::OnContentHandlerError(
447 ContentHandlerConnection* content_handler) {
448 // Remove the mapping to the content handler.
449 auto it =
450 url_to_content_handler_.find(content_handler->content_handler_url());
451 DCHECK(it != url_to_content_handler_.end());
452 delete it->second;
453 url_to_content_handler_.erase(it);
456 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
457 const GURL& application_url,
458 const std::string& interface_name) {
459 ServiceProviderPtr services;
460 ConnectToApplication(application_url, GURL(), GetProxy(&services), nullptr,
461 base::Closure());
462 MessagePipe pipe;
463 services->ConnectToService(interface_name, pipe.handle1.Pass());
464 return pipe.handle0.Pass();
467 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
468 native_runners_.erase(
469 std::find(native_runners_.begin(), native_runners_.end(), runner));
472 } // namespace shell
473 } // namespace mojo