Stop retring GCM unregistration requests after reaching maximum number
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blobd953a9013ef81095b6a990b827c29044db671e73
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/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"
25 namespace mojo {
26 namespace shell {
28 namespace {
30 // Used by TestAPI.
31 bool has_created_instance = false;
33 } // namespace
35 class ApplicationManager::ContentHandlerConnection : public ErrorHandler {
36 public:
37 ContentHandlerConnection(ApplicationManager* manager,
38 const GURL& content_handler_url,
39 const GURL& requestor_url,
40 const std::string& qualifier)
41 : manager_(manager),
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());
50 MessagePipe pipe;
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_; }
62 private:
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);
74 // static
75 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
76 : manager_(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)) {
136 return;
139 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
140 if (ConnectToRunningApplication(resolved_url, qualifier, requestor_url,
141 &services, &exposed_services)) {
142 return;
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))) {
149 return;
152 if (ConnectToApplicationWithLoader(
153 requested_gurl, qualifier, resolved_url, requestor_url, &services,
154 &exposed_services, on_application_end,
155 GetLoaderForURL(resolved_url))) {
156 return;
159 if (ConnectToApplicationWithLoader(
160 requested_gurl, qualifier, resolved_url, requestor_url, &services,
161 &exposed_services, on_application_end, default_loader_.get())) {
162 return;
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(
171 resolved_url,
172 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE))) {
173 return;
176 if (resolved_url.SchemeIsFile()) {
177 new LocalFetcher(
178 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
179 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
180 return;
183 if (mapped_url.SchemeIs("mojo") &&
184 base::CommandLine::ForCurrentProcess()->HasSwitch(
185 switches::kUseUpdater)) {
186 ConnectToService(GURL("mojo:updater"), &updater_);
187 new UpdateFetcher(
188 mapped_url, updater_.get(),
189 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
190 return;
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));
209 return;
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);
224 if (!shell_impl)
225 return false;
227 ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(),
228 exposed_services->Pass());
229 return true;
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) {
241 if (!loader)
242 return false;
244 const GURL app_url =
245 requested_url.SchemeIs("mojo") ? requested_url : resolved_url;
247 loader->Load(
248 resolved_url,
249 RegisterShell(app_url, qualifier, requestor_url, services->Pass(),
250 exposed_services->Pass(), on_application_end));
251 return true;
254 InterfaceRequest<Application> ApplicationManager::RegisterShell(
255 const GURL& app_url,
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);
265 ShellImpl* shell =
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;
279 return nullptr;
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) {
301 if (!fetcher) {
302 // Network error. Drop |application_request| to tell requestor.
303 return;
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(),
318 on_application_end);
319 return;
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
327 // already running.
328 if (ConnectToRunningApplication(requested_url, qualifier, requestor_url,
329 &services, &exposed_services)) {
330 return;
333 const GURL app_url =
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;
342 std::string shebang;
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())));
348 return;
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,
354 request.Pass(),
355 fetcher->AsURLResponse(blocking_pool_, 0));
356 return;
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());
382 return;
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
388 // library.
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];
400 fetcher->AsPath(
401 blocking_pool_,
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,
413 bool path_exists) {
414 // We only passed fetcher to keep it alive. Done with it now.
415 fetcher.reset();
417 DCHECK(application_request.is_pending());
419 if (!path_exists) {
420 LOG(ERROR) << "Library not started because library path '" << path.value()
421 << "' does not exist.";
422 return;
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(
443 const GURL& alias,
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;
461 } else {
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,
472 const GURL& url) {
473 URLToLoaderMap::iterator it = url_to_loader_.find(url);
474 if (it != url_to_loader_.end())
475 delete it->second;
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())
484 delete it->second;
485 scheme_to_loader_[scheme] = loader.release();
488 void ApplicationManager::SetNativeOptionsForURL(
489 const NativeRunnerFactory::Options& options,
490 const GURL& url) {
491 DCHECK(!url.has_query()); // Precondition.
492 // Apply mappings and resolution to get the resolved URL.
493 GURL 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;
510 return nullptr;
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();
517 // Remove the shell.
518 auto it = identity_to_shell_impl_.find(identity);
519 DCHECK(it != identity_to_shell_impl_.end());
520 delete it->second;
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());
533 delete it->second;
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,
544 base::Closure());
545 MessagePipe pipe;
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));
555 } // namespace shell
556 } // namespace mojo