Commit 9067baa1 authored by Mythri Alle's avatar Mythri Alle Committed by Commit Bot

Add a separate GeneratedCodeCache to decouple HttpCache and metadata

Storing the generated code for javascript resources in a separate
cache gives more flexibility in the implementation of the code
cache.More details in the design doc [1]

This cl just adds the GeneratedCodeCache class but does not
use it yet. There will be follow up cls to actually use
the cache.

[1] https://docs.google.com/document/d/1O_PVZPn37Ev3_DWJjQLX-gid__OKxnXP1UsE8g_YkRY/edit?usp=sharing

Bug: chromium:812168
Change-Id: I04b709d8e443217f8235859731e75fdf94c62971
Reviewed-on: https://chromium-review.googlesource.com/1107703
Commit-Queue: Mythri Alle <mythria@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarCharlie Reis <creis@chromium.org>
Reviewed-by: default avatarMaks Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576453}
parent 120d9832
...@@ -573,6 +573,8 @@ jumbo_source_set("browser") { ...@@ -573,6 +573,8 @@ jumbo_source_set("browser") {
"cocoa/system_hotkey_helper_mac.mm", "cocoa/system_hotkey_helper_mac.mm",
"cocoa/system_hotkey_map.h", "cocoa/system_hotkey_map.h",
"cocoa/system_hotkey_map.mm", "cocoa/system_hotkey_map.mm",
"code_cache/generated_code_cache.cc",
"code_cache/generated_code_cache.h",
"startup_data_impl.cc", "startup_data_impl.cc",
"startup_data_impl.h", "startup_data_impl.h",
......
This diff is collapsed.
// 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 "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "content/common/content_export.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 is keyed on two keys: |resource_url| and |requesting_origin|.
// The |resource_url| is the url of the resource that was requested and the
// |requesting_origin| is the origin that requested this resource. This origin
// is used to enforce site isolation policy on stored code. We don't cache the
// code corresponding to unique origins or invalid URLs.
//
// This uses a simple disk_cache backend. It just stores one data stream and
// stores response_time + generated code as one data blob.
// TODO(mythria): Update this comment if the design changes.
//
// 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(scoped_refptr<net::IOBufferWithSize>)>;
// Creates a GeneratedCodeCache with the specified path and the maximum size.
static std::unique_ptr<GeneratedCodeCache> Create(const base::FilePath& path,
int max_size);
~GeneratedCodeCache();
// Writes data to the cache. If there is an entry corresponding to
// <|url|, |origin|> this overwrites the existing data. If there is no entry
// it creates a new one.
void WriteData(const GURL& url,
const url::Origin& origin,
scoped_refptr<net::IOBufferWithSize>);
// Fetch entry corresponding to <url, origin> from the cache and pass
// it using the ReadDataCallback.
void FetchEntry(const GURL& url, const url::Origin& origin, ReadDataCallback);
// Delete the entry corresponding to <url, origin>
void DeleteEntry(const GURL& url, const url::Origin& origin);
const base::FilePath& path() const { return path_; }
private:
class PendingOperation;
using ScopedBackendPtr = std::unique_ptr<disk_cache::Backend>;
// State of the backend.
enum BackendState { kUnInitialized, kInitializing, kInitialized, kFailed };
// The operation requested.
enum Operation { kFetch, kWrite, kDelete };
// Data streams corresponding to each entry.
enum { kDataIndex = 1 };
GeneratedCodeCache(const base::FilePath& path, int max_size_bytes);
// 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 OpenCompleteForWriteData(
scoped_refptr<net::IOBufferWithSize> buffer,
const std::string& key,
scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
int rv);
void CreateCompleteForWriteData(
scoped_refptr<net::IOBufferWithSize> buffer,
scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
int rv);
// Fetch entry from cache
void FetchEntryImpl(const std::string& key, ReadDataCallback);
void OpenCompleteForReadData(
ReadDataCallback callback,
scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
int rv);
void ReadDataComplete(ReadDataCallback callback,
scoped_refptr<net::IOBufferWithSize> buffer,
int rv);
// Delete entry from cache
void DeleteEntryImpl(const std::string& key);
std::unique_ptr<disk_cache::Backend> backend_;
BackendState backend_state_;
std::vector<std::unique_ptr<PendingOperation>> pending_ops_;
base::FilePath path_;
int max_size_bytes_;
base::WeakPtrFactory<GeneratedCodeCache> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(GeneratedCodeCache);
};
} // namespace content
#endif // CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_
// 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.
#include "content/browser/code_cache/generated_code_cache.h"
#include "base/files/scoped_temp_dir.h"
#include "base/test/scoped_task_environment.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
class GeneratedCodeCacheTest : public testing::Test {
public:
static const int kMaxSizeInBytes = 1024 * 1024;
static constexpr char kInitialUrl[] = "http://example.com/script.js";
static constexpr char kInitialOrigin[] = "http://example.com";
static constexpr char kInitialData[] = "InitialData";
GeneratedCodeCacheTest() = default;
void SetUp() override {
ASSERT_TRUE(cache_dir_.CreateUniqueTempDir());
cache_path_ = cache_dir_.GetPath();
generated_code_cache_ =
GeneratedCodeCache::Create(cache_path_, kMaxSizeInBytes);
}
void TearDown() override {
disk_cache::FlushCacheThreadForTesting();
scoped_task_environment_.RunUntilIdle();
}
// This function initializes the cache and waits till the transaction is
// finished. When this function returns, the backend is already initialized.
void InitializeCache() {
GURL url(kInitialUrl);
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
WriteToCache(url, origin, kInitialData);
scoped_task_environment_.RunUntilIdle();
}
// This function initializes the cache and reopens it. When this function
// returns, the backend initialization is not complete yet. This is used
// to test the pending operaions path.
void InitializeCacheAndReOpen() {
InitializeCache();
generated_code_cache_.reset();
generated_code_cache_ =
GeneratedCodeCache::Create(cache_path_, kMaxSizeInBytes);
}
void WriteToCache(const GURL& url,
const url::Origin& origin,
const std::string& data) {
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(data.length()));
memcpy(buffer->data(), data.c_str(), data.length());
generated_code_cache_->WriteData(url, origin, buffer);
}
void DeleteFromCache(const GURL& url, const url::Origin& origin) {
generated_code_cache_->DeleteEntry(url, origin);
}
void FetchFromCache(const GURL& url, const url::Origin& origin) {
received_ = false;
GeneratedCodeCache::ReadDataCallback callback = base::BindRepeating(
&GeneratedCodeCacheTest::FetchEntryCallback, base::Unretained(this));
generated_code_cache_->FetchEntry(url, origin, callback);
}
void FetchEntryCallback(scoped_refptr<net::IOBufferWithSize> buffer) {
if (!buffer || !buffer->data()) {
received_ = true;
received_null_ = true;
return;
}
std::string str(buffer->data(), buffer->size());
received_ = true;
received_null_ = false;
received_data_ = str;
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<GeneratedCodeCache> generated_code_cache_;
base::ScopedTempDir cache_dir_;
std::string received_data_;
bool received_;
bool received_null_;
base::FilePath cache_path_;
};
constexpr char GeneratedCodeCacheTest::kInitialUrl[];
constexpr char GeneratedCodeCacheTest::kInitialOrigin[];
constexpr char GeneratedCodeCacheTest::kInitialData[];
TEST_F(GeneratedCodeCacheTest, FetchEntry) {
GURL url(kInitialUrl);
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCache();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(kInitialData, received_data_);
}
TEST_F(GeneratedCodeCacheTest, WriteEntry) {
GURL new_url("http://example1.com/script.js");
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCache();
std::string data = "SerializedCodeForScript";
WriteToCache(new_url, origin, data);
FetchFromCache(new_url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(data, received_data_);
}
TEST_F(GeneratedCodeCacheTest, DeleteEntry) {
GURL url(kInitialUrl);
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCache();
DeleteFromCache(url, origin);
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
ASSERT_TRUE(received_null_);
}
TEST_F(GeneratedCodeCacheTest, FetchEntryPendingOp) {
GURL url(kInitialUrl);
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCacheAndReOpen();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(kInitialData, received_data_);
}
TEST_F(GeneratedCodeCacheTest, WriteEntryPendingOp) {
GURL new_url("http://example1.com/script1.js");
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCache();
std::string data = "SerializedCodeForScript";
WriteToCache(new_url, origin, data);
scoped_task_environment_.RunUntilIdle();
FetchFromCache(new_url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(data, received_data_);
}
TEST_F(GeneratedCodeCacheTest, DeleteEntryPendingOp) {
GURL url(kInitialUrl);
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCacheAndReOpen();
DeleteFromCache(url, origin);
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
ASSERT_TRUE(received_null_);
}
TEST_F(GeneratedCodeCacheTest, UpdateDataOfExistingEntry) {
GURL url(kInitialUrl);
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
InitializeCache();
std::string new_data = "SerializedCodeForScriptOverwrite";
WriteToCache(url, origin, new_data);
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(new_data, received_data_);
}
TEST_F(GeneratedCodeCacheTest, FetchFailsForNonexistingOrigin) {
InitializeCache();
url::Origin new_origin = url::Origin::Create(GURL("http://not-example.com"));
FetchFromCache(GURL(kInitialUrl), new_origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
ASSERT_TRUE(received_null_);
}
TEST_F(GeneratedCodeCacheTest, FetchEntriesFromSameOrigin) {
GURL url("http://example.com/script.js");
GURL second_url("http://script.com/one.js");
url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
std::string data_first_resource = "SerializedCodeForFirstResource";
WriteToCache(url, origin, data_first_resource);
std::string data_second_resource = "SerializedCodeForSecondResource";
WriteToCache(second_url, origin, data_second_resource);
scoped_task_environment_.RunUntilIdle();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(data_first_resource, received_data_);
FetchFromCache(second_url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(data_second_resource, received_data_);
}
TEST_F(GeneratedCodeCacheTest, FetchSucceedsFromDifferentOrigins) {
GURL url("http://example.com/script.js");
url::Origin origin = url::Origin::Create(GURL("http://example.com"));
url::Origin origin1 = url::Origin::Create(GURL("http://example1.com"));
std::string data_origin = "SerializedCodeForFirstOrigin";
WriteToCache(url, origin, data_origin);
std::string data_origin1 = "SerializedCodeForSecondOrigin";
WriteToCache(url, origin1, data_origin1);
scoped_task_environment_.RunUntilIdle();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(data_origin, received_data_);
FetchFromCache(url, origin1);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
EXPECT_EQ(data_origin1, received_data_);
}
TEST_F(GeneratedCodeCacheTest, FetchFailsForUniqueOrigin) {
GURL url("http://example.com/script.js");
url::Origin origin =
url::Origin::Create(GURL("data:text/html,<script></script>"));
std::string data = "SerializedCodeForUniqueOrigin";
WriteToCache(url, origin, data);
scoped_task_environment_.RunUntilIdle();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
ASSERT_TRUE(received_null_);
}
TEST_F(GeneratedCodeCacheTest, FetchFailsForInvalidOrigin) {
GURL url("http://example.com/script.js");
url::Origin origin = url::Origin::Create(GURL("invalidURL"));
std::string data = "SerializedCodeForInvalidOrigin";
WriteToCache(url, origin, data);
scoped_task_environment_.RunUntilIdle();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
ASSERT_TRUE(received_null_);
}
TEST_F(GeneratedCodeCacheTest, FetchFailsForInvalidURL) {
GURL url("InvalidURL");
url::Origin origin = url::Origin::Create(GURL("http://example.com"));
std::string data = "SerializedCodeForInvalidURL";
WriteToCache(url, origin, data);
scoped_task_environment_.RunUntilIdle();
FetchFromCache(url, origin);
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(received_);
ASSERT_TRUE(received_null_);
}
} // namespace content
...@@ -1291,6 +1291,7 @@ test("content_unittests") { ...@@ -1291,6 +1291,7 @@ test("content_unittests") {
"../browser/cache_storage/cache_storage_scheduler_unittest.cc", "../browser/cache_storage/cache_storage_scheduler_unittest.cc",
"../browser/child_process_security_policy_unittest.cc", "../browser/child_process_security_policy_unittest.cc",
"../browser/cocoa/system_hotkey_map_unittest.mm", "../browser/cocoa/system_hotkey_map_unittest.mm",
"../browser/code_cache/generated_code_cache_unittest.cc",
"../browser/compositor/reflector_impl_unittest.cc", "../browser/compositor/reflector_impl_unittest.cc",
"../browser/compositor/software_browser_compositor_output_surface_unittest.cc", "../browser/compositor/software_browser_compositor_output_surface_unittest.cc",
"../browser/cookie_store/cookie_store_manager_unittest.cc", "../browser/cookie_store/cookie_store_manager_unittest.cc",
......
...@@ -9,12 +9,14 @@ namespace net { ...@@ -9,12 +9,14 @@ namespace net {
// The types of caches that can be created. // The types of caches that can be created.
enum CacheType { enum CacheType {
DISK_CACHE, // Disk is used as the backing storage. DISK_CACHE, // Disk is used as the backing storage.
MEMORY_CACHE, // Data is stored only in memory. MEMORY_CACHE, // Data is stored only in memory.
MEDIA_CACHE, // Optimized to handle media files. MEDIA_CACHE, // Optimized to handle media files.
APP_CACHE, // Backing store for an AppCache. APP_CACHE, // Backing store for an AppCache.
SHADER_CACHE, // Backing store for the GL shader cache. SHADER_CACHE, // Backing store for the GL shader cache.
PNACL_CACHE, // Backing store the PNaCl translation cache PNACL_CACHE, // Backing store the PNaCl translation cache
GENERATED_CODE_CACHE, // Backing store for data (like code for JavaScript)
// generated by renderer.
}; };
// The types of disk cache backend, only used at backend instantiation. // The types of disk cache backend, only used at backend instantiation.
......
...@@ -102,6 +102,8 @@ ...@@ -102,6 +102,8 @@
case net::PNACL_CACHE: \ case net::PNACL_CACHE: \
CACHE_HISTOGRAM_##type(my_name.data(), sample); \ CACHE_HISTOGRAM_##type(my_name.data(), sample); \
break; \ break; \
case net::GENERATED_CODE_CACHE: \
break; \
} \ } \
} }
......
...@@ -240,7 +240,8 @@ SimpleBackendImpl::SimpleBackendImpl( ...@@ -240,7 +240,8 @@ SimpleBackendImpl::SimpleBackendImpl(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING, {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
orig_max_size_(max_bytes), orig_max_size_(max_bytes),
entry_operations_mode_(cache_type == net::DISK_CACHE entry_operations_mode_((cache_type == net::DISK_CACHE ||
cache_type == net::GENERATED_CODE_CACHE)
? SimpleEntryImpl::OPTIMISTIC_OPERATIONS ? SimpleEntryImpl::OPTIMISTIC_OPERATIONS
: SimpleEntryImpl::NON_OPTIMISTIC_OPERATIONS), : SimpleEntryImpl::NON_OPTIMISTIC_OPERATIONS),
net_log_(net_log) { net_log_(net_log) {
......
...@@ -18,27 +18,30 @@ ...@@ -18,27 +18,30 @@
// TODO(pasko): add histograms for shader cache as soon as it becomes possible // TODO(pasko): add histograms for shader cache as soon as it becomes possible
// for a user to get shader cache with the |SimpleBackendImpl| without altering // for a user to get shader cache with the |SimpleBackendImpl| without altering
// any flags. // any flags.
#define SIMPLE_CACHE_UMA(uma_type, uma_name, cache_type, ...) \ // TODO(mythria): add histograms for generated_code_cache when we actually start
do { \ // using the generated_code_cache.
switch (cache_type) { \ #define SIMPLE_CACHE_UMA(uma_type, uma_name, cache_type, ...) \
case net::DISK_CACHE: \ do { \
SIMPLE_CACHE_THUNK( \ switch (cache_type) { \
uma_type, ("SimpleCache.Http." uma_name, ##__VA_ARGS__)); \ case net::DISK_CACHE: \
break; \ SIMPLE_CACHE_THUNK(uma_type, \
case net::APP_CACHE: \ ("SimpleCache.Http." uma_name, ##__VA_ARGS__)); \
SIMPLE_CACHE_THUNK( \ break; \
uma_type, ("SimpleCache.App." uma_name, ##__VA_ARGS__)); \ case net::APP_CACHE: \
break; \ SIMPLE_CACHE_THUNK(uma_type, \
case net::MEDIA_CACHE: \ ("SimpleCache.App." uma_name, ##__VA_ARGS__)); \
SIMPLE_CACHE_THUNK( \ break; \
uma_type, ("SimpleCache.Media." uma_name, ##__VA_ARGS__)); \ case net::MEDIA_CACHE: \
break; \ SIMPLE_CACHE_THUNK(uma_type, \
case net::SHADER_CACHE: \ ("SimpleCache.Media." uma_name, ##__VA_ARGS__)); \
break; \ break; \
default: \ case net::GENERATED_CODE_CACHE: \
NOTREACHED(); \ case net::SHADER_CACHE: \
break; \ break; \
} \ default: \
NOTREACHED(); \
break; \
} \
} while (0) } while (0)
#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_HISTOGRAM_MACROS_H_ #endif // NET_DISK_CACHE_SIMPLE_SIMPLE_HISTOGRAM_MACROS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment