This source file includes following definitions.
- CloseBaseFile
- weak_factory_
- GetInstance
- cache_info
- TranslationMayBeCached
- GetCachePath
- OnCacheInitialized
- Init
- InitForTest
- DoCreateTemporaryFile
- CreateTemporaryFile
- GetNexeFd
- SendCacheQueryAndTempFileRequest
- OnCacheQueryReturn
- OnTempFileReturn
- CheckCacheQueryReady
- ReturnMiss
- CopyFileToBuffer
- TranslationFinished
- StoreTranslatedNexe
- OnTranslatedNexeStored
- RequeryMatchingTranslations
- CopyBufferToFile
- OnBufferCopiedToTempFile
- RendererClosing
- ClearTranslationCacheEntriesBetween
- OnEntriesDoomed
- DeInitIfSafe
#include "components/nacl/browser/pnacl_host.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "components/nacl/browser/nacl_browser.h"
#include "components/nacl/browser/pnacl_translation_cache.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
using content::BrowserThread;
namespace {
static const base::FilePath::CharType kTranslationCacheDirectoryName[] =
FILE_PATH_LITERAL("PnaclTranslationCache");
static const int kTranslationCacheInitializationDelayMs = 20;
void CloseBaseFile(base::File file) {
file.Close();
}
}
namespace pnacl {
PnaclHost::PnaclHost()
: pending_backend_operations_(0),
cache_state_(CacheUninitialized),
weak_factory_(this) {}
PnaclHost::~PnaclHost() {
pnacl::PnaclTranslationCache* cache = disk_cache_.release();
(void)cache;
}
PnaclHost* PnaclHost::GetInstance() { return Singleton<PnaclHost>::get(); }
PnaclHost::PendingTranslation::PendingTranslation()
: process_handle(base::kNullProcessHandle),
render_view_id(0),
nexe_fd(base::kInvalidPlatformFileValue),
got_nexe_fd(false),
got_cache_reply(false),
got_cache_hit(false),
is_incognito(false),
callback(NexeFdCallback()),
cache_info(nacl::PnaclCacheInfo()) {}
PnaclHost::PendingTranslation::~PendingTranslation() {}
bool PnaclHost::TranslationMayBeCached(
const PendingTranslationMap::iterator& entry) {
return !entry->second.is_incognito &&
!entry->second.cache_info.has_no_store_header;
}
static base::FilePath GetCachePath() {
NaClBrowserDelegate* browser_delegate = nacl::NaClBrowser::GetDelegate();
base::FilePath user_data_dir;
if (!browser_delegate ||
!browser_delegate->GetUserDirectory(&user_data_dir)) {
return base::FilePath();
}
base::FilePath cache_file_path;
browser_delegate->GetCacheDirectory(&cache_file_path);
return cache_file_path.Append(kTranslationCacheDirectoryName);
}
void PnaclHost::OnCacheInitialized(int net_error) {
DCHECK(thread_checker_.CalledOnValidThread());
if (cache_state_ == CacheReady)
return;
if (net_error != net::OK) {
cache_state_ = CacheUninitialized;
} else {
cache_state_ = CacheReady;
}
}
void PnaclHost::Init() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(thread_checker_.CalledOnValidThread());
base::FilePath cache_path(GetCachePath());
if (cache_path.empty() || cache_state_ != CacheUninitialized)
return;
disk_cache_.reset(new pnacl::PnaclTranslationCache());
cache_state_ = CacheInitializing;
int rv = disk_cache_->InitOnDisk(
cache_path,
base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
if (rv != net::ERR_IO_PENDING)
OnCacheInitialized(rv);
}
void PnaclHost::InitForTest(base::FilePath temp_dir, bool in_memory) {
DCHECK(thread_checker_.CalledOnValidThread());
disk_cache_.reset(new pnacl::PnaclTranslationCache());
cache_state_ = CacheInitializing;
temp_dir_ = temp_dir;
int rv;
if (in_memory) {
rv = disk_cache_->InitInMemory(
base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
} else {
rv = disk_cache_->InitOnDisk(
temp_dir,
base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
}
if (rv != net::ERR_IO_PENDING)
OnCacheInitialized(rv);
}
void PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir,
TempFileCallback cb) {
DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
base::FilePath file_path;
base::File file;
bool rv = temp_dir.empty()
? base::CreateTemporaryFile(&file_path)
: base::CreateTemporaryFileInDir(temp_dir, &file_path);
if (!rv) {
PLOG(ERROR) << "Temp file creation failed.";
} else {
file.Initialize(
file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ |
base::File::FLAG_WRITE | base::File::FLAG_TEMPORARY |
base::File::FLAG_DELETE_ON_CLOSE);
if (!file.IsValid())
PLOG(ERROR) << "Temp file open failed: " << file.error_details();
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, base::Bind(cb, Passed(file.Pass())));
}
void PnaclHost::CreateTemporaryFile(TempFileCallback cb) {
if (!BrowserThread::PostBlockingPoolSequencedTask(
"PnaclHostCreateTempFile",
FROM_HERE,
base::Bind(&PnaclHost::DoCreateTemporaryFile, temp_dir_, cb))) {
DCHECK(thread_checker_.CalledOnValidThread());
cb.Run(base::File());
}
}
void PnaclHost::GetNexeFd(int render_process_id,
int render_view_id,
int pp_instance,
bool is_incognito,
const nacl::PnaclCacheInfo& cache_info,
const NexeFdCallback& cb) {
DCHECK(thread_checker_.CalledOnValidThread());
if (cache_state_ == CacheUninitialized) {
Init();
}
if (cache_state_ != CacheReady) {
BrowserThread::PostDelayedTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&PnaclHost::GetNexeFd,
weak_factory_.GetWeakPtr(),
render_process_id,
render_view_id,
pp_instance,
is_incognito,
cache_info,
cb),
base::TimeDelta::FromMilliseconds(
kTranslationCacheInitializationDelayMs));
return;
}
TranslationID id(render_process_id, pp_instance);
PendingTranslationMap::iterator entry = pending_translations_.find(id);
if (entry != pending_translations_.end()) {
LOG(ERROR) << "GetNexeFd for already-pending translation";
pending_translations_.erase(entry);
}
std::string cache_key(disk_cache_->GetKey(cache_info));
if (cache_key.empty()) {
LOG(ERROR) << "GetNexeFd: Invalid cache info";
cb.Run(base::kInvalidPlatformFileValue, false);
return;
}
PendingTranslation pt;
pt.render_view_id = render_view_id;
pt.callback = cb;
pt.cache_info = cache_info;
pt.cache_key = cache_key;
pt.is_incognito = is_incognito;
pending_translations_[id] = pt;
SendCacheQueryAndTempFileRequest(cache_key, id);
}
void PnaclHost::SendCacheQueryAndTempFileRequest(const std::string& cache_key,
const TranslationID& id) {
pending_backend_operations_++;
disk_cache_->GetNexe(
cache_key,
base::Bind(
&PnaclHost::OnCacheQueryReturn, weak_factory_.GetWeakPtr(), id));
CreateTemporaryFile(
base::Bind(&PnaclHost::OnTempFileReturn, weak_factory_.GetWeakPtr(), id));
}
void PnaclHost::OnCacheQueryReturn(
const TranslationID& id,
int net_error,
scoped_refptr<net::DrainableIOBuffer> buffer) {
DCHECK(thread_checker_.CalledOnValidThread());
pending_backend_operations_--;
PendingTranslationMap::iterator entry(pending_translations_.find(id));
if (entry == pending_translations_.end()) {
LOG(ERROR) << "OnCacheQueryReturn: id not found";
DeInitIfSafe();
return;
}
PendingTranslation* pt = &entry->second;
pt->got_cache_reply = true;
pt->got_cache_hit = (net_error == net::OK);
if (pt->got_cache_hit)
pt->nexe_read_buffer = buffer;
CheckCacheQueryReady(entry);
}
void PnaclHost::OnTempFileReturn(const TranslationID& id,
base::File file) {
DCHECK(thread_checker_.CalledOnValidThread());
PendingTranslationMap::iterator entry(pending_translations_.find(id));
if (entry == pending_translations_.end()) {
LOG(ERROR) << "OnTempFileReturn: id not found";
BrowserThread::PostBlockingPoolTask(
FROM_HERE, base::Bind(CloseBaseFile, Passed(file.Pass())));
return;
}
if (!file.IsValid()) {
LOG(ERROR) << "OnTempFileReturn: temp file creation failed";
std::string key(entry->second.cache_key);
entry->second.callback.Run(base::kInvalidPlatformFileValue, false);
bool may_be_cached = TranslationMayBeCached(entry);
pending_translations_.erase(entry);
if (may_be_cached)
RequeryMatchingTranslations(key);
return;
}
PendingTranslation* pt = &entry->second;
pt->got_nexe_fd = true;
pt->nexe_fd = file.TakePlatformFile();
CheckCacheQueryReady(entry);
}
void PnaclHost::CheckCacheQueryReady(
const PendingTranslationMap::iterator& entry) {
PendingTranslation* pt = &entry->second;
if (!(pt->got_cache_reply && pt->got_nexe_fd))
return;
if (!pt->got_cache_hit) {
for (PendingTranslationMap::iterator it = pending_translations_.begin();
it != pending_translations_.end();
++it) {
if (it->second.cache_key == entry->second.cache_key &&
it->first != entry->first &&
TranslationMayBeCached(it) &&
it->second.got_cache_reply &&
it->second.got_nexe_fd) {
return;
}
}
ReturnMiss(entry);
return;
}
if (!base::PostTaskAndReplyWithResult(
BrowserThread::GetBlockingPool(),
FROM_HERE,
base::Bind(
&PnaclHost::CopyBufferToFile, pt->nexe_fd, pt->nexe_read_buffer),
base::Bind(&PnaclHost::OnBufferCopiedToTempFile,
weak_factory_.GetWeakPtr(),
entry->first))) {
pt->callback.Run(base::kInvalidPlatformFileValue, false);
}
}
void PnaclHost::ReturnMiss(const PendingTranslationMap::iterator& entry) {
PendingTranslation* pt = &entry->second;
NexeFdCallback cb(pt->callback);
if (pt->nexe_fd == base::kInvalidPlatformFileValue) {
pending_translations_.erase(entry);
}
cb.Run(pt->nexe_fd, false);
}
scoped_refptr<net::DrainableIOBuffer> PnaclHost::CopyFileToBuffer(
base::PlatformFile fd) {
base::PlatformFileInfo info;
scoped_refptr<net::DrainableIOBuffer> buffer;
bool error = false;
if (!base::GetPlatformFileInfo(fd, &info) ||
info.size >= std::numeric_limits<int>::max()) {
PLOG(ERROR) << "GetPlatformFileInfo failed";
error = true;
} else {
buffer = new net::DrainableIOBuffer(
new net::IOBuffer(static_cast<int>(info.size)), info.size);
if (base::ReadPlatformFile(fd, 0, buffer->data(), buffer->size()) !=
info.size) {
PLOG(ERROR) << "CopyFileToBuffer file read failed";
error = true;
}
}
if (error) {
buffer = NULL;
}
base::ClosePlatformFile(fd);
return buffer;
}
void PnaclHost::TranslationFinished(int render_process_id,
int pp_instance,
bool success) {
DCHECK(thread_checker_.CalledOnValidThread());
if (cache_state_ != CacheReady)
return;
TranslationID id(render_process_id, pp_instance);
PendingTranslationMap::iterator entry(pending_translations_.find(id));
if (entry == pending_translations_.end()) {
LOG(ERROR) << "TranslationFinished: TranslationID " << render_process_id
<< "," << pp_instance << " not found.";
return;
}
bool store_nexe = true;
if (!entry->second.got_nexe_fd || !entry->second.got_cache_reply ||
!success || !TranslationMayBeCached(entry)) {
store_nexe = false;
} else if (!base::PostTaskAndReplyWithResult(
BrowserThread::GetBlockingPool(),
FROM_HERE,
base::Bind(&PnaclHost::CopyFileToBuffer,
entry->second.nexe_fd),
base::Bind(&PnaclHost::StoreTranslatedNexe,
weak_factory_.GetWeakPtr(),
id))) {
store_nexe = false;
}
if (!store_nexe) {
if (entry->second.got_nexe_fd) {
BrowserThread::PostBlockingPoolTask(
FROM_HERE,
base::Bind(base::IgnoreResult(base::ClosePlatformFile),
entry->second.nexe_fd));
}
pending_translations_.erase(entry);
}
}
void PnaclHost::StoreTranslatedNexe(
TranslationID id,
scoped_refptr<net::DrainableIOBuffer> buffer) {
DCHECK(thread_checker_.CalledOnValidThread());
if (cache_state_ != CacheReady)
return;
PendingTranslationMap::iterator it(pending_translations_.find(id));
if (it == pending_translations_.end()) {
LOG(ERROR) << "StoreTranslatedNexe: TranslationID " << id.first << ","
<< id.second << " not found.";
return;
}
if (buffer.get() == NULL) {
LOG(ERROR) << "Error reading translated nexe";
return;
}
pending_backend_operations_++;
disk_cache_->StoreNexe(it->second.cache_key,
buffer,
base::Bind(&PnaclHost::OnTranslatedNexeStored,
weak_factory_.GetWeakPtr(),
it->first));
}
void PnaclHost::OnTranslatedNexeStored(const TranslationID& id, int net_error) {
PendingTranslationMap::iterator entry(pending_translations_.find(id));
pending_backend_operations_--;
if (entry == pending_translations_.end()) {
DeInitIfSafe();
return;
}
std::string key(entry->second.cache_key);
pending_translations_.erase(entry);
RequeryMatchingTranslations(key);
}
void PnaclHost::RequeryMatchingTranslations(const std::string& key) {
for (PendingTranslationMap::iterator it = pending_translations_.begin();
it != pending_translations_.end();
++it) {
if (it->second.cache_key == key) {
it->second.got_cache_reply = false;
pending_backend_operations_++;
disk_cache_->GetNexe(key,
base::Bind(&PnaclHost::OnCacheQueryReturn,
weak_factory_.GetWeakPtr(),
it->first));
}
}
}
int PnaclHost::CopyBufferToFile(base::PlatformFile fd,
scoped_refptr<net::DrainableIOBuffer> buffer) {
int rv = base::WritePlatformFile(fd, 0, buffer->data(), buffer->size());
if (rv == -1)
PLOG(ERROR) << "CopyBufferToFile write error";
return rv;
}
void PnaclHost::OnBufferCopiedToTempFile(const TranslationID& id,
int file_error) {
DCHECK(thread_checker_.CalledOnValidThread());
PendingTranslationMap::iterator entry(pending_translations_.find(id));
if (entry == pending_translations_.end()) {
return;
}
if (file_error == -1) {
BrowserThread::PostBlockingPoolTask(
FROM_HERE,
base::Bind(base::IgnoreResult(base::ClosePlatformFile),
entry->second.nexe_fd));
entry->second.got_nexe_fd = false;
CreateTemporaryFile(base::Bind(&PnaclHost::OnTempFileReturn,
weak_factory_.GetWeakPtr(),
entry->first));
return;
}
base::PlatformFile fd = entry->second.nexe_fd;
entry->second.callback.Run(fd, true);
BrowserThread::PostBlockingPoolTask(
FROM_HERE, base::Bind(base::IgnoreResult(base::ClosePlatformFile), fd));
pending_translations_.erase(entry);
}
void PnaclHost::RendererClosing(int render_process_id) {
DCHECK(thread_checker_.CalledOnValidThread());
if (cache_state_ != CacheReady)
return;
for (PendingTranslationMap::iterator it = pending_translations_.begin();
it != pending_translations_.end();) {
PendingTranslationMap::iterator to_erase(it++);
if (to_erase->first.first == render_process_id) {
BrowserThread::PostBlockingPoolTask(
FROM_HERE,
base::Bind(base::IgnoreResult(base::ClosePlatformFile),
to_erase->second.nexe_fd));
std::string key(to_erase->second.cache_key);
bool may_be_cached = TranslationMayBeCached(to_erase);
pending_translations_.erase(to_erase);
if (may_be_cached)
RequeryMatchingTranslations(key);
}
}
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr()));
}
void PnaclHost::ClearTranslationCacheEntriesBetween(
base::Time initial_time,
base::Time end_time,
const base::Closure& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (cache_state_ == CacheUninitialized) {
Init();
}
if (cache_state_ == CacheInitializing) {
BrowserThread::PostDelayedTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&PnaclHost::ClearTranslationCacheEntriesBetween,
weak_factory_.GetWeakPtr(),
initial_time,
end_time,
callback),
base::TimeDelta::FromMilliseconds(
kTranslationCacheInitializationDelayMs));
return;
}
pending_backend_operations_++;
int rv = disk_cache_->DoomEntriesBetween(
initial_time,
end_time,
base::Bind(
&PnaclHost::OnEntriesDoomed, weak_factory_.GetWeakPtr(), callback));
if (rv != net::ERR_IO_PENDING)
OnEntriesDoomed(callback, rv);
}
void PnaclHost::OnEntriesDoomed(const base::Closure& callback, int net_error) {
DCHECK(thread_checker_.CalledOnValidThread());
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback);
pending_backend_operations_--;
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&PnaclHost::DeInitIfSafe, weak_factory_.GetWeakPtr()));
}
void PnaclHost::DeInitIfSafe() {
DCHECK(pending_backend_operations_ >= 0);
if (pending_translations_.empty() &&
pending_backend_operations_ <= 0 &&
cache_state_ == CacheReady) {
cache_state_ = CacheUninitialized;
disk_cache_.reset();
}
}
}