blob: a91e888a1bbf165b3d25ad475af904a1863f40d5 [file] [log] [blame]
// Copyright 2018 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.
#ifndef CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_
#define CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_
#include <queue>
#include "base/containers/queue.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "content/common/content_export.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/disk_cache.h"
#include "url/origin.h"
namespace content {
// Cache for storing generated code from the renderer on the disk. This cache
// uses |resource_url| + |origin_lock| as a key for storing the generated code.
// |resource_url| is the url corresponding to the requested resource.
// |origin_lock| is the origin that the renderer which requested this resource
// is locked to. This is used to enforce site isolation policy on cached code.
// For example, if SitePerProcess is enabled and http://script.com/script1.js is
// requested by http://example.com, then http://script.com/script.js is the
// resource_url and http://example.com is the origin_lock.
//
// The key is generated by concatenating the serialized url and origin lock
// with a separator in between. The separator is non-valid URL characters, to
// prevent any attacks by crafting the URLs. |origin_lock| could be empty when
// renderer is not locked to an origin (ex:SitePerProcess is disabled) and it
// is safe to use only |resource_url| as the key in such cases.
//
// This uses a simple disk_cache backend. It just stores one data stream and
// stores response_time + generated code as one data blob.
//
// There exists one cache per storage partition and is owned by the storage
// partition. This cache is created, accessed and destroyed on the I/O
// thread.
class CONTENT_EXPORT GeneratedCodeCache {
public:
using ReadDataCallback =
base::RepeatingCallback<void(const base::Time&,
const std::vector<uint8_t>&)>;
using GetBackendCallback = base::OnceCallback<void(disk_cache::Backend*)>;
static const int kResponseTimeSizeInBytes = sizeof(int64_t);
// Cache type. Used for collecting statistics for JS and Wasm in separate
// buckets.
enum CodeCacheType { kJavaScript, kWebAssembly };
// Used for collecting statistics about cache behaviour.
enum CacheEntryStatus {
kHit,
kMiss,
kClear,
kUpdate,
kCreate,
kError,
kIncompleteEntry,
kWriteFailed,
kMaxValue = kWriteFailed
};
// Returns the resource URL from the key. The key has the format prefix +
// resource URL + separator + requesting origin. This function extracts and
// returns resource URL from the key.
static std::string GetResourceURLFromKey(const std::string& key);
// Creates a GeneratedCodeCache with the specified path and the maximum size.
// If |max_size_bytes| is 0, then disk_cache picks a default size based on
// some heuristics.
GeneratedCodeCache(const base::FilePath& path,
int max_size_bytes,
CodeCacheType cache_type);
~GeneratedCodeCache();
// Runs the callback with a raw pointer to the backend. If we could not create
// the backend then it will return a null. This runs the callback
// synchronously if the backend is already open or asynchronously on the
// completion of a pending backend creation.
void GetBackend(GetBackendCallback callback);
// Writes data to the cache. If there is an entry corresponding to
// <|resource_url|, |origin_lock|> this overwrites the existing data. If
// there is no entry it creates a new one.
void WriteData(const GURL& resource_url,
const GURL& origin_lock,
const base::Time& response_time,
const std::vector<uint8_t>& data);
// Fetch entry corresponding to <resource_url, origin_lock> from the cache
// and return it using the ReadDataCallback.
void FetchEntry(const GURL& resource_url,
const GURL& origin_lock,
ReadDataCallback);
// Delete the entry corresponding to <resource_url, origin_lock>
void DeleteEntry(const GURL& resource_url, const GURL& origin_lock);
// Should be only used for tests. Sets the last accessed timestamp of an
// entry.
void SetLastUsedTimeForTest(const GURL& resource_url,
const GURL& origin_lock,
base::Time time,
base::RepeatingCallback<void(void)> callback);
const base::FilePath& path() const { return path_; }
private:
class PendingOperation;
using ScopedBackendPtr = std::unique_ptr<disk_cache::Backend>;
// State of the backend.
enum BackendState { kInitializing, kInitialized, kFailed };
// The operation requested.
enum Operation { kFetch, kWrite, kDelete, kGetBackend };
// Data streams corresponding to each entry.
enum { kDataIndex = 1 };
// Creates a simple_disk_cache backend.
void CreateBackend();
void DidCreateBackend(
scoped_refptr<base::RefCountedData<ScopedBackendPtr>> backend_ptr,
int rv);
// The requests that are received while tha backend is being initialized
// are recorded in pending operations list. This function issues all pending
// operations.
void IssuePendingOperations();
// Write entry to cache
void WriteDataImpl(const std::string& key,
scoped_refptr<net::IOBufferWithSize> buffer);
void CompleteForWriteData(
scoped_refptr<net::IOBufferWithSize> buffer,
const std::string& key,
scoped_refptr<base::RefCountedData<disk_cache::EntryWithOpened>>
entry_struct,
int rv);
void WriteDataCompleted(const std::string& key, int rv);
// Fetch entry from cache
void FetchEntryImpl(const std::string& key, ReadDataCallback);
void OpenCompleteForReadData(
ReadDataCallback callback,
const std::string& key,
scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
int rv);
void ReadDataComplete(const std::string& key,
ReadDataCallback callback,
scoped_refptr<net::IOBufferWithSize> buffer,
int rv);
// Delete entry from cache
void DeleteEntryImpl(const std::string& key);
// Issues the queued operation at the front of the queue for the given |key|.
void IssueQueuedOperationForEntry(const std::string& key);
// Enqueues into the list if there is an in-progress operation. Otherwise
// creates an entry to indicate there is an active operation.
bool EnqueueAsPendingOperation(const std::string& key,
std::unique_ptr<PendingOperation> op);
void IssueOperation(PendingOperation* op);
void DoPendingGetBackend(GetBackendCallback callback);
void OpenCompleteForSetLastUsedForTest(
scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
base::Time time,
base::RepeatingCallback<void(void)> callback,
int rv);
void CollectStatistics(GeneratedCodeCache::CacheEntryStatus status);
std::unique_ptr<disk_cache::Backend> backend_;
BackendState backend_state_;
std::vector<std::unique_ptr<PendingOperation>> pending_ops_;
// Map from key to queue ops.
std::map<std::string, base::queue<std::unique_ptr<PendingOperation>>>
active_entries_map_;
base::FilePath path_;
int max_size_bytes_;
CodeCacheType cache_type_;
base::WeakPtrFactory<GeneratedCodeCache> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(GeneratedCodeCache);
};
} // namespace content
#endif // CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_