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

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

Issue 775343004: Move //mojo/shell to //shell (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 6 years 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/dynamic_application_loader.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/format_macros.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "mojo/common/common_type_converters.h"
20 #include "mojo/common/data_pipe_utils.h"
21 #include "mojo/public/cpp/system/data_pipe.h"
22 #include "mojo/services/public/interfaces/network/url_loader.mojom.h"
23 #include "mojo/shell/context.h"
24 #include "mojo/shell/data_pipe_peek.h"
25 #include "mojo/shell/filename_util.h"
26 #include "mojo/shell/switches.h"
27 #include "url/url_util.h"
28
29 namespace mojo {
30 namespace shell {
31
32 namespace {
33
34 static const char kMojoMagic[] = "#!mojo:";
35 static const size_t kMaxShebangLength = 2048;
36
37 void IgnoreResult(bool result) {
38 }
39
40 } // namespace
41
42 // Encapsulates loading and running one individual application.
43 //
44 // Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must
45 // ensure that all the parameters passed to Loader subclasses stay valid through
46 // Loader's lifetime.
47 //
48 // Async operations are done with WeakPtr to protect against
49 // DynamicApplicationLoader going away (and taking all the Loaders with it)
50 // while the async operation is outstanding.
51 class DynamicApplicationLoader::Loader {
52 public:
53 Loader(MimeTypeToURLMap* mime_type_to_url,
54 Context* context,
55 DynamicServiceRunnerFactory* runner_factory,
56 ScopedMessagePipeHandle shell_handle,
57 ApplicationLoader::LoadCallback load_callback,
58 const LoaderCompleteCallback& loader_complete_callback)
59 : shell_handle_(shell_handle.Pass()),
60 load_callback_(load_callback),
61 loader_complete_callback_(loader_complete_callback),
62 context_(context),
63 mime_type_to_url_(mime_type_to_url),
64 runner_factory_(runner_factory),
65 weak_ptr_factory_(this) {}
66
67 virtual ~Loader() {}
68
69 protected:
70 virtual URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
71 uint32_t skip) = 0;
72
73 virtual void AsPath(
74 base::TaskRunner* task_runner,
75 base::Callback<void(const base::FilePath&, bool)> callback) = 0;
76
77 virtual std::string MimeType() = 0;
78
79 virtual bool HasMojoMagic() = 0;
80
81 virtual bool PeekFirstLine(std::string* line) = 0;
82
83 void Load() {
84 // If the response begins with a #!mojo:<content-handler-url>, use it.
85 GURL url;
86 std::string shebang;
87 if (PeekContentHandler(&shebang, &url)) {
88 load_callback_.Run(
89 url, shell_handle_.Pass(),
90 AsURLResponse(context_->task_runners()->blocking_pool(),
91 static_cast<int>(shebang.size())));
92 return;
93 }
94
95 MimeTypeToURLMap::iterator iter = mime_type_to_url_->find(MimeType());
96 if (iter != mime_type_to_url_->end()) {
97 load_callback_.Run(
98 iter->second, shell_handle_.Pass(),
99 AsURLResponse(context_->task_runners()->blocking_pool(), 0));
100 return;
101 }
102
103 // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo
104 // application. That could either mean looking for the platform-specific dll
105 // header, or looking for some specific mojo signature prepended to the
106 // library.
107
108 AsPath(context_->task_runners()->blocking_pool(),
109 base::Bind(&Loader::RunLibrary, weak_ptr_factory_.GetWeakPtr()));
110 }
111
112 void ReportComplete() { loader_complete_callback_.Run(this); }
113
114 private:
115 bool PeekContentHandler(std::string* mojo_shebang,
116 GURL* mojo_content_handler_url) {
117 std::string shebang;
118 if (HasMojoMagic() && PeekFirstLine(&shebang)) {
119 GURL url(shebang.substr(2, std::string::npos));
120 if (url.is_valid()) {
121 *mojo_shebang = shebang;
122 *mojo_content_handler_url = url;
123 return true;
124 }
125 }
126 return false;
127 }
128
129 void RunLibrary(const base::FilePath& path, bool path_exists) {
130 DCHECK(shell_handle_.is_valid());
131
132 if (!path_exists) {
133 LOG(ERROR) << "Library not started because library path '" << path.value()
134 << "' does not exist.";
135 ReportComplete();
136 return;
137 }
138
139 runner_ = runner_factory_->Create(context_);
140 runner_->Start(
141 path, shell_handle_.Pass(),
142 base::Bind(&Loader::ReportComplete, weak_ptr_factory_.GetWeakPtr()));
143 }
144
145 ScopedMessagePipeHandle shell_handle_;
146 ApplicationLoader::LoadCallback load_callback_;
147 LoaderCompleteCallback loader_complete_callback_;
148 Context* context_;
149 MimeTypeToURLMap* mime_type_to_url_;
150 DynamicServiceRunnerFactory* runner_factory_;
151 scoped_ptr<DynamicServiceRunner> runner_;
152 base::WeakPtrFactory<Loader> weak_ptr_factory_;
153 };
154
155 // A loader for local files.
156 class DynamicApplicationLoader::LocalLoader : public Loader {
157 public:
158 LocalLoader(const GURL& url,
159 MimeTypeToURLMap* mime_type_to_url,
160 Context* context,
161 DynamicServiceRunnerFactory* runner_factory,
162 ScopedMessagePipeHandle shell_handle,
163 ApplicationLoader::LoadCallback load_callback,
164 const LoaderCompleteCallback& loader_complete_callback)
165 : Loader(mime_type_to_url,
166 context,
167 runner_factory,
168 shell_handle.Pass(),
169 load_callback,
170 loader_complete_callback),
171 url_(url),
172 path_(UrlToFile(url)) {
173 Load();
174 }
175
176 private:
177 static base::FilePath UrlToFile(const GURL& url) {
178 DCHECK(url.SchemeIsFile());
179 url::RawCanonOutputW<1024> output;
180 url::DecodeURLEscapeSequences(
181 url.path().data(), static_cast<int>(url.path().length()), &output);
182 base::string16 decoded_path =
183 base::string16(output.data(), output.length());
184 #if defined(OS_WIN)
185 base::TrimString(decoded_path, L"/", &decoded_path);
186 base::FilePath path(decoded_path);
187 #else
188 base::FilePath path(base::UTF16ToUTF8(decoded_path));
189 #endif
190 return path;
191 }
192
193 URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
194 uint32_t skip) override {
195 URLResponsePtr response(URLResponse::New());
196 response->url = String::From(url_);
197 DataPipe data_pipe;
198 response->body = data_pipe.consumer_handle.Pass();
199 int64 file_size;
200 if (base::GetFileSize(path_, &file_size)) {
201 response->headers = Array<String>(1);
202 response->headers[0] =
203 base::StringPrintf("Content-Length: %" PRId64, file_size);
204 }
205 common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
206 task_runner, base::Bind(&IgnoreResult));
207 return response.Pass();
208 }
209
210 void AsPath(
211 base::TaskRunner* task_runner,
212 base::Callback<void(const base::FilePath&, bool)> callback) override {
213 // Async for consistency with network case.
214 base::MessageLoop::current()->PostTask(
215 FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
216 }
217
218 std::string MimeType() override { return ""; }
219
220 bool HasMojoMagic() override {
221 std::string magic;
222 ReadFileToString(path_, &magic, strlen(kMojoMagic));
223 return magic == kMojoMagic;
224 }
225
226 bool PeekFirstLine(std::string* line) override {
227 std::string start_of_file;
228 ReadFileToString(path_, &start_of_file, kMaxShebangLength);
229 size_t return_position = start_of_file.find('\n');
230 if (return_position == std::string::npos)
231 return false;
232 *line = start_of_file.substr(0, return_position + 1);
233 return true;
234 }
235
236 GURL url_;
237 base::FilePath path_;
238
239 DISALLOW_COPY_AND_ASSIGN(LocalLoader);
240 };
241
242 // A loader for network files.
243 class DynamicApplicationLoader::NetworkLoader : public Loader {
244 public:
245 NetworkLoader(const GURL& url,
246 NetworkService* network_service,
247 MimeTypeToURLMap* mime_type_to_url,
248 Context* context,
249 DynamicServiceRunnerFactory* runner_factory,
250 ScopedMessagePipeHandle shell_handle,
251 ApplicationLoader::LoadCallback load_callback,
252 const LoaderCompleteCallback& loader_complete_callback)
253 : Loader(mime_type_to_url,
254 context,
255 runner_factory,
256 shell_handle.Pass(),
257 load_callback,
258 loader_complete_callback),
259 weak_ptr_factory_(this) {
260 StartNetworkRequest(url, network_service);
261 }
262
263 ~NetworkLoader() override {
264 if (!path_.empty())
265 base::DeleteFile(path_, false);
266 }
267
268 private:
269 // TODO(hansmuller): Revisit this when a real peek operation is available.
270 static const MojoDeadline kPeekTimeout = MOJO_DEADLINE_INDEFINITE;
271
272 URLResponsePtr AsURLResponse(base::TaskRunner* task_runner,
273 uint32_t skip) override {
274 if (skip != 0) {
275 MojoResult result = ReadDataRaw(
276 response_->body.get(), nullptr, &skip,
277 MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD);
278 DCHECK_EQ(result, MOJO_RESULT_OK);
279 }
280 return response_.Pass();
281 }
282
283 void AsPath(
284 base::TaskRunner* task_runner,
285 base::Callback<void(const base::FilePath&, bool)> callback) override {
286 if (!path_.empty() || !response_) {
287 base::MessageLoop::current()->PostTask(
288 FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
289 return;
290 }
291 base::CreateTemporaryFile(&path_);
292 common::CopyToFile(response_->body.Pass(), path_, task_runner,
293 base::Bind(callback, path_));
294 }
295
296 std::string MimeType() override {
297 DCHECK(response_);
298 return response_->mime_type;
299 }
300
301 bool HasMojoMagic() override {
302 std::string magic;
303 return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
304 kPeekTimeout) &&
305 magic == kMojoMagic;
306 }
307
308 bool PeekFirstLine(std::string* line) override {
309 return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength,
310 kPeekTimeout);
311 }
312
313 void StartNetworkRequest(const GURL& url, NetworkService* network_service) {
314 URLRequestPtr request(URLRequest::New());
315 request->url = String::From(url);
316 request->auto_follow_redirects = true;
317
318 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
319 switches::kDisableCache)) {
320 request->bypass_cache = true;
321 }
322
323 network_service->CreateURLLoader(GetProxy(&url_loader_));
324 url_loader_->Start(request.Pass(),
325 base::Bind(&NetworkLoader::OnLoadComplete,
326 weak_ptr_factory_.GetWeakPtr()));
327 }
328
329 void OnLoadComplete(URLResponsePtr response) {
330 if (response->error) {
331 LOG(ERROR) << "Error (" << response->error->code << ": "
332 << response->error->description << ") while fetching "
333 << response->url;
334 ReportComplete();
335 return;
336 }
337 response_ = response.Pass();
338 Load();
339 }
340
341 URLLoaderPtr url_loader_;
342 URLResponsePtr response_;
343 base::FilePath path_;
344 base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_;
345
346 DISALLOW_COPY_AND_ASSIGN(NetworkLoader);
347 };
348
349 DynamicApplicationLoader::DynamicApplicationLoader(
350 Context* context,
351 scoped_ptr<DynamicServiceRunnerFactory> runner_factory)
352 : context_(context),
353 runner_factory_(runner_factory.Pass()),
354
355 // Unretained() is correct here because DynamicApplicationLoader owns the
356 // loaders that we pass this callback to.
357 loader_complete_callback_(
358 base::Bind(&DynamicApplicationLoader::LoaderComplete,
359 base::Unretained(this))) {
360 }
361
362 DynamicApplicationLoader::~DynamicApplicationLoader() {
363 }
364
365 void DynamicApplicationLoader::RegisterContentHandler(
366 const std::string& mime_type,
367 const GURL& content_handler_url) {
368 DCHECK(content_handler_url.is_valid())
369 << "Content handler URL is invalid for mime type " << mime_type;
370 mime_type_to_url_[mime_type] = content_handler_url;
371 }
372
373 void DynamicApplicationLoader::Load(ApplicationManager* manager,
374 const GURL& url,
375 ScopedMessagePipeHandle shell_handle,
376 LoadCallback load_callback) {
377 if (url.SchemeIsFile()) {
378 loaders_.push_back(new LocalLoader(
379 url, &mime_type_to_url_, context_, runner_factory_.get(),
380 shell_handle.Pass(), load_callback, loader_complete_callback_));
381 return;
382 }
383
384 if (!network_service_) {
385 context_->application_manager()->ConnectToService(
386 GURL("mojo:network_service"), &network_service_);
387 }
388
389 loaders_.push_back(
390 new NetworkLoader(url, network_service_.get(), &mime_type_to_url_,
391 context_, runner_factory_.get(), shell_handle.Pass(),
392 load_callback, loader_complete_callback_));
393 }
394
395 void DynamicApplicationLoader::OnApplicationError(ApplicationManager* manager,
396 const GURL& url) {
397 // TODO(darin): What should we do about service errors? This implies that
398 // the app closed its handle to the service manager. Maybe we don't care?
399 }
400
401 void DynamicApplicationLoader::LoaderComplete(Loader* loader) {
402 loaders_.erase(std::find(loaders_.begin(), loaders_.end(), loader));
403 }
404
405 } // namespace shell
406 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/shell/dynamic_application_loader.h ('k') | mojo/shell/dynamic_application_loader_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698