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

Side by Side Diff: chrome/browser/extensions/api/image_writer_private/operation.cc

Issue 149313003: Significantly cleans up the ImageWriter Operation class and subclasses. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removes self-reference that was causing the operation to not be deleted. Removes some extra loggin… Created 6 years, 10 months 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/file_util.h" 5 #include "base/file_util.h"
6 #include "base/files/file_enumerator.h" 6 #include "base/files/file_enumerator.h"
7 #include "base/threading/worker_pool.h" 7 #include "base/threading/worker_pool.h"
8 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" 8 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
9 #include "chrome/browser/extensions/api/image_writer_private/operation.h" 9 #include "chrome/browser/extensions/api/image_writer_private/operation.h"
10 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h " 10 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h "
11 #include "content/public/browser/browser_thread.h" 11 #include "content/public/browser/browser_thread.h"
12 12
13 namespace extensions { 13 namespace extensions {
14 namespace image_writer { 14 namespace image_writer {
15 15
16 using content::BrowserThread; 16 using content::BrowserThread;
17 17
18 namespace {
19
20 const int kMD5BufferSize = 1024; 18 const int kMD5BufferSize = 1024;
21 19 #if defined(OS_CHROMEOS)
22 void RemoveTempDirectory(const base::FilePath path) { 20 // Chrome OS only has a 1 GB temporary partition. This is too small to hold our
23 base::DeleteFile(path, true); 21 // unzipped image. Fortunately we mount part of the temporary partition under
24 } 22 // /var/tmp.
25 23 const char kChromeOSTempRoot[] = "/var/tmp";
26 } // namespace 24 #endif
27 25
28 Operation::Operation(base::WeakPtr<OperationManager> manager, 26 Operation::Operation(base::WeakPtr<OperationManager> manager,
29 const ExtensionId& extension_id, 27 const ExtensionId& extension_id,
30 const std::string& storage_unit_id) 28 const std::string& device_path)
31 : manager_(manager), 29 : manager_(manager),
32 extension_id_(extension_id), 30 extension_id_(extension_id),
33 storage_unit_id_(storage_unit_id), 31 #if defined(OS_WIN)
34 verify_write_(true), 32 device_path_(base::FilePath::FromUTF8Unsafe(device_path)),
33 #else
34 device_path_(device_path),
35 #endif
36 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
37 source_(base::kInvalidPlatformFileValue),
38 target_(base::kInvalidPlatformFileValue),
39 #endif
35 stage_(image_writer_api::STAGE_UNKNOWN), 40 stage_(image_writer_api::STAGE_UNKNOWN),
36 progress_(0) { 41 progress_(0) {
37 } 42 }
38 43
39 Operation::~Operation() { 44 Operation::~Operation() {
40 } 45 }
41 46
42 void Operation::Cancel() { 47 void Operation::Cancel() {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 48 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
44 49
45 DVLOG(1) << "Cancelling image writing operation for ext: " << extension_id_;
46
47 stage_ = image_writer_api::STAGE_NONE; 50 stage_ = image_writer_api::STAGE_NONE;
48 51
49 CleanUp(); 52 CleanUp();
50 } 53 }
51 54
52 void Operation::Abort() { 55 void Operation::Abort() {
53 Error(error::kAborted); 56 Error(error::kAborted);
54 } 57 }
55 58
56 int Operation::GetProgress() { 59 int Operation::GetProgress() {
57 return progress_; 60 return progress_;
58 } 61 }
59 62
60 image_writer_api::Stage Operation::GetStage() { 63 image_writer_api::Stage Operation::GetStage() {
61 return stage_; 64 return stage_;
62 } 65 }
63 66
67 void Operation::Start() {
68 #if defined(OS_CHROMEOS)
69 if (!temp_dir_.CreateUniqueTempDirUnderPath(
70 base::FilePath(kChromeOSTempRoot))) {
71 #else
72 if (!temp_dir_.CreateUniqueTempDir()) {
73 #endif
74 Error(error::kTempDirError);
75 Cancel();
76 }
tbarzic 2014/02/13 06:58:52 a suggestion: Instead of calling Operation::Start
Drew Haven 2014/02/13 20:48:10 That makes more sense. It will help guarantee tha
77 }
78
79 void Operation::Unzip(const base::Closure& continuation) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
81 if (IsCancelled()) {
82 return;
83 }
84
85 if (image_path_.Extension() != FILE_PATH_LITERAL(".zip")) {
86 continuation.Run();
87 return;
88 }
89
90 SetStage(image_writer_api::STAGE_UNZIP);
91
92 if (!(zip_reader_.Open(image_path_) && zip_reader_.AdvanceToNextEntry() &&
93 zip_reader_.OpenCurrentEntryInZip())) {
94 Error(error::kUnzipGenericError);
95 return;
96 }
97
98 if (zip_reader_.HasMore()) {
99 Error(error::kUnzipInvalidArchive);
100 return;
101 }
102
103 // Create a new target to unzip to. The original file is opened by the
104 // zip_reader_.
105 zip::ZipReader::EntryInfo* entry_info;
106 if ((entry_info = zip_reader_.current_entry_info())) {
tbarzic 2014/02/13 06:58:52 I think this would look better: ZipReader::EntryIn
Drew Haven 2014/02/13 20:48:10 Done.
107 image_path_ = temp_dir_.path().Append(entry_info->file_path().BaseName());
108 } else {
109 Error(error::kTempDirError);
110 return;
111 }
112
113 zip_reader_.ExtractCurrentEntryToFilePathAsync(
114 image_path_,
115 base::Bind(&Operation::OnUnzipSuccess, this, continuation),
116 base::Bind(&Operation::OnUnzipFailure, this),
117 base::Bind(&Operation::OnUnzipProgress,
118 this,
119 zip_reader_.current_entry_info()->original_size()));
120 }
121
122 void Operation::Finish() {
123 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
124 BrowserThread::PostTask(
125 BrowserThread::FILE, FROM_HERE, base::Bind(&Operation::Finish, this));
126 return;
127 }
128
129 CleanUp();
130
131 BrowserThread::PostTask(
132 BrowserThread::UI,
133 FROM_HERE,
134 base::Bind(&OperationManager::OnComplete, manager_, extension_id_));
135 }
136
64 void Operation::Error(const std::string& error_message) { 137 void Operation::Error(const std::string& error_message) {
65 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { 138 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
66 BrowserThread::PostTask(BrowserThread::FILE, 139 BrowserThread::PostTask(BrowserThread::FILE,
67 FROM_HERE, 140 FROM_HERE,
68 base::Bind(&Operation::Error, this, error_message)); 141 base::Bind(&Operation::Error, this, error_message));
69 return; 142 return;
70 } 143 }
71 144
72 BrowserThread::PostTask( 145 BrowserThread::PostTask(
73 BrowserThread::UI, 146 BrowserThread::UI,
(...skipping 12 matching lines...) Expand all
86 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { 159 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
87 BrowserThread::PostTask( 160 BrowserThread::PostTask(
88 BrowserThread::FILE, 161 BrowserThread::FILE,
89 FROM_HERE, 162 FROM_HERE,
90 base::Bind(&Operation::SetProgress, 163 base::Bind(&Operation::SetProgress,
91 this, 164 this,
92 progress)); 165 progress));
93 return; 166 return;
94 } 167 }
95 168
169 if (progress <= progress_) {
170 return;
171 }
172
96 if (IsCancelled()) { 173 if (IsCancelled()) {
97 return; 174 return;
98 } 175 }
99 176
100 progress_ = progress; 177 progress_ = progress;
101 178
102 BrowserThread::PostTask( 179 BrowserThread::PostTask(
103 BrowserThread::UI, 180 BrowserThread::UI,
104 FROM_HERE, 181 FROM_HERE,
105 base::Bind(&OperationManager::OnProgress, 182 base::Bind(&OperationManager::OnProgress,
(...skipping 24 matching lines...) Expand all
130 BrowserThread::PostTask( 207 BrowserThread::PostTask(
131 BrowserThread::UI, 208 BrowserThread::UI,
132 FROM_HERE, 209 FROM_HERE,
133 base::Bind(&OperationManager::OnProgress, 210 base::Bind(&OperationManager::OnProgress,
134 manager_, 211 manager_,
135 extension_id_, 212 extension_id_,
136 stage_, 213 stage_,
137 progress_)); 214 progress_));
138 } 215 }
139 216
140 void Operation::Finish() {
141 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
142 BrowserThread::PostTask(BrowserThread::FILE,
143 FROM_HERE,
144 base::Bind(&Operation::Finish, this));
145 return;
146 }
147 DVLOG(1) << "Write operation complete.";
148
149 CleanUp();
150
151 BrowserThread::PostTask(
152 BrowserThread::UI,
153 FROM_HERE,
154 base::Bind(&OperationManager::OnComplete,
155 manager_,
156 extension_id_));
157 }
158
159 bool Operation::IsCancelled() { 217 bool Operation::IsCancelled() {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
161 219
162 return stage_ == image_writer_api::STAGE_NONE; 220 return stage_ == image_writer_api::STAGE_NONE;
163 } 221 }
164 222
165 void Operation::AddCleanUpFunction(base::Closure callback) { 223 void Operation::AddCleanUpFunction(const base::Closure& callback) {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
167
168 cleanup_functions_.push_back(callback); 225 cleanup_functions_.push_back(callback);
169 } 226 }
170 227
171 void Operation::CleanUp() {
172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
173 for (std::vector<base::Closure>::iterator it = cleanup_functions_.begin();
174 it != cleanup_functions_.end();
175 ++it) {
176 it->Run();
177 }
178 cleanup_functions_.clear();
179 }
180
181 void Operation::UnzipStart(scoped_ptr<base::FilePath> zip_path) {
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
183 if (IsCancelled()) {
184 return;
185 }
186
187 DVLOG(1) << "Starting unzip stage for " << zip_path->value();
188
189 SetStage(image_writer_api::STAGE_UNZIP);
190
191 base::FilePath tmp_dir;
192 if (!base::CreateTemporaryDirInDir(zip_path->DirName(),
193 FILE_PATH_LITERAL("image_writer"),
194 &tmp_dir)) {
195 Error(error::kTempDirError);
196 return;
197 }
198
199 AddCleanUpFunction(base::Bind(&RemoveTempDirectory, tmp_dir));
200
201 if (!base::CreateTemporaryFileInDir(tmp_dir, &image_path_)) {
202 DLOG(ERROR) << "Failed create temporary unzip target in "
203 << tmp_dir.value();
204 Error(error::kTempDirError);
205 return;
206 }
207
208 if (!(zip_reader_.Open(*zip_path) &&
209 zip_reader_.AdvanceToNextEntry() &&
210 zip_reader_.OpenCurrentEntryInZip())) {
211 DLOG(ERROR) << "Failed to open zip file.";
212 Error(error::kUnzipGenericError);
213 return;
214 }
215
216 if (zip_reader_.HasMore()) {
217 DLOG(ERROR) << "Image zip has more than one file.";
218 Error(error::kUnzipInvalidArchive);
219 return;
220 }
221
222 zip_reader_.ExtractCurrentEntryToFilePathAsync(
223 image_path_,
224 base::Bind(&Operation::OnUnzipSuccess, this),
225 base::Bind(&Operation::OnUnzipFailure, this),
226 base::Bind(&Operation::OnUnzipProgress,
227 this,
228 zip_reader_.current_entry_info()->original_size()));
229 }
230
231 void Operation::GetMD5SumOfFile( 228 void Operation::GetMD5SumOfFile(
232 scoped_ptr<base::FilePath> file_path, 229 const base::FilePath file_path,
tbarzic 2014/02/13 06:58:52 missing &
Drew Haven 2014/02/13 20:48:10 Done.
233 int64 file_size, 230 int64 file_size,
234 int progress_offset, 231 int progress_offset,
235 int progress_scale, 232 int progress_scale,
236 const base::Callback<void(scoped_ptr<std::string>)>& callback) { 233 const base::Callback<void(const std::string&)>& callback) {
237 if (IsCancelled()) { 234 if (IsCancelled()) {
238 return; 235 return;
239 } 236 }
240 237
241 base::MD5Init(&md5_context_); 238 base::MD5Init(&md5_context_);
242 scoped_ptr<image_writer_utils::ImageReader> reader(
243 new image_writer_utils::ImageReader());
244 239
245 if (!reader->Open(*file_path)) { 240 base::PlatformFile file = base::CreatePlatformFile(
241 file_path,
242 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
243 NULL,
244 NULL);
245 if (file == base::kInvalidPlatformFileValue) {
246 Error(error::kImageOpenError); 246 Error(error::kImageOpenError);
247 return; 247 return;
248 } 248 }
249
249 if (file_size <= 0) { 250 if (file_size <= 0) {
250 file_size = reader->GetSize(); 251 if (!base::GetFileSize(file_path, &file_size)) {
252 Error(error::kImageOpenError);
253 return;
254 }
251 } 255 }
252 256
253 BrowserThread::PostTask(BrowserThread::FILE, 257 BrowserThread::PostTask(BrowserThread::FILE,
254 FROM_HERE, 258 FROM_HERE,
255 base::Bind(&Operation::MD5Chunk, 259 base::Bind(&Operation::MD5Chunk,
256 this, 260 this,
257 base::Passed(&reader), 261 file,
258 0, 262 0,
259 file_size, 263 file_size,
260 progress_offset, 264 progress_offset,
261 progress_scale, 265 progress_scale,
262 callback)); 266 callback));
263 } 267 }
264 268
265 void Operation::MD5Chunk( 269 void Operation::MD5Chunk(
266 scoped_ptr<image_writer_utils::ImageReader> reader, 270 base::PlatformFile file,
267 int64 bytes_processed, 271 int64 bytes_processed,
268 int64 bytes_total, 272 int64 bytes_total,
269 int progress_offset, 273 int progress_offset,
270 int progress_scale, 274 int progress_scale,
271 const base::Callback<void(scoped_ptr<std::string>)>& callback) { 275 const base::Callback<void(const std::string&)>& callback) {
272 if (IsCancelled()) { 276 if (IsCancelled()) {
273 reader->Close(); 277 base::ClosePlatformFile(file);
274 return; 278 return;
275 } 279 }
276 280
277 char buffer[kMD5BufferSize];
278 int len; 281 int len;
282 scoped_ptr<char[]> buffer(new char[kMD5BufferSize]);
279 283
280 if (bytes_total - bytes_processed <= kMD5BufferSize) { 284 if (bytes_total - bytes_processed <= kMD5BufferSize) {
281 len = reader->Read(buffer, bytes_total - bytes_processed); 285 len = base::ReadPlatformFile(
tbarzic 2014/02/13 06:58:52 what do you think about: int read_size = std::min
Drew Haven 2014/02/13 20:48:10 I like it. I've had people complain about the use
286 file, bytes_processed, buffer.get(), bytes_total - bytes_processed);
282 } else { 287 } else {
283 len = reader->Read(buffer, kMD5BufferSize); 288 len = base::ReadPlatformFile(
289 file, bytes_processed, buffer.get(), kMD5BufferSize);
284 } 290 }
285 291
286 if (len > 0) { 292 if (len > 0) {
287 base::MD5Update(&md5_context_, base::StringPiece(buffer, len)); 293 base::MD5Update(&md5_context_, base::StringPiece(buffer.get(), len));
288 int percent_prev = (bytes_processed * progress_scale + progress_offset) / 294 int percent_curr =
289 (bytes_total); 295 ((bytes_processed + len) * progress_scale) / bytes_total +
290 int percent_curr = ((bytes_processed + len) * progress_scale + 296 progress_offset;
291 progress_offset) / 297 SetProgress(percent_curr);
292 (bytes_total);
293 if (percent_curr > percent_prev) {
294 SetProgress(progress_);
295 }
296 298
297 BrowserThread::PostTask(BrowserThread::FILE, 299 BrowserThread::PostTask(BrowserThread::FILE,
298 FROM_HERE, 300 FROM_HERE,
299 base::Bind(&Operation::MD5Chunk, 301 base::Bind(&Operation::MD5Chunk,
300 this, 302 this,
301 base::Passed(&reader), 303 file,
302 bytes_processed + len, 304 bytes_processed + len,
303 bytes_total, 305 bytes_total,
304 progress_offset, 306 progress_offset,
305 progress_scale, 307 progress_scale,
306 callback)); 308 callback));
309 // Skip closing the file.
310 return;
307 } else if (len == 0) { 311 } else if (len == 0) {
308 if (bytes_processed + len < bytes_total) { 312 if (bytes_processed + len < bytes_total) {
tbarzic 2014/02/13 06:58:52 you should not attempt reading if (bytes_process =
Drew Haven 2014/02/13 20:48:10 I think I cleaned up the logic a bit. Thanks for
309 reader->Close();
310 Error(error::kHashReadError); 313 Error(error::kHashReadError);
311 } else { 314 } else {
312 base::MD5Digest digest; 315 base::MD5Digest digest;
313 base::MD5Final(&digest, &md5_context_); 316 base::MD5Final(&digest, &md5_context_);
314 scoped_ptr<std::string> hash( 317 std::string hash = base::MD5DigestToBase16(digest);
tbarzic 2014/02/13 06:58:52 you don't need tmp var
Drew Haven 2014/02/13 20:48:10 Done.
315 new std::string(base::MD5DigestToBase16(digest))); 318 callback.Run(hash);
316 callback.Run(hash.Pass());
317 } 319 }
318 } else { // len < 0 320 } else { // len < 0
319 reader->Close();
320 Error(error::kHashReadError); 321 Error(error::kHashReadError);
321 } 322 }
323 base::ClosePlatformFile(file);
322 } 324 }
323 325
324 void Operation::OnUnzipSuccess() { 326 void Operation::OnUnzipSuccess(const base::Closure& continuation) {
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
326 SetProgress(kProgressComplete); 328 SetProgress(kProgressComplete);
327 WriteStart(); 329 continuation.Run();
328 } 330 }
329 331
330 void Operation::OnUnzipFailure() { 332 void Operation::OnUnzipFailure() {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
332 Error(error::kUnzipGenericError); 334 Error(error::kUnzipGenericError);
333 } 335 }
334 336
335 void Operation::OnUnzipProgress(int64 total_bytes, int64 progress_bytes) { 337 void Operation::OnUnzipProgress(int64 total_bytes, int64 progress_bytes) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
337 339
338 int progress_percent = 100 * progress_bytes / total_bytes; 340 int progress_percent = 100 * progress_bytes / total_bytes;
339 SetProgress(progress_percent); 341 SetProgress(progress_percent);
340 } 342 }
341 343
344 void Operation::CleanUp() {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
346 for (std::vector<base::Closure>::iterator it = cleanup_functions_.begin();
347 it != cleanup_functions_.end();
348 ++it) {
349 it->Run();
350 }
351 cleanup_functions_.clear();
352 }
353
342 } // namespace image_writer 354 } // namespace image_writer
343 } // namespace extensions 355 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698