Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |