1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <algorithm>
6 #include <memory>
7 #include <set>
8 #include <string>
9 #include <vector>
10 
11 #include "base/bind.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/task/post_task.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/threading/platform_thread.h"
17 #include "build/build_config.h"
18 #include "content/browser/browsing_data/conditional_cache_deletion_helper.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/storage_partition.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "content/public/test/content_browser_test.h"
26 #include "content/shell/browser/shell.h"
27 #include "net/disk_cache/disk_cache.h"
28 #include "net/dns/mock_host_resolver.h"
29 #include "net/http/http_cache.h"
30 #include "net/url_request/url_request_context.h"
31 #include "net/url_request/url_request_context_getter.h"
32 
33 namespace content {
34 
35 class ConditionalCacheDeletionHelperBrowserTest : public ContentBrowserTest {
36  public:
SetUpOnMainThread()37   void SetUpOnMainThread() override {
38     host_resolver()->AddRule("*", "127.0.0.1");
39     ASSERT_TRUE(embedded_test_server()->Start());
40   }
41 
TearDownOnMainThread()42   void TearDownOnMainThread() override {}
43 
TestCacheEntry(const GURL & url)44   bool TestCacheEntry(const GURL& url) {
45     return LoadBasicRequest(storage_partition()->GetNetworkContext(), url,
46                             0 /* process_id */, 0 /* render_frame_id */,
47                             net::LOAD_ONLY_FROM_CACHE |
48                                 net::LOAD_SKIP_CACHE_VALIDATION) == net::OK;
49   }
50 
CreateCacheEntries(const std::set<GURL> & urls)51   void CreateCacheEntries(const std::set<GURL>& urls) {
52     for (auto& url : urls) {
53       ASSERT_EQ(net::OK, LoadBasicRequest(
54                              storage_partition()->GetNetworkContext(), url));
55     }
56 
57     // Wait for the entries to be written. There is no callback for this action
58     // being completed, only scheduled. Therefore, we need to continuously poll
59     // every |tiny_timeout|. However, wait at most |action_timeout| for this
60     // action to be performed.
61     base::Time start = base::Time::Now();
62     bool all_entries_written = false;
63 
64     while (base::Time::Now() - start < TestTimeouts::action_timeout()) {
65       all_entries_written = true;
66       for (auto& url : urls) {
67         if (!TestCacheEntry(url)) {
68           all_entries_written = false;
69           break;
70         }
71       }
72 
73       if (all_entries_written)
74         break;
75 
76       base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
77     }
78 
79     ASSERT_TRUE(all_entries_written)
80         << "Unable to write cache entries. The deletion test can't proceed.";
81   }
82 
CompareRemainingKeys(const std::set<GURL> & expected_urls,const std::set<GURL> & erase_urls)83   void CompareRemainingKeys(const std::set<GURL>& expected_urls,
84                             const std::set<GURL>& erase_urls) {
85     for (auto& url : expected_urls)
86       EXPECT_TRUE(TestCacheEntry(url));
87     for (auto& url : erase_urls)
88       EXPECT_FALSE(TestCacheEntry(url));
89   }
90 
DoneCallback(int value)91   void DoneCallback(int value) {
92     DCHECK_GE(value, 0);  // Negative values represent an error.
93     DCHECK_CURRENTLY_ON(BrowserThread::IO);
94     waitable_event_->Signal();
95   }
96 
WaitForTasksOnIOThread()97   void WaitForTasksOnIOThread() {
98     DCHECK_CURRENTLY_ON(BrowserThread::UI);
99     waitable_event_->Wait();
100   }
101 
storage_partition()102   StoragePartition* storage_partition() {
103     return BrowserContext::GetDefaultStoragePartition(browser_context());
104   }
105 
106  private:
browser_context()107   BrowserContext* browser_context() {
108     return shell()->web_contents()->GetBrowserContext();
109   }
110 
111   base::OnceCallback<void(int)> done_callback_;
112   std::unique_ptr<base::WaitableEvent> waitable_event_;
113 };
114 
115 // Tests that ConditionalCacheDeletionHelper only deletes those cache entries
116 // that match the condition.
IN_PROC_BROWSER_TEST_F(ConditionalCacheDeletionHelperBrowserTest,Condition)117 IN_PROC_BROWSER_TEST_F(ConditionalCacheDeletionHelperBrowserTest, Condition) {
118   std::set<GURL> urls = {
119       embedded_test_server()->GetURL("foo.com", "/title1.html"),
120       embedded_test_server()->GetURL("bar.com", "/title1.html"),
121       embedded_test_server()->GetURL("baz.com", "/title1.html"),
122       embedded_test_server()->GetURL("qux.com", "/title1.html")};
123 
124   CreateCacheEntries(urls);
125 
126   std::set<GURL> erase_urls = {
127       embedded_test_server()->GetURL("bar.com", "/title1.html"),
128       embedded_test_server()->GetURL("baz.com", "/title1.html"),
129   };
130 
131   for (auto& url : erase_urls)
132     urls.erase(url);
133 
134   network::mojom::ClearDataFilterPtr filter =
135       network::mojom::ClearDataFilter::New();
136   filter->type = network::mojom::ClearDataFilter::Type::DELETE_MATCHES;
137   for (auto& url : erase_urls)
138     filter->origins.push_back(url::Origin::Create(url));
139 
140   base::RunLoop run_loop;
141   storage_partition()->GetNetworkContext()->ClearHttpCache(
142       base::Time(), base::Time::Max(), std::move(filter),
143       run_loop.QuitClosure());
144   run_loop.Run();
145 
146   CompareRemainingKeys(urls, erase_urls);
147 }
148 
149 // Tests that ConditionalCacheDeletionHelper correctly constructs a condition
150 // for time and URL.
151 // crbug.com/1010102: fails on win.
152 #if defined(OS_WIN)
153 #define MAYBE_TimeAndURL DISABLED_TimeAndURL
154 #else
155 #define MAYBE_TimeAndURL TimeAndURL
156 #endif
IN_PROC_BROWSER_TEST_F(ConditionalCacheDeletionHelperBrowserTest,MAYBE_TimeAndURL)157 IN_PROC_BROWSER_TEST_F(ConditionalCacheDeletionHelperBrowserTest,
158                        MAYBE_TimeAndURL) {
159   // Create some entries.
160   std::set<GURL> urls = {
161       embedded_test_server()->GetURL("foo.com", "/title1.html"),
162       embedded_test_server()->GetURL("example.com", "/title1.html"),
163       embedded_test_server()->GetURL("bar.com", "/title1.html")};
164   CreateCacheEntries(urls);
165 
166   // Wait a short time after writing the entries.
167   // This assures that future entries will have timestamps strictly greater than
168   // the ones we just added.
169   base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
170   base::Time now = base::Time::Now();
171 
172   // Create a few more entries with a later timestamp.
173   std::set<GURL> newer_urls = {
174       embedded_test_server()->GetURL("foo.com", "/simple_page.html"),
175       embedded_test_server()->GetURL("example.com", "/title2.html"),
176       embedded_test_server()->GetURL("example.com", "/title3.html"),
177       embedded_test_server()->GetURL("example2.com", "/simple_page.html")};
178   CreateCacheEntries(newer_urls);
179 
180   // Create a condition for entries with the "http://example.com" origin
181   // created after waiting.
182   network::mojom::ClearDataFilterPtr filter =
183       network::mojom::ClearDataFilter::New();
184   filter->type = network::mojom::ClearDataFilter::Type::DELETE_MATCHES;
185   filter->origins.push_back(
186       url::Origin::Create(embedded_test_server()->GetURL("example.com", "/")));
187 
188   base::RunLoop run_loop;
189   storage_partition()->GetNetworkContext()->ClearHttpCache(
190       now, base::Time::Max(), std::move(filter), run_loop.QuitClosure());
191   run_loop.Run();
192 
193   // Expect that only "title2.html" and "title3.html" were deleted.
194   std::set<GURL> erase_urls = {
195       embedded_test_server()->GetURL("example.com", "/title2.html"),
196       embedded_test_server()->GetURL("example.com", "/title3.html"),
197   };
198 
199   for (auto& url : newer_urls)
200     urls.insert(url);
201 
202   for (auto& url : erase_urls)
203     urls.erase(url);
204 
205   CompareRemainingKeys(urls, erase_urls);
206 }
207 
208 }  // namespace content
209