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

Side by Side Diff: content/browser/cross_site_transfer_browsertest.cc

Issue 185053007: Split cross-process transfer tests out of SitePerProcessBrowserTest. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 9 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | content/browser/site_per_process_browsertest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
mmenke 2014/03/06 18:40:54 Since this is largely copying old code, should we
Charlie Reis 2014/03/06 18:50:23 Done.
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 "base/command_line.h"
6 #include "base/strings/stringprintf.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "content/browser/loader/resource_dispatcher_host_impl.h"
9 #include "content/browser/renderer_host/render_view_host_impl.h"
10 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/public/browser/navigation_entry.h"
12 #include "content/public/browser/notification_observer.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_types.h"
15 #include "content/public/browser/resource_dispatcher_host_delegate.h"
16 #include "content/public/browser/resource_throttle.h"
17 #include "content/public/browser/web_contents_observer.h"
18 #include "content/public/common/content_switches.h"
19 #include "content/public/common/url_constants.h"
20 #include "content/public/test/browser_test_utils.h"
21 #include "content/public/test/test_navigation_observer.h"
22 #include "content/public/test/test_utils.h"
23 #include "content/shell/browser/shell.h"
24 #include "content/shell/browser/shell_content_browser_client.h"
25 #include "content/shell/browser/shell_resource_dispatcher_host_delegate.h"
26 #include "content/test/content_browser_test.h"
27 #include "content/test/content_browser_test_utils.h"
28 #include "net/base/escape.h"
29 #include "net/dns/mock_host_resolver.h"
30 #include "net/url_request/url_request.h"
31 #include "net/url_request/url_request_status.h"
32
33 namespace content {
34
35 // Tracks a single request for a specified URL, and allows waiting until the
36 // request is destroyed, and then inspecting whether it completed successfully.
37 class TrackingResourceDispatcherHostDelegate
38 : public ShellResourceDispatcherHostDelegate {
39 public:
40 TrackingResourceDispatcherHostDelegate() : throttle_created_(false) {
41 }
42
43 virtual void RequestBeginning(
44 net::URLRequest* request,
45 ResourceContext* resource_context,
46 appcache::AppCacheService* appcache_service,
47 ResourceType::Type resource_type,
48 int child_id,
49 int route_id,
50 ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
51 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
52 ShellResourceDispatcherHostDelegate::RequestBeginning(
53 request, resource_context, appcache_service, resource_type, child_id,
54 route_id, throttles);
55 // Expect only a single request for the tracked url.
56 ASSERT_FALSE(throttle_created_);
57 // If this is a request for the tracked URL, add a throttle to track it.
58 if (request->url() == tracked_url_)
59 throttles->push_back(new TrackingThrottle(request, this));
60 }
61
62 // Starts tracking a URL. The request for previously tracked URL, if any,
63 // must have been made and deleted before calling this function.
64 void SetTrackedURL(const GURL& tracked_url) {
65 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
66 // Should not currently be tracking any URL.
67 ASSERT_FALSE(run_loop_);
68
69 // Create a RunLoop that will be stopped once the request for the tracked
70 // URL has been destroyed, to allow tracking the URL while also waiting for
71 // other events.
72 run_loop_.reset(new base::RunLoop());
73
74 BrowserThread::PostTask(
75 BrowserThread::IO, FROM_HERE,
76 base::Bind(
77 &TrackingResourceDispatcherHostDelegate::SetTrackedURLOnIOThread,
78 base::Unretained(this),
79 tracked_url));
80 }
81
82 // Waits until the tracked URL has been requests, and the request for it has
83 // been destroyed.
84 bool WaitForTrackedURLAndGetCompleted() {
85 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86 run_loop_->Run();
87 run_loop_.reset();
88 return tracked_request_completed_;
89 }
90
91 private:
92 // ResourceThrottle attached to request for the tracked URL. On destruction,
93 // passes the final URLRequestStatus back to the delegate.
94 class TrackingThrottle : public ResourceThrottle {
95 public:
96 TrackingThrottle(net::URLRequest* request,
97 TrackingResourceDispatcherHostDelegate* tracker)
98 : request_(request), tracker_(tracker) {
99 }
100
101 virtual ~TrackingThrottle() {
102 // If the request is deleted without being cancelled, its status will
103 // indicate it succeeded, so have to check if the request is still pending
104 // as well.
105 tracker_->OnTrackedRequestDestroyed(
106 !request_->is_pending() && request_->status().is_success());
107 }
108
109 // ResourceThrottle implementation:
110 virtual const char* GetNameForLogging() const OVERRIDE {
111 return "TrackingThrottle";
112 }
113
114 private:
115 net::URLRequest* request_;
116 TrackingResourceDispatcherHostDelegate* tracker_;
117
118 DISALLOW_COPY_AND_ASSIGN(TrackingThrottle);
119 };
120
121 void SetTrackedURLOnIOThread(const GURL& tracked_url) {
122 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
123 throttle_created_ = false;
124 tracked_url_ = tracked_url;
125 }
126
127 void OnTrackedRequestDestroyed(bool completed) {
128 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
129 tracked_request_completed_ = completed;
130 tracked_url_ = GURL();
131
132 BrowserThread::PostTask(
133 BrowserThread::UI, FROM_HERE, run_loop_->QuitClosure());
134 }
135
136 // These live on the IO thread.
137 GURL tracked_url_;
138 bool throttle_created_;
139
140 // This is created and destroyed on the UI thread, but stopped on the IO
141 // thread.
142 scoped_ptr<base::RunLoop> run_loop_;
143
144 // Set on the IO thread while |run_loop_| is non-NULL, read on the UI thread
145 // after deleting run_loop_.
146 bool tracked_request_completed_;
147
148 DISALLOW_COPY_AND_ASSIGN(TrackingResourceDispatcherHostDelegate);
149 };
150
151 // WebContentsDelegate that fails to open a URL when there's a request that
152 // needs to be transferred between renderers.
153 class NoTransferRequestDelegate : public WebContentsDelegate {
154 public:
155 NoTransferRequestDelegate() {}
156
157 virtual WebContents* OpenURLFromTab(WebContents* source,
158 const OpenURLParams& params) OVERRIDE {
159 bool is_transfer =
160 (params.transferred_global_request_id != GlobalRequestID());
161 if (is_transfer)
162 return NULL;
163 NavigationController::LoadURLParams load_url_params(params.url);
164 load_url_params.referrer = params.referrer;
165 load_url_params.frame_tree_node_id = params.frame_tree_node_id;
166 load_url_params.transition_type = params.transition;
167 load_url_params.extra_headers = params.extra_headers;
168 load_url_params.should_replace_current_entry =
169 params.should_replace_current_entry;
170 load_url_params.is_renderer_initiated = true;
171 source->GetController().LoadURLWithParams(load_url_params);
172 return source;
173 }
174
175 private:
176 DISALLOW_COPY_AND_ASSIGN(NoTransferRequestDelegate);
177 };
178
179 class CrossSiteTransferTest : public ContentBrowserTest {
180 public:
181 CrossSiteTransferTest() : old_delegate_(NULL) {
182 }
183
184 // ContentBrowserTest implementation:
185 virtual void SetUpOnMainThread() OVERRIDE {
186 BrowserThread::PostTask(
187 BrowserThread::IO, FROM_HERE,
188 base::Bind(
189 &CrossSiteTransferTest::InjectResourceDisptcherHostDelegate,
190 base::Unretained(this)));
191 }
192
193 virtual void TearDownOnMainThread() OVERRIDE {
194 BrowserThread::PostTask(
195 BrowserThread::IO, FROM_HERE,
196 base::Bind(
197 &CrossSiteTransferTest::RestoreResourceDisptcherHostDelegate,
198 base::Unretained(this)));
199 }
200
201 protected:
202 void NavigateToURLContentInitiated(Shell* window,
203 const GURL& url,
204 bool should_replace_current_entry,
205 bool should_wait_for_navigation) {
206 std::string script;
207 if (should_replace_current_entry)
208 script = base::StringPrintf("location.replace('%s')", url.spec().c_str());
209 else
210 script = base::StringPrintf("location.href = '%s'", url.spec().c_str());
211 TestNavigationObserver load_observer(shell()->web_contents(), 1);
212 bool result = ExecuteScript(window->web_contents(), script);
213 EXPECT_TRUE(result);
214 if (should_wait_for_navigation)
215 load_observer.Wait();
216 }
217
218 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
219 // Use --site-per-process to force process swaps for cross-site transfers.
220 command_line->AppendSwitch(switches::kSitePerProcess);
221 }
222
223 void InjectResourceDisptcherHostDelegate() {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
225 old_delegate_ = ResourceDispatcherHostImpl::Get()->delegate();
226 ResourceDispatcherHostImpl::Get()->SetDelegate(&tracking_delegate_);
227 }
228
229 void RestoreResourceDisptcherHostDelegate() {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
231 ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate_);
232 old_delegate_ = NULL;
233 }
234
235 TrackingResourceDispatcherHostDelegate& tracking_delegate() {
236 return tracking_delegate_;
237 }
238
239 private:
240 TrackingResourceDispatcherHostDelegate tracking_delegate_;
241 ResourceDispatcherHostDelegate* old_delegate_;
242 };
243
244 // Tests that the |should_replace_current_entry| flag persists correctly across
245 // request transfers that began with a cross-process navigation.
246 IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest,
247 ReplaceEntryCrossProcessThenTransfer) {
248 const NavigationController& controller =
249 shell()->web_contents()->GetController();
250 host_resolver()->AddRule("*", "127.0.0.1");
251 ASSERT_TRUE(test_server()->Start());
252
253 // These must all stay in scope with replace_host.
254 GURL::Replacements replace_host;
255 std::string a_com("A.com");
256 std::string b_com("B.com");
257
258 // Navigate to a starting URL, so there is a history entry to replace.
259 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
260 NavigateToURL(shell(), url1);
261
262 // Force all future navigations to transfer. Note that this includes same-site
263 // navigiations which may cause double process swaps (via OpenURL and then via
264 // transfer). This test intentionally exercises that case.
265 ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
266
267 // Navigate to a page on A.com with entry replacement. This navigation is
268 // cross-site, so the renderer will send it to the browser via OpenURL to give
269 // to a new process. It will then be transferred into yet another process due
270 // to the call above.
271 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
272 replace_host.SetHostStr(a_com);
273 url2 = url2.ReplaceComponents(replace_host);
274 // Used to make sure the request for url2 succeeds, and there was only one of
275 // them.
276 tracking_delegate().SetTrackedURL(url2);
277 NavigateToURLContentInitiated(shell(), url2, true, true);
278
279 // There should be one history entry. url2 should have replaced url1.
280 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
281 EXPECT_EQ(1, controller.GetEntryCount());
282 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
283 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
284 // Make sure the request succeeded.
285 EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
286
287 // Now navigate as before to a page on B.com, but normally (without
288 // replacement). This will still perform a double process-swap as above, via
289 // OpenURL and then transfer.
290 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
291 replace_host.SetHostStr(b_com);
292 url3 = url3.ReplaceComponents(replace_host);
293 // Used to make sure the request for url3 succeeds, and there was only one of
294 // them.
295 tracking_delegate().SetTrackedURL(url3);
296 NavigateToURLContentInitiated(shell(), url3, false, true);
297
298 // There should be two history entries. url2 should have replaced url1. url2
299 // should not have replaced url3.
300 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
301 EXPECT_EQ(2, controller.GetEntryCount());
302 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
303 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
304 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
305
306 // Make sure the request succeeded.
307 EXPECT_TRUE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
308 }
309
310 // Tests that the |should_replace_current_entry| flag persists correctly across
311 // request transfers that began with a content-initiated in-process
312 // navigation. This test is the same as the test above, except transfering from
313 // in-process.
314 IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest,
315 ReplaceEntryInProcessThenTranfers) {
316 const NavigationController& controller =
317 shell()->web_contents()->GetController();
318 ASSERT_TRUE(test_server()->Start());
319
320 // Navigate to a starting URL, so there is a history entry to replace.
321 GURL url = test_server()->GetURL("files/site_isolation/blank.html?1");
322 NavigateToURL(shell(), url);
323
324 // Force all future navigations to transfer. Note that this includes same-site
325 // navigiations which may cause double process swaps (via OpenURL and then via
326 // transfer). All navigations in this test are same-site, so it only swaps
327 // processes via request transfer.
328 ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
329
330 // Navigate in-process with entry replacement. It will then be transferred
331 // into a new one due to the call above.
332 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
333 NavigateToURLContentInitiated(shell(), url2, true, true);
334
335 // There should be one history entry. url2 should have replaced url1.
336 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
337 EXPECT_EQ(1, controller.GetEntryCount());
338 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
339 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
340
341 // Now navigate as before, but without replacement.
342 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3");
343 NavigateToURLContentInitiated(shell(), url3, false, true);
344
345 // There should be two history entries. url2 should have replaced url1. url2
346 // should not have replaced url3.
347 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
348 EXPECT_EQ(2, controller.GetEntryCount());
349 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
350 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
351 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
352 }
353
354 // Tests that the |should_replace_current_entry| flag persists correctly across
355 // request transfers that cross processes twice from renderer policy.
356 IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest,
357 ReplaceEntryCrossProcessTwice) {
358 const NavigationController& controller =
359 shell()->web_contents()->GetController();
360 host_resolver()->AddRule("*", "127.0.0.1");
361 ASSERT_TRUE(test_server()->Start());
362
363 // These must all stay in scope with replace_host.
364 GURL::Replacements replace_host;
365 std::string a_com("A.com");
366 std::string b_com("B.com");
367
368 // Navigate to a starting URL, so there is a history entry to replace.
369 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
370 NavigateToURL(shell(), url1);
371
372 // Navigate to a page on A.com which redirects to B.com with entry
373 // replacement. This will switch processes via OpenURL twice. First to A.com,
374 // and second in response to the server redirect to B.com. The second swap is
375 // also renderer-initiated via OpenURL because decidePolicyForNavigation is
376 // currently applied on redirects.
377 GURL url2b = test_server()->GetURL("files/site_isolation/blank.html?2");
378 replace_host.SetHostStr(b_com);
379 url2b = url2b.ReplaceComponents(replace_host);
380 GURL url2a = test_server()->GetURL(
381 "server-redirect?" + net::EscapeQueryParamValue(url2b.spec(), false));
382 replace_host.SetHostStr(a_com);
383 url2a = url2a.ReplaceComponents(replace_host);
384 NavigateToURLContentInitiated(shell(), url2a, true, true);
385
386 // There should be one history entry. url2b should have replaced url1.
387 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
388 EXPECT_EQ(1, controller.GetEntryCount());
389 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
390 EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
391
392 // Now repeat without replacement.
393 GURL url3b = test_server()->GetURL("files/site_isolation/blank.html?3");
394 replace_host.SetHostStr(b_com);
395 url3b = url3b.ReplaceComponents(replace_host);
396 GURL url3a = test_server()->GetURL(
397 "server-redirect?" + net::EscapeQueryParamValue(url3b.spec(), false));
398 replace_host.SetHostStr(a_com);
399 url3a = url3a.ReplaceComponents(replace_host);
400 NavigateToURLContentInitiated(shell(), url3a, false, true);
401
402 // There should be two history entries. url2b should have replaced url1. url2b
403 // should not have replaced url3b.
404 EXPECT_TRUE(controller.GetPendingEntry() == NULL);
405 EXPECT_EQ(2, controller.GetEntryCount());
406 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
407 EXPECT_EQ(url2b, controller.GetEntryAtIndex(0)->GetURL());
408 EXPECT_EQ(url3b, controller.GetEntryAtIndex(1)->GetURL());
409 }
410
411 // Tests that the request is destroyed when a cross process navigation is
412 // cancelled.
413 IN_PROC_BROWSER_TEST_F(CrossSiteTransferTest, NoLeakOnCrossSiteCancel) {
414 const NavigationController& controller =
415 shell()->web_contents()->GetController();
416 host_resolver()->AddRule("*", "127.0.0.1");
417 ASSERT_TRUE(test_server()->Start());
418
419 // These must all stay in scope with replace_host.
420 GURL::Replacements replace_host;
421 std::string a_com("A.com");
422 std::string b_com("B.com");
423
424 // Navigate to a starting URL, so there is a history entry to replace.
425 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1");
426 NavigateToURL(shell(), url1);
427
428 // Force all future navigations to transfer.
429 ShellContentBrowserClient::SetSwapProcessesForRedirect(true);
430
431 NoTransferRequestDelegate no_transfer_request_delegate;
432 WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
433 shell()->web_contents()->SetDelegate(&no_transfer_request_delegate);
434
435 // Navigate to a page on A.com with entry replacement. This navigation is
436 // cross-site, so the renderer will send it to the browser via OpenURL to give
437 // to a new process. It will then be transferred into yet another process due
438 // to the call above.
439 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2");
440 replace_host.SetHostStr(a_com);
441 url2 = url2.ReplaceComponents(replace_host);
442 // Used to make sure the second request is cancelled, and there is only one
443 // request for url2.
444 tracking_delegate().SetTrackedURL(url2);
445
446 // Don't wait for the navigation to complete, since that never happens in
447 // this case.
448 NavigateToURLContentInitiated(shell(), url2, false, false);
449
450 // There should be one history entry, with url1.
451 EXPECT_EQ(1, controller.GetEntryCount());
452 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
453 EXPECT_EQ(url1, controller.GetEntryAtIndex(0)->GetURL());
454
455 // Make sure the request for url2 did not complete.
456 EXPECT_FALSE(tracking_delegate().WaitForTrackedURLAndGetCompleted());
457
458 shell()->web_contents()->SetDelegate(old_delegate);
459 }
460
461 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/browser/site_per_process_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698