Fix layout of the Connection Tab of the Origin Info Bubble.
[chromium-blink-merge.git] / mojo / shell / application_manager.cc
blob7ca0d392c2ea1c208f3ab120155761e7ab32f01e
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/package_manager.h"
20 #include "mojo/shell/query_util.h"
21 #include "mojo/shell/switches.h"
23 namespace mojo {
24 namespace shell {
26 namespace {
28 // Used by TestAPI.
29 bool has_created_instance = false;
31 void OnEmptyOnConnectCallback(uint32_t content_handler_id) {}
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(
54 scoped_ptr<PackageManager> package_manager)
55 : package_manager_(package_manager.Pass()),
56 content_handler_id_counter_(0u),
57 weak_ptr_factory_(this) {
58 package_manager_->SetApplicationManager(this);
61 ApplicationManager::~ApplicationManager() {
62 IdentityToContentHandlerMap identity_to_content_handler(
63 identity_to_content_handler_);
64 for (auto& pair : identity_to_content_handler)
65 pair.second->CloseConnection();
66 TerminateShellConnections();
67 STLDeleteValues(&url_to_loader_);
70 void ApplicationManager::TerminateShellConnections() {
71 STLDeleteValues(&identity_to_instance_);
74 void ApplicationManager::ConnectToApplication(
75 scoped_ptr<ConnectToApplicationParams> params) {
76 TRACE_EVENT_INSTANT1("mojo_shell", "ApplicationManager::ConnectToApplication",
77 TRACE_EVENT_SCOPE_THREAD, "original_url",
78 params->app_url().spec());
79 DCHECK(params->app_url().is_valid());
81 // Connect to an existing matching instance, if possible.
82 if (ConnectToRunningApplication(&params))
83 return;
85 ApplicationLoader* loader = GetLoaderForURL(params->app_url());
86 if (loader) {
87 GURL url = params->app_url();
88 loader->Load(url, CreateInstance(params.Pass(), nullptr));
89 return;
92 URLRequestPtr original_url_request = params->TakeAppURLRequest();
93 auto callback =
94 base::Bind(&ApplicationManager::HandleFetchCallback,
95 weak_ptr_factory_.GetWeakPtr(), base::Passed(&params));
96 package_manager_->FetchRequest(original_url_request.Pass(), callback);
99 bool ApplicationManager::ConnectToRunningApplication(
100 scoped_ptr<ConnectToApplicationParams>* params) {
101 ApplicationInstance* instance = GetApplicationInstance(
102 Identity((*params)->app_url(), (*params)->qualifier()));
103 if (!instance)
104 return false;
106 instance->ConnectToClient(params->Pass());
107 return true;
110 InterfaceRequest<Application> ApplicationManager::CreateInstance(
111 scoped_ptr<ConnectToApplicationParams> params,
112 ApplicationInstance** resulting_instance) {
113 Identity app_identity(params->app_url(), params->qualifier());
115 ApplicationPtr application;
116 InterfaceRequest<Application> application_request = GetProxy(&application);
117 ApplicationInstance* instance = new ApplicationInstance(
118 application.Pass(), this, params->originator_identity(), app_identity,
119 params->filter(), Shell::kInvalidContentHandlerID,
120 params->on_application_end());
121 DCHECK(identity_to_instance_.find(app_identity) ==
122 identity_to_instance_.end());
123 identity_to_instance_[app_identity] = instance;
124 instance->InitializeApplication();
125 instance->ConnectToClient(params.Pass());
126 if (resulting_instance)
127 *resulting_instance = instance;
128 return application_request.Pass();
131 ApplicationInstance* ApplicationManager::GetApplicationInstance(
132 const Identity& identity) const {
133 const auto& it = identity_to_instance_.find(identity);
134 return it != identity_to_instance_.end() ? it->second : nullptr;
137 void ApplicationManager::HandleFetchCallback(
138 scoped_ptr<ConnectToApplicationParams> params,
139 scoped_ptr<Fetcher> fetcher) {
140 if (!fetcher) {
141 // Network error. Drop |params| to tell the requestor.
142 params->connect_callback().Run(Shell::kInvalidContentHandlerID);
143 return;
146 GURL redirect_url = fetcher->GetRedirectURL();
147 if (!redirect_url.is_empty()) {
148 // And around we go again... Whee!
149 // TODO(sky): this loses the original URL info.
150 URLRequestPtr new_request = URLRequest::New();
151 new_request->url = redirect_url.spec();
152 HttpHeaderPtr header = HttpHeader::New();
153 header->name = "Referer";
154 header->value = fetcher->GetRedirectReferer().spec();
155 new_request->headers.push_back(header.Pass());
156 params->SetURLInfo(new_request.Pass());
157 ConnectToApplication(params.Pass());
158 return;
161 // We already checked if the application was running before we fetched it, but
162 // it might have started while the fetch was outstanding. We don't want to
163 // have two copies of the app running, so check again.
164 if (ConnectToRunningApplication(&params))
165 return;
167 Identity originator_identity = params->originator_identity();
168 CapabilityFilter originator_filter = params->originator_filter();
169 CapabilityFilter filter = params->filter();
170 GURL app_url = params->app_url();
171 std::string qualifier = params->qualifier();
172 Shell::ConnectToApplicationCallback connect_callback =
173 params->connect_callback();
174 params->set_connect_callback(EmptyConnectCallback());
175 ApplicationInstance* app = nullptr;
176 InterfaceRequest<Application> request(CreateInstance(params.Pass(), &app));
179 GURL content_handler_url;
180 URLResponsePtr new_response;
181 if (package_manager_->HandleWithContentHandler(fetcher.get(),
182 app_url,
183 blocking_pool_,
184 &new_response,
185 &content_handler_url,
186 &qualifier)) {
187 LoadWithContentHandler(originator_identity, originator_filter,
188 content_handler_url, qualifier, filter,
189 connect_callback, app, request.Pass(),
190 new_response.Pass());
191 } else {
192 // TODO(erg): Have a better way of switching the sandbox on. For now, switch
193 // it on hard coded when we're using some of the sandboxable core services.
194 bool start_sandboxed = false;
195 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
196 switches::kMojoNoSandbox)) {
197 if (app_url == GURL("mojo://core_services/") && qualifier == "Core")
198 start_sandboxed = true;
199 else if (app_url == GURL("mojo://html_viewer/"))
200 start_sandboxed = true;
203 connect_callback.Run(Shell::kInvalidContentHandlerID);
205 fetcher->AsPath(blocking_pool_,
206 base::Bind(&ApplicationManager::RunNativeApplication,
207 weak_ptr_factory_.GetWeakPtr(),
208 base::Passed(request.Pass()), start_sandboxed,
209 base::Passed(fetcher.Pass())));
213 void ApplicationManager::RunNativeApplication(
214 InterfaceRequest<Application> application_request,
215 bool start_sandboxed,
216 scoped_ptr<Fetcher> fetcher,
217 const base::FilePath& path,
218 bool path_exists) {
219 // We only passed fetcher to keep it alive. Done with it now.
220 fetcher.reset();
222 DCHECK(application_request.is_pending());
224 if (!path_exists) {
225 LOG(ERROR) << "Library not started because library path '" << path.value()
226 << "' does not exist.";
227 return;
230 TRACE_EVENT1("mojo_shell", "ApplicationManager::RunNativeApplication", "path",
231 path.AsUTF8Unsafe());
232 NativeRunner* runner = native_runner_factory_->Create().release();
233 native_runners_.push_back(runner);
234 runner->Start(path, start_sandboxed, application_request.Pass(),
235 base::Bind(&ApplicationManager::CleanupRunner,
236 weak_ptr_factory_.GetWeakPtr(), runner));
239 void ApplicationManager::LoadWithContentHandler(
240 const Identity& originator_identity,
241 const CapabilityFilter& originator_filter,
242 const GURL& content_handler_url,
243 const std::string& qualifier,
244 const CapabilityFilter& filter,
245 const Shell::ConnectToApplicationCallback& connect_callback,
246 ApplicationInstance* app,
247 InterfaceRequest<Application> application_request,
248 URLResponsePtr url_response) {
249 ContentHandlerConnection* connection = nullptr;
250 Identity content_handler_identity(content_handler_url, qualifier);
251 // TODO(beng): Figure out the extent to which capability filter should be
252 // factored into handler identity.
253 IdentityToContentHandlerMap::iterator iter =
254 identity_to_content_handler_.find(content_handler_identity);
255 if (iter != identity_to_content_handler_.end()) {
256 connection = iter->second;
257 } else {
258 connection = new ContentHandlerConnection(
259 this, originator_identity, originator_filter, content_handler_url,
260 qualifier, filter, ++content_handler_id_counter_);
261 identity_to_content_handler_[content_handler_identity] = connection;
264 app->set_requesting_content_handler_id(connection->id());
265 connection->content_handler()->StartApplication(application_request.Pass(),
266 url_response.Pass());
267 connect_callback.Run(connection->id());
270 void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
271 const GURL& url) {
272 URLToLoaderMap::iterator it = url_to_loader_.find(url);
273 if (it != url_to_loader_.end())
274 delete it->second;
275 url_to_loader_[url] = loader.release();
278 ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
279 auto url_it = url_to_loader_.find(GetBaseURLAndQuery(url, nullptr));
280 if (url_it != url_to_loader_.end())
281 return url_it->second;
282 return default_loader_.get();
285 void ApplicationManager::OnApplicationInstanceError(
286 ApplicationInstance* instance) {
287 // Called from ~ApplicationInstance, so we do not need to call Destroy here.
288 const Identity identity = instance->identity();
289 base::Closure on_application_end = instance->on_application_end();
290 // Remove the shell.
291 auto it = identity_to_instance_.find(identity);
292 DCHECK(it != identity_to_instance_.end());
293 delete it->second;
294 identity_to_instance_.erase(it);
295 if (!on_application_end.is_null())
296 on_application_end.Run();
299 void ApplicationManager::OnContentHandlerConnectionClosed(
300 ContentHandlerConnection* content_handler) {
301 // Remove the mapping to the content handler.
302 auto it = identity_to_content_handler_.find(
303 Identity(content_handler->content_handler_url(),
304 content_handler->content_handler_qualifier()));
305 DCHECK(it != identity_to_content_handler_.end());
306 identity_to_content_handler_.erase(it);
309 void ApplicationManager::CleanupRunner(NativeRunner* runner) {
310 native_runners_.erase(
311 std::find(native_runners_.begin(), native_runners_.end(), runner));
314 Shell::ConnectToApplicationCallback EmptyConnectCallback() {
315 return base::Bind(&OnEmptyOnConnectCallback);
318 } // namespace shell
319 } // namespace mojo