blob: fcdc3bd2b203c3167085211a8a57b08fa4a6fc33 [file] [log] [blame]
// Copyright 2016 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 "services/asset_bundle/zip_asset_bundle.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/task_runner.h"
#include "base/message_loop/message_loop.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include "third_party/zlib/contrib/minizip/unzip.h"
namespace mojo {
namespace asset_bundle {
namespace {
void Ignored(bool) {
}
} // namespace
void* ScopedUnzFileTraits::InvalidValue() {
return nullptr;
}
void ScopedUnzFileTraits::Free(void* file) {
unzClose(file);
}
void ZipAssetService::Create(
InterfaceRequest<AssetBundle> request,
const scoped_refptr<ZipAssetBundle>& zip_asset_bundle) {
new ZipAssetService(request.Pass(), zip_asset_bundle);
}
ZipAssetService::ZipAssetService(
InterfaceRequest<AssetBundle> request,
const scoped_refptr<ZipAssetBundle>& zip_asset_bundle)
: binding_(this, request.Pass()),
zip_asset_bundle_(zip_asset_bundle) {
}
ZipAssetService::~ZipAssetService() {
}
void ZipAssetService::GetAsStream(
const String& asset_name,
const Callback<void(ScopedDataPipeConsumerHandle)>& callback) {
zip_asset_bundle_->GetAsStream(asset_name, callback);
}
ZipAssetBundle::ZipAssetBundle(
const base::FilePath& zip_path,
scoped_refptr<base::TaskRunner> worker_runner)
: zip_path_(zip_path),
worker_runner_(worker_runner.Pass()) {
}
ZipAssetBundle::~ZipAssetBundle() {
}
void ZipAssetBundle::AddOverlayFile(const std::string& asset_name,
const base::FilePath& file_path) {
overlay_files_.insert(std::make_pair(String(asset_name), file_path));
}
void ZipAssetBundle::GetAsStream(
const String& asset_name,
const Callback<void(ScopedDataPipeConsumerHandle)>& callback) {
DataPipe pipe;
callback.Run(pipe.consumer_handle.Pass());
auto overlay = overlay_files_.find(asset_name);
if (overlay != overlay_files_.end()) {
common::CopyFromFile(overlay->second,
pipe.producer_handle.Pass(),
0,
worker_runner_.get(),
base::Bind(&Ignored));
return;
}
ZipAssetHandler* handler = new ZipAssetHandler(
zip_path_,
asset_name.To<std::string>(),
pipe.producer_handle.Pass(),
worker_runner_);
worker_runner_->PostTask(
FROM_HERE,
base::Bind(&ZipAssetHandler::Start, base::Unretained(handler)));
}
bool ZipAssetBundle::GetAsBuffer(const std::string& asset_name,
std::vector<uint8_t>* data) {
ScopedUnzFile zip_file(unzOpen2(zip_path_.AsUTF8Unsafe().c_str(), NULL));
if (!zip_file.is_valid()) {
LOG(ERROR) << "Unable to open ZIP file: " << zip_path_.value();
return false;
}
int result = unzLocateFile(zip_file.get(), asset_name.c_str(), 0);
if (result != UNZ_OK) {
return false;
}
unz_file_info file_info;
result = unzGetCurrentFileInfo(zip_file.get(), &file_info, nullptr, 0,
nullptr, 0, nullptr, 0);
if (result != UNZ_OK) {
LOG(WARNING) << "unzGetCurrentFileInfo failed, error=" << result;
return false;
}
result = unzOpenCurrentFile(zip_file.get());
if (result != UNZ_OK) {
LOG(WARNING) << "unzOpenCurrentFile failed, error=" << result;
return false;
}
data->resize(file_info.uncompressed_size);
int total_read = 0;
while (total_read < static_cast<int>(data->size())) {
int bytes_read = unzReadCurrentFile(zip_file.get(),
data->data() + total_read,
data->size() - total_read);
if (bytes_read <= 0) {
return false;
}
total_read += bytes_read;
}
return true;
}
ZipAssetHandler::ZipAssetHandler(
const base::FilePath& zip_path,
const std::string& asset_name,
ScopedDataPipeProducerHandle producer,
scoped_refptr<base::TaskRunner> worker_runner)
: zip_path_(zip_path),
asset_name_(asset_name),
producer_(producer.Pass()),
main_runner_(base::MessageLoop::current()->task_runner()),
worker_runner_(worker_runner.Pass()),
buffer_(nullptr),
buffer_size_(0) {
}
ZipAssetHandler::~ZipAssetHandler() {
}
void ZipAssetHandler::Start() {
zip_file_.reset(unzOpen2(zip_path_.AsUTF8Unsafe().c_str(), NULL));
if (!zip_file_.is_valid()) {
LOG(ERROR) << "Unable to open ZIP file: " << zip_path_.value();
delete this;
return;
}
int result = unzLocateFile(zip_file_.get(), asset_name_.c_str(), 0);
if (result != UNZ_OK) {
LOG(WARNING) << "Requested asset '" << asset_name_ << "' does not exist.";
delete this;
return;
}
result = unzOpenCurrentFile(zip_file_.get());
if (result != UNZ_OK) {
LOG(WARNING) << "unzOpenCurrentFile failed, error=" << result;
delete this;
return;
}
CopyData();
}
void ZipAssetHandler::CopyData() {
while (true) {
MojoResult mojo_result = BeginWriteDataRaw(producer_.get(),
&buffer_, &buffer_size_,
MOJO_WRITE_DATA_FLAG_NONE);
if (mojo_result == MOJO_RESULT_SHOULD_WAIT) {
main_runner_->PostTask(FROM_HERE,
base::Bind(&ZipAssetHandler::WaitForWritable,
base::Unretained(this)));
return;
} else if (mojo_result != MOJO_RESULT_OK) {
LOG(WARNING) << "Mojo BeginWrite failed, error=" << mojo_result;
delete this;
return;
}
int bytes_read = unzReadCurrentFile(zip_file_.get(), buffer_, buffer_size_);
mojo_result = EndWriteDataRaw(producer_.get(), std::max(0, bytes_read));
if (bytes_read == 0) {
// Unzip is complete.
delete this;
return;
}
if (bytes_read < 0) {
LOG(WARNING) << "Asset unzip failed, error=" << bytes_read;
delete this;
return;
}
if (mojo_result != MOJO_RESULT_OK) {
LOG(WARNING) << "Mojo EndWrite failed, error=" << mojo_result;
delete this;
return;
}
}
}
void ZipAssetHandler::WaitForWritable() {
waiter_.reset(new AsyncWaiter(
producer_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
base::Bind(&ZipAssetHandler::OnWritable, base::Unretained(this))));
}
void ZipAssetHandler::OnWritable(MojoResult mojo_result) {
if (mojo_result == MOJO_RESULT_OK) {
worker_runner_->PostTask(FROM_HERE,
base::Bind(&ZipAssetHandler::CopyData,
base::Unretained(this)));
} else {
delete this;
}
}
} // namespace asset_bundle
} // namespace mojo