Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / image_writer_private / operation.cc
1 // Copyright 2013 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 "chrome/browser/extensions/api/image_writer_private/operation.h"
6
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_util.h"
9 #include "base/lazy_instance.h"
10 #include "base/threading/worker_pool.h"
11 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
12 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
13 #include "content/public/browser/browser_thread.h"
14
15 namespace extensions {
16 namespace image_writer {
17
18 using content::BrowserThread;
19
20 const int kMD5BufferSize = 1024;
21 #if defined(OS_CHROMEOS)
22 // Chrome OS only has a 1 GB temporary partition.  This is too small to hold our
23 // unzipped image. Fortunately we mount part of the temporary partition under
24 // /var/tmp.
25 const char kChromeOSTempRoot[] = "/var/tmp";
26 #endif
27
28 #if !defined(OS_CHROMEOS)
29 static base::LazyInstance<scoped_refptr<ImageWriterUtilityClient> >
30     g_utility_client = LAZY_INSTANCE_INITIALIZER;
31 #endif
32
33 Operation::Operation(base::WeakPtr<OperationManager> manager,
34                      const ExtensionId& extension_id,
35                      const std::string& device_path)
36     : manager_(manager),
37       extension_id_(extension_id),
38 #if defined(OS_WIN)
39       device_path_(base::FilePath::FromUTF8Unsafe(device_path)),
40 #else
41       device_path_(device_path),
42 #endif
43       stage_(image_writer_api::STAGE_UNKNOWN),
44       progress_(0) {
45 }
46
47 Operation::~Operation() {}
48
49 void Operation::Cancel() {
50   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
51
52   stage_ = image_writer_api::STAGE_NONE;
53
54   CleanUp();
55 }
56
57 void Operation::Abort() {
58   Error(error::kAborted);
59 }
60
61 int Operation::GetProgress() {
62   return progress_;
63 }
64
65 image_writer_api::Stage Operation::GetStage() {
66   return stage_;
67 }
68
69 #if !defined(OS_CHROMEOS)
70 // static
71 void Operation::SetUtilityClientForTesting(
72     scoped_refptr<ImageWriterUtilityClient> client) {
73   g_utility_client.Get() = client;
74 }
75 #endif
76
77 void Operation::Start() {
78 #if defined(OS_CHROMEOS)
79   if (!temp_dir_.CreateUniqueTempDirUnderPath(
80            base::FilePath(kChromeOSTempRoot))) {
81 #else
82   if (!temp_dir_.CreateUniqueTempDir()) {
83 #endif
84     Error(error::kTempDirError);
85     return;
86   }
87
88   AddCleanUpFunction(
89       base::Bind(base::IgnoreResult(&base::ScopedTempDir::Delete),
90                  base::Unretained(&temp_dir_)));
91
92   StartImpl();
93 }
94
95 void Operation::Unzip(const base::Closure& continuation) {
96   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
97   if (IsCancelled()) {
98     return;
99   }
100
101   if (image_path_.Extension() != FILE_PATH_LITERAL(".zip")) {
102     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
103     return;
104   }
105
106   SetStage(image_writer_api::STAGE_UNZIP);
107
108   if (!(zip_reader_.Open(image_path_) && zip_reader_.AdvanceToNextEntry() &&
109         zip_reader_.OpenCurrentEntryInZip())) {
110     Error(error::kUnzipGenericError);
111     return;
112   }
113
114   if (zip_reader_.HasMore()) {
115     Error(error::kUnzipInvalidArchive);
116     return;
117   }
118
119   // Create a new target to unzip to.  The original file is opened by the
120   // zip_reader_.
121   zip::ZipReader::EntryInfo* entry_info = zip_reader_.current_entry_info();
122   if (entry_info) {
123     image_path_ = temp_dir_.path().Append(entry_info->file_path().BaseName());
124   } else {
125     Error(error::kTempDirError);
126     return;
127   }
128
129   zip_reader_.ExtractCurrentEntryToFilePathAsync(
130       image_path_,
131       base::Bind(&Operation::CompleteAndContinue, this, continuation),
132       base::Bind(&Operation::OnUnzipFailure, this),
133       base::Bind(&Operation::OnUnzipProgress,
134                  this,
135                  zip_reader_.current_entry_info()->original_size()));
136 }
137
138 void Operation::Finish() {
139   if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
140     BrowserThread::PostTask(
141         BrowserThread::FILE, FROM_HERE, base::Bind(&Operation::Finish, this));
142     return;
143   }
144
145   CleanUp();
146
147   BrowserThread::PostTask(
148       BrowserThread::UI,
149       FROM_HERE,
150       base::Bind(&OperationManager::OnComplete, manager_, extension_id_));
151 }
152
153 void Operation::Error(const std::string& error_message) {
154   if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
155     BrowserThread::PostTask(BrowserThread::FILE,
156                             FROM_HERE,
157                             base::Bind(&Operation::Error, this, error_message));
158     return;
159   }
160
161   BrowserThread::PostTask(
162       BrowserThread::UI,
163       FROM_HERE,
164       base::Bind(&OperationManager::OnError,
165                  manager_,
166                  extension_id_,
167                  stage_,
168                  progress_,
169                  error_message));
170
171   CleanUp();
172 }
173
174 void Operation::SetProgress(int progress) {
175   if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
176     BrowserThread::PostTask(
177         BrowserThread::FILE,
178         FROM_HERE,
179         base::Bind(&Operation::SetProgress,
180                    this,
181                    progress));
182     return;
183   }
184
185   if (progress <= progress_) {
186     return;
187   }
188
189   if (IsCancelled()) {
190     return;
191   }
192
193   progress_ = progress;
194
195   BrowserThread::PostTask(BrowserThread::UI,
196                           FROM_HERE,
197                           base::Bind(&OperationManager::OnProgress,
198                                      manager_,
199                                      extension_id_,
200                                      stage_,
201                                      progress_));
202 }
203
204 void Operation::SetStage(image_writer_api::Stage stage) {
205   if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
206     BrowserThread::PostTask(
207         BrowserThread::FILE,
208         FROM_HERE,
209         base::Bind(&Operation::SetStage,
210                    this,
211                    stage));
212     return;
213   }
214
215   if (IsCancelled()) {
216     return;
217   }
218
219   stage_ = stage;
220   progress_ = 0;
221
222   BrowserThread::PostTask(
223       BrowserThread::UI,
224       FROM_HERE,
225       base::Bind(&OperationManager::OnProgress,
226                  manager_,
227                  extension_id_,
228                  stage_,
229                  progress_));
230 }
231
232 bool Operation::IsCancelled() {
233   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
234
235   return stage_ == image_writer_api::STAGE_NONE;
236 }
237
238 void Operation::AddCleanUpFunction(const base::Closure& callback) {
239   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
240   cleanup_functions_.push_back(callback);
241 }
242
243 void Operation::CompleteAndContinue(const base::Closure& continuation) {
244   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
245   SetProgress(kProgressComplete);
246   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
247 }
248
249 #if !defined(OS_CHROMEOS)
250 void Operation::StartUtilityClient() {
251   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
252   if (g_utility_client.Get().get()) {
253     image_writer_client_ = g_utility_client.Get();
254     return;
255   }
256   if (!image_writer_client_.get()) {
257     image_writer_client_ = new ImageWriterUtilityClient();
258     AddCleanUpFunction(base::Bind(&Operation::StopUtilityClient, this));
259   }
260 }
261
262 void Operation::StopUtilityClient() {
263   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
264   BrowserThread::PostTask(
265       BrowserThread::IO,
266       FROM_HERE,
267       base::Bind(&ImageWriterUtilityClient::Shutdown, image_writer_client_));
268 }
269
270 void Operation::WriteImageProgress(int64 total_bytes, int64 curr_bytes) {
271   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
272   if (IsCancelled()) {
273     return;
274   }
275
276   int progress = kProgressComplete * curr_bytes / total_bytes;
277
278   if (progress > GetProgress()) {
279     SetProgress(progress);
280   }
281 }
282 #endif
283
284 void Operation::GetMD5SumOfFile(
285     const base::FilePath& file_path,
286     int64 file_size,
287     int progress_offset,
288     int progress_scale,
289     const base::Callback<void(const std::string&)>& callback) {
290   if (IsCancelled()) {
291     return;
292   }
293
294   base::MD5Init(&md5_context_);
295
296   base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
297   if (!file.IsValid()) {
298     Error(error::kImageOpenError);
299     return;
300   }
301
302   if (file_size <= 0) {
303     file_size = file.GetLength();
304     if (file_size < 0) {
305       Error(error::kImageOpenError);
306       return;
307     }
308   }
309
310   BrowserThread::PostTask(BrowserThread::FILE,
311                           FROM_HERE,
312                           base::Bind(&Operation::MD5Chunk,
313                                      this,
314                                      Passed(file.Pass()),
315                                      0,
316                                      file_size,
317                                      progress_offset,
318                                      progress_scale,
319                                      callback));
320 }
321
322 void Operation::MD5Chunk(
323     base::File file,
324     int64 bytes_processed,
325     int64 bytes_total,
326     int progress_offset,
327     int progress_scale,
328     const base::Callback<void(const std::string&)>& callback) {
329   if (IsCancelled())
330     return;
331
332   CHECK_LE(bytes_processed, bytes_total);
333
334   scoped_ptr<char[]> buffer(new char[kMD5BufferSize]);
335   int read_size = std::min(bytes_total - bytes_processed,
336                            static_cast<int64>(kMD5BufferSize));
337
338   if (read_size == 0) {
339     // Nothing to read, we are done.
340     base::MD5Digest digest;
341     base::MD5Final(&digest, &md5_context_);
342     callback.Run(base::MD5DigestToBase16(digest));
343   } else {
344     int len = file.Read(bytes_processed, buffer.get(), read_size);
345
346     if (len == read_size) {
347       // Process data.
348       base::MD5Update(&md5_context_, base::StringPiece(buffer.get(), len));
349       int percent_curr =
350           ((bytes_processed + len) * progress_scale) / bytes_total +
351           progress_offset;
352       SetProgress(percent_curr);
353
354       BrowserThread::PostTask(BrowserThread::FILE,
355                               FROM_HERE,
356                               base::Bind(&Operation::MD5Chunk,
357                                          this,
358                                          Passed(file.Pass()),
359                                          bytes_processed + len,
360                                          bytes_total,
361                                          progress_offset,
362                                          progress_scale,
363                                          callback));
364       // Skip closing the file.
365       return;
366     } else {
367       // We didn't read the bytes we expected.
368       Error(error::kHashReadError);
369     }
370   }
371 }
372
373 void Operation::OnUnzipFailure() {
374   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
375   Error(error::kUnzipGenericError);
376 }
377
378 void Operation::OnUnzipProgress(int64 total_bytes, int64 progress_bytes) {
379   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
380
381   int progress_percent = kProgressComplete * progress_bytes / total_bytes;
382   SetProgress(progress_percent);
383 }
384
385 void Operation::CleanUp() {
386   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
387   for (std::vector<base::Closure>::iterator it = cleanup_functions_.begin();
388        it != cleanup_functions_.end();
389        ++it) {
390     it->Run();
391   }
392   cleanup_functions_.clear();
393 }
394
395 }  // namespace image_writer
396 }  // namespace extensions