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"
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"
29 bool has_created_instance
= false;
31 void OnEmptyOnConnectCallback(uint32_t content_handler_id
) {}
36 ApplicationManager::TestAPI::TestAPI(ApplicationManager
* 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(¶ms
))
85 ApplicationLoader
* loader
= GetLoaderForURL(params
->app_url());
87 GURL url
= params
->app_url();
88 loader
->Load(url
, CreateInstance(params
.Pass(), nullptr));
92 URLRequestPtr original_url_request
= params
->TakeAppURLRequest();
94 base::Bind(&ApplicationManager::HandleFetchCallback
,
95 weak_ptr_factory_
.GetWeakPtr(), base::Passed(¶ms
));
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()));
106 instance
->ConnectToClient(params
->Pass());
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
) {
141 // Network error. Drop |params| to tell the requestor.
142 params
->connect_callback().Run(Shell::kInvalidContentHandlerID
);
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());
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(¶ms
))
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(),
185 &content_handler_url
,
187 LoadWithContentHandler(originator_identity
, originator_filter
,
188 content_handler_url
, qualifier
, filter
,
189 connect_callback
, app
, request
.Pass(),
190 new_response
.Pass());
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
,
219 // We only passed fetcher to keep it alive. Done with it now.
222 DCHECK(application_request
.is_pending());
225 LOG(ERROR
) << "Library not started because library path '" << path
.value()
226 << "' does not exist.";
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
;
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
,
272 URLToLoaderMap::iterator it
= url_to_loader_
.find(url
);
273 if (it
!= url_to_loader_
.end())
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();
291 auto it
= identity_to_instance_
.find(identity
);
292 DCHECK(it
!= identity_to_instance_
.end());
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
);