blob: 9bb5b727e14ca23f225c0d10b9dd31e83d46bba5 [file] [log] [blame]
Ben Murdochca12bfa2013-07-23 11:17:05 +01001// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/nacl_host/pnacl_host.h"
6
7#include "base/bind.h"
8#include "base/file_util.h"
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/task_runner_util.h"
12#include "base/threading/sequenced_worker_pool.h"
13#include "chrome/browser/nacl_host/nacl_browser.h"
14#include "chrome/browser/nacl_host/pnacl_translation_cache.h"
15#include "content/public/browser/browser_thread.h"
16#include "net/base/net_errors.h"
17
18using content::BrowserThread;
19
20namespace {
21static const base::FilePath::CharType kTranslationCacheDirectoryName[] =
22 FILE_PATH_LITERAL("PnaclTranslationCache");
23}
24
25PnaclHost::PnaclHost()
26 : cache_state_(CacheUninitialized), weak_factory_(this) {}
27
28PnaclHost::~PnaclHost() {}
29
30PnaclHost* PnaclHost::GetInstance() { return Singleton<PnaclHost>::get(); }
31
32PnaclHost::PendingTranslation::PendingTranslation() {}
33PnaclHost::PendingTranslation::~PendingTranslation() {}
34
35/////////////////////////////////////// Initialization
36
37static base::FilePath GetCachePath() {
38 NaClBrowserDelegate* browser_delegate = NaClBrowser::GetDelegate();
39 // Determine where the translation cache resides in the file system. It
40 // exists in Chrome's cache directory and is not tied to any specific
41 // profile. If we fail, return an empty path.
42 // Start by finding the user data directory.
43 base::FilePath user_data_dir;
44 if (!browser_delegate ||
45 !browser_delegate->GetUserDirectory(&user_data_dir)) {
46 return base::FilePath();
47 }
48 // The cache directory may or may not be the user data directory.
49 base::FilePath cache_file_path;
50 browser_delegate->GetCacheDirectory(&cache_file_path);
51
52 // Append the base file name to the cache directory.
53 return cache_file_path.Append(kTranslationCacheDirectoryName);
54}
55
56void PnaclHost::OnCacheInitialized(int error) {
57 // If the cache was cleared before the load completed, ignore.
58 if (cache_state_ == CacheReady)
59 return;
60 if (error != net::OK) {
61 LOG(ERROR) << "PNaCl translation cache initalization failure: " << error
62 << "\n";
63 } else {
64 cache_state_ = CacheReady;
65 }
66}
67
68void PnaclHost::Init() {
69 DCHECK(thread_checker_.CalledOnValidThread());
70 base::FilePath cache_path(GetCachePath());
71 if (cache_path.empty() || cache_state_ != CacheUninitialized)
72 return;
73 disk_cache_.reset(new pnacl::PnaclTranslationCache());
74 cache_state_ = CacheInitializing;
75 disk_cache_->InitCache(
76 cache_path,
77 true,
78 base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
79}
80
81// Initialize using the in-memory backend, and manually set the temporary file
82// directory instead of using the system directory.
83void PnaclHost::InitForTest(base::FilePath temp_dir) {
84 DCHECK(thread_checker_.CalledOnValidThread());
85 disk_cache_.reset(new pnacl::PnaclTranslationCache());
86 cache_state_ = CacheInitializing;
87 temp_dir_ = temp_dir;
88 disk_cache_->InitCache(
89 temp_dir,
90 true, // Use in-memory backend
91 base::Bind(&PnaclHost::OnCacheInitialized, weak_factory_.GetWeakPtr()));
92}
93
94///////////////////////////////////////// Temp files
95
96// Create a temporary file on the blocking pool
97IPC::PlatformFileForTransit PnaclHost::DoCreateTemporaryFile(
98 base::ProcessHandle process_handle,
99 base::FilePath temp_dir) {
100 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
101
102 base::FilePath file_path;
103 bool rv = temp_dir.empty()
Ben Murdoch558790d2013-07-30 15:19:42 +0100104 ? file_util::CreateTemporaryFile(&file_path)
105 : file_util::CreateTemporaryFileInDir(temp_dir, &file_path);
106
Ben Murdochca12bfa2013-07-23 11:17:05 +0100107 if (!rv)
108 return IPC::InvalidPlatformFileForTransit();
109
110 base::PlatformFileError error;
111 base::PlatformFile file_handle(base::CreatePlatformFile(
112 file_path,
113 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ |
114 base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_TEMPORARY |
115 base::PLATFORM_FILE_DELETE_ON_CLOSE,
116 NULL,
117 &error));
118
119 if (error != base::PLATFORM_FILE_OK)
120 return IPC::InvalidPlatformFileForTransit();
121
122 // Do any DuplicateHandle magic that is necessary first.
123 return IPC::GetFileHandleForProcess(file_handle, process_handle, true);
124}
125
126void PnaclHost::CreateTemporaryFile(base::ProcessHandle process_handle,
127 TempFileCallback cb) {
128 if (!base::PostTaskAndReplyWithResult(
129 BrowserThread::GetBlockingPool(),
130 FROM_HERE,
131 base::Bind(
132 &PnaclHost::DoCreateTemporaryFile, process_handle, temp_dir_),
133 cb)) {
134 DCHECK(thread_checker_.CalledOnValidThread());
135 cb.Run(IPC::InvalidPlatformFileForTransit());
136 }
137}
138
139///////////////////////////////////////// GetNexeFd implementation
140
141void PnaclHost::ReturnMiss(TranslationID id, IPC::PlatformFileForTransit fd) {
142 DCHECK(thread_checker_.CalledOnValidThread());
143 PendingTranslationMap::iterator entry(pending_translations_.find(id));
144 if (entry == pending_translations_.end()) {
145 LOG(ERROR) << "PnaclHost::ReturnMiss: Failed to find TranslationID "
146 << id.first << "," << id.second;
147 return;
148 }
149 NexeFdCallback cb(entry->second.callback);
150 if (fd == IPC::InvalidPlatformFileForTransit()) {
151 pending_translations_.erase(entry);
152 } else {
153 entry->second.nexe_fd = fd;
154 }
155 cb.Run(fd, false);
156}
157
158void PnaclHost::GetNexeFd(int render_process_id,
159 base::ProcessHandle process_handle,
160 int render_view_id,
161 int pp_instance,
162 const nacl::PnaclCacheInfo& cache_info,
163 const NexeFdCallback& cb) {
164 DCHECK(thread_checker_.CalledOnValidThread());
165 if (cache_state_ != CacheReady)
166 return;
167 PendingTranslation pt;
168 pt.render_view_id = render_view_id;
169 pt.callback = cb;
170 pt.cache_info = cache_info;
171
172 pending_translations_[TranslationID(render_process_id, pp_instance)] = pt;
173
174 CreateTemporaryFile(
175 process_handle,
176 base::Bind(&PnaclHost::ReturnMiss,
177 weak_factory_.GetWeakPtr(),
178 TranslationID(render_process_id, pp_instance)));
179}
180
181/////////////////// Cleanup
182
Ben Murdochbb1529c2013-08-08 10:24:53 +0100183void PnaclHost::TranslationFinished(int render_process_id,
184 int pp_instance,
185 bool success) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100186 DCHECK(thread_checker_.CalledOnValidThread());
187 if (cache_state_ != CacheReady)
188 return;
189 PendingTranslationMap::iterator it(pending_translations_.find(
190 TranslationID(render_process_id, pp_instance)));
191 if (it == pending_translations_.end()) {
192 LOG(ERROR) << "TranslationFinished: TranslationID " << render_process_id
193 << "," << pp_instance << " not found.";
194 } else {
195 pending_translations_.erase(it);
196 }
197}
198
199void PnaclHost::RendererClosing(int render_process_id) {
200 if (cache_state_ != CacheReady)
201 return;
202 DCHECK(thread_checker_.CalledOnValidThread());
203 for (PendingTranslationMap::iterator it = pending_translations_.begin();
204 it != pending_translations_.end();) {
205 PendingTranslationMap::iterator to_erase(it++);
206 if (to_erase->first.first == render_process_id) {
207 // clean up the open files
208 pending_translations_.erase(to_erase);
209 }
210 }
211}