Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |