Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(553)

Side by Side Diff: content/browser/browsing_data/clear_site_data_throttle_browsertest.cc

Issue 2368923003: Support the Clear-Site-Data header on resource requests (Closed)
Patch Set: Addressed comments, improved the SW test Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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"
11 #include "base/command_line.h" 11 #include "base/command_line.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
12 #include "content/public/browser/content_browser_client.h" 14 #include "content/public/browser/content_browser_client.h"
13 #include "content/public/browser/web_contents.h" 15 #include "content/public/browser/web_contents.h"
14 #include "content/public/common/content_switches.h" 16 #include "content/public/common/content_switches.h"
17 #include "content/public/test/browser_test_utils.h"
15 #include "content/public/test/content_browser_test.h" 18 #include "content/public/test/content_browser_test.h"
16 #include "content/public/test/content_browser_test_utils.h" 19 #include "content/public/test/content_browser_test_utils.h"
17 #include "content/public/test/test_navigation_observer.h" 20 #include "content/public/test/test_navigation_observer.h"
18 #include "content/shell/browser/shell.h" 21 #include "content/shell/browser/shell.h"
19 #include "net/base/escape.h" 22 #include "net/base/escape.h"
20 #include "net/base/url_util.h" 23 #include "net/base/url_util.h"
21 #include "net/dns/mock_host_resolver.h" 24 #include "net/dns/mock_host_resolver.h"
22 #include "net/test/embedded_test_server/http_request.h" 25 #include "net/test/embedded_test_server/http_request.h"
23 #include "net/test/embedded_test_server/http_response.h" 26 #include "net/test/embedded_test_server/http_response.h"
24 #include "testing/gmock/include/gmock/gmock.h" 27 #include "testing/gmock/include/gmock/gmock.h"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 callback.Run(); 60 callback.Run();
58 } 61 }
59 }; 62 };
60 63
61 // Adds a key=value pair to the url's query. 64 // Adds a key=value pair to the url's query.
62 void AddQuery(GURL* url, const std::string& key, const std::string& value) { 65 void AddQuery(GURL* url, const std::string& key, const std::string& value) {
63 *url = GURL(url->spec() + (url->has_query() ? "&" : "?") + key + "=" + 66 *url = GURL(url->spec() + (url->has_query() ? "&" : "?") + key + "=" +
64 net::EscapeQueryParamValue(value, false)); 67 net::EscapeQueryParamValue(value, false));
65 } 68 }
66 69
70 // A helper function to synchronize with JS side of the tests. JS can append
71 // information to the loaded website's title and C++ will wait until that
72 // happens.
73 void WaitForTitle(const Shell* shell, const char* expected_title) {
74 base::string16 expected_title_16 = base::ASCIIToUTF16(expected_title);
75 TitleWatcher title_watcher(shell->web_contents(), expected_title_16);
76 ASSERT_EQ(expected_title_16, title_watcher.WaitAndGetTitle());
77 }
78
67 // A value of the Clear-Site-Data header that requests cookie deletion. Reused 79 // A value of the Clear-Site-Data header that requests cookie deletion. Reused
68 // in tests that need a valid header but do not depend on its value. 80 // in tests that need a valid header but do not depend on its value.
69 static const char* kClearCookiesHeader = "{ \"types\": [ \"cookies\" ] }"; 81 static const char* kClearCookiesHeader = "{ \"types\": [ \"cookies\" ] }";
70 82
71 } // namespace 83 } // namespace
72 84
73 class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest { 85 class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
74 public: 86 public:
75 void SetUpCommandLine(base::CommandLine* command_line) override { 87 void SetUpCommandLine(base::CommandLine* command_line) override {
76 ContentBrowserTest::SetUpCommandLine(command_line); 88 ContentBrowserTest::SetUpCommandLine(command_line);
(...skipping 25 matching lines...) Expand all
102 base::Bind(&ClearSiteDataThrottleBrowserTest::HandleRequest, 114 base::Bind(&ClearSiteDataThrottleBrowserTest::HandleRequest,
103 base::Unretained(this))); 115 base::Unretained(this)));
104 ASSERT_TRUE(https_server_->Start()); 116 ASSERT_TRUE(https_server_->Start());
105 } 117 }
106 118
107 TestContentBrowserClient* GetContentBrowserClient() { return &test_client_; } 119 TestContentBrowserClient* GetContentBrowserClient() { return &test_client_; }
108 120
109 net::EmbeddedTestServer* https_server() { return https_server_.get(); } 121 net::EmbeddedTestServer* https_server() { return https_server_.get(); }
110 122
111 private: 123 private:
112 // Handles all requests. If the request url query contains a "header" key, 124 // Handles all requests.
113 // responds with the "Clear-Site-Data" header of the corresponding value. 125 //
114 // If the query contains a "redirect" key, responds with a redirect to a url 126 // Supports the following <key>=<value> query parameters in the url:
115 // given by the corresponding value. 127 // <key>="header" responds with the header "Clear-Site-Data: <value>"
128 // <key>="redirect" responds with a redirect to the url <value>
129 // <key>="html" responds with a text/html content <value>
130 // <key>="file" responds with the content of file <value>
116 // 131 //
117 // Example: "https://localhost/?header={}&redirect=example.com" will respond 132 // Example: "https://localhost/?header={}&redirect=example.com" will respond
118 // with headers 133 // with headers
119 // Clear-Site-Data: {} 134 // Clear-Site-Data: {}
120 // Location: example.com 135 // Location: example.com
136 //
137 // Example: "https://localhost/?html=<html><head></head><body></body></html>"
138 // will respond with the header
139 // Content-Type: text/html
140 // and content
141 // <html><head></head><body></body></html>
142 //
143 // Example: "https://localhost/?file=file.html
144 // will respond with the header
145 // Content-Type: text/html
146 // and content from the file content/test/data/file.html
121 std::unique_ptr<net::test_server::HttpResponse> HandleRequest( 147 std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
122 const net::test_server::HttpRequest& request) { 148 const net::test_server::HttpRequest& request) {
123 std::unique_ptr<net::test_server::BasicHttpResponse> response( 149 std::unique_ptr<net::test_server::BasicHttpResponse> response(
124 new net::test_server::BasicHttpResponse()); 150 new net::test_server::BasicHttpResponse());
125 151
126 std::string value; 152 std::string value;
127 if (net::GetValueForKeyInQuery(request.GetURL(), "header", &value)) 153 if (net::GetValueForKeyInQuery(request.GetURL(), "header", &value))
128 response->AddCustomHeader("Clear-Site-Data", value); 154 response->AddCustomHeader("Clear-Site-Data", value);
129 155
130 if (net::GetValueForKeyInQuery(request.GetURL(), "redirect", &value)) { 156 if (net::GetValueForKeyInQuery(request.GetURL(), "redirect", &value)) {
131 response->set_code(net::HTTP_FOUND); 157 response->set_code(net::HTTP_FOUND);
132 response->AddCustomHeader("Location", value); 158 response->AddCustomHeader("Location", value);
133 } else { 159 } else {
134 response->set_code(net::HTTP_OK); 160 response->set_code(net::HTTP_OK);
135 } 161 }
136 162
163 if (net::GetValueForKeyInQuery(request.GetURL(), "html", &value)) {
164 response->set_content_type("text/html");
165 response->set_content(value);
166
167 // The "html" parameter is telling the server what to serve, and the XSS
168 // auditor will complain if its |value| contains JS code. Disable that
169 // protection.
170 response->AddCustomHeader("X-XSS-Protection", "0");
171 }
172
173 if (net::GetValueForKeyInQuery(request.GetURL(), "file", &value)) {
174 base::FilePath path(GetTestFilePath("browsing_data", value.c_str()));
175 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
176 EXPECT_TRUE(file.IsValid());
177 int64_t length = file.GetLength();
178 EXPECT_GE(length, 0);
179 std::unique_ptr<char[]> buffer(new char[length + 1]);
180 file.Read(0, buffer.get(), length);
181 buffer[length] = '\0';
182
183 if (path.Extension() == FILE_PATH_LITERAL(".js"))
184 response->set_content_type("application/javascript");
185 else if (path.Extension() == FILE_PATH_LITERAL(".html"))
186 response->set_content_type("text/html");
187 else
188 NOTREACHED();
189
190 response->set_content(buffer.get());
191 }
192
137 return std::move(response); 193 return std::move(response);
138 } 194 }
139 195
140 TestContentBrowserClient test_client_; 196 TestContentBrowserClient test_client_;
141 std::unique_ptr<net::EmbeddedTestServer> https_server_; 197 std::unique_ptr<net::EmbeddedTestServer> https_server_;
142 }; 198 };
143 199
144 // Tests that the header is recognized on the beginning, in the middle, and on 200 // 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 201 // 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. 202 // may or may not send the header, so there are 8 configurations to test.
147 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Redirect) { 203 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, RedirectNavigation) {
148 GURL base_urls[3] = { 204 GURL base_urls[3] = {
149 https_server()->GetURL("origin1.com", "/"), 205 https_server()->GetURL("origin1.com", "/"),
150 https_server()->GetURL("origin2.com", "/foo/bar"), 206 https_server()->GetURL("origin2.com", "/foo/bar"),
151 https_server()->GetURL("origin3.com", "/index.html"), 207 https_server()->GetURL("origin3.com", "/index.html"),
152 }; 208 };
153 209
154 // Iterate through the configurations. URLs whose index is matched by the mask 210 // Iterate through the configurations. URLs whose index is matched by the mask
155 // will send the header, the others won't. 211 // will send the header, the others won't.
156 for (int mask = 0; mask < (1 << 3); ++mask) { 212 for (int mask = 0; mask < (1 << 3); ++mask) {
157 GURL urls[3]; 213 GURL urls[3];
(...skipping 17 matching lines...) Expand all
175 // Navigate to the first url of the redirect chain. 231 // Navigate to the first url of the redirect chain.
176 NavigateToURL(shell(), urls[0]); 232 NavigateToURL(shell(), urls[0]);
177 233
178 // We reached the end of the redirect chain. 234 // We reached the end of the redirect chain.
179 EXPECT_EQ(urls[2], shell()->web_contents()->GetURL()); 235 EXPECT_EQ(urls[2], shell()->web_contents()->GetURL());
180 236
181 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); 237 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
182 } 238 }
183 } 239 }
184 240
241 // Tests that the header is recognized on the beginning, in the middle, and on
242 // the end of a resource load redirect chain. Each of the three parts of the
243 // chain may or may not send the header, so there are 8 configurations to test.
244 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, RedirectResourceLoad) {
245 GURL base_urls[3] = {
246 https_server()->GetURL("origin1.com", "/image.png"),
247 https_server()->GetURL("origin2.com", "/redirected-image.png"),
248 https_server()->GetURL("origin3.com", "/actual-image.png"),
249 };
250
251 // Iterate through the configurations. URLs whose index is matched by the mask
252 // will send the header, the others won't.
253 for (int mask = 0; mask < (1 << 3); ++mask) {
254 GURL urls[3];
255
256 // Set up the expectations.
257 for (int i = 0; i < 3; ++i) {
258 urls[i] = base_urls[i];
259 if (mask & (1 << i))
260 AddQuery(&urls[i], "header", kClearCookiesHeader);
261
262 EXPECT_CALL(*GetContentBrowserClient(),
263 ClearSiteData(shell()->web_contents()->GetBrowserContext(),
264 url::Origin(urls[i]), _, _, _, _))
265 .Times((mask & (1 << i)) ? 1 : 0);
266 }
267
268 // Set up redirects between urls 0 --> 1 --> 2.
269 AddQuery(&urls[1], "redirect", urls[2].spec());
270 AddQuery(&urls[0], "redirect", urls[1].spec());
271
272 // Navigate to a page that embeds "https://origin1.com/image.png"
273 // and observe the loading of that resource.
274 GURL page_with_image = https_server()->GetURL("origin4.com", "/index.html");
275 std::string content_with_image =
276 "<html><head></head><body>"
277 "<img src=\"" +
278 urls[0].spec() +
279 "\" />"
280 "</body></html>";
281 AddQuery(&page_with_image, "html", content_with_image);
282 NavigateToURL(shell(), page_with_image);
283
284 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
285 }
286 }
287
185 // Tests that the Clear-Site-Data header is ignored for insecure origins. 288 // Tests that the Clear-Site-Data header is ignored for insecure origins.
186 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Insecure) { 289 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, InsecureNavigation) {
187 // ClearSiteData() should not be called on HTTP. 290 // ClearSiteData() should not be called on HTTP.
188 GURL url = embedded_test_server()->GetURL("example.com", "/"); 291 GURL url = embedded_test_server()->GetURL("example.com", "/");
189 AddQuery(&url, "header", kClearCookiesHeader); 292 AddQuery(&url, "header", kClearCookiesHeader);
190 ASSERT_FALSE(url.SchemeIsCryptographic()); 293 ASSERT_FALSE(url.SchemeIsCryptographic());
191 294
192 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _)) 295 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
193 .Times(0); 296 .Times(0);
194 297
195 NavigateToURL(shell(), url); 298 NavigateToURL(shell(), url);
196 } 299 }
197 300
301 // Tests that the Clear-Site-Data header is honored for secure resource loads
302 // and ignored for insecure ones.
303 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest,
304 SecureAndInsecureResourceLoad) {
305 GURL insecure_image =
306 embedded_test_server()->GetURL("example.com", "/image.png");
307 GURL secure_image = https_server()->GetURL("example.com", "/image.png");
308
309 ASSERT_TRUE(secure_image.SchemeIsCryptographic());
310 ASSERT_FALSE(insecure_image.SchemeIsCryptographic());
311
312 AddQuery(&secure_image, "header", kClearCookiesHeader);
313 AddQuery(&insecure_image, "header", kClearCookiesHeader);
314
315 std::string content_with_insecure_image =
316 "<html><head></head><body>"
317 "<img src=\"" +
318 insecure_image.spec() +
319 "\" />"
320 "</body></html>";
321
322 std::string content_with_secure_image =
323 "<html><head></head><body>"
324 "<img src=\"" +
325 secure_image.spec() +
326 "\" />"
327 "</body></html>";
328
329 // Test insecure resources.
330 GURL insecure_page = embedded_test_server()->GetURL("example.com", "/");
331 GURL secure_page = https_server()->GetURL("example.com", "/");
332
333 AddQuery(&insecure_page, "html", content_with_insecure_image);
334 AddQuery(&secure_page, "html", content_with_insecure_image);
335
336 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
337 .Times(0);
338
339 // Insecure resource on an insecure page does not execute Clear-Site-Data.
340 NavigateToURL(shell(), insecure_page);
341
342 // Insecure resource on a secure page does not execute Clear-Site-Data.
343 NavigateToURL(shell(), secure_page);
344
345 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
346
347 // Test secure resources.
348 insecure_page = embedded_test_server()->GetURL("example.com", "/");
349 secure_page = https_server()->GetURL("example.com", "/");
350
351 AddQuery(&insecure_page, "html", content_with_secure_image);
352 AddQuery(&secure_page, "html", content_with_secure_image);
353
354 // Secure resource on an insecure page does execute Clear-Site-Data.
355 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
356 .Times(1);
357
358 NavigateToURL(shell(), secure_page);
359 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
360
361 // Secure resource on a secure page does execute Clear-Site-Data.
362 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
363 .Times(1);
364
365 NavigateToURL(shell(), secure_page);
366 }
367
368 // Tests that the Clear-Site-Data header is ignored for service worker resource
369 // loads.
370 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, ServiceWorker) {
371 GURL origin1 = https_server()->GetURL("origin1.com", "/");
372 GURL origin2 = https_server()->GetURL("origin2.com", "/");
373 GURL origin3 = https_server()->GetURL("origin3.com", "/");
374 GURL origin4 = https_server()->GetURL("origin4.com", "/");
375
376 // Navigation to worker_setup.html will install a service worker. Since
377 // the installation is asynchronous, the JS side will inform us about it in
378 // the page title.
379 GURL url = origin1;
380 AddQuery(&url, "file", "worker_setup.html");
381 NavigateToURL(shell(), url);
382 WaitForTitle(shell(), "service worker registered");
383
384 // The service worker will now serve a page containing several images, which
385 // the browser will try to fetch. The service worker will be instructed
386 // to handle some of the fetches itself, while others will be handled by
387 // the testing server. The setup is the following:
388 //
389 // origin1.com/resource (-> server; should respect header)
390 // origin1.com/resource_from_sw (-> service worker; should not respect header)
391 // origin2.com/resource_from_sw (-> service worker; should not respect header)
392 // origin2.com/resource (-> server; should respect header)
393 // origin3.com/resource_from_sw (-> service worker; should not respect header)
394 // origin3.com/resource_from_sw (-> service worker; should not respect header)
395 // origin4.com/resource (-> server; should respect header)
396 // origin4.com/resource (-> server; should respect header)
397 //
398 // |origin1| and |origin2| are used to test that there is no difference
399 // between same-origin and third-party fetches. Clear-Site-Data should be
400 // called once for each of these origins - caused by the "/resource" fetch,
401 // but not by the "/resource_from_sw" fetch. |origin3| and |origin4| prove
402 // that the number of calls is dependent on the number of network responses,
403 // i.e. that it isn't always 1 as in the case of |origin1| and |origin2|.
falken 2016/10/19 07:14:38 Nice documentation!
msramek 2016/10/19 12:20:12 Thanks!
404 EXPECT_CALL(*GetContentBrowserClient(),
405 ClearSiteData(_, url::Origin(origin1), _, _, _, _)).Times(1);
406 EXPECT_CALL(*GetContentBrowserClient(),
407 ClearSiteData(_, url::Origin(origin2), _, _, _, _)).Times(1);
408 EXPECT_CALL(*GetContentBrowserClient(),
409 ClearSiteData(_, url::Origin(origin3), _, _, _, _)).Times(0);
410 EXPECT_CALL(*GetContentBrowserClient(),
411 ClearSiteData(_, url::Origin(origin4), _, _, _, _)).Times(2);
412
413 url = https_server()->GetURL("origin1.com", "/anything-in-workers-scope");
414 AddQuery(&url, "origin1", origin1.spec());
415 AddQuery(&url, "origin2", origin2.spec());
416 AddQuery(&url, "origin3", origin3.spec());
417 AddQuery(&url, "origin4", origin4.spec());
418 NavigateToURL(shell(), url);
419 WaitForTitle(shell(), "done");
420 }
421
422 // Tests that Clear-Site-Data is only executed on a resource fetch
423 // if credentials are allowed in that fetch.
424 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Credentials) {
425 GURL page_template = https_server()->GetURL("origin1.com", "/");
426 GURL same_origin_resource =
427 https_server()->GetURL("origin1.com", "/resource");
428 GURL different_origin_resource =
429 https_server()->GetURL("origin2.com", "/resource");
430
431 AddQuery(&same_origin_resource, "header", kClearCookiesHeader);
432 AddQuery(&different_origin_resource, "header", kClearCookiesHeader);
433
434 const struct TestCase {
435 bool same_origin;
436 std::string credentials;
437 bool should_run;
438 } kTestCases[] = {
439 { true, "", false },
440 { true, "omit", false },
441 { true, "same-origin", true },
442 { true, "include", true },
443 { false, "", false },
444 { false, "omit", false },
445 { false, "same-origin", false },
446 { false, "include", true },
447 };
448
449 for (const TestCase& test_case : kTestCases) {
450 const GURL& resource = test_case.same_origin
451 ? same_origin_resource : different_origin_resource;
452 std::string credentials = test_case.credentials.empty()
453 ? "" : "credentials: '" + test_case.credentials + "'";
454
455 // Fetch a resource. Note that the script also handles fetch() error which
456 // might be thrown for third-party fetches because of missing
457 // Access-Control-Allow-Origin. However, that only affects the visibility
458 // of the response; the header will still be processed.
459 std::string content = base::StringPrintf(
460 "<html><head></head><body><script>"
461 "fetch('%s', {%s})"
462 ".then(function() { document.title = 'done'; })"
463 ".catch(function() { document.title = 'done'; })"
464 "</script></body></html>",
465 resource.spec().c_str(),
466 credentials.c_str());
467
468 GURL page = page_template;
469 AddQuery(&page, "html", content);
470
471 EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
472 .Times(test_case.should_run ? 1 : 0);
473 NavigateToURL(shell(), page);
474 WaitForTitle(shell(), "done");
475 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
476 }
477 }
478
198 // Tests that ClearSiteData() is called for the correct datatypes. 479 // Tests that ClearSiteData() is called for the correct datatypes.
199 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) { 480 IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) {
200 GURL base_url = https_server()->GetURL("example.com", "/"); 481 GURL base_url = https_server()->GetURL("example.com", "/");
201 482
202 struct TestCase { 483 struct TestCase {
203 const char* value; 484 const char* value;
204 bool remove_cookies; 485 bool remove_cookies;
205 bool remove_storage; 486 bool remove_storage;
206 bool remove_cache; 487 bool remove_cache;
207 } test_cases[] = { 488 } test_cases[] = {
(...skipping 17 matching lines...) Expand all
225 url::Origin(url), test_case.remove_cookies, 506 url::Origin(url), test_case.remove_cookies,
226 test_case.remove_storage, test_case.remove_cache, _)); 507 test_case.remove_storage, test_case.remove_cache, _));
227 508
228 NavigateToURL(shell(), url); 509 NavigateToURL(shell(), url);
229 510
230 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient()); 511 testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
231 } 512 }
232 } 513 }
233 514
234 } // namespace content 515 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698