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

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

Issue 1091513005: Separate mojo/shell into a runner and the application manager (shell) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . 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 } // namespace
33
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);
47 }
48
49 ContentHandler* content_handler() { return content_handler_.get(); }
50
51 GURL content_handler_url() { return content_handler_url_; }
52
53 private:
54 // ErrorHandler implementation:
55 void OnConnectionError() override { manager_->OnContentHandlerError(this); }
56
57 ApplicationManager* manager_;
58 GURL content_handler_url_;
59 ContentHandlerPtr content_handler_;
60
61 DISALLOW_COPY_AND_ASSIGN(ContentHandlerConnection);
62 };
63
64 // static
65 ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
66 : manager_(manager) {
67 }
68
69 ApplicationManager::TestAPI::~TestAPI() {
70 }
71
72 bool ApplicationManager::TestAPI::HasCreatedInstance() {
73 return has_created_instance;
74 }
75
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();
79 }
80
81 ApplicationManager::ApplicationManager(Delegate* delegate)
82 : delegate_(delegate), weak_ptr_factory_(this) {
83 }
84
85 ApplicationManager::~ApplicationManager() {
86 STLDeleteValues(&url_to_content_handler_);
87 TerminateShellConnections();
88 STLDeleteValues(&url_to_loader_);
89 STLDeleteValues(&scheme_to_loader_);
90 }
91
92 void ApplicationManager::TerminateShellConnections() {
93 STLDeleteValues(&identity_to_shell_impl_);
94 }
95
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>());
105 }
106
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());
118
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.
121
122 GURL mapped_url = delegate_->ResolveMappings(requested_url);
123 if (ConnectToRunningApplication(mapped_url, requestor_url, &services,
124 &exposed_services)) {
125 return;
126 }
127
128 GURL resolved_url = delegate_->ResolveMojoURL(mapped_url);
129 if (ConnectToRunningApplication(resolved_url, requestor_url, &services,
130 &exposed_services)) {
131 return;
132 }
133
134 // The application is not running, let's compute the parameters.
135 if (ConnectToApplicationWithLoader(mapped_url, requestor_url, &services,
136 &exposed_services, on_application_end,
137 pre_redirect_parameters,
138 GetLoaderForURL(mapped_url))) {
139 return;
140 }
141
142 if (ConnectToApplicationWithLoader(resolved_url, requestor_url, &services,
143 &exposed_services, on_application_end,
144 pre_redirect_parameters,
145 GetLoaderForURL(resolved_url))) {
146 return;
147 }
148
149 if (ConnectToApplicationWithLoader(
150 resolved_url, requestor_url, &services, &exposed_services,
151 on_application_end, pre_redirect_parameters, default_loader_.get())) {
152 return;
153 }
154
155 auto callback = base::Bind(&ApplicationManager::HandleFetchCallback,
156 weak_ptr_factory_.GetWeakPtr(), requestor_url,
157 base::Passed(services.Pass()),
158 base::Passed(exposed_services.Pass()),
159 on_application_end, pre_redirect_parameters);
160
161 if (resolved_url.SchemeIsFile()) {
162 new LocalFetcher(
163 resolved_url, GetBaseURLAndQuery(resolved_url, nullptr),
164 base::Bind(callback, NativeApplicationCleanup::DONT_DELETE));
165 return;
166 }
167
168 if (!network_service_)
169 ConnectToService(GURL("mojo:network_service"), &network_service_);
170
171 const NativeApplicationCleanup cleanup =
172 base::CommandLine::ForCurrentProcess()->HasSwitch(
173 switches::kDontDeleteOnDownload)
174 ? NativeApplicationCleanup::DONT_DELETE
175 : NativeApplicationCleanup::DELETE;
176
177 new NetworkFetcher(disable_cache_, resolved_url, network_service_.get(),
178 base::Bind(callback, cleanup));
179 }
180
181 bool ApplicationManager::ConnectToRunningApplication(
182 const GURL& resolved_url,
183 const GURL& requestor_url,
184 InterfaceRequest<ServiceProvider>* services,
185 ServiceProviderPtr* exposed_services) {
186 GURL application_url = GetBaseURLAndQuery(resolved_url, nullptr);
187 ShellImpl* shell_impl = GetShellImpl(application_url);
188 if (!shell_impl)
189 return false;
190
191 ConnectToClient(shell_impl, resolved_url, requestor_url, services->Pass(),
192 exposed_services->Pass());
193 return true;
194 }
195
196 bool ApplicationManager::ConnectToApplicationWithLoader(
197 const GURL& resolved_url,
198 const GURL& requestor_url,
199 InterfaceRequest<ServiceProvider>* services,
200 ServiceProviderPtr* exposed_services,
201 const base::Closure& on_application_end,
202 const std::vector<std::string>& parameters,
203 ApplicationLoader* loader) {
204 if (!loader)
205 return false;
206
207 loader->Load(
208 resolved_url,
209 RegisterShell(resolved_url, requestor_url, services->Pass(),
210 exposed_services->Pass(), on_application_end, parameters));
211 return true;
212 }
213
214 InterfaceRequest<Application> ApplicationManager::RegisterShell(
215 const GURL& resolved_url,
216 const GURL& requestor_url,
217 InterfaceRequest<ServiceProvider> services,
218 ServiceProviderPtr exposed_services,
219 const base::Closure& on_application_end,
220 const std::vector<std::string>& parameters) {
221 Identity app_identity(resolved_url);
222
223 ApplicationPtr application;
224 InterfaceRequest<Application> application_request = GetProxy(&application);
225 ShellImpl* shell =
226 new ShellImpl(application.Pass(), this, app_identity, on_application_end);
227 identity_to_shell_impl_[app_identity] = shell;
228 shell->InitializeApplication(Array<String>::From(parameters));
229 ConnectToClient(shell, resolved_url, requestor_url, services.Pass(),
230 exposed_services.Pass());
231 return application_request.Pass();
232 }
233
234 ShellImpl* ApplicationManager::GetShellImpl(const GURL& url) {
235 const auto& shell_it = identity_to_shell_impl_.find(Identity(url));
236 if (shell_it != identity_to_shell_impl_.end())
237 return shell_it->second;
238 return nullptr;
239 }
240
241 void ApplicationManager::ConnectToClient(
242 ShellImpl* shell_impl,
243 const GURL& resolved_url,
244 const GURL& requestor_url,
245 InterfaceRequest<ServiceProvider> services,
246 ServiceProviderPtr exposed_services) {
247 shell_impl->ConnectToClient(resolved_url, requestor_url, services.Pass(),
248 exposed_services.Pass());
249 }
250
251 void ApplicationManager::HandleFetchCallback(
252 const GURL& requestor_url,
253 InterfaceRequest<ServiceProvider> services,
254 ServiceProviderPtr exposed_services,
255 const base::Closure& on_application_end,
256 const std::vector<std::string>& parameters,
257 NativeApplicationCleanup cleanup,
258 scoped_ptr<Fetcher> fetcher) {
259 if (!fetcher) {
260 // Network error. Drop |application_request| to tell requestor.
261 return;
262 }
263
264 GURL redirect_url = fetcher->GetRedirectURL();
265 if (!redirect_url.is_empty()) {
266 // And around we go again... Whee!
267 ConnectToApplicationWithParameters(redirect_url, requestor_url,
268 services.Pass(), exposed_services.Pass(),
269 on_application_end, parameters);
270 return;
271 }
272
273 // We already checked if the application was running before we fetched it, but
274 // it might have started while the fetch was outstanding. We don't want to
275 // have two copies of the app running, so check again.
276 //
277 // Also, it's possible the original URL was redirected to an app that is
278 // already running.
279 if (ConnectToRunningApplication(fetcher->GetURL(), requestor_url, &services,
280 &exposed_services)) {
281 return;
282 }
283
284 InterfaceRequest<Application> request(
285 RegisterShell(fetcher->GetURL(), requestor_url, services.Pass(),
286 exposed_services.Pass(), on_application_end, parameters));
287
288 // If the response begins with a #!mojo <content-handler-url>, use it.
289 GURL content_handler_url;
290 std::string shebang;
291 if (fetcher->PeekContentHandler(&shebang, &content_handler_url)) {
292 LoadWithContentHandler(
293 content_handler_url, request.Pass(),
294 fetcher->AsURLResponse(blocking_pool_,
295 static_cast<int>(shebang.size())));
296 return;
297 }
298
299 MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(fetcher->MimeType());
300 if (iter != mime_type_to_url_.end()) {
301 LoadWithContentHandler(iter->second, request.Pass(),
302 fetcher->AsURLResponse(blocking_pool_, 0));
303 return;
304 }
305
306 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
307 // application. That could either mean looking for the platform-specific dll
308 // header, or looking for some specific mojo signature prepended to the
309 // library.
310 // TODO(vtl): (Maybe this should be done by the factory/runner?)
311
312 GURL base_resolved_url = GetBaseURLAndQuery(fetcher->GetURL(), nullptr);
313 NativeRunnerFactory::Options options;
314 if (url_to_native_options_.find(base_resolved_url) !=
315 url_to_native_options_.end()) {
316 DVLOG(2) << "Applying stored native options to resolved URL "
317 << fetcher->GetURL();
318 options = url_to_native_options_[base_resolved_url];
319 }
320
321 fetcher->AsPath(
322 blocking_pool_,
323 base::Bind(&ApplicationManager::RunNativeApplication,
324 weak_ptr_factory_.GetWeakPtr(), base::Passed(request.Pass()),
325 options, cleanup, base::Passed(fetcher.Pass())));
326 }
327
328 void ApplicationManager::RunNativeApplication(
329 InterfaceRequest<Application> application_request,
330 const NativeRunnerFactory::Options& options,
331 NativeApplicationCleanup cleanup,
332 scoped_ptr<Fetcher> fetcher,
333 const base::FilePath& path,
334 bool path_exists) {
335 // We only passed fetcher to keep it alive. Done with it now.
336 fetcher.reset();
337
338 DCHECK(application_request.is_pending());
339
340 if (!path_exists) {
341 LOG(ERROR) << "Library not started because library path '" << path.value()
342 << "' does not exist.";
343 return;
344 }
345
346 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
347 path.AsUTF8Unsafe());
348 NativeRunner* runner = native_runner_factory_->Create(options).release();
349 native_runners_.push_back(runner);
350 runner->Start(path, cleanup, application_request.Pass(),
351 base::Bind(&ApplicationManager::CleanupRunner,
352 weak_ptr_factory_.GetWeakPtr(), runner));
353 }
354
355 void ApplicationManager::RegisterContentHandler(
356 const std::string& mime_type,
357 const GURL& content_handler_url) {
358 DCHECK(content_handler_url.is_valid())
359 << "Content handler URL is invalid for mime type " << mime_type;
360 mime_type_to_url_[mime_type] = content_handler_url;
361 }
362
363 void ApplicationManager::LoadWithContentHandler(
364 const GURL& content_handler_url,
365 InterfaceRequest<Application> application_request,
366 URLResponsePtr url_response) {
367 ContentHandlerConnection* connection = nullptr;
368 URLToContentHandlerMap::iterator iter =
369 url_to_content_handler_.find(content_handler_url);
370 if (iter != url_to_content_handler_.end()) {
371 connection = iter->second;
372 } else {
373 connection = new ContentHandlerConnection(this, content_handler_url);
374 url_to_content_handler_[content_handler_url] = connection;
375 }
376
377 connection->content_handler()->StartApplication(application_request.Pass(),
378 url_response.Pass());
379 }
380
381 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
382 const GURL& url) {
383 URLToLoaderMap::iterator it = url_to_loader_.find(url);
384 if (it != url_to_loader_.end())
385 delete it->second;
386 url_to_loader_[url] = loader.release();
387 }
388
389 void ApplicationManager::SetLoaderForScheme(
390 scoped_ptr<ApplicationLoader> loader,
391 const std::string& scheme) {
392 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
393 if (it != scheme_to_loader_.end())
394 delete it->second;
395 scheme_to_loader_[scheme] = loader.release();
396 }
397
398 void ApplicationManager::SetNativeOptionsForURL(
399 const NativeRunnerFactory::Options& options,
400 const GURL& url) {
401 DCHECK(!url.has_query()); // Precondition.
402 // Apply mappings and resolution to get the resolved URL.
403 GURL resolved_url =
404 delegate_->ResolveMojoURL(delegate_->ResolveMappings(url));
405 DCHECK(!resolved_url.has_query()); // Still shouldn't have query.
406 // TODO(vtl): We should probably also remove/disregard the query string (and
407 // maybe canonicalize in other ways).
408 DVLOG(2) << "Storing native options for resolved URL " << resolved_url
409 << " (original URL " << url << ")";
410 url_to_native_options_[resolved_url] = options;
411 }
412
413 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
414 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
415 if (url_it != url_to_loader_.end())
416 return url_it->second;
417 auto scheme_it = scheme_to_loader_.find(url.scheme());
418 if (scheme_it != scheme_to_loader_.end())
419 return scheme_it->second;
420 return nullptr;
421 }
422
423 void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
424 // Called from ~ShellImpl, so we do not need to call Destroy here.
425 const Identity identity = shell_impl->identity();
426 base::Closure on_application_end = shell_impl->on_application_end();
427 // Remove the shell.
428 auto it = identity_to_shell_impl_.find(identity);
429 DCHECK(it != identity_to_shell_impl_.end());
430 delete it->second;
431 identity_to_shell_impl_.erase(it);
432 if (!on_application_end.is_null())
433 on_application_end.Run();
434 }
435
436 void ApplicationManager::OnContentHandlerError(
437 ContentHandlerConnection* content_handler) {
438 // Remove the mapping to the content handler.
439 auto it =
440 url_to_content_handler_.find(content_handler->content_handler_url());
441 DCHECK(it != url_to_content_handler_.end());
442 delete it->second;
443 url_to_content_handler_.erase(it);
444 }
445
446 ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
447 const GURL& application_url,
448 const std::string& interface_name) {
449 ServiceProviderPtr services;
450 ConnectToApplication(application_url, GURL(), GetProxy(&services), nullptr,
451 base::Closure());
452 MessagePipe pipe;
453 services->ConnectToService(interface_name, pipe.handle1.Pass());
454 return pipe.handle0.Pass();
455 }
456
457 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
458 native_runners_.erase(
459 std::find(native_runners_.begin(), native_runners_.end(), runner));
460 }
461
462 } // namespace shell
463 } // 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