| // Copyright 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 "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/test/sequenced_worker_pool_owner.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "base/time/time.h" |
| #include "content/browser/net/quota_policy_cookie_store.h" |
| #include "content/public/test/mock_special_storage_policy.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "net/cookies/cookie_util.h" |
| #include "net/ssl/ssl_client_cert_type.h" |
| #include "net/test/cert_test_util.h" |
| #include "net/test/test_data_directory.h" |
| #include "sql/statement.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| const base::FilePath::CharType kTestCookiesFilename[] = |
| FILE_PATH_LITERAL("Cookies"); |
| } |
| |
| namespace content { |
| namespace { |
| |
| using CanonicalCookieVector = |
| std::vector<std::unique_ptr<net::CanonicalCookie>>; |
| |
| class QuotaPolicyCookieStoreTest : public testing::Test { |
| public: |
| QuotaPolicyCookieStoreTest() |
| : pool_owner_(new base::SequencedWorkerPoolOwner(3, "Background Pool")), |
| loaded_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| destroy_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED) {} |
| |
| void OnLoaded(CanonicalCookieVector cookies) { |
| cookies_.swap(cookies); |
| loaded_event_.Signal(); |
| } |
| |
| void Load(CanonicalCookieVector* cookies) { |
| EXPECT_FALSE(loaded_event_.IsSignaled()); |
| store_->Load(base::Bind(&QuotaPolicyCookieStoreTest::OnLoaded, |
| base::Unretained(this))); |
| loaded_event_.Wait(); |
| cookies->swap(cookies_); |
| } |
| |
| void ReleaseStore() { |
| EXPECT_TRUE(background_task_runner()->RunsTasksOnCurrentThread()); |
| store_ = nullptr; |
| destroy_event_.Signal(); |
| } |
| |
| void DestroyStoreOnBackgroundThread() { |
| background_task_runner()->PostTask( |
| FROM_HERE, base::Bind(&QuotaPolicyCookieStoreTest::ReleaseStore, |
| base::Unretained(this))); |
| destroy_event_.Wait(); |
| DestroyStore(); |
| } |
| |
| protected: |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner() { |
| return pool_owner_->pool()->GetSequencedTaskRunner( |
| pool_owner_->pool()->GetNamedSequenceToken("background")); |
| } |
| |
| scoped_refptr<base::SequencedTaskRunner> client_task_runner() { |
| return pool_owner_->pool()->GetSequencedTaskRunner( |
| pool_owner_->pool()->GetNamedSequenceToken("client")); |
| } |
| |
| void CreateAndLoad(storage::SpecialStoragePolicy* storage_policy, |
| CanonicalCookieVector* cookies) { |
| scoped_refptr<net::SQLitePersistentCookieStore> sqlite_store( |
| new net::SQLitePersistentCookieStore( |
| temp_dir_.GetPath().Append(kTestCookiesFilename), |
| client_task_runner(), background_task_runner(), true, nullptr)); |
| store_ = new QuotaPolicyCookieStore(sqlite_store.get(), storage_policy); |
| Load(cookies); |
| } |
| |
| // Adds a persistent cookie to store_. |
| void AddCookie(const GURL& url, |
| const std::string& name, |
| const std::string& value, |
| const std::string& domain, |
| const std::string& path, |
| const base::Time& creation) { |
| store_->AddCookie(*net::CanonicalCookie::Create( |
| url, name, value, domain, path, creation, creation, false, false, |
| net::CookieSameSite::DEFAULT_MODE, false, |
| net::COOKIE_PRIORITY_DEFAULT)); |
| } |
| |
| void DestroyStore() { |
| store_ = nullptr; |
| // Ensure that |store_|'s destructor has run by shutting down the pool and |
| // then forcing the pool to be destructed. This will ensure that all the |
| // tasks that block pool shutdown (e.g. |store_|'s cleanup) have run before |
| // yielding control. |
| pool_owner_->pool()->FlushForTesting(); |
| pool_owner_.reset(new base::SequencedWorkerPoolOwner(3, "Background Pool")); |
| } |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| } |
| |
| void TearDown() override { |
| DestroyStore(); |
| } |
| |
| TestBrowserThreadBundle bundle_; |
| std::unique_ptr<base::SequencedWorkerPoolOwner> pool_owner_; |
| base::WaitableEvent loaded_event_; |
| base::WaitableEvent destroy_event_; |
| base::ScopedTempDir temp_dir_; |
| scoped_refptr<QuotaPolicyCookieStore> store_; |
| CanonicalCookieVector cookies_; |
| }; |
| |
| // Test if data is stored as expected in the QuotaPolicy database. |
| TEST_F(QuotaPolicyCookieStoreTest, TestPersistence) { |
| CanonicalCookieVector cookies; |
| CreateAndLoad(nullptr, &cookies); |
| ASSERT_EQ(0U, cookies.size()); |
| |
| base::Time t = base::Time::Now(); |
| AddCookie(GURL("http://foo.com"), "A", "B", std::string(), "/", t); |
| t += base::TimeDelta::FromInternalValue(10); |
| AddCookie(GURL("http://persistent.com"), "A", "B", std::string(), "/", t); |
| |
| // Replace the store, which forces the current store to flush data to |
| // disk. Then, after reloading the store, confirm that the data was flushed by |
| // making sure it loads successfully. This ensures that all pending commits |
| // are made to the store before allowing it to be closed. |
| DestroyStore(); |
| |
| // Reload and test for persistence. |
| cookies.clear(); |
| CreateAndLoad(nullptr, &cookies); |
| EXPECT_EQ(2U, cookies.size()); |
| bool found_foo_cookie = false; |
| bool found_persistent_cookie = false; |
| for (const auto& cookie : cookies) { |
| if (cookie->Domain() == "foo.com") |
| found_foo_cookie = true; |
| else if (cookie->Domain() == "persistent.com") |
| found_persistent_cookie = true; |
| } |
| EXPECT_TRUE(found_foo_cookie); |
| EXPECT_TRUE(found_persistent_cookie); |
| |
| // Now delete the cookies and check persistence again. |
| store_->DeleteCookie(*cookies[0]); |
| store_->DeleteCookie(*cookies[1]); |
| DestroyStore(); |
| |
| // Reload and check if the cookies have been removed. |
| cookies.clear(); |
| CreateAndLoad(nullptr, &cookies); |
| EXPECT_EQ(0U, cookies.size()); |
| cookies.clear(); |
| } |
| |
| // Test if data is stored as expected in the QuotaPolicy database. |
| TEST_F(QuotaPolicyCookieStoreTest, TestPolicy) { |
| CanonicalCookieVector cookies; |
| CreateAndLoad(nullptr, &cookies); |
| ASSERT_EQ(0U, cookies.size()); |
| |
| base::Time t = base::Time::Now(); |
| AddCookie(GURL("http://foo.com"), "A", "B", std::string(), "/", t); |
| t += base::TimeDelta::FromInternalValue(10); |
| AddCookie(GURL("http://persistent.com"), "A", "B", std::string(), "/", t); |
| t += base::TimeDelta::FromInternalValue(10); |
| AddCookie(GURL("http://nonpersistent.com"), "A", "B", std::string(), "/", t); |
| |
| // Replace the store, which forces the current store to flush data to |
| // disk. Then, after reloading the store, confirm that the data was flushed by |
| // making sure it loads successfully. This ensures that all pending commits |
| // are made to the store before allowing it to be closed. |
| DestroyStore(); |
| // Specify storage policy that makes "nonpersistent.com" session only. |
| scoped_refptr<content::MockSpecialStoragePolicy> storage_policy = |
| new content::MockSpecialStoragePolicy(); |
| storage_policy->AddSessionOnly( |
| net::cookie_util::CookieOriginToURL("nonpersistent.com", false)); |
| |
| // Reload and test for persistence. |
| cookies.clear(); |
| CreateAndLoad(storage_policy.get(), &cookies); |
| EXPECT_EQ(3U, cookies.size()); |
| |
| t += base::TimeDelta::FromInternalValue(10); |
| AddCookie(GURL("http://nonpersistent.com"), "A", "B", std::string(), |
| "/second", t); |
| |
| // Now close the store, and "nonpersistent.com" should be deleted according to |
| // policy. |
| DestroyStore(); |
| cookies.clear(); |
| CreateAndLoad(nullptr, &cookies); |
| |
| EXPECT_EQ(2U, cookies.size()); |
| for (const auto& cookie : cookies) { |
| EXPECT_NE("nonpersistent.com", cookie->Domain()); |
| } |
| cookies.clear(); |
| } |
| |
| TEST_F(QuotaPolicyCookieStoreTest, ForceKeepSessionState) { |
| CanonicalCookieVector cookies; |
| CreateAndLoad(nullptr, &cookies); |
| ASSERT_EQ(0U, cookies.size()); |
| |
| base::Time t = base::Time::Now(); |
| AddCookie(GURL("http://foo.com"), "A", "B", std::string(), "/", t); |
| |
| // Recreate |store_| with a storage policy that makes "nonpersistent.com" |
| // session only, but then instruct the store to forcibly keep all cookies. |
| DestroyStore(); |
| scoped_refptr<content::MockSpecialStoragePolicy> storage_policy = |
| new content::MockSpecialStoragePolicy(); |
| storage_policy->AddSessionOnly( |
| net::cookie_util::CookieOriginToURL("nonpersistent.com", false)); |
| |
| // Reload and test for persistence |
| cookies.clear(); |
| CreateAndLoad(storage_policy.get(), &cookies); |
| EXPECT_EQ(1U, cookies.size()); |
| |
| t += base::TimeDelta::FromInternalValue(10); |
| AddCookie(GURL("http://persistent.com"), "A", "B", std::string(), "/", t); |
| t += base::TimeDelta::FromInternalValue(10); |
| AddCookie(GURL("http://nonpersistent.com"), "A", "B", std::string(), "/", t); |
| |
| // Now close the store, but the "nonpersistent.com" cookie should not be |
| // deleted. |
| store_->SetForceKeepSessionState(); |
| DestroyStore(); |
| cookies.clear(); |
| CreateAndLoad(nullptr, &cookies); |
| |
| EXPECT_EQ(3U, cookies.size()); |
| cookies.clear(); |
| } |
| |
| // Tests that the special storage policy is properly applied even when the store |
| // is destroyed on a background thread. |
| TEST_F(QuotaPolicyCookieStoreTest, TestDestroyOnBackgroundThread) { |
| // Specify storage policy that makes "nonpersistent.com" session only. |
| scoped_refptr<content::MockSpecialStoragePolicy> storage_policy = |
| new content::MockSpecialStoragePolicy(); |
| storage_policy->AddSessionOnly( |
| net::cookie_util::CookieOriginToURL("nonpersistent.com", false)); |
| |
| CanonicalCookieVector cookies; |
| CreateAndLoad(storage_policy.get(), &cookies); |
| ASSERT_EQ(0U, cookies.size()); |
| |
| base::Time t = base::Time::Now(); |
| AddCookie(GURL("http://nonpersistent.com"), "A", "B", std::string(), "/", t); |
| |
| // Replace the store, which forces the current store to flush data to |
| // disk. Then, after reloading the store, confirm that the data was flushed by |
| // making sure it loads successfully. This ensures that all pending commits |
| // are made to the store before allowing it to be closed. |
| DestroyStoreOnBackgroundThread(); |
| |
| // Reload and test for persistence. |
| cookies.clear(); |
| CreateAndLoad(storage_policy.get(), &cookies); |
| EXPECT_EQ(0U, cookies.size()); |
| |
| cookies.clear(); |
| } |
| |
| } // namespace |
| } // namespace content |