OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/browsing_data/clear_site_data_throttle.h" | 5 #include "content/browser/browsing_data/clear_site_data_throttle.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
105 } | 105 } |
106 | 106 |
107 TestContentBrowserClient* GetContentBrowserClient() { return &test_client_; } | 107 TestContentBrowserClient* GetContentBrowserClient() { return &test_client_; } |
108 | 108 |
109 net::EmbeddedTestServer* https_server() { return https_server_.get(); } | 109 net::EmbeddedTestServer* https_server() { return https_server_.get(); } |
110 | 110 |
111 private: | 111 private: |
112 // Handles all requests. If the request url query contains a "header" key, | 112 // Handles all requests. If the request url query contains a "header" key, |
113 // responds with the "Clear-Site-Data" header of the corresponding value. | 113 // responds with the "Clear-Site-Data" header of the corresponding value. |
114 // If the query contains a "redirect" key, responds with a redirect to a url | 114 // If the query contains a "redirect" key, responds with a redirect to a url |
115 // given by the corresponding value. | 115 // given by the corresponding value. If the query contains a "html" key, |
116 // the response will contain a text/html content given by the corresponding | |
117 // value. If the query contains a "file" key, it will instead serve content | |
118 // from a file with the corresponding | |
116 // | 119 // |
117 // Example: "https://localhost/?header={}&redirect=example.com" will respond | 120 // Example: "https://localhost/?header={}&redirect=example.com" will respond |
118 // with headers | 121 // with headers |
119 // Clear-Site-Data: {} | 122 // Clear-Site-Data: {} |
120 // Location: example.com | 123 // Location: example.com |
124 // | |
125 // Example: "https://localhost/?html=<html><head></head><body></body></html>" | |
126 // will respond with the header | |
127 // Content-Type: text/html | |
128 // and content | |
129 // <html><head></head><body></body></html> | |
130 // | |
131 // Example: "https://localhost/?file=file.html | |
132 // will respond with the header | |
133 // Content-Type: text/html | |
134 // and content from the file content/test/data/file.html | |
121 std::unique_ptr<net::test_server::HttpResponse> HandleRequest( | 135 std::unique_ptr<net::test_server::HttpResponse> HandleRequest( |
122 const net::test_server::HttpRequest& request) { | 136 const net::test_server::HttpRequest& request) { |
123 std::unique_ptr<net::test_server::BasicHttpResponse> response( | 137 std::unique_ptr<net::test_server::BasicHttpResponse> response( |
124 new net::test_server::BasicHttpResponse()); | 138 new net::test_server::BasicHttpResponse()); |
125 | 139 |
126 std::string value; | 140 std::string value; |
127 if (net::GetValueForKeyInQuery(request.GetURL(), "header", &value)) | 141 if (net::GetValueForKeyInQuery(request.GetURL(), "header", &value)) |
128 response->AddCustomHeader("Clear-Site-Data", value); | 142 response->AddCustomHeader("Clear-Site-Data", value); |
129 | 143 |
130 if (net::GetValueForKeyInQuery(request.GetURL(), "redirect", &value)) { | 144 if (net::GetValueForKeyInQuery(request.GetURL(), "redirect", &value)) { |
131 response->set_code(net::HTTP_FOUND); | 145 response->set_code(net::HTTP_FOUND); |
132 response->AddCustomHeader("Location", value); | 146 response->AddCustomHeader("Location", value); |
133 } else { | 147 } else { |
134 response->set_code(net::HTTP_OK); | 148 response->set_code(net::HTTP_OK); |
135 } | 149 } |
136 | 150 |
151 if (net::GetValueForKeyInQuery(request.GetURL(), "html", &value)) { | |
152 response->set_content_type("text/html"); | |
153 response->set_content(value); | |
154 } | |
155 | |
156 if (net::GetValueForKeyInQuery(request.GetURL(), "file", &value)) { | |
157 base::FilePath path(GetTestFilePath("browsing_data", value.c_str())); | |
158 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); | |
159 EXPECT_TRUE(file.IsValid()); | |
160 int64_t length = file.GetLength(); | |
161 EXPECT_GE(length, 0); | |
162 std::unique_ptr<char[]> buffer(new char[length + 1]); | |
163 file.Read(0, buffer.get(), length); | |
164 buffer[length] = '\0'; | |
165 | |
166 if (path.Extension() == ".js") | |
167 response->set_content_type("application/javascript"); | |
168 else if (path.Extension() == ".html") | |
169 response->set_content_type("text/html"); | |
170 else | |
171 NOTREACHED(); | |
172 | |
173 response->set_content(buffer.get()); | |
174 } | |
175 | |
137 return std::move(response); | 176 return std::move(response); |
138 } | 177 } |
139 | 178 |
140 TestContentBrowserClient test_client_; | 179 TestContentBrowserClient test_client_; |
141 std::unique_ptr<net::EmbeddedTestServer> https_server_; | 180 std::unique_ptr<net::EmbeddedTestServer> https_server_; |
142 }; | 181 }; |
143 | 182 |
144 // Tests that the header is recognized on the beginning, in the middle, and on | 183 // Tests that the header is recognized on the beginning, in the middle, and on |
145 // the end of a redirect chain. Each of the three parts of the chain may or | 184 // the end of a navigation redirect chain. Each of the three parts of the chain |
146 // may not send the header, so there are 8 configurations to test. | 185 // may or may not send the header, so there are 8 configurations to test. |
147 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Redirect) { | 186 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, RedirectNavigation) { |
148 GURL base_urls[3] = { | 187 GURL base_urls[3] = { |
149 https_server()->GetURL("origin1.com", "/"), | 188 https_server()->GetURL("origin1.com", "/"), |
150 https_server()->GetURL("origin2.com", "/foo/bar"), | 189 https_server()->GetURL("origin2.com", "/foo/bar"), |
151 https_server()->GetURL("origin3.com", "/index.html"), | 190 https_server()->GetURL("origin3.com", "/index.html"), |
152 }; | 191 }; |
153 | 192 |
154 // Iterate through the configurations. URLs whose index is matched by the mask | 193 // Iterate through the configurations. URLs whose index is matched by the mask |
155 // will send the header, the others won't. | 194 // will send the header, the others won't. |
156 for (int mask = 0; mask < (1 << 3); ++mask) { | 195 for (int mask = 0; mask < (1 << 3); ++mask) { |
157 GURL urls[3]; | 196 GURL urls[3]; |
(...skipping 17 matching lines...) Expand all Loading... | |
175 // Navigate to the first url of the redirect chain. | 214 // Navigate to the first url of the redirect chain. |
176 NavigateToURL(shell(), urls[0]); | 215 NavigateToURL(shell(), urls[0]); |
177 | 216 |
178 // We reached the end of the redirect chain. | 217 // We reached the end of the redirect chain. |
179 EXPECT_EQ(urls[2], shell()->web_contents()->GetURL()); | 218 EXPECT_EQ(urls[2], shell()->web_contents()->GetURL()); |
180 | 219 |
181 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); | 220 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); |
182 } | 221 } |
183 } | 222 } |
184 | 223 |
224 // Tests that the header is recognized on the beginning, in the middle, and on | |
225 // the end of a resource load redirect chain. Each of the three parts of the | |
226 // chain may or may not send the header, so there are 8 configurations to test. | |
227 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, RedirectResourceLoad) { | |
228 GURL base_urls[3] = { | |
229 https_server()->GetURL("origin1.com", "/image.png"), | |
230 https_server()->GetURL("origin2.com", "/redirected-image.png"), | |
231 https_server()->GetURL("origin3.com", "/actual-image.png"), | |
232 }; | |
233 | |
234 // Iterate through the configurations. URLs whose index is matched by the mask | |
235 // will send the header, the others won't. | |
236 for (int mask = 0; mask < (1 << 3); ++mask) { | |
237 GURL urls[3]; | |
238 | |
239 // Set up the expectations. | |
240 for (int i = 0; i < 3; ++i) { | |
241 urls[i] = base_urls[i]; | |
242 if (mask & (1 << i)) | |
243 AddQuery(&urls[i], "header", kClearCookiesHeader); | |
244 | |
245 EXPECT_CALL(*GetContentBrowserClient(), | |
246 ClearSiteData(shell()->web_contents()->GetBrowserContext(), | |
247 url::Origin(urls[i]), _, _, _, _)) | |
248 .Times((mask & (1 << i)) ? 1 : 0); | |
249 } | |
250 | |
251 // Set up redirects between urls 0 --> 1 --> 2. | |
252 AddQuery(&urls[1], "redirect", urls[2].spec()); | |
253 AddQuery(&urls[0], "redirect", urls[1].spec()); | |
254 | |
255 // Navigate to a page that embeds "https://origin1.com/image.png" | |
256 // and observe the loading of that resource. | |
257 GURL page_with_image = https_server()->GetURL("origin4.com", "/index.html"); | |
258 std::string content_with_image = | |
259 "<html><head></head><body>" | |
260 "<img src=\"" + | |
261 urls[0].spec() + | |
262 "\" />" | |
263 "</body></html>"; | |
264 AddQuery(&page_with_image, "html", content_with_image); | |
265 NavigateToURL(shell(), page_with_image); | |
266 | |
267 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); | |
268 } | |
269 } | |
270 | |
185 // Tests that the Clear-Site-Data header is ignored for insecure origins. | 271 // Tests that the Clear-Site-Data header is ignored for insecure origins. |
186 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Insecure) { | 272 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, InsecureNavigation) { |
187 // ClearSiteData() should not be called on HTTP. | 273 // ClearSiteData() should not be called on HTTP. |
188 GURL url = embedded_test_server()->GetURL("example.com", "/"); | 274 GURL url = embedded_test_server()->GetURL("example.com", "/"); |
189 AddQuery(&url, "header", kClearCookiesHeader); | 275 AddQuery(&url, "header", kClearCookiesHeader); |
190 ASSERT_FALSE(url.SchemeIsCryptographic()); | 276 ASSERT_FALSE(url.SchemeIsCryptographic()); |
191 | 277 |
192 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | 278 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) |
193 .Times(0); | 279 .Times(0); |
194 | 280 |
195 NavigateToURL(shell(), url); | 281 NavigateToURL(shell(), url); |
196 } | 282 } |
197 | 283 |
284 // Tests that the Clear-Site-Data header is honored for secure resource loads | |
285 // and ignored for insecure ones. | |
286 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, | |
287 SecureAndInsecureResourceLoad) { | |
288 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | |
msramek
2016/10/13 14:26:03
Removing this as it's duplicated below.
| |
289 .Times(0); | |
290 | |
291 GURL insecure_image = | |
292 embedded_test_server()->GetURL("example.com", "/image.png"); | |
293 GURL secure_image = https_server()->GetURL("example.com", "/image.png"); | |
294 | |
295 ASSERT_TRUE(secure_image.SchemeIsCryptographic()); | |
296 ASSERT_FALSE(insecure_image.SchemeIsCryptographic()); | |
297 | |
298 AddQuery(&secure_image, "header", kClearCookiesHeader); | |
299 AddQuery(&insecure_image, "header", kClearCookiesHeader); | |
300 | |
301 std::string content_with_insecure_image = | |
302 "<html><head></head><body>" | |
303 "<img src=\"" + | |
304 insecure_image.spec() + | |
305 "\" />" | |
306 "</body></html>"; | |
307 | |
308 std::string content_with_secure_image = | |
309 "<html><head></head><body>" | |
310 "<img src=\"" + | |
311 secure_image.spec() + | |
312 "\" />" | |
313 "</body></html>"; | |
314 | |
315 // Test insecure resources. | |
316 GURL insecure_page = embedded_test_server()->GetURL("example.com", "/"); | |
317 GURL secure_page = https_server()->GetURL("example.com", "/"); | |
318 | |
319 AddQuery(&insecure_page, "html", content_with_insecure_image); | |
320 AddQuery(&secure_page, "html", content_with_insecure_image); | |
321 | |
322 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | |
323 .Times(0); | |
324 | |
325 // Insecure resource on an insecure page does not execute Clear-Site-Data. | |
326 NavigateToURL(shell(), insecure_page); | |
327 | |
328 // Insecure resource on a secure page does not execute Clear-Site-Data. | |
329 NavigateToURL(shell(), secure_page); | |
330 | |
331 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); | |
332 | |
333 // Test secure resources. | |
334 insecure_page = embedded_test_server()->GetURL("example.com", "/"); | |
335 secure_page = https_server()->GetURL("example.com", "/"); | |
336 | |
337 AddQuery(&insecure_page, "html", content_with_secure_image); | |
338 AddQuery(&secure_page, "html", content_with_secure_image); | |
339 | |
340 // Secure resource on an insecure page does execute Clear-Site-Data. | |
341 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | |
342 .Times(1); | |
343 | |
344 NavigateToURL(shell(), secure_page); | |
345 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); | |
346 | |
347 // Secure resource on a secure page does execute Clear-Site-Data. | |
348 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | |
349 .Times(1); | |
350 | |
351 NavigateToURL(shell(), secure_page); | |
352 } | |
353 | |
354 // Tests that the Clear-Site-Data header is ignored for service worker resource | |
355 // loads. Specifically, we test it on a cross-origin request; the header is | |
356 // ignored for same-origin requests as well, but there it isn't harmful. | |
357 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, | |
358 DISABLED_ServiceWorker) { | |
359 GURL origin1 = https_server()->GetURL("origin1.com", "/"); | |
360 GURL origin2 = https_server()->GetURL("origin2.com", "/"); | |
361 | |
362 // We will load |origin1| and make cross-origin requests for |origin2| from | |
363 // there. We need to inform the JS side about |origin2| using a query | |
364 // parameter; it cannot be hardcoded in the JS code, as the port number | |
365 // of the test server is chosen randomly. | |
mmenke
2016/09/26 12:59:17
The service worker should redirect requests to |or
msramek
2016/10/13 14:26:03
The first part of the test makes a fetch from |ori
| |
366 GURL url = origin1; | |
367 AddQuery(&url, "file", "worker_setup.html"); | |
368 AddQuery(&url, "other_origin", origin2.spec()); | |
369 | |
370 // Navigation to worker_setup.html will first request a resource with the | |
371 // Clear-Site-Data header. This is done for control - to make sure that | |
372 // the second part of this test will ignore the header because it's processed | |
373 // by a service worker, and not because the test is set up incorrectly. | |
374 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | |
375 .Times(1); | |
376 NavigateToURL(shell(), url); | |
377 | |
378 // After the resource was fetched, a service worker should have been | |
379 // installed and the page reloaded. The service worker will now serve a page | |
380 // containing an image, and fetching that image will again return the | |
381 // Clear-Site-Data header. This time, it should be ignored. | |
382 // | |
383 // Since the installation of a service worker is asynchronous, the JS side | |
384 // will inform us about it in its URL hash. | |
385 // TODO(msramek): Find a solution without polling. | |
386 while (true) { | |
387 if (shell()->web_contents()->GetLastCommittedURL().ref_piece() == | |
388 "service-worker-registered") { | |
389 break; | |
390 } | |
391 | |
392 base::RunLoop run_loop; | |
393 run_loop.RunUntilIdle(); | |
394 } | |
395 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); | |
396 | |
397 // The service worker should be now installed. Stop the server so that we | |
398 // lose connectivity and the service worker can handle requests. | |
399 ASSERT_TRUE(https_server()->ShutdownAndWaitUntilComplete()); | |
400 | |
401 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) | |
402 .Times(0); | |
403 | |
404 NavigateToURL(shell(), origin1); | |
405 while (true) { | |
406 if (shell()->web_contents()->GetVisibleURL().ref_piece() == | |
407 "service-worker-active") { | |
408 break; | |
409 } | |
410 | |
411 base::RunLoop run_loop; | |
412 run_loop.RunUntilIdle(); | |
413 } | |
414 } | |
415 | |
mmenke
2016/09/27 18:24:02
Should also check CORS and non-CORS cross-site res
msramek
2016/10/13 14:26:03
I chatted with mkwst@ about this as well.
While A
| |
198 // Tests that ClearSiteData() is called for the correct datatypes. | 416 // Tests that ClearSiteData() is called for the correct datatypes. |
199 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) { | 417 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) { |
200 GURL base_url = https_server()->GetURL("example.com", "/"); | 418 GURL base_url = https_server()->GetURL("example.com", "/"); |
201 | 419 |
202 struct TestCase { | 420 struct TestCase { |
203 const char* value; | 421 const char* value; |
204 bool remove_cookies; | 422 bool remove_cookies; |
205 bool remove_storage; | 423 bool remove_storage; |
206 bool remove_cache; | 424 bool remove_cache; |
207 } test_cases[] = { | 425 } test_cases[] = { |
(...skipping 17 matching lines...) Expand all Loading... | |
225 url::Origin(url), test_case.remove_cookies, | 443 url::Origin(url), test_case.remove_cookies, |
226 test_case.remove_storage, test_case.remove_cache, _)); | 444 test_case.remove_storage, test_case.remove_cache, _)); |
227 | 445 |
228 NavigateToURL(shell(), url); | 446 NavigateToURL(shell(), url); |
229 | 447 |
230 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); | 448 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); |
231 } | 449 } |
232 } | 450 } |
233 | 451 |
234 } // namespace content | 452 } // namespace content |
OLD | NEW |