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/dynamic_application_loader.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "mojo/common/common_type_converters.h"
16 #include "mojo/common/data_pipe_utils.h"
17 #include "mojo/services/public/interfaces/network/url_loader.mojom.h"
18 #include "mojo/shell/context.h"
19 #include "mojo/shell/filename_util.h"
20 #include "mojo/shell/switches.h"
21 #include "url/url_util.h"
26 // Encapsulates loading and running one individual application.
28 // Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must
29 // ensure that all the parameters passed to Loader subclasses stay valid through
32 // Async operations are done with WeakPtr to protect against
33 // DynamicApplicationLoader going away (and taking all the Loaders with it)
34 // while the async operation is outstanding.
35 class DynamicApplicationLoader::Loader
{
37 Loader(Context
* context
,
38 DynamicServiceRunnerFactory
* runner_factory
,
39 scoped_refptr
<ApplicationLoader::LoadCallbacks
> load_callbacks
,
40 const LoaderCompleteCallback
& loader_complete_callback
)
41 : load_callbacks_(load_callbacks
),
42 loader_complete_callback_(loader_complete_callback
),
44 runner_factory_(runner_factory
),
45 weak_ptr_factory_(this) {}
50 void RunLibrary(const base::FilePath
& path
, bool path_exists
) {
51 ScopedMessagePipeHandle shell_handle
=
52 load_callbacks_
->RegisterApplication();
53 if (!shell_handle
.is_valid()) {
59 DVLOG(1) << "Library not started because library path '" << path
.value()
60 << "' does not exist.";
65 runner_
= runner_factory_
->Create(context_
);
69 base::Bind(&Loader::LoaderComplete
, weak_ptr_factory_
.GetWeakPtr()));
72 void LoaderComplete() { loader_complete_callback_
.Run(this); }
74 scoped_refptr
<ApplicationLoader::LoadCallbacks
> load_callbacks_
;
75 LoaderCompleteCallback loader_complete_callback_
;
79 DynamicServiceRunnerFactory
* runner_factory_
;
80 scoped_ptr
<DynamicServiceRunner
> runner_
;
81 base::WeakPtrFactory
<Loader
> weak_ptr_factory_
;
84 // A loader for local files.
85 class DynamicApplicationLoader::LocalLoader
: public Loader
{
87 LocalLoader(const GURL
& url
,
89 DynamicServiceRunnerFactory
* runner_factory
,
90 scoped_refptr
<ApplicationLoader::LoadCallbacks
> load_callbacks
,
91 const LoaderCompleteCallback
& loader_complete_callback
)
95 loader_complete_callback
),
96 weak_ptr_factory_(this) {
97 DCHECK(url
.SchemeIsFile());
98 url::RawCanonOutputW
<1024> output
;
99 url::DecodeURLEscapeSequences(
100 url
.path().data(), static_cast<int>(url
.path().length()), &output
);
101 base::string16 decoded_path
=
102 base::string16(output
.data(), output
.length());
104 base::FilePath
path(decoded_path
);
106 base::FilePath
path(base::UTF16ToUTF8(decoded_path
));
109 // Async for consistency with network case.
110 base::MessageLoop::current()->PostTask(
112 base::Bind(&LocalLoader::RunLibrary
,
113 weak_ptr_factory_
.GetWeakPtr(),
115 base::PathExists(path
)));
118 ~LocalLoader() override
{}
121 base::WeakPtrFactory
<LocalLoader
> weak_ptr_factory_
;
124 // A loader for network files.
125 class DynamicApplicationLoader::NetworkLoader
: public Loader
{
127 NetworkLoader(const GURL
& url
,
128 MimeTypeToURLMap
* mime_type_to_url
,
130 DynamicServiceRunnerFactory
* runner_factory
,
131 NetworkService
* network_service
,
132 scoped_refptr
<ApplicationLoader::LoadCallbacks
> load_callbacks
,
133 const LoaderCompleteCallback
& loader_complete_callback
)
137 loader_complete_callback
),
138 mime_type_to_url_(mime_type_to_url
),
139 weak_ptr_factory_(this) {
140 URLRequestPtr
request(URLRequest::New());
141 request
->url
= String::From(url
);
142 request
->auto_follow_redirects
= true;
144 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
145 switches::kDisableCache
)) {
146 request
->bypass_cache
= true;
149 network_service
->CreateURLLoader(GetProxy(&url_loader_
));
150 url_loader_
->Start(request
.Pass(),
151 base::Bind(&NetworkLoader::OnLoadComplete
,
152 weak_ptr_factory_
.GetWeakPtr()));
155 ~NetworkLoader() override
{
157 base::DeleteFile(file_
, false);
161 void OnLoadComplete(URLResponsePtr response
) {
162 if (response
->error
) {
163 LOG(ERROR
) << "Error (" << response
->error
->code
<< ": "
164 << response
->error
->description
<< ") while fetching "
170 MimeTypeToURLMap::iterator iter
=
171 mime_type_to_url_
->find(response
->mime_type
);
172 if (iter
!= mime_type_to_url_
->end()) {
173 load_callbacks_
->LoadWithContentHandler(iter
->second
, response
.Pass());
177 base::CreateTemporaryFile(&file_
);
179 response
->body
.Pass(),
181 context_
->task_runners()->blocking_pool(),
183 &NetworkLoader::RunLibrary
, weak_ptr_factory_
.GetWeakPtr(), file_
));
186 MimeTypeToURLMap
* mime_type_to_url_
;
187 URLLoaderPtr url_loader_
;
188 base::FilePath file_
;
189 base::WeakPtrFactory
<NetworkLoader
> weak_ptr_factory_
;
192 DynamicApplicationLoader::DynamicApplicationLoader(
194 scoped_ptr
<DynamicServiceRunnerFactory
> runner_factory
)
196 runner_factory_(runner_factory
.Pass()),
198 // Unretained() is correct here because DynamicApplicationLoader owns the
199 // loaders that we pass this callback to.
200 loader_complete_callback_(
201 base::Bind(&DynamicApplicationLoader::LoaderComplete
,
202 base::Unretained(this))) {
205 DynamicApplicationLoader::~DynamicApplicationLoader() {
208 void DynamicApplicationLoader::RegisterContentHandler(
209 const std::string
& mime_type
,
210 const GURL
& content_handler_url
) {
211 mime_type_to_url_
[mime_type
] = content_handler_url
;
214 void DynamicApplicationLoader::Load(
215 ApplicationManager
* manager
,
217 scoped_refptr
<LoadCallbacks
> load_callbacks
) {
219 if (url
.SchemeIs("mojo")) {
220 resolved_url
= context_
->mojo_url_resolver()->Resolve(url
);
225 if (resolved_url
.SchemeIsFile()) {
226 loaders_
.push_back(new LocalLoader(resolved_url
,
228 runner_factory_
.get(),
230 loader_complete_callback_
));
234 if (!network_service_
) {
235 context_
->application_manager()->ConnectToService(
236 GURL("mojo:network_service"), &network_service_
);
239 loaders_
.push_back(new NetworkLoader(resolved_url
,
242 runner_factory_
.get(),
243 network_service_
.get(),
245 loader_complete_callback_
));
248 void DynamicApplicationLoader::OnApplicationError(ApplicationManager
* manager
,
250 // TODO(darin): What should we do about service errors? This implies that
251 // the app closed its handle to the service manager. Maybe we don't care?
254 void DynamicApplicationLoader::LoaderComplete(Loader
* loader
) {
255 loaders_
.erase(std::find(loaders_
.begin(), loaders_
.end(), loader
));