root/chrome/browser/extensions/api/image_writer_private/operation.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. progress_
  2. Cancel
  3. Abort
  4. GetProgress
  5. GetStage
  6. SetUtilityClientForTesting
  7. Start
  8. Unzip
  9. Finish
  10. Error
  11. SetProgress
  12. SetStage
  13. IsCancelled
  14. AddCleanUpFunction
  15. CompleteAndContinue
  16. StartUtilityClient
  17. StopUtilityClient
  18. WriteImageProgress
  19. GetMD5SumOfFile
  20. MD5Chunk
  21. OnUnzipFailure
  22. OnUnzipProgress
  23. CleanUp

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/image_writer_private/operation.h"

#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/threading/worker_pool.h"
#include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
#include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
#include "content/public/browser/browser_thread.h"

namespace extensions {
namespace image_writer {

using content::BrowserThread;

const int kMD5BufferSize = 1024;
#if defined(OS_CHROMEOS)
// Chrome OS only has a 1 GB temporary partition.  This is too small to hold our
// unzipped image. Fortunately we mount part of the temporary partition under
// /var/tmp.
const char kChromeOSTempRoot[] = "/var/tmp";
#endif

Operation::Operation(base::WeakPtr<OperationManager> manager,
                     const ExtensionId& extension_id,
                     const std::string& device_path)
    : manager_(manager),
      extension_id_(extension_id),
#if defined(OS_WIN)
      device_path_(base::FilePath::FromUTF8Unsafe(device_path)),
#else
      device_path_(device_path),
#endif
      stage_(image_writer_api::STAGE_UNKNOWN),
      progress_(0) {
}

Operation::~Operation() {}

void Operation::Cancel() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);

  stage_ = image_writer_api::STAGE_NONE;

  CleanUp();
}

void Operation::Abort() {
  Error(error::kAborted);
}

int Operation::GetProgress() {
  return progress_;
}

image_writer_api::Stage Operation::GetStage() {
  return stage_;
}

#if !defined(OS_CHROMEOS)
void Operation::SetUtilityClientForTesting(
    scoped_refptr<ImageWriterUtilityClient> client) {
  image_writer_client_ = client;
  AddCleanUpFunction(
      base::Bind(&ImageWriterUtilityClient::Shutdown, image_writer_client_));
}
#endif

void Operation::Start() {
#if defined(OS_CHROMEOS)
  if (!temp_dir_.CreateUniqueTempDirUnderPath(
           base::FilePath(kChromeOSTempRoot))) {
#else
  if (!temp_dir_.CreateUniqueTempDir()) {
#endif
    Error(error::kTempDirError);
    return;
  }

  AddCleanUpFunction(
      base::Bind(base::IgnoreResult(&base::ScopedTempDir::Delete),
                 base::Unretained(&temp_dir_)));

  StartImpl();
}

void Operation::Unzip(const base::Closure& continuation) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  if (IsCancelled()) {
    return;
  }

  if (image_path_.Extension() != FILE_PATH_LITERAL(".zip")) {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
    return;
  }

  SetStage(image_writer_api::STAGE_UNZIP);

  if (!(zip_reader_.Open(image_path_) && zip_reader_.AdvanceToNextEntry() &&
        zip_reader_.OpenCurrentEntryInZip())) {
    Error(error::kUnzipGenericError);
    return;
  }

  if (zip_reader_.HasMore()) {
    Error(error::kUnzipInvalidArchive);
    return;
  }

  // Create a new target to unzip to.  The original file is opened by the
  // zip_reader_.
  zip::ZipReader::EntryInfo* entry_info = zip_reader_.current_entry_info();
  if (entry_info) {
    image_path_ = temp_dir_.path().Append(entry_info->file_path().BaseName());
  } else {
    Error(error::kTempDirError);
    return;
  }

  zip_reader_.ExtractCurrentEntryToFilePathAsync(
      image_path_,
      base::Bind(&Operation::CompleteAndContinue, this, continuation),
      base::Bind(&Operation::OnUnzipFailure, this),
      base::Bind(&Operation::OnUnzipProgress,
                 this,
                 zip_reader_.current_entry_info()->original_size()));
}

void Operation::Finish() {
  if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE, base::Bind(&Operation::Finish, this));
    return;
  }

  CleanUp();

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&OperationManager::OnComplete, manager_, extension_id_));
}

void Operation::Error(const std::string& error_message) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
    BrowserThread::PostTask(BrowserThread::FILE,
                            FROM_HERE,
                            base::Bind(&Operation::Error, this, error_message));
    return;
  }

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&OperationManager::OnError,
                 manager_,
                 extension_id_,
                 stage_,
                 progress_,
                 error_message));

  CleanUp();
}

void Operation::SetProgress(int progress) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
    BrowserThread::PostTask(
        BrowserThread::FILE,
        FROM_HERE,
        base::Bind(&Operation::SetProgress,
                   this,
                   progress));
    return;
  }

  if (progress <= progress_) {
    return;
  }

  if (IsCancelled()) {
    return;
  }

  progress_ = progress;

  BrowserThread::PostTask(BrowserThread::UI,
                          FROM_HERE,
                          base::Bind(&OperationManager::OnProgress,
                                     manager_,
                                     extension_id_,
                                     stage_,
                                     progress_));
}

void Operation::SetStage(image_writer_api::Stage stage) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
    BrowserThread::PostTask(
        BrowserThread::FILE,
        FROM_HERE,
        base::Bind(&Operation::SetStage,
                   this,
                   stage));
    return;
  }

  if (IsCancelled()) {
    return;
  }

  stage_ = stage;
  progress_ = 0;

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(&OperationManager::OnProgress,
                 manager_,
                 extension_id_,
                 stage_,
                 progress_));
}

bool Operation::IsCancelled() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);

  return stage_ == image_writer_api::STAGE_NONE;
}

void Operation::AddCleanUpFunction(const base::Closure& callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  cleanup_functions_.push_back(callback);
}

void Operation::CompleteAndContinue(const base::Closure& continuation) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  SetProgress(kProgressComplete);
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, continuation);
}

#if !defined(OS_CHROMEOS)
void Operation::StartUtilityClient() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  if (!image_writer_client_) {
    image_writer_client_ = new ImageWriterUtilityClient();
    AddCleanUpFunction(base::Bind(&Operation::StopUtilityClient, this));
  }
}

void Operation::StopUtilityClient() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      base::Bind(&ImageWriterUtilityClient::Shutdown, image_writer_client_));
}

void Operation::WriteImageProgress(int64 total_bytes, int64 curr_bytes) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  if (IsCancelled()) {
    return;
  }

  int progress = kProgressComplete * curr_bytes / total_bytes;

  if (progress > GetProgress()) {
    SetProgress(progress);
  }
}
#endif

void Operation::GetMD5SumOfFile(
    const base::FilePath& file_path,
    int64 file_size,
    int progress_offset,
    int progress_scale,
    const base::Callback<void(const std::string&)>& callback) {
  if (IsCancelled()) {
    return;
  }

  base::MD5Init(&md5_context_);

  base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
  if (!file.IsValid()) {
    Error(error::kImageOpenError);
    return;
  }

  if (file_size <= 0) {
    file_size = file.GetLength();
    if (file_size < 0) {
      Error(error::kImageOpenError);
      return;
    }
  }

  BrowserThread::PostTask(BrowserThread::FILE,
                          FROM_HERE,
                          base::Bind(&Operation::MD5Chunk,
                                     this,
                                     Passed(file.Pass()),
                                     0,
                                     file_size,
                                     progress_offset,
                                     progress_scale,
                                     callback));
}

void Operation::MD5Chunk(
    base::File file,
    int64 bytes_processed,
    int64 bytes_total,
    int progress_offset,
    int progress_scale,
    const base::Callback<void(const std::string&)>& callback) {
  if (IsCancelled())
    return;

  CHECK_LE(bytes_processed, bytes_total);

  scoped_ptr<char[]> buffer(new char[kMD5BufferSize]);
  int read_size = std::min(bytes_total - bytes_processed,
                           static_cast<int64>(kMD5BufferSize));

  if (read_size == 0) {
    // Nothing to read, we are done.
    base::MD5Digest digest;
    base::MD5Final(&digest, &md5_context_);
    callback.Run(base::MD5DigestToBase16(digest));
  } else {
    int len = file.Read(bytes_processed, buffer.get(), read_size);

    if (len == read_size) {
      // Process data.
      base::MD5Update(&md5_context_, base::StringPiece(buffer.get(), len));
      int percent_curr =
          ((bytes_processed + len) * progress_scale) / bytes_total +
          progress_offset;
      SetProgress(percent_curr);

      BrowserThread::PostTask(BrowserThread::FILE,
                              FROM_HERE,
                              base::Bind(&Operation::MD5Chunk,
                                         this,
                                         Passed(file.Pass()),
                                         bytes_processed + len,
                                         bytes_total,
                                         progress_offset,
                                         progress_scale,
                                         callback));
      // Skip closing the file.
      return;
    } else {
      // We didn't read the bytes we expected.
      Error(error::kHashReadError);
    }
  }
}

void Operation::OnUnzipFailure() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  Error(error::kUnzipGenericError);
}

void Operation::OnUnzipProgress(int64 total_bytes, int64 progress_bytes) {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);

  int progress_percent = kProgressComplete * progress_bytes / total_bytes;
  SetProgress(progress_percent);
}

void Operation::CleanUp() {
  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
  for (std::vector<base::Closure>::iterator it = cleanup_functions_.begin();
       it != cleanup_functions_.end();
       ++it) {
    it->Run();
  }
  cleanup_functions_.clear();
}

}  // namespace image_writer
}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */