#include "content/browser/code_cache/generated_code_cache.h"
#include <optional>
#include "base/feature_list.h"
#include "base/i18n/time_formatting.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "content/browser/code_cache/generated_code_cache_context.h"
#include "content/browser/renderer_host/code_cache_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/renderer.mojom.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/base/features.h"
#include "net/dns/mock_host_resolver.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/common/loader/code_cache_util.h"
#include "third_party/blink/public/common/page/v8_compile_hints_histograms.h"
namespace content {
namespace {
bool SupportsSharedWorker() {
return base::FeatureList::IsEnabled(blink::features::kSharedWorker);
}
}
enum class CodeCacheTestCase {
kCachePartitioningEnabled,
kCachePartitioningDisabled,
};
std::string CodeCacheTestCaseToString(CodeCacheTestCase param) {
switch (param) {
case CodeCacheTestCase::kCachePartitioningEnabled:
return "CachePartitioningEnabled";
case CodeCacheTestCase::kCachePartitioningDisabled:
return "CachePartitioningDisabled";
}
}
enum class BackgroundResourceFetchTestCase {
kBackgroundResourceFetchEnabled,
kBackgroundResourceFetchDisabled,
};
std::string BackgroundResourceFetchTestCaseToString(
BackgroundResourceFetchTestCase param) {
switch (param) {
case BackgroundResourceFetchTestCase::kBackgroundResourceFetchEnabled:
return "BackgroundResourceFetchEnabled";
case BackgroundResourceFetchTestCase::kBackgroundResourceFetchDisabled:
return "BackgroundResourceFetchDisabled";
}
}
class CodeCacheBrowserTest
: public ContentBrowserTest,
public testing::WithParamInterface<
std::pair<CodeCacheTestCase, BackgroundResourceFetchTestCase>> {
public:
CodeCacheBrowserTest() {
feature_use_persistent_cache_for_code_cache_.InitAndDisableFeature(
blink::features::kUsePersistentCacheForCodeCache);
feature_split_cache_by_network_isolation_key_.InitAndEnableFeature(
net::features::kSplitCacheByNetworkIsolationKey);
feature_third_party_storage_partitioning_.InitAndEnableFeature(
net::features::kThirdPartyStoragePartitioning);
feature_split_code_cache_by_network_isolation_key_.InitWithFeatureState(
net::features::kSplitCodeCacheByNetworkIsolationKey,
IsCachePartitioningEnabled());
if (IsBackgroundResourceFetchEnabled()) {
feature_background_resource_fetch_.InitAndEnableFeature(
blink::features::kBackgroundResourceFetch);
} else {
feature_background_resource_fetch_.InitAndDisableFeature(
blink::features::kBackgroundResourceFetch);
}
}
bool IsCachePartitioningEnabled() const {
return GetParam().first == CodeCacheTestCase::kCachePartitioningEnabled;
}
bool IsBackgroundResourceFetchEnabled() const {
return GetParam().second ==
BackgroundResourceFetchTestCase::kBackgroundResourceFetchEnabled;
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CodeCacheBrowserTest::CachedScriptHandler, base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
}
std::unique_ptr<net::test_server::HttpResponse> CachedScriptHandler(
const net::test_server::HttpRequest& request) {
GURL absolute_url = embedded_test_server()->GetURL(request.relative_url);
if (absolute_url.GetPath() == "/done.js") {
GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(done_callback_));
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content("//done!");
http_response->set_content_type("application/javascript");
return http_response;
}
if (absolute_url.GetPath() == "/cacheable.js") {
if (trigger_validation_requests_ &&
base::Contains(request.headers, "If-Modified-Since")) {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_NOT_MODIFIED);
last_cache_js_response_code_ = net::HTTP_NOT_MODIFIED;
return http_response;
}
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
last_cache_js_response_code_ = net::HTTP_OK;
std::string content = "let variable = 'hello!';\n";
for (int i = 0; i < 16; i++) {
content += std::string(64, '/') + '\n';
}
http_response->set_content(content);
http_response->set_content_type("application/javascript");
if (trigger_validation_requests_) {
http_response->AddCustomHeader("Age", "3000");
http_response->AddCustomHeader("Last-Modified",
base::TimeFormatHTTP(base::Time::Now()));
} else {
http_response->AddCustomHeader("Cache-Control", "max-age=100000");
}
return http_response;
}
if (absolute_url.GetPath() == "/cacheable.html") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
std::string content =
"<html><head><title>Title</title></head>"
"<script src='/cacheable.js'></script></html>";
http_response->set_content(content);
return http_response;
}
if (absolute_url.GetPath() == "/worker.js") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
std::string content = base::StringPrintf(
"importScripts('cacheable.js', 'done.js');"
"close();");
for (int i = 0; i < 16; i++) {
content += std::string(64, '/') + '\n';
}
http_response->set_content(content);
http_response->set_content_type("application/javascript");
return http_response;
}
if (absolute_url.GetPath() == "/shared-worker.html") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
std::string content =
"<html><head><title>Title</title></head>"
"<script>let w = new SharedWorker('worker.js');</script></html>";
http_response->set_content(content);
return http_response;
}
if (absolute_url.GetPath() == "/cacheable_module.js") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
std::string content =
"const title = () => { return 'Module Loaded'; }\n"
"export default title\n";
for (int i = 0; i < 16; i++) {
content += std::string(64, '/') + '\n';
}
http_response->set_content(content);
http_response->set_content_type("application/javascript");
http_response->AddCustomHeader("Cache-Control", "max-age=100000");
return http_response;
}
if (absolute_url.GetPath() == "/cacheable_module.html") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
std::string content =
"<html><head><title>Title</title></head>"
"<script type='module'>\n"
"import title from \"./cacheable_module.js\";\n"
"document.title = title();"
"</script></html>";
http_response->set_content(content);
return http_response;
}
return nullptr;
}
void LoadIframe(const GURL& url) {
EXPECT_TRUE(
ExecJs(shell()->web_contents(),
JsReplace("const iframe = document.createElement('iframe');"
"iframe.src = $1;"
"document.body.appendChild(iframe);",
url)));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
}
protected:
void PurgeResourceCacheFromTheMainFrame() {
base::RunLoop loop;
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root()
->current_frame_host()
->GetProcess()
->GetRendererInterface()
->PurgeResourceCache(loop.QuitClosure());
loop.Run();
}
void PurgeResourceCacheFromTheFirstSubFrame() {
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
CHECK(root->child_count() != 0);
base::RunLoop loop;
root->child_at(root->child_count() - 1)
->current_frame_host()
->GetProcess()
->GetRendererInterface()
->PurgeResourceCache(loop.QuitClosure());
loop.Run();
}
GeneratedCodeCacheContext* GetGeneratedCodeCacheContext() {
return shell()
->web_contents()
->GetBrowserContext()
->GetDefaultStoragePartition()
->GetGeneratedCodeCacheContext();
}
base::OnceClosure done_callback_;
bool trigger_validation_requests_ = false;
std::optional<net::HttpStatusCode> last_cache_js_response_code_;
private:
base::test::ScopedFeatureList feature_use_persistent_cache_for_code_cache_;
base::test::ScopedFeatureList feature_split_cache_by_network_isolation_key_;
base::test::ScopedFeatureList feature_third_party_storage_partitioning_;
base::test::ScopedFeatureList
feature_split_code_cache_by_network_isolation_key_;
base::test::ScopedFeatureList feature_background_resource_fetch_;
};
INSTANTIATE_TEST_SUITE_P(
All,
CodeCacheBrowserTest,
testing::Values(
std::make_pair(
CodeCacheTestCase::kCachePartitioningEnabled,
BackgroundResourceFetchTestCase::kBackgroundResourceFetchEnabled),
std::make_pair(
CodeCacheTestCase::kCachePartitioningEnabled,
BackgroundResourceFetchTestCase::kBackgroundResourceFetchDisabled),
std::make_pair(
CodeCacheTestCase::kCachePartitioningDisabled,
BackgroundResourceFetchTestCase::kBackgroundResourceFetchEnabled),
std::make_pair(
CodeCacheTestCase::kCachePartitioningDisabled,
BackgroundResourceFetchTestCase::kBackgroundResourceFetchDisabled)),
[](const testing::TestParamInfo<
std::pair<CodeCacheTestCase, BackgroundResourceFetchTestCase>>& info) {
return CodeCacheTestCaseToString(info.param.first) +
BackgroundResourceFetchTestCaseToString(info.param.second);
});
IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest, CachingFromThirdPartyFrames) {
GURL a_com_parent_page =
embedded_test_server()->GetURL("a.com", "/empty.html");
GURL b_com_parent_page =
embedded_test_server()->GetURL("b.com", "/empty.html");
GURL c_com_cacheable_js_page =
embedded_test_server()->GetURL("c.com", "/cacheable.html");
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
LoadIframe(c_com_cacheable_js_page);
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 1);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kCreate, 1);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 0);
PurgeResourceCacheFromTheFirstSubFrame();
}
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
LoadIframe(c_com_cacheable_js_page);
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 1);
PurgeResourceCacheFromTheFirstSubFrame();
}
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), b_com_parent_page));
LoadIframe(c_com_cacheable_js_page);
FetchHistogramsFromChildProcesses();
if (IsCachePartitioningEnabled()) {
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 1);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 0);
} else {
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 1);
}
}
}
IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest, CachingFromIFrame) {
GURL a_com_parent_page =
embedded_test_server()->GetURL("a.com", "/empty.html");
const std::string_view kLoadCacheableJSInIframeScript = R"(
(async () => {
await new Promise(resolve => {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const script = iframe.contentWindow.document.createElement('script');
script.addEventListener('load', resolve);
script.src = '/cacheable.js';
iframe.contentWindow.document.body.appendChild(script);
});
})();
)";
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
kLoadCacheableJSInIframeScript));
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 1);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kCreate, 1);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 0);
PurgeResourceCacheFromTheFirstSubFrame();
}
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
kLoadCacheableJSInIframeScript));
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 1);
PurgeResourceCacheFromTheFirstSubFrame();
}
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
kLoadCacheableJSInIframeScript));
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 1);
PurgeResourceCacheFromTheFirstSubFrame();
}
}
IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest,
CachingFromThirdPartySharedWorkers) {
if (!SupportsSharedWorker()) {
return;
}
GURL a_com_parent_page =
embedded_test_server()->GetURL("a.com", "/empty.html");
GURL b_com_parent_page =
embedded_test_server()->GetURL("b.com", "/empty.html");
GURL c_com_worker_js_page =
embedded_test_server()->GetURL("c.com", "/shared-worker.html");
{
base::test::TestFuture<void> worker_done;
done_callback_ = worker_done.GetCallback();
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
LoadIframe(c_com_worker_js_page);
ASSERT_TRUE(worker_done.Wait());
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kCreate, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 0);
}
{
base::test::TestFuture<void> worker_done;
done_callback_ = worker_done.GetCallback();
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), a_com_parent_page));
LoadIframe(c_com_worker_js_page);
ASSERT_TRUE(worker_done.Wait());
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 0);
}
{
base::test::TestFuture<void> worker_done;
done_callback_ = worker_done.GetCallback();
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), b_com_parent_page));
LoadIframe(c_com_worker_js_page);
ASSERT_TRUE(worker_done.Wait());
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kMiss, 0);
histogram_tester.ExpectBucketCount(
"SiteIsolatedCodeCache.JS.Behaviour",
GeneratedCodeCache::CacheEntryStatus::kHit, 0);
}
}
class CodeCacheSizeChecker {
public:
CodeCacheSizeChecker(GeneratedCodeCacheContext* cache_context,
const GURL& url,
const GURL& origin,
size_t expected_size)
: cache_context_(cache_context),
url_(url),
origin_(origin),
expected_size_(expected_size) {}
size_t Wait() {
base::test::TestFuture<void> done;
done_callback_ = done.GetCallback();
GeneratedCodeCacheContext::GetTaskRunner(cache_context_)
->PostTask(FROM_HERE,
base::BindOnce(&CodeCacheSizeChecker::GetCodeCache,
base::Unretained(this)));
CHECK(done.Wait());
return expected_size_;
}
private:
void GetCodeCache() {
net::NetworkIsolationKey nik = net::NetworkIsolationKey(
net::SchemefulSite(origin_), net::SchemefulSite(origin_));
cache_context_->generated_js_code_cache()->FetchEntry(
url_, GURL(), nik,
base::BindOnce(&CodeCacheSizeChecker::FetchCallback,
base::Unretained(this)));
}
void FetchCallback(const base::Time&, mojo_base::BigBuffer data) {
if (data.size() >= expected_size_) {
GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(done_callback_));
} else {
GeneratedCodeCacheContext::GetTaskRunner(cache_context_)
->PostDelayedTask(FROM_HERE,
base::BindOnce(&CodeCacheSizeChecker::GetCodeCache,
base::Unretained(this)),
base::Microseconds(100));
}
}
scoped_refptr<GeneratedCodeCacheContext> cache_context_;
const GURL url_;
const GURL origin_;
const size_t expected_size_;
base::OnceClosure done_callback_;
};
IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest,
GeneratedCodeCacheSizeClassicScript) {
CodeCacheHostImpl::SetUseEmptySecondaryKeyForTesting();
GURL url = embedded_test_server()->GetURL("c.com", "/cacheable.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
GeneratedCodeCacheContext* cache_context = GetGeneratedCodeCacheContext();
const GURL& cacheable_script =
embedded_test_server()->GetURL("c.com", "/cacheable.js");
CodeCacheSizeChecker code_cache_size_checker(
cache_context, cacheable_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize);
EXPECT_EQ(blink::kCodeCacheTimestampCachedMetaSize,
code_cache_size_checker.Wait());
PurgeResourceCacheFromTheMainFrame();
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
EXPECT_TRUE(NavigateToURL(shell(), url));
CodeCacheSizeChecker code_cache_size_checker2(
cache_context, cacheable_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize + 1);
code_cache_size_checker2.Wait();
}
IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest, KeepCodeCacheWhenNotModified) {
CodeCacheHostImpl::SetUseEmptySecondaryKeyForTesting();
trigger_validation_requests_ = true;
const GURL url = embedded_test_server()->GetURL("c.com", "/cacheable.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
GeneratedCodeCacheContext* cache_context = GetGeneratedCodeCacheContext();
const GURL cacheable_script =
embedded_test_server()->GetURL("c.com", "/cacheable.js");
CodeCacheSizeChecker code_cache_size_checker(
cache_context, cacheable_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize);
EXPECT_EQ(blink::kCodeCacheTimestampCachedMetaSize,
code_cache_size_checker.Wait());
PurgeResourceCacheFromTheMainFrame();
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
EXPECT_TRUE(NavigateToURL(shell(), url));
CodeCacheSizeChecker code_cache_size_checker2(
cache_context, cacheable_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize + 1);
code_cache_size_checker2.Wait();
ASSERT_TRUE(last_cache_js_response_code_.has_value());
ASSERT_EQ(net::HTTP_NOT_MODIFIED, last_cache_js_response_code_.value());
PurgeResourceCacheFromTheMainFrame();
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
last_cache_js_response_code_.reset();
EXPECT_TRUE(NavigateToURL(shell(), url));
CodeCacheSizeChecker code_cache_size_checker3(
cache_context, cacheable_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize + 1);
code_cache_size_checker3.Wait();
ASSERT_TRUE(last_cache_js_response_code_.has_value());
ASSERT_EQ(net::HTTP_NOT_MODIFIED, last_cache_js_response_code_.value());
}
IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest,
GeneratedCodeCacheSizeModuleScript) {
CodeCacheHostImpl::SetUseEmptySecondaryKeyForTesting();
GURL url = embedded_test_server()->GetURL("c.com", "/cacheable_module.html");
const std::u16string expected_title = u"Module Loaded";
{
TitleWatcher watcher(shell()->web_contents(), expected_title);
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(expected_title, watcher.WaitAndGetTitle());
}
GeneratedCodeCacheContext* cache_context = GetGeneratedCodeCacheContext();
const GURL& cacheable_module_script =
embedded_test_server()->GetURL("c.com", "/cacheable_module.js");
CodeCacheSizeChecker code_cache_size_checker(
cache_context, cacheable_module_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize);
EXPECT_EQ(blink::kCodeCacheTimestampCachedMetaSize,
code_cache_size_checker.Wait());
PurgeResourceCacheFromTheMainFrame();
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
{
TitleWatcher watcher(shell()->web_contents(), expected_title);
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_EQ(expected_title, watcher.WaitAndGetTitle());
}
CodeCacheSizeChecker code_cache_size_checker2(
cache_context, cacheable_module_script,
embedded_test_server()->GetURL("c.com", "/"),
blink::kCodeCacheTimestampCachedMetaSize + 1);
code_cache_size_checker2.Wait();
}
class CompileHintsBrowserTest : public ContentBrowserTest {
public:
CompileHintsBrowserTest() = default;
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&CompileHintsBrowserTest::CachedScriptHandler, base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
}
std::unique_ptr<net::test_server::HttpResponse> CachedScriptHandler(
const net::test_server::HttpRequest& request) {
GURL absolute_url = embedded_test_server()->GetURL(request.relative_url);
if (absolute_url.GetPath() == "/cacheable.js") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->AddCustomHeader("Cache-Control", "max-age=604800");
std::string content = R"(
let variable = 'hello!';
function foo() {}
foo();
)";
for (int i = 0; i < 16; i++) {
content += std::string(64, '/') + '\n';
}
http_response->set_content(content);
http_response->set_content_type("application/javascript");
return http_response;
}
if (absolute_url.GetPath() == "/cacheable.html") {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
std::string content =
"<html><head><title>Title</title></head>"
"<script src='/cacheable.js'></script></html>";
http_response->set_content(content);
return http_response;
}
return nullptr;
}
};
class LocalCompileHintsBrowserTest : public CompileHintsBrowserTest {
public:
LocalCompileHintsBrowserTest() {
local_compile_hints_.InitAndEnableFeature(
blink::features::kLocalCompileHints);
interactive_detector_ignore_fcp_.InitAndEnableFeature(
blink::features::kInteractiveDetectorIgnoreFcp);
feature_use_persistent_cache_for_code_cache_.InitAndDisableFeature(
blink::features::kUsePersistentCacheForCodeCache);
}
private:
base::test::ScopedFeatureList local_compile_hints_;
base::test::ScopedFeatureList interactive_detector_ignore_fcp_;
base::test::ScopedFeatureList feature_use_persistent_cache_for_code_cache_;
};
class NoLocalCompileHintsBrowserTest : public CompileHintsBrowserTest {
public:
NoLocalCompileHintsBrowserTest() {
feature_use_persistent_cache_for_code_cache_.InitAndDisableFeature(
blink::features::kUsePersistentCacheForCodeCache);
local_compile_hints_.InitAndDisableFeature(
blink::features::kLocalCompileHints);
}
private:
base::test::ScopedFeatureList feature_use_persistent_cache_for_code_cache_;
base::test::ScopedFeatureList local_compile_hints_;
};
IN_PROC_BROWSER_TEST_F(NoLocalCompileHintsBrowserTest, NoCompileHints) {
CodeCacheHostImpl::SetUseEmptySecondaryKeyForTesting();
GURL cacheable_page =
embedded_test_server()->GetURL("c.com", "/cacheable.html");
GURL other_page = embedded_test_server()->GetURL("a.com", "/empty.html");
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), cacheable_page));
FetchHistogramsFromChildProcesses();
EXPECT_EQ(
1, histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::
kNoCompileHintsClassicNonStreaming) +
histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::kNoCompileHintsStreaming));
}
EXPECT_TRUE(NavigateToURL(shell(), other_page));
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), cacheable_page));
FetchHistogramsFromChildProcesses();
EXPECT_EQ(
1, histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::
kNoCompileHintsClassicNonStreaming) +
histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::kNoCompileHintsStreaming));
}
EXPECT_TRUE(NavigateToURL(shell(), other_page));
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), cacheable_page));
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::kConsumeCodeCacheClassicNonStreaming,
1);
}
}
IN_PROC_BROWSER_TEST_F(LocalCompileHintsBrowserTest, LocalCompileHints) {
CodeCacheHostImpl::SetUseEmptySecondaryKeyForTesting();
GURL cacheable_page =
embedded_test_server()->GetURL("c.com", "/cacheable.html");
GURL other_page = embedded_test_server()->GetURL("a.com", "/empty.html");
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), cacheable_page));
GeneratedCodeCacheContext* cache_context =
shell()
->web_contents()
->GetBrowserContext()
->GetDefaultStoragePartition()
->GetGeneratedCodeCacheContext();
const GURL& cacheable_script =
embedded_test_server()->GetURL("c.com", "/cacheable.js");
constexpr int kCompileHintsCacheSize = 28;
CodeCacheSizeChecker code_cache_size_checker(
cache_context, cacheable_script, GURL("http://c.com/"),
kCompileHintsCacheSize);
code_cache_size_checker.Wait();
FetchHistogramsFromChildProcesses();
EXPECT_EQ(1, histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::
kProduceCompileHintsClassicNonStreaming) +
histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::
kProduceCompileHintsStreaming));
EXPECT_EQ(1,
histogram_tester.GetBucketCount(
blink::v8_compile_hints::kLocalCompileHintsGeneratedHistogram,
blink::v8_compile_hints::LocalCompileHintsGenerated::kFinal));
}
EXPECT_TRUE(NavigateToURL(shell(), other_page));
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), cacheable_page));
FetchHistogramsFromChildProcesses();
EXPECT_EQ(1, histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::
kConsumeLocalCompileHintsClassicNonStreaming) +
histogram_tester.GetBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::
kConsumeLocalCompileHintsStreaming));
}
EXPECT_TRUE(NavigateToURL(shell(), other_page));
{
base::HistogramTester histogram_tester;
EXPECT_TRUE(NavigateToURL(shell(), cacheable_page));
FetchHistogramsFromChildProcesses();
histogram_tester.ExpectBucketCount(
blink::v8_compile_hints::kStatusHistogram,
blink::v8_compile_hints::Status::kConsumeCodeCacheClassicNonStreaming,
1);
}
}
}