SupervisedUser SafeSites: Switch to the new SafeSearch API
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blob232e4b046c4fa4d2d070a33d937d3b4730ed7403
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/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"
25 namespace mojo {
26 namespace shell {
28 namespace {
30 // Used by TestAPI.
31 bool has_created_instance = false;
33 } // namespace
35 // static
36 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
37 : manager_(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>());
80 TRACE_EVENT_INSTANT1(
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)) {
92 return;
95 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
96 if (ConnectToRunningApplication(originator, resolved_url, qualifier,
97 requestor_url, &services,
98 &exposed_services, filter)) {
99 return;
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))) {
107 return;
110 if (ConnectToApplicationWithLoader(
111 originator, requested_gurl, qualifier, resolved_url, requestor_url,
112 &services, &exposed_services, filter, on_application_end,
113 GetLoaderForURL(resolved_url))) {
114 return;
117 if (ConnectToApplicationWithLoader(
118 originator, requested_gurl, qualifier, resolved_url, requestor_url,
119 &services, &exposed_services, filter, on_application_end,
120 default_loader_.get())) {
121 return;
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(
131 resolved_url,
132 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
133 return;
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_);
141 new LocalFetcher(
142 network_service_.get(), resolved_url,
143 GetBaseURLAndQuery(resolved_url, nullptr),
144 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
145 return;
148 if (mapped_url.SchemeIs("mojo") &&
149 base::CommandLine::ForCurrentProcess()->HasSwitch(
150 switches::kUseUpdater)) {
151 ConnectToService(GURL("mojo:updater"), &updater_);
152 new UpdateFetcher(
153 mapped_url, updater_.get(),
154 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
155 return;
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));
174 return;
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));
192 if (!instance)
193 return false;
195 instance->ConnectToClient(originator, resolved_url, requestor_url,
196 services->Pass(), exposed_services->Pass(),
197 filter);
198 return true;
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) {
212 if (!loader)
213 return false;
215 const GURL app_url =
216 requested_url.SchemeIs("mojo") ? requested_url : resolved_url;
218 loader->Load(
219 resolved_url,
220 RegisterInstance(originator, app_url, qualifier, requestor_url,
221 services->Pass(), exposed_services->Pass(),
222 filter, on_application_end));
223 return true;
226 InterfaceRequest<Application> ApplicationManager::RegisterInstance(
227 ApplicationInstance* originator,
228 const GURL& app_url,
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;
255 return nullptr;
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) {
269 if (!fetcher) {
270 // Network error. Drop |application_request| to tell requestor.
271 return;
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,
286 on_application_end);
287 return;
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
295 // already running.
296 if (ConnectToRunningApplication(originator, requested_url, qualifier,
297 requestor_url, &services,
298 &exposed_services, filter)) {
299 return;
302 const GURL app_url =
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
311 // by site.
313 // If the response begins with a #!mojo <content-handler-url>, use it.
314 GURL content_handler_url;
315 std::string shebang;
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())));
322 std::string site =
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());
326 return;
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));
332 std::string site =
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());
336 return;
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(),
362 response.Pass());
363 return;
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
369 // library.
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,
403 bool path_exists) {
404 // We only passed fetcher to keep it alive. Done with it now.
405 fetcher.reset();
407 DCHECK(application_request.is_pending());
409 if (!path_exists) {
410 LOG(ERROR) << "Library not started because library path '" << path.value()
411 << "' does not exist.";
412 return;
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(
433 const GURL& alias,
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;
455 } else {
456 connection = new ContentHandlerConnection(
457 originator, this, content_handler_url, requestor_url, qualifier,
458 filter);
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,
467 const GURL& url) {
468 URLToLoaderMap::iterator it = url_to_loader_.find(url);
469 if (it != url_to_loader_.end())
470 delete it->second;
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())
479 delete it->second;
480 scheme_to_loader_[scheme] = loader.release();
483 void ApplicationManager::SetNativeOptionsForURL(
484 const NativeRunnerFactory::Options& options,
485 const GURL& url) {
486 DCHECK(!url.has_query()); // Precondition.
487 // Apply mappings and resolution to get the resolved URL.
488 GURL 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;
505 return nullptr;
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();
513 // Remove the shell.
514 auto it = identity_to_instance_.find(identity);
515 DCHECK(it != identity_to_instance_.end());
516 delete it->second;
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());
546 MessagePipe pipe;
547 services->ConnectToService(interface_name, pipe.handle1.Pass());
548 return pipe.handle0.Pass();
551 } // namespace shell
552 } // namespace mojo