Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: mojo/shell/application_manager/application_manager.cc

Issue 1049993002: Get mojo_shell building inside chromium checkout. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix presubmit Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "mojo/shell/application_manager/application_manager.h"
6
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/application_manager/fetcher.h"
17 #include "mojo/shell/application_manager/local_fetcher.h"
18 #include "mojo/shell/application_manager/network_fetcher.h"
19 #include "mojo/shell/application_manager/query_util.h"
20 #include "mojo/shell/application_manager/shell_impl.h"
21 #include "mojo/shell/switches.h"
22 #include "third_party/mojo_services/src/content_handler/public/interfaces/conten t_handler.mojom.h"
23
24 namespace mojo {
25 namespace shell {
26
27 namespace {
28
29 // Used by TestAPI.
30 bool has_created_instance = false;
31
32 std::vector<std::string> Concatenate(const std::vector<std::string>& v1,
33 const std::vector<std::string>& v2) {
34 if (!v1.size())
35 return v2;
36 if (!v2.size())
37 return v1;
38 std::vector<std::string> result(v1);
39 result.insert(result.end(), v1.begin(), v1.end());
40 return result;
41 }
42
43 } // namespace
44
45 ApplicationManager::Delegate::~Delegate() {
46 }
47
48 GURL ApplicationManager::Delegate::ResolveURL(const GURL& url) {
49 return url;
50 }
51
52 GURL ApplicationManager::Delegate::ResolveMappings(const GURL& url) {
53 return url;
54 }
55
56 class ApplicationManager::ContentHandlerConnection : public ErrorHandler {
57 public:
58 ContentHandlerConnection(ApplicationManager* manager,
59 const GURL& content_handler_url)
60 : manager_(manager), content_handler_url_(content_handler_url) {
61 ServiceProviderPtr services;
62 manager->ConnectToApplication(content_handler_url, GURL(),
63 GetProxy(&services), nullptr,
64 base::Closure());
65 MessagePipe pipe;
66 content_handler_.Bind(pipe.handle0.Pass());
67 services->ConnectToService(ContentHandler::Name_, pipe.handle1.Pass());
68 content_handler_.set_error_handler(this);
69 }
70
71 ContentHandler* content_handler() { return content_handler_.get(); }
72
73 GURL content_handler_url() { return content_handler_url_; }
74
75 private:
76 // ErrorHandler implementation:
77 void OnConnectionError() override { manager_->OnContentHandlerError(this); }
78
79 ApplicationManager* manager_;
80 GURL content_handler_url_;
81 ContentHandlerPtr content_handler_;
82
83 DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
84 };
85
86 // static
87 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
88 : manager_(manager) {
89 }
90
91 ApplicationManager::TestAPI::~TestAPI() {
92 }
93
94 bool ApplicationManager::TestAPI::HasCreatedInstance() {
95 return has_created_instance;
96 }
97
98 bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
99 return manager_->identity_to_shell_impl_.find(Identity(url)) !=
100 manager_->identity_to_shell_impl_.end();
101 }
102
103 ApplicationManager::ApplicationManager(Delegate* delegate)
104 : delegate_(delegate), weak_ptr_factory_(this) {
105 }
106
107 ApplicationManager::~ApplicationManager() {
108 STLDeleteValues(&url_to_content_handler_);
109 TerminateShellConnections();
110 STLDeleteValues(&url_to_loader_);
111 STLDeleteValues(&scheme_to_loader_);
112 }
113
114 void ApplicationManager::TerminateShellConnections() {
115 STLDeleteValues(&identity_to_shell_impl_);
116 }
117
118 void ApplicationManager::ConnectToApplication(
119 const GURL& requested_url,
120 const GURL& requestor_url,
121 InterfaceRequest<ServiceProvider> services,
122 ServiceProviderPtr exposed_services,
123 const base::Closure& on_application_end) {
124 ConnectToApplicationWithParameters(
125 requested_url, requestor_url, services.Pass(), exposed_services.Pass(),
126 on_application_end, std::vector<std::string>());
127 }
128
129 void ApplicationManager::ConnectToApplicationWithParameters(
130 const GURL& requested_url,
131 const GURL& requestor_url,
132 InterfaceRequest<ServiceProvider> services,
133 ServiceProviderPtr exposed_services,
134 const base::Closure& on_application_end,
135 const std::vector<std::string>& pre_redirect_parameters) {
136 TRACE_EVENT_INSTANT1(
137 "mojo_shell", "ApplicationManager::ConnectToApplicationWithParameters",
138 TRACE_EVENT_SCOPE_THREAD, "requested_url", requested_url.spec());
139 DCHECK(requested_url.is_valid());
140
141 // We check both the mapped and resolved urls for existing shell_impls because
142 // external applications can be registered for the unresolved mojo:foo urls.
143
144 GURL mapped_url = delegate_->ResolveMappings(requested_url);
145 if (ConnectToRunningApplication(mapped_url, requestor_url, &services,
146 &exposed_services)) {
147 return;
148 }
149
150 GURL resolved_url = delegate_->ResolveURL(mapped_url);
151 if (ConnectToRunningApplication(resolved_url, requestor_url, &services,
152 &exposed_services)) {
153 return;
154 }
155
156 // The application is not running, let's compute the parameters.
157 std::vector<std::string> parameters =
158 Concatenate(pre_redirect_parameters, GetArgsForURL(resolved_url));
159
160 if (ConnectToApplicationWithLoader(mapped_url, requestor_url, &services,
161 &exposed_services, on_application_end,
162 parameters, GetLoaderForURL(mapped_url))) {
163 return;
164 }
165
166 if (ConnectToApplicationWithLoader(
167 resolved_url, requestor_url, &services, &exposed_services,
168 on_application_end, parameters, GetLoaderForURL(resolved_url))) {
169 return;
170 }
171
172 if (ConnectToApplicationWithLoader(resolved_url, requestor_url, &services,
173 &exposed_services, on_application_end,
174 parameters, default_loader_.get())) {
175 return;
176 }
177
178 auto callback = base::Bind(
179 &ApplicationManager::HandleFetchCallback, weak_ptr_factory_.GetWeakPtr(),
180 requestor_url, base::Passed(services.Pass()),
181 base::Passed(exposed_services.Pass()), on_application_end,
182 parameters);
183
184 if (resolved_url.SchemeIsFile()) {
185 new LocalFetcher(
186 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
187 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
188 return;
189 }
190
191 if (!network_service_)
192 ConnectToService(GURL("mojo:network_service"), &network_service_);
193
194 const NativeApplicationCleanup cleanup =
195 base::CommandLine::ForCurrentProcess()->HasSwitch(
196 switches::kDontDeleteOnDownload)
197 ? NativeApplicationCleanup::DONT_DELETE
198 : NativeApplicationCleanup::DELETE;
199
200 new NetworkFetcher(disable_cache_, resolved_url, network_service_.get(),
201 base::Bind(callback, cleanup));
202 }
203
204 bool ApplicationManager::ConnectToRunningApplication(
205 const GURL& resolved_url,
206 const GURL& requestor_url,
207 InterfaceRequest<ServiceProvider>* services,
208 ServiceProviderPtr* exposed_services) {
209 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
210 ShellImpl* shell_impl = GetShellImpl(application_url);
211 if (!shell_impl)
212 return false;
213
214 ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(),
215 exposed_services->Pass());
216 return true;
217 }
218
219 bool ApplicationManager::ConnectToApplicationWithLoader(
220 const GURL& resolved_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 ApplicationLoader* loader) {
227 if (!loader)
228 return false;
229
230 loader->Load(
231 resolved_url,
232 RegisterShell(resolved_url, requestor_url, services->Pass(),
233 exposed_services->Pass(), on_application_end, parameters));
234 return true;
235 }
236
237 InterfaceRequest<Application> ApplicationManager::RegisterShell(
238 const GURL& resolved_url,
239 const GURL& requestor_url,
240 InterfaceRequest<ServiceProvider> services,
241 ServiceProviderPtr exposed_services,
242 const base::Closure& on_application_end,
243 const std::vector<std::string>& parameters) {
244 Identity app_identity(resolved_url);
245
246 ApplicationPtr application;
247 InterfaceRequest<Application> application_request = GetProxy(&application);
248 ShellImpl* shell =
249 new ShellImpl(application.Pass(), this, app_identity, on_application_end);
250 identity_to_shell_impl_[app_identity] = shell;
251 shell->InitializeApplication(Array<String>::From(parameters));
252 ConnectToClient(shell, resolved_url, requestor_url, services.Pass(),
253 exposed_services.Pass());
254 return application_request.Pass();
255 }
256
257 ShellImpl* ApplicationManager::GetShellImpl(const GURL& url) {
258 const auto& shell_it = identity_to_shell_impl_.find(Identity(url));
259 if (shell_it != identity_to_shell_impl_.end())
260 return shell_it->second;
261 return nullptr;
262 }
263
264 void ApplicationManager::ConnectToClient(
265 ShellImpl* shell_impl,
266 const GURL& resolved_url,
267 const GURL& requestor_url,
268 InterfaceRequest<ServiceProvider> services,
269 ServiceProviderPtr exposed_services) {
270 shell_impl->ConnectToClient(resolved_url, requestor_url, services.Pass(),
271 exposed_services.Pass());
272 }
273
274 void ApplicationManager::HandleFetchCallback(
275 const GURL& requestor_url,
276 InterfaceRequest<ServiceProvider> services,
277 ServiceProviderPtr exposed_services,
278 const base::Closure& on_application_end,
279 const std::vector<std::string>& parameters,
280 NativeApplicationCleanup cleanup,
281 scoped_ptr<Fetcher> fetcher) {
282 if (!fetcher) {
283 // Network error. Drop |application_request| to tell requestor.
284 return;
285 }
286
287 GURL redirect_url = fetcher->GetRedirectURL();
288 if (!redirect_url.is_empty()) {
289 // And around we go again... Whee!
290 ConnectToApplicationWithParameters(redirect_url, requestor_url,
291 services.Pass(), exposed_services.Pass(),
292 on_application_end, parameters);
293 return;
294 }
295
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.
299 //
300 // Also, it's possible the original URL was redirected to an app that is
301 // already running.
302 if (ConnectToRunningApplication(fetcher->GetURL(), requestor_url, &services,
303 &exposed_services)) {
304 return;
305 }
306
307 InterfaceRequest<Application> request(
308 RegisterShell(fetcher->GetURL(), requestor_url, services.Pass(),
309 exposed_services.Pass(), on_application_end, parameters));
310
311 // If the response begins with a #!mojo <content-handler-url>, use it.
312 GURL content_handler_url;
313 std::string shebang;
314 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
315 LoadWithContentHandler(
316 content_handler_url, request.Pass(),
317 fetcher->AsURLResponse(blocking_pool_,
318 static_cast<int>(shebang.size())));
319 return;
320 }
321
322 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
323 if (iter != mime_type_to_url_.end()) {
324 LoadWithContentHandler(iter->second, request.Pass(),
325 fetcher->AsURLResponse(blocking_pool_, 0));
326 return;
327 }
328
329 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
330 // application. That could either mean looking for the platform-specific dll
331 // header, or looking for some specific mojo signature prepended to the
332 // library.
333 // TODO(vtl): (Maybe this should be done by the factory/runner?)
334
335 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
336 NativeRunnerFactory::Options options;
337 if (url_to_native_options_.find(base_resolved_url) !=
338 url_to_native_options_.end()) {
339 DVLOG(2) << "Applying stored native options to resolved URL "
340 << fetcher->GetURL();
341 options = url_to_native_options_[base_resolved_url];
342 }
343
344 fetcher->AsPath(
345 blocking_pool_,
346 base::Bind(&ApplicationManager::RunNativeApplication,
347 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
348 options, cleanup, base::Passed(fetcher.Pass())));
349 }
350
351 void ApplicationManager::RunNativeApplication(
352 InterfaceRequest<Application> application_request,
353 const NativeRunnerFactory::Options& options,
354 NativeApplicationCleanup cleanup,
355 scoped_ptr<Fetcher> fetcher,
356 const base::FilePath& path,
357 bool path_exists) {
358 // We only passed fetcher to keep it alive. Done with it now.
359 fetcher.reset();
360
361 DCHECK(application_request.is_pending());
362
363 if (!path_exists) {
364 LOG(ERROR) << "Library not started because library path '" << path.value()
365 << "' does not exist.";
366 return;
367 }
368
369 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
370 path.AsUTF8Unsafe());
371 NativeRunner* runner = native_runner_factory_->Create(options).release();
372 native_runners_.push_back(runner);
373 runner->Start(path, cleanup, application_request.Pass(),
374 base::Bind(&ApplicationManager::CleanupRunner,
375 weak_ptr_factory_.GetWeakPtr(), runner));
376 }
377
378 void ApplicationManager::RegisterExternalApplication(
379 const GURL& url,
380 const std::vector<std::string>& args,
381 ApplicationPtr application) {
382 const auto& args_it = url_to_args_.find(url);
383 if (args_it != url_to_args_.end()) {
384 LOG(WARNING) << "--args-for provided for external application " << url
385 << " <ignored>";
386 }
387 Identity identity(url);
388 ShellImpl* shell_impl =
389 new ShellImpl(application.Pass(), this, identity, base::Closure());
390 identity_to_shell_impl_[identity] = shell_impl;
391 shell_impl->InitializeApplication(Array<String>::From(args));
392 }
393
394 void ApplicationManager::RegisterContentHandler(
395 const std::string& mime_type,
396 const GURL& content_handler_url) {
397 DCHECK(content_handler_url.is_valid())
398 << "Content handler URL is invalid for mime type " << mime_type;
399 mime_type_to_url_[mime_type] = content_handler_url;
400 }
401
402 void ApplicationManager::LoadWithContentHandler(
403 const GURL& content_handler_url,
404 InterfaceRequest<Application> application_request,
405 URLResponsePtr url_response) {
406 ContentHandlerConnection* connection = nullptr;
407 URLToContentHandlerMap::iterator iter =
408 url_to_content_handler_.find(content_handler_url);
409 if (iter != url_to_content_handler_.end()) {
410 connection = iter->second;
411 } else {
412 connection = new ContentHandlerConnection(this, content_handler_url);
413 url_to_content_handler_[content_handler_url] = connection;
414 }
415
416 connection->content_handler()->StartApplication(application_request.Pass(),
417 url_response.Pass());
418 }
419
420 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
421 const GURL& url) {
422 URLToLoaderMap::iterator it = url_to_loader_.find(url);
423 if (it != url_to_loader_.end())
424 delete it->second;
425 url_to_loader_[url] = loader.release();
426 }
427
428 void ApplicationManager::SetLoaderForScheme(
429 scoped_ptr<ApplicationLoader> loader,
430 const std::string& scheme) {
431 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
432 if (it != scheme_to_loader_.end())
433 delete it->second;
434 scheme_to_loader_[scheme] = loader.release();
435 }
436
437 void ApplicationManager::SetArgsForURL(const std::vector<std::string>& args,
438 const GURL& url) {
439 url_to_args_[url].insert(url_to_args_[url].end(), args.begin(), args.end());
440 GURL mapped_url = delegate_->ResolveMappings(url);
441 if (mapped_url != url) {
442 url_to_args_[mapped_url].insert(url_to_args_[mapped_url].end(),
443 args.begin(), args.end());
444 }
445 GURL resolved_url = delegate_->ResolveURL(mapped_url);
446 if (resolved_url != mapped_url) {
447 url_to_args_[resolved_url].insert(url_to_args_[resolved_url].end(),
448 args.begin(), args.end());
449 }
450 }
451
452 void ApplicationManager::SetNativeOptionsForURL(
453 const NativeRunnerFactory::Options& options,
454 const GURL& url) {
455 DCHECK(!url.has_query()); // Precondition.
456 // Apply mappings and resolution to get the resolved URL.
457 GURL resolved_url = delegate_->ResolveURL(delegate_->ResolveMappings(url));
458 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
459 // TODO(vtl): We should probably also remove/disregard the query string (and
460 // maybe canonicalize in other ways).
461 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
462 << " (original URL " << url << ")";
463 url_to_native_options_[resolved_url] = options;
464 }
465
466 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
467 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
468 if (url_it != url_to_loader_.end())
469 return url_it->second;
470 auto scheme_it = scheme_to_loader_.find(url.scheme());
471 if (scheme_it != scheme_to_loader_.end())
472 return scheme_it->second;
473 return nullptr;
474 }
475
476 void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
477 // Called from ~ShellImpl, so we do not need to call Destroy here.
478 const Identity identity = shell_impl->identity();
479 base::Closure on_application_end = shell_impl->on_application_end();
480 // Remove the shell.
481 auto it = identity_to_shell_impl_.find(identity);
482 DCHECK(it != identity_to_shell_impl_.end());
483 delete it->second;
484 identity_to_shell_impl_.erase(it);
485 if (!on_application_end.is_null())
486 on_application_end.Run();
487 }
488
489 void ApplicationManager::OnContentHandlerError(
490 ContentHandlerConnection* content_handler) {
491 // Remove the mapping to the content handler.
492 auto it =
493 url_to_content_handler_.find(content_handler->content_handler_url());
494 DCHECK(it != url_to_content_handler_.end());
495 delete it->second;
496 url_to_content_handler_.erase(it);
497 }
498
499 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
500 const GURL& application_url,
501 const std::string& interface_name) {
502 ServiceProviderPtr services;
503 ConnectToApplication(application_url, GURL(), GetProxy(&services), nullptr,
504 base::Closure());
505 MessagePipe pipe;
506 services->ConnectToService(interface_name, pipe.handle1.Pass());
507 return pipe.handle0.Pass();
508 }
509
510 std::vector<std::string> ApplicationManager::GetArgsForURL(const GURL& url) {
511 const auto& args_it = url_to_args_.find(url);
512 if (args_it != url_to_args_.end())
513 return args_it->second;
514 return std::vector<std::string>();
515 }
516
517 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
518 native_runners_.erase(
519 std::find(native_runners_.begin(), native_runners_.end(), runner));
520 }
521
522 } // namespace shell
523 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/shell/application_manager/application_manager.h ('k') | mojo/shell/application_manager/application_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698