blob: a1211e1352f162bc2abadaaee6a6c882c9ca59d3 [file] [log] [blame]
// Copyright (c) 2015 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 <algorithm>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/post_task.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "content/browser/browsing_data/conditional_cache_deletion_helper.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/cache_test_util.h"
#include "content/public/test/content_browser_test.h"
#include "content/shell/browser/shell.h"
#include "net/disk_cache/disk_cache.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_cache.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/features.h"
namespace content {
namespace {
bool DeletionCondition(const std::set<GURL>& erase_urls,
const disk_cache::Entry* entry) {
return !!erase_urls.count(GURL(entry->GetKey()));
}
bool HasHttpExampleOrigin(const GURL& url) {
return url.host() == "example.com";
}
} // namespace
class ConditionalCacheDeletionHelperBrowserTest : public ContentBrowserTest {
public:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
return;
cache_util_ = std::make_unique<CacheTestUtil>(
content::BrowserContext::GetDefaultStoragePartition(
shell()->web_contents()->GetBrowserContext()));
done_callback_ =
base::Bind(&ConditionalCacheDeletionHelperBrowserTest::DoneCallback,
base::Unretained(this));
// UI and IO thread synchronization.
waitable_event_ = std::make_unique<base::WaitableEvent>(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
}
void TearDownOnMainThread() override { cache_util_.reset(); }
void CreateCacheEntry(const std::set<GURL>& urls) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
for (auto& url : urls) {
ASSERT_EQ(net::OK, LoadBasicRequest(
storage_partition()->GetNetworkContext(), url));
}
} else {
for (auto& url : urls)
cache_util_->CreateCacheEntries({url.spec()});
}
}
void DeleteEntries(
const base::Callback<bool(const disk_cache::Entry*)>& condition) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
auto* helper =
new ConditionalCacheDeletionHelper(cache_util_->backend(), condition);
helper->DeleteAndDestroySelfWhenFinished(done_callback_);
}
bool TestCacheEntry(const GURL& url) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
return LoadBasicRequest(storage_partition()->GetNetworkContext(), url,
0 /* process_id */, 0 /* render_frame_id */,
net::LOAD_ONLY_FROM_CACHE |
net::LOAD_SKIP_CACHE_VALIDATION) == net::OK;
} else {
return base::ContainsValue(cache_util_->GetEntryKeys(), url.spec());
}
}
void CompareRemainingKeys(const std::set<GURL>& expected_urls,
const std::set<GURL>& erase_urls) {
for (auto& url : expected_urls)
EXPECT_TRUE(TestCacheEntry(url));
for (auto& url : erase_urls)
EXPECT_FALSE(TestCacheEntry(url));
}
void DoneCallback(int value) {
DCHECK_GE(value, 0); // Negative values represent an error.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
waitable_event_->Signal();
}
void WaitForTasksOnIOThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
waitable_event_->Wait();
}
CacheTestUtil* GetCacheTestUtil() { return cache_util_.get(); }
StoragePartition* storage_partition() {
return BrowserContext::GetDefaultStoragePartition(browser_context());
}
private:
BrowserContext* browser_context() {
return shell()->web_contents()->GetBrowserContext();
}
base::Callback<void(int)> done_callback_;
std::unique_ptr<CacheTestUtil> cache_util_;
std::unique_ptr<base::WaitableEvent> waitable_event_;
};
// Tests that ConditionalCacheDeletionHelper only deletes those cache entries
// that match the condition.
IN_PROC_BROWSER_TEST_F(ConditionalCacheDeletionHelperBrowserTest, Condition) {
std::set<GURL> urls = {
embedded_test_server()->GetURL("foo.com", "/title1.html"),
embedded_test_server()->GetURL("bar.com", "/title1.html"),
embedded_test_server()->GetURL("baz.com", "/title1.html"),
embedded_test_server()->GetURL("qux.com", "/title1.html")};
CreateCacheEntry(urls);
std::set<GURL> erase_urls = {
embedded_test_server()->GetURL("bar.com", "/title1.html"),
embedded_test_server()->GetURL("baz.com", "/title1.html"),
};
for (auto& url : erase_urls)
urls.erase(url);
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
network::mojom::ClearDataFilterPtr filter =
network::mojom::ClearDataFilter::New();
filter->type = network::mojom::ClearDataFilter::Type::DELETE_MATCHES;
for (auto& url : erase_urls)
filter->origins.push_back(url::Origin::Create(url));
base::RunLoop run_loop;
storage_partition()->GetNetworkContext()->ClearHttpCache(
base::Time(), base::Time::Max(), std::move(filter),
run_loop.QuitClosure());
run_loop.Run();
} else {
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(
&ConditionalCacheDeletionHelperBrowserTest::DeleteEntries,
base::Unretained(this),
base::Bind(&DeletionCondition, erase_urls)));
WaitForTasksOnIOThread();
}
CompareRemainingKeys(urls, erase_urls);
}
// Tests that ConditionalCacheDeletionHelper correctly constructs a condition
// for time and URL.
//
// Note: This test depends on the timing in cache backends and can be flaky
// if those backends are slow.
//
// Flakily timing out on Mac 10.11 (crbug.com/646119) and flakily
// failing on Linux/ChromeOS (crbug.com/624836)
// --> Switched to tiny_timeout and enabled the test on all platforms to check
// if it will be more stable now.
#if false
#define MAYBE_TimeAndURL DISABLED_TimeAndURL
#else
#define MAYBE_TimeAndURL TimeAndURL
#endif
IN_PROC_BROWSER_TEST_F(ConditionalCacheDeletionHelperBrowserTest,
MAYBE_TimeAndURL) {
// Create some entries.
std::set<GURL> urls = {
embedded_test_server()->GetURL("foo.com", "/title1.html"),
embedded_test_server()->GetURL("example.com", "/title1.html"),
embedded_test_server()->GetURL("bar.com", "/title1.html")};
CreateCacheEntry(urls);
// Wait some milliseconds for the cache to write the entries.
// This assures that future entries will have timestamps strictly greater than
// the ones we just added.
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
base::Time now = base::Time::Now();
// Create a few more entries with a later timestamp.
std::set<GURL> newer_urls = {
embedded_test_server()->GetURL("foo.com", "/simple_page.html"),
embedded_test_server()->GetURL("example.com", "/title2.html"),
embedded_test_server()->GetURL("example.com", "/title3.html"),
embedded_test_server()->GetURL("example2.com", "/simple_page.html")};
CreateCacheEntry(newer_urls);
// Create a condition for entries with the "http://example.com" origin
// created after waiting.
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
network::mojom::ClearDataFilterPtr filter =
network::mojom::ClearDataFilter::New();
filter->type = network::mojom::ClearDataFilter::Type::DELETE_MATCHES;
filter->origins.push_back(url::Origin::Create(
embedded_test_server()->GetURL("example.com", "/")));
base::RunLoop run_loop;
storage_partition()->GetNetworkContext()->ClearHttpCache(
now, base::Time::Max(), std::move(filter), run_loop.QuitClosure());
run_loop.Run();
} else {
base::Callback<bool(const disk_cache::Entry*)> condition =
ConditionalCacheDeletionHelper::CreateURLAndTimeCondition(
base::Bind(&HasHttpExampleOrigin), now, base::Time::Max());
// Delete the entries.
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(
&ConditionalCacheDeletionHelperBrowserTest::DeleteEntries,
base::Unretained(this), std::move(condition)));
WaitForTasksOnIOThread();
}
// Expect that only "title2.html" and "title3.html" were deleted.
std::set<GURL> erase_urls = {
embedded_test_server()->GetURL("example.com", "/title2.html"),
embedded_test_server()->GetURL("example.com", "/title3.html"),
};
for (auto& url : newer_urls)
urls.insert(url);
for (auto& url : erase_urls)
urls.erase(url);
CompareRemainingKeys(urls, erase_urls);
}
} // namespace content