Android Chromoting: Remove exit-fullscreen button.
[chromium-blink-merge.git] / components / nacl / browser / pnacl_translation_cache.cc
blob5ab1d38fa03d4f07063c95be3ae3927ea6f89678
1 // 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.
5 #include "components/nacl/browser/pnacl_translation_cache.h"
7 #include <string>
9 #include "base/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/threading/thread_checker.h"
15 #include "components/nacl/common/pnacl_types.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/net_errors.h"
19 #include "net/disk_cache/disk_cache.h"
21 using base::IntToString;
22 using content::BrowserThread;
24 namespace {
26 void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); }
28 } // namespace
30 namespace pnacl {
31 // This is in pnacl namespace instead of static so they can be used
32 // by the unit test.
33 const int kMaxMemCacheSize = 100 * 1024 * 1024;
35 //////////////////////////////////////////////////////////////////////
36 // Handle Reading/Writing to Cache.
38 // PnaclTranslationCacheEntry is a shim that provides storage for the
39 // 'key' and 'data' strings as the disk_cache is performing various async
40 // operations. It also tracks the open disk_cache::Entry
41 // and ensures that the entry is closed.
42 class PnaclTranslationCacheEntry
43 : public base::RefCounted<PnaclTranslationCacheEntry> {
44 public:
45 static PnaclTranslationCacheEntry* GetReadEntry(
46 base::WeakPtr<PnaclTranslationCache> cache,
47 const std::string& key,
48 const GetNexeCallback& callback);
49 static PnaclTranslationCacheEntry* GetWriteEntry(
50 base::WeakPtr<PnaclTranslationCache> cache,
51 const std::string& key,
52 net::DrainableIOBuffer* write_nexe,
53 const CompletionCallback& callback);
55 void Start();
57 // Writes: ---
58 // v |
59 // Start -> Open Existing --------------> Write ---> Close
60 // \ ^
61 // \ /
62 // --> Create --
63 // Reads:
64 // Start -> Open --------Read ----> Close
65 // | ^
66 // |__|
67 enum CacheStep {
68 UNINITIALIZED,
69 OPEN_ENTRY,
70 CREATE_ENTRY,
71 TRANSFER_ENTRY,
72 CLOSE_ENTRY,
73 FINISHED
76 private:
77 friend class base::RefCounted<PnaclTranslationCacheEntry>;
78 PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
79 const std::string& key,
80 bool is_read);
81 ~PnaclTranslationCacheEntry();
83 // Try to open an existing entry in the backend
84 void OpenEntry();
85 // Create a new entry in the backend (for writes)
86 void CreateEntry();
87 // Write |len| bytes to the backend, starting at |offset|
88 void WriteEntry(int offset, int len);
89 // Read |len| bytes from the backend, starting at |offset|
90 void ReadEntry(int offset, int len);
91 // If there was an error, doom the entry. Then post a task to the IO
92 // thread to close (and delete) it.
93 void CloseEntry(int rv);
94 // Call the user callback, and signal to the cache to delete this.
95 void Finish(int rv);
96 // Used as the callback for all operations to the backend. Handle state
97 // transitions, track bytes transferred, and call the other helper methods.
98 void DispatchNext(int rv);
100 base::WeakPtr<PnaclTranslationCache> cache_;
101 std::string key_;
102 disk_cache::Entry* entry_;
103 CacheStep step_;
104 bool is_read_;
105 GetNexeCallback read_callback_;
106 CompletionCallback write_callback_;
107 scoped_refptr<net::DrainableIOBuffer> io_buf_;
108 base::ThreadChecker thread_checker_;
109 DISALLOW_COPY_AND_ASSIGN(PnaclTranslationCacheEntry);
112 // static
113 PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetReadEntry(
114 base::WeakPtr<PnaclTranslationCache> cache,
115 const std::string& key,
116 const GetNexeCallback& callback) {
117 PnaclTranslationCacheEntry* entry(
118 new PnaclTranslationCacheEntry(cache, key, true));
119 entry->read_callback_ = callback;
120 return entry;
123 // static
124 PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetWriteEntry(
125 base::WeakPtr<PnaclTranslationCache> cache,
126 const std::string& key,
127 net::DrainableIOBuffer* write_nexe,
128 const CompletionCallback& callback) {
129 PnaclTranslationCacheEntry* entry(
130 new PnaclTranslationCacheEntry(cache, key, false));
131 entry->io_buf_ = write_nexe;
132 entry->write_callback_ = callback;
133 return entry;
136 PnaclTranslationCacheEntry::PnaclTranslationCacheEntry(
137 base::WeakPtr<PnaclTranslationCache> cache,
138 const std::string& key,
139 bool is_read)
140 : cache_(cache),
141 key_(key),
142 entry_(NULL),
143 step_(UNINITIALIZED),
144 is_read_(is_read) {}
146 PnaclTranslationCacheEntry::~PnaclTranslationCacheEntry() {
147 // Ensure we have called the user's callback
148 if (step_ != FINISHED) {
149 if (!read_callback_.is_null()) {
150 BrowserThread::PostTask(
151 BrowserThread::IO,
152 FROM_HERE,
153 base::Bind(read_callback_,
154 net::ERR_ABORTED,
155 scoped_refptr<net::DrainableIOBuffer>()));
157 if (!write_callback_.is_null()) {
158 BrowserThread::PostTask(BrowserThread::IO,
159 FROM_HERE,
160 base::Bind(write_callback_, net::ERR_ABORTED));
165 void PnaclTranslationCacheEntry::Start() {
166 DCHECK(thread_checker_.CalledOnValidThread());
167 step_ = OPEN_ENTRY;
168 OpenEntry();
171 // OpenEntry, CreateEntry, WriteEntry, ReadEntry and CloseEntry are only called
172 // from DispatchNext, so they know that cache_ is still valid.
173 void PnaclTranslationCacheEntry::OpenEntry() {
174 int rv = cache_->backend()->OpenEntry(
175 key_,
176 &entry_,
177 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
178 if (rv != net::ERR_IO_PENDING)
179 DispatchNext(rv);
182 void PnaclTranslationCacheEntry::CreateEntry() {
183 int rv = cache_->backend()->CreateEntry(
184 key_,
185 &entry_,
186 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
187 if (rv != net::ERR_IO_PENDING)
188 DispatchNext(rv);
191 void PnaclTranslationCacheEntry::WriteEntry(int offset, int len) {
192 DCHECK(io_buf_->BytesRemaining() == len);
193 int rv = entry_->WriteData(
195 offset,
196 io_buf_.get(),
197 len,
198 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this),
199 false);
200 if (rv != net::ERR_IO_PENDING)
201 DispatchNext(rv);
204 void PnaclTranslationCacheEntry::ReadEntry(int offset, int len) {
205 int rv = entry_->ReadData(
207 offset,
208 io_buf_.get(),
209 len,
210 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
211 if (rv != net::ERR_IO_PENDING)
212 DispatchNext(rv);
215 void PnaclTranslationCacheEntry::CloseEntry(int rv) {
216 DCHECK(entry_);
217 if (rv < 0) {
218 LOG(ERROR) << "Failed to close entry: " << net::ErrorToString(rv);
219 entry_->Doom();
221 BrowserThread::PostTask(
222 BrowserThread::IO, FROM_HERE, base::Bind(&CloseDiskCacheEntry, entry_));
223 Finish(rv);
226 void PnaclTranslationCacheEntry::Finish(int rv) {
227 step_ = FINISHED;
228 if (is_read_) {
229 if (!read_callback_.is_null()) {
230 BrowserThread::PostTask(BrowserThread::IO,
231 FROM_HERE,
232 base::Bind(read_callback_, rv, io_buf_));
234 } else {
235 if (!write_callback_.is_null()) {
236 BrowserThread::PostTask(
237 BrowserThread::IO, FROM_HERE, base::Bind(write_callback_, rv));
240 cache_->OpComplete(this);
243 void PnaclTranslationCacheEntry::DispatchNext(int rv) {
244 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
245 tracked_objects::ScopedTracker tracking_profile(
246 FROM_HERE_WITH_EXPLICIT_FUNCTION(
247 "422516 PnaclTranslationCacheEntry::DispatchNext"));
249 DCHECK(thread_checker_.CalledOnValidThread());
250 if (!cache_)
251 return;
253 switch (step_) {
254 case UNINITIALIZED:
255 case FINISHED:
256 LOG(ERROR) << "DispatchNext called uninitialized";
257 break;
259 case OPEN_ENTRY:
260 if (rv == net::OK) {
261 step_ = TRANSFER_ENTRY;
262 if (is_read_) {
263 int bytes_to_transfer = entry_->GetDataSize(1);
264 io_buf_ = new net::DrainableIOBuffer(
265 new net::IOBuffer(bytes_to_transfer), bytes_to_transfer);
266 ReadEntry(0, bytes_to_transfer);
267 } else {
268 WriteEntry(0, io_buf_->size());
270 } else {
271 if (rv != net::ERR_FAILED) {
272 // ERROR_FAILED is what we expect if the entry doesn't exist.
273 LOG(ERROR) << "OpenEntry failed: " << net::ErrorToString(rv);
275 if (is_read_) {
276 // Just a cache miss, not necessarily an error.
277 entry_ = NULL;
278 Finish(rv);
279 } else {
280 step_ = CREATE_ENTRY;
281 CreateEntry();
284 break;
286 case CREATE_ENTRY:
287 if (rv == net::OK) {
288 step_ = TRANSFER_ENTRY;
289 WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
290 } else {
291 LOG(ERROR) << "Failed to Create Entry: " << net::ErrorToString(rv);
292 Finish(rv);
294 break;
296 case TRANSFER_ENTRY:
297 if (rv < 0) {
298 // We do not call DispatchNext directly if WriteEntry/ReadEntry returns
299 // ERR_IO_PENDING, and the callback should not return that value either.
300 LOG(ERROR) << "Failed to complete write to entry: "
301 << net::ErrorToString(rv);
302 step_ = CLOSE_ENTRY;
303 CloseEntry(rv);
304 break;
305 } else if (rv > 0) {
306 io_buf_->DidConsume(rv);
307 if (io_buf_->BytesRemaining() > 0) {
308 is_read_
309 ? ReadEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining())
310 : WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
311 break;
314 // rv == 0 or we fell through (i.e. we have transferred all the bytes)
315 step_ = CLOSE_ENTRY;
316 DCHECK(io_buf_->BytesConsumed() == io_buf_->size());
317 if (is_read_)
318 io_buf_->SetOffset(0);
319 CloseEntry(0);
320 break;
322 case CLOSE_ENTRY:
323 step_ = UNINITIALIZED;
324 break;
328 //////////////////////////////////////////////////////////////////////
329 void PnaclTranslationCache::OpComplete(PnaclTranslationCacheEntry* entry) {
330 open_entries_.erase(entry);
333 //////////////////////////////////////////////////////////////////////
334 // Construction and cache backend initialization
335 PnaclTranslationCache::PnaclTranslationCache() : in_memory_(false) {}
337 PnaclTranslationCache::~PnaclTranslationCache() {}
339 int PnaclTranslationCache::Init(net::CacheType cache_type,
340 const base::FilePath& cache_dir,
341 int cache_size,
342 const CompletionCallback& callback) {
343 int rv = disk_cache::CreateCacheBackend(
344 cache_type,
345 net::CACHE_BACKEND_DEFAULT,
346 cache_dir,
347 cache_size,
348 true /* force_initialize */,
349 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
350 NULL, /* dummy net log */
351 &disk_cache_,
352 base::Bind(&PnaclTranslationCache::OnCreateBackendComplete, AsWeakPtr()));
353 if (rv == net::ERR_IO_PENDING) {
354 init_callback_ = callback;
356 return rv;
359 void PnaclTranslationCache::OnCreateBackendComplete(int rv) {
360 if (rv < 0) {
361 LOG(ERROR) << "Backend init failed:" << net::ErrorToString(rv);
363 // Invoke our client's callback function.
364 if (!init_callback_.is_null()) {
365 BrowserThread::PostTask(
366 BrowserThread::IO, FROM_HERE, base::Bind(init_callback_, rv));
370 //////////////////////////////////////////////////////////////////////
371 // High-level API
373 void PnaclTranslationCache::StoreNexe(const std::string& key,
374 net::DrainableIOBuffer* nexe_data,
375 const CompletionCallback& callback) {
376 PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetWriteEntry(
377 AsWeakPtr(), key, nexe_data, callback);
378 open_entries_[entry] = entry;
379 entry->Start();
382 void PnaclTranslationCache::GetNexe(const std::string& key,
383 const GetNexeCallback& callback) {
384 PnaclTranslationCacheEntry* entry =
385 PnaclTranslationCacheEntry::GetReadEntry(AsWeakPtr(), key, callback);
386 open_entries_[entry] = entry;
387 entry->Start();
390 int PnaclTranslationCache::InitOnDisk(const base::FilePath& cache_directory,
391 const CompletionCallback& callback) {
392 in_memory_ = false;
393 return Init(net::PNACL_CACHE, cache_directory, 0 /* auto size */, callback);
396 int PnaclTranslationCache::InitInMemory(const CompletionCallback& callback) {
397 in_memory_ = true;
398 return Init(net::MEMORY_CACHE, base::FilePath(), kMaxMemCacheSize, callback);
401 int PnaclTranslationCache::Size() {
402 if (!disk_cache_)
403 return -1;
404 return disk_cache_->GetEntryCount();
407 // Beware that any changes to this function or to PnaclCacheInfo will
408 // effectively invalidate existing translation cache entries.
410 // static
411 std::string PnaclTranslationCache::GetKey(const nacl::PnaclCacheInfo& info) {
412 if (!info.pexe_url.is_valid() || info.abi_version < 0 || info.opt_level < 0 ||
413 info.extra_flags.size() > 512)
414 return std::string();
415 std::string retval("ABI:");
416 retval += IntToString(info.abi_version) + ";" + "opt:" +
417 IntToString(info.opt_level) +
418 (info.use_subzero ? "subzero;" : ";") + "URL:";
419 // Filter the username, password, and ref components from the URL
420 GURL::Replacements replacements;
421 replacements.ClearUsername();
422 replacements.ClearPassword();
423 replacements.ClearRef();
424 GURL key_url(info.pexe_url.ReplaceComponents(replacements));
425 retval += key_url.spec() + ";";
426 // You would think that there is already code to format base::Time values
427 // somewhere, but I haven't found it yet. In any case, doing it ourselves
428 // here means we can keep the format stable.
429 base::Time::Exploded exploded;
430 info.last_modified.UTCExplode(&exploded);
431 if (info.last_modified.is_null() || !exploded.HasValidValues()) {
432 memset(&exploded, 0, sizeof(exploded));
434 retval += "modified:" + IntToString(exploded.year) + ":" +
435 IntToString(exploded.month) + ":" +
436 IntToString(exploded.day_of_month) + ":" +
437 IntToString(exploded.hour) + ":" + IntToString(exploded.minute) +
438 ":" + IntToString(exploded.second) + ":" +
439 IntToString(exploded.millisecond) + ":UTC;";
440 retval += "etag:" + info.etag + ";";
441 retval += "sandbox:" + info.sandbox_isa + ";";
442 retval += "extra_flags:" + info.extra_flags + ";";
443 return retval;
446 int PnaclTranslationCache::DoomEntriesBetween(
447 base::Time initial,
448 base::Time end,
449 const CompletionCallback& callback) {
450 return disk_cache_->DoomEntriesBetween(initial, end, callback);
453 } // namespace pnacl