| // 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 |