Fix various typos, error handling for GN auto-roller.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_unittest.cc
blobd50994083b71e60041a080173b033964469a0011
1 // Copyright 2013 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.
5 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/cross_site_transferring_request.h"
15 #include "content/browser/frame_host/frame_navigation_entry.h"
16 #include "content/browser/frame_host/navigation_controller_impl.h"
17 #include "content/browser/frame_host/navigation_entry_impl.h"
18 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
19 #include "content/browser/frame_host/navigation_request.h"
20 #include "content/browser/frame_host/navigator.h"
21 #include "content/browser/frame_host/navigator_impl.h"
22 #include "content/browser/site_instance_impl.h"
23 #include "content/browser/web_contents/web_contents_impl.h"
24 #include "content/common/frame_messages.h"
25 #include "content/common/site_isolation_policy.h"
26 #include "content/common/ssl_status_serialization.h"
27 #include "content/common/view_messages.h"
28 #include "content/public/browser/navigation_details.h"
29 #include "content/public/browser/notification_registrar.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/web_contents_delegate.h"
33 #include "content/public/browser/web_contents_observer.h"
34 #include "content/public/common/content_switches.h"
35 #include "content/public/common/page_state.h"
36 #include "content/public/common/page_type.h"
37 #include "content/public/common/url_constants.h"
38 #include "content/public/test/mock_render_process_host.h"
39 #include "content/public/test/test_notification_tracker.h"
40 #include "content/public/test/test_utils.h"
41 #include "content/test/test_render_frame_host.h"
42 #include "content/test/test_render_view_host.h"
43 #include "content/test/test_web_contents.h"
44 #include "net/base/net_util.h"
45 #include "skia/ext/platform_canvas.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
49 using base::Time;
51 namespace {
53 // Creates an image with a 1x1 SkBitmap of the specified |color|.
54 gfx::Image CreateImage(SkColor color) {
55 SkBitmap bitmap;
56 bitmap.allocN32Pixels(1, 1);
57 bitmap.eraseColor(color);
58 return gfx::Image::CreateFrom1xBitmap(bitmap);
61 // Returns true if images |a| and |b| have the same pixel data.
62 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
63 // Assume that if the 1x bitmaps match, the images match.
64 SkBitmap a_bitmap = a.AsBitmap();
65 SkBitmap b_bitmap = b.AsBitmap();
67 if (a_bitmap.width() != b_bitmap.width() ||
68 a_bitmap.height() != b_bitmap.height()) {
69 return false;
71 SkAutoLockPixels a_bitmap_lock(a_bitmap);
72 SkAutoLockPixels b_bitmap_lock(b_bitmap);
73 return memcmp(a_bitmap.getPixels(),
74 b_bitmap.getPixels(),
75 a_bitmap.getSize()) == 0;
78 class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
79 public:
80 explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
81 : content::NavigationEntryScreenshotManager(owner),
82 encoding_screenshot_in_progress_(false) {
85 ~MockScreenshotManager() override {}
87 void TakeScreenshotFor(content::NavigationEntryImpl* entry) {
88 SkBitmap bitmap;
89 bitmap.allocPixels(SkImageInfo::Make(
90 1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
91 bitmap.eraseARGB(0, 0, 0, 0);
92 encoding_screenshot_in_progress_ = true;
93 OnScreenshotTaken(entry->GetUniqueID(), bitmap, content::READBACK_SUCCESS);
94 WaitUntilScreenshotIsReady();
97 int GetScreenshotCount() {
98 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
101 void WaitUntilScreenshotIsReady() {
102 if (!encoding_screenshot_in_progress_)
103 return;
104 message_loop_runner_ = new content::MessageLoopRunner;
105 message_loop_runner_->Run();
108 private:
109 // Overridden from content::NavigationEntryScreenshotManager:
110 void TakeScreenshotImpl(content::RenderViewHost* host,
111 content::NavigationEntryImpl* entry) override {}
113 void OnScreenshotSet(content::NavigationEntryImpl* entry) override {
114 encoding_screenshot_in_progress_ = false;
115 NavigationEntryScreenshotManager::OnScreenshotSet(entry);
116 if (message_loop_runner_.get())
117 message_loop_runner_->Quit();
120 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
121 bool encoding_screenshot_in_progress_;
123 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
126 } // namespace
128 namespace content {
130 // TimeSmoother tests ----------------------------------------------------------
132 // With no duplicates, GetSmoothedTime should be the identity
133 // function.
134 TEST(TimeSmoother, Basic) {
135 NavigationControllerImpl::TimeSmoother smoother;
136 for (int64 i = 1; i < 1000; ++i) {
137 base::Time t = base::Time::FromInternalValue(i);
138 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
142 // With a single duplicate and timestamps thereafter increasing by one
143 // microsecond, the smoothed time should always be one behind.
144 TEST(TimeSmoother, SingleDuplicate) {
145 NavigationControllerImpl::TimeSmoother smoother;
146 base::Time t = base::Time::FromInternalValue(1);
147 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
148 for (int64 i = 1; i < 1000; ++i) {
149 base::Time expected_t = base::Time::FromInternalValue(i + 1);
150 t = base::Time::FromInternalValue(i);
151 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
155 // With k duplicates and timestamps thereafter increasing by one
156 // microsecond, the smoothed time should always be k behind.
157 TEST(TimeSmoother, ManyDuplicates) {
158 const int64 kNumDuplicates = 100;
159 NavigationControllerImpl::TimeSmoother smoother;
160 base::Time t = base::Time::FromInternalValue(1);
161 for (int64 i = 0; i < kNumDuplicates; ++i) {
162 base::Time expected_t = base::Time::FromInternalValue(i + 1);
163 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
165 for (int64 i = 1; i < 1000; ++i) {
166 base::Time expected_t =
167 base::Time::FromInternalValue(i + kNumDuplicates);
168 t = base::Time::FromInternalValue(i);
169 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
173 // If the clock jumps far back enough after a run of duplicates, it
174 // should immediately jump to that value.
175 TEST(TimeSmoother, ClockBackwardsJump) {
176 const int64 kNumDuplicates = 100;
177 NavigationControllerImpl::TimeSmoother smoother;
178 base::Time t = base::Time::FromInternalValue(1000);
179 for (int64 i = 0; i < kNumDuplicates; ++i) {
180 base::Time expected_t = base::Time::FromInternalValue(i + 1000);
181 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
183 t = base::Time::FromInternalValue(500);
184 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
187 // NavigationControllerTest ----------------------------------------------------
189 class NavigationControllerTest
190 : public RenderViewHostImplTestHarness,
191 public WebContentsObserver {
192 public:
193 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
196 void SetUp() override {
197 RenderViewHostImplTestHarness::SetUp();
198 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents();
199 ASSERT_TRUE(web_contents); // The WebContents should be created by now.
200 WebContentsObserver::Observe(web_contents);
203 // WebContentsObserver:
204 void DidStartNavigationToPendingEntry(
205 const GURL& url,
206 NavigationController::ReloadType reload_type) override {
207 navigated_url_ = url;
210 void NavigationEntryCommitted(
211 const LoadCommittedDetails& load_details) override {
212 navigation_entry_committed_counter_++;
215 const GURL& navigated_url() const {
216 return navigated_url_;
219 NavigationControllerImpl& controller_impl() {
220 return static_cast<NavigationControllerImpl&>(controller());
223 bool HasNavigationRequest() {
224 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
225 switches::kEnableBrowserSideNavigation)) {
226 return contents()->GetFrameTree()->root()->navigation_request() !=
227 nullptr;
229 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID)
230 != nullptr;
233 const GURL GetLastNavigationURL() {
234 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
235 switches::kEnableBrowserSideNavigation)) {
236 NavigationRequest* navigation_request =
237 contents()->GetFrameTree()->root()->navigation_request();
238 CHECK(navigation_request);
239 return navigation_request->common_params().url;
241 const IPC::Message* message =
242 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
243 CHECK(message);
244 base::Tuple<CommonNavigationParams, StartNavigationParams,
245 RequestNavigationParams> nav_params;
246 FrameMsg_Navigate::Read(message, &nav_params);
247 return base::get<0>(nav_params).url;
250 protected:
251 GURL navigated_url_;
252 size_t navigation_entry_committed_counter_;
255 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
256 NavigationController* controller) {
257 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED,
258 Source<NavigationController>(controller));
259 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED,
260 Source<NavigationController>(controller));
263 class TestWebContentsDelegate : public WebContentsDelegate {
264 public:
265 explicit TestWebContentsDelegate() :
266 navigation_state_change_count_(0),
267 repost_form_warning_count_(0) {}
269 int navigation_state_change_count() {
270 return navigation_state_change_count_;
273 int repost_form_warning_count() {
274 return repost_form_warning_count_;
277 // Keep track of whether the tab has notified us of a navigation state change.
278 void NavigationStateChanged(WebContents* source,
279 InvalidateTypes changed_flags) override {
280 navigation_state_change_count_++;
283 void ShowRepostFormWarningDialog(WebContents* source) override {
284 repost_form_warning_count_++;
287 private:
288 // The number of times NavigationStateChanged has been called.
289 int navigation_state_change_count_;
291 // The number of times ShowRepostFormWarningDialog() was called.
292 int repost_form_warning_count_;
295 // -----------------------------------------------------------------------------
297 TEST_F(NavigationControllerTest, Defaults) {
298 NavigationControllerImpl& controller = controller_impl();
300 EXPECT_FALSE(controller.GetPendingEntry());
301 EXPECT_FALSE(controller.GetVisibleEntry());
302 EXPECT_FALSE(controller.GetLastCommittedEntry());
303 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
304 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
305 EXPECT_EQ(controller.GetEntryCount(), 0);
306 EXPECT_FALSE(controller.CanGoBack());
307 EXPECT_FALSE(controller.CanGoForward());
310 TEST_F(NavigationControllerTest, GoToOffset) {
311 NavigationControllerImpl& controller = controller_impl();
312 TestNotificationTracker notifications;
313 RegisterForAllNavNotifications(&notifications, &controller);
315 const int kNumUrls = 5;
316 std::vector<GURL> urls(kNumUrls);
317 for (int i = 0; i < kNumUrls; ++i) {
318 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
321 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls[0], true);
322 main_test_rfh()->PrepareForCommit();
323 main_test_rfh()->SendNavigate(0, 0, true, urls[0]);
324 EXPECT_EQ(1U, navigation_entry_committed_counter_);
325 navigation_entry_committed_counter_ = 0;
326 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
327 EXPECT_FALSE(controller.CanGoBack());
328 EXPECT_FALSE(controller.CanGoForward());
329 EXPECT_FALSE(controller.CanGoToOffset(1));
331 for (int i = 1; i <= 4; ++i) {
332 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls[i], true);
333 main_test_rfh()->PrepareForCommit();
334 main_test_rfh()->SendNavigate(i, 0, true, urls[i]);
335 EXPECT_EQ(1U, navigation_entry_committed_counter_);
336 navigation_entry_committed_counter_ = 0;
337 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
338 EXPECT_TRUE(controller.CanGoToOffset(-i));
339 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
340 EXPECT_FALSE(controller.CanGoToOffset(1));
343 // We have loaded 5 pages, and are currently at the last-loaded page.
344 int url_index = 4;
346 enum Tests {
347 GO_TO_MIDDLE_PAGE = -2,
348 GO_FORWARDS = 1,
349 GO_BACKWARDS = -1,
350 GO_TO_BEGINNING = -2,
351 GO_TO_END = 4,
352 NUM_TESTS = 5,
355 const int test_offsets[NUM_TESTS] = {
356 GO_TO_MIDDLE_PAGE,
357 GO_FORWARDS,
358 GO_BACKWARDS,
359 GO_TO_BEGINNING,
360 GO_TO_END
363 for (int test = 0; test < NUM_TESTS; ++test) {
364 int offset = test_offsets[test];
365 controller.GoToOffset(offset);
366 int entry_id = controller.GetPendingEntry()->GetUniqueID();
367 url_index += offset;
368 // Check that the GoToOffset will land on the expected page.
369 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
370 main_test_rfh()->PrepareForCommit();
371 main_test_rfh()->SendNavigate(url_index, entry_id, false, urls[url_index]);
372 EXPECT_EQ(1U, navigation_entry_committed_counter_);
373 navigation_entry_committed_counter_ = 0;
374 // Check that we can go to any valid offset into the history.
375 for (size_t j = 0; j < urls.size(); ++j)
376 EXPECT_TRUE(controller.CanGoToOffset(j - url_index));
377 // Check that we can't go beyond the beginning or end of the history.
378 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1)));
379 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index));
383 TEST_F(NavigationControllerTest, LoadURL) {
384 NavigationControllerImpl& controller = controller_impl();
385 TestNotificationTracker notifications;
386 RegisterForAllNavNotifications(&notifications, &controller);
388 const GURL url1("http://foo1");
389 const GURL url2("http://foo2");
391 controller.LoadURL(
392 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
393 int entry_id = controller.GetPendingEntry()->GetUniqueID();
394 // Creating a pending notification should not have issued any of the
395 // notifications we're listening for.
396 EXPECT_EQ(0U, notifications.size());
398 // The load should now be pending.
399 EXPECT_EQ(controller.GetEntryCount(), 0);
400 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
401 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
402 EXPECT_FALSE(controller.GetLastCommittedEntry());
403 ASSERT_TRUE(controller.GetPendingEntry());
404 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
405 EXPECT_FALSE(controller.CanGoBack());
406 EXPECT_FALSE(controller.CanGoForward());
407 EXPECT_EQ(contents()->GetMaxPageID(), -1);
409 // Neither the timestamp nor the status code should have been set yet.
410 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
411 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
413 // We should have gotten no notifications from the preceeding checks.
414 EXPECT_EQ(0U, notifications.size());
416 main_test_rfh()->PrepareForCommit();
417 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
418 EXPECT_EQ(1U, navigation_entry_committed_counter_);
419 navigation_entry_committed_counter_ = 0;
421 // The load should now be committed.
422 EXPECT_EQ(controller.GetEntryCount(), 1);
423 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
424 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
425 EXPECT_TRUE(controller.GetLastCommittedEntry());
426 EXPECT_FALSE(controller.GetPendingEntry());
427 ASSERT_TRUE(controller.GetVisibleEntry());
428 EXPECT_FALSE(controller.CanGoBack());
429 EXPECT_FALSE(controller.CanGoForward());
430 EXPECT_EQ(contents()->GetMaxPageID(), 0);
431 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
433 // The timestamp should have been set.
434 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
436 // Load another...
437 controller.LoadURL(
438 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
439 entry_id = controller.GetPendingEntry()->GetUniqueID();
441 // The load should now be pending.
442 EXPECT_EQ(controller.GetEntryCount(), 1);
443 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
444 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
445 EXPECT_TRUE(controller.GetLastCommittedEntry());
446 ASSERT_TRUE(controller.GetPendingEntry());
447 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
448 // TODO(darin): maybe this should really be true?
449 EXPECT_FALSE(controller.CanGoBack());
450 EXPECT_FALSE(controller.CanGoForward());
451 EXPECT_EQ(contents()->GetMaxPageID(), 0);
453 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
455 // Simulate the beforeunload ack for the cross-site transition, and then the
456 // commit.
457 main_test_rfh()->PrepareForCommit();
458 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id, true, url2);
459 EXPECT_EQ(1U, navigation_entry_committed_counter_);
460 navigation_entry_committed_counter_ = 0;
462 // The load should now be committed.
463 EXPECT_EQ(controller.GetEntryCount(), 2);
464 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
465 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
466 EXPECT_TRUE(controller.GetLastCommittedEntry());
467 EXPECT_FALSE(controller.GetPendingEntry());
468 ASSERT_TRUE(controller.GetVisibleEntry());
469 EXPECT_TRUE(controller.CanGoBack());
470 EXPECT_FALSE(controller.CanGoForward());
471 EXPECT_EQ(contents()->GetMaxPageID(), 1);
473 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
476 namespace {
478 base::Time GetFixedTime(base::Time time) {
479 return time;
482 } // namespace
484 TEST_F(NavigationControllerTest, LoadURLSameTime) {
485 NavigationControllerImpl& controller = controller_impl();
486 TestNotificationTracker notifications;
487 RegisterForAllNavNotifications(&notifications, &controller);
489 // Set the clock to always return a timestamp of 1.
490 controller.SetGetTimestampCallbackForTest(
491 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
493 const GURL url1("http://foo1");
494 const GURL url2("http://foo2");
496 controller.LoadURL(
497 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
498 int entry_id = controller.GetPendingEntry()->GetUniqueID();
500 main_test_rfh()->PrepareForCommit();
501 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
502 EXPECT_EQ(1U, navigation_entry_committed_counter_);
503 navigation_entry_committed_counter_ = 0;
505 // Load another...
506 controller.LoadURL(
507 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
508 entry_id = controller.GetPendingEntry()->GetUniqueID();
510 // Simulate the beforeunload ack for the cross-site transition, and then the
511 // commit.
512 main_test_rfh()->PrepareForCommit();
513 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id, true, url2);
514 EXPECT_EQ(1U, navigation_entry_committed_counter_);
515 navigation_entry_committed_counter_ = 0;
517 // The two loads should now be committed.
518 ASSERT_EQ(controller.GetEntryCount(), 2);
520 // Timestamps should be distinct despite the clock returning the
521 // same value.
522 EXPECT_EQ(1u,
523 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
524 EXPECT_EQ(2u,
525 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
528 void CheckNavigationEntryMatchLoadParams(
529 NavigationController::LoadURLParams& load_params,
530 NavigationEntryImpl* entry) {
531 EXPECT_EQ(load_params.url, entry->GetURL());
532 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
533 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
534 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
535 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
537 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
538 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
539 if (!load_params.virtual_url_for_data_url.is_empty()) {
540 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
542 if (NavigationController::UA_OVERRIDE_INHERIT !=
543 load_params.override_user_agent) {
544 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
545 load_params.override_user_agent);
546 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
548 EXPECT_EQ(load_params.browser_initiated_post_data.get(),
549 entry->GetBrowserInitiatedPostData());
550 EXPECT_EQ(load_params.transferred_global_request_id,
551 entry->transferred_global_request_id());
554 TEST_F(NavigationControllerTest, LoadURLWithParams) {
555 NavigationControllerImpl& controller = controller_impl();
557 NavigationController::LoadURLParams load_params(GURL("http://foo"));
558 load_params.referrer =
559 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
560 load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
561 load_params.extra_headers = "content-type: text/plain";
562 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
563 load_params.is_renderer_initiated = true;
564 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
565 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
567 controller.LoadURLWithParams(load_params);
568 NavigationEntryImpl* entry = controller.GetPendingEntry();
570 // The timestamp should not have been set yet.
571 ASSERT_TRUE(entry);
572 EXPECT_TRUE(entry->GetTimestamp().is_null());
574 CheckNavigationEntryMatchLoadParams(load_params, entry);
577 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
578 NavigationControllerImpl& controller = controller_impl();
580 NavigationController::LoadURLParams load_params(
581 GURL("data:text/html,dataurl"));
582 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
583 load_params.base_url_for_data_url = GURL("http://foo");
584 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
585 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
587 controller.LoadURLWithParams(load_params);
588 NavigationEntryImpl* entry = controller.GetPendingEntry();
590 CheckNavigationEntryMatchLoadParams(load_params, entry);
593 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
594 NavigationControllerImpl& controller = controller_impl();
596 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
597 load_params.transition_type = ui::PAGE_TRANSITION_TYPED;
598 load_params.load_type =
599 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
600 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
603 const unsigned char* raw_data =
604 reinterpret_cast<const unsigned char*>("d\n\0a2");
605 const int length = 5;
606 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
607 scoped_refptr<base::RefCountedBytes> data =
608 base::RefCountedBytes::TakeVector(&post_data_vector);
609 load_params.browser_initiated_post_data = data.get();
611 controller.LoadURLWithParams(load_params);
612 NavigationEntryImpl* entry = controller.GetPendingEntry();
614 CheckNavigationEntryMatchLoadParams(load_params, entry);
617 // Tests what happens when the same page is loaded again. Should not create a
618 // new session history entry. This is what happens when you press enter in the
619 // URL bar to reload: a pending entry is created and then it is discarded when
620 // the load commits (because WebCore didn't actually make a new entry).
621 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
622 NavigationControllerImpl& controller = controller_impl();
623 TestNotificationTracker notifications;
624 RegisterForAllNavNotifications(&notifications, &controller);
626 const GURL url1("http://foo1");
628 controller.LoadURL(
629 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
630 int entry_id = controller.GetPendingEntry()->GetUniqueID();
631 EXPECT_EQ(0U, notifications.size());
632 main_test_rfh()->PrepareForCommit();
633 main_test_rfh()->SendNavigateWithTransition(
634 0, entry_id, true, url1, ui::PAGE_TRANSITION_TYPED);
635 EXPECT_EQ(1U, navigation_entry_committed_counter_);
636 navigation_entry_committed_counter_ = 0;
638 ASSERT_TRUE(controller.GetVisibleEntry());
639 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
640 EXPECT_FALSE(timestamp.is_null());
642 controller.LoadURL(
643 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
644 entry_id = controller.GetPendingEntry()->GetUniqueID();
645 EXPECT_EQ(0U, notifications.size());
646 main_test_rfh()->PrepareForCommit();
647 main_test_rfh()->SendNavigateWithTransition(
648 0, entry_id, false, url1, ui::PAGE_TRANSITION_TYPED);
649 EXPECT_EQ(1U, navigation_entry_committed_counter_);
650 navigation_entry_committed_counter_ = 0;
652 // We should not have produced a new session history entry.
653 EXPECT_EQ(controller.GetEntryCount(), 1);
654 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
655 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
656 EXPECT_TRUE(controller.GetLastCommittedEntry());
657 EXPECT_FALSE(controller.GetPendingEntry());
658 ASSERT_TRUE(controller.GetVisibleEntry());
659 EXPECT_FALSE(controller.CanGoBack());
660 EXPECT_FALSE(controller.CanGoForward());
662 // The timestamp should have been updated.
664 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
665 // EXPECT_GT once we guarantee that timestamps are unique.
666 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
669 // Load the same page twice, once as a GET and once as a POST.
670 // We should update the post state on the NavigationEntry.
671 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
672 NavigationControllerImpl& controller = controller_impl();
673 TestNotificationTracker notifications;
674 RegisterForAllNavNotifications(&notifications, &controller);
676 const GURL url1("http://foo1");
678 controller.LoadURL(
679 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
680 FrameHostMsg_DidCommitProvisionalLoad_Params params;
681 params.page_id = 0;
682 params.nav_entry_id = controller.GetPendingEntry()->GetUniqueID();
683 params.did_create_new_entry = true;
684 params.url = url1;
685 params.transition = ui::PAGE_TRANSITION_TYPED;
686 params.is_post = true;
687 params.post_id = 123;
688 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
689 main_test_rfh()->PrepareForCommit();
690 main_test_rfh()->SendNavigateWithParams(&params);
692 // The post data should be visible.
693 NavigationEntry* entry = controller.GetVisibleEntry();
694 ASSERT_TRUE(entry);
695 EXPECT_TRUE(entry->GetHasPostData());
696 EXPECT_EQ(entry->GetPostID(), 123);
698 controller.LoadURL(
699 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
700 main_test_rfh()->PrepareForCommit();
701 main_test_rfh()->SendNavigateWithTransition(
702 0, controller.GetPendingEntry()->GetUniqueID(),
703 false, url1, ui::PAGE_TRANSITION_TYPED);
705 // We should not have produced a new session history entry.
706 ASSERT_EQ(controller.GetVisibleEntry(), entry);
708 // The post data should have been cleared due to the GET.
709 EXPECT_FALSE(entry->GetHasPostData());
710 EXPECT_EQ(entry->GetPostID(), 0);
713 // Tests loading a URL but discarding it before the load commits.
714 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
715 NavigationControllerImpl& controller = controller_impl();
716 TestNotificationTracker notifications;
717 RegisterForAllNavNotifications(&notifications, &controller);
719 const GURL url1("http://foo1");
720 const GURL url2("http://foo2");
722 controller.LoadURL(
723 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
724 int entry_id = controller.GetPendingEntry()->GetUniqueID();
725 EXPECT_EQ(0U, notifications.size());
726 main_test_rfh()->PrepareForCommit();
727 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
728 EXPECT_EQ(1U, navigation_entry_committed_counter_);
729 navigation_entry_committed_counter_ = 0;
731 ASSERT_TRUE(controller.GetVisibleEntry());
732 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
733 EXPECT_FALSE(timestamp.is_null());
735 controller.LoadURL(
736 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
737 controller.DiscardNonCommittedEntries();
738 EXPECT_EQ(0U, notifications.size());
740 // Should not have produced a new session history entry.
741 EXPECT_EQ(controller.GetEntryCount(), 1);
742 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
743 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
744 EXPECT_TRUE(controller.GetLastCommittedEntry());
745 EXPECT_FALSE(controller.GetPendingEntry());
746 ASSERT_TRUE(controller.GetVisibleEntry());
747 EXPECT_FALSE(controller.CanGoBack());
748 EXPECT_FALSE(controller.CanGoForward());
750 // Timestamp should not have changed.
751 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
754 // Tests navigations that come in unrequested. This happens when the user
755 // navigates from the web page, and here we test that there is no pending entry.
756 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
757 NavigationControllerImpl& controller = controller_impl();
758 TestNotificationTracker notifications;
759 RegisterForAllNavNotifications(&notifications, &controller);
761 // First make an existing committed entry.
762 const GURL kExistingURL1("http://eh");
763 controller.LoadURL(
764 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
765 int entry_id = controller.GetPendingEntry()->GetUniqueID();
766 main_test_rfh()->PrepareForCommit();
767 main_test_rfh()->SendNavigate(0, entry_id, true, kExistingURL1);
768 EXPECT_EQ(1U, navigation_entry_committed_counter_);
769 navigation_entry_committed_counter_ = 0;
771 // Do a new navigation without making a pending one.
772 const GURL kNewURL("http://see");
773 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL);
775 // There should no longer be any pending entry, and the second navigation we
776 // just made should be committed.
777 EXPECT_EQ(1U, navigation_entry_committed_counter_);
778 navigation_entry_committed_counter_ = 0;
779 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
780 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
781 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
784 // Tests navigating to a new URL when there is a new pending navigation that is
785 // not the one that just loaded. This will happen if the user types in a URL to
786 // somewhere slow, and then navigates the current page before the typed URL
787 // commits.
788 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
789 NavigationControllerImpl& controller = controller_impl();
790 TestNotificationTracker notifications;
791 RegisterForAllNavNotifications(&notifications, &controller);
793 // First make an existing committed entry.
794 const GURL kExistingURL1("http://eh");
795 controller.LoadURL(
796 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
797 int entry_id = controller.GetPendingEntry()->GetUniqueID();
798 main_test_rfh()->PrepareForCommit();
799 main_test_rfh()->SendNavigate(0, entry_id, true, kExistingURL1);
800 EXPECT_EQ(1U, navigation_entry_committed_counter_);
801 navigation_entry_committed_counter_ = 0;
803 // Make a pending entry to somewhere new.
804 const GURL kExistingURL2("http://bee");
805 controller.LoadURL(
806 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
807 EXPECT_EQ(0U, notifications.size());
809 // After the beforeunload but before it commits...
810 main_test_rfh()->PrepareForCommit();
812 // ... Do a new navigation.
813 const GURL kNewURL("http://see");
814 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL, true);
815 main_test_rfh()->PrepareForCommit();
816 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL);
818 // There should no longer be any pending entry, and the third navigation we
819 // just made should be committed.
820 EXPECT_EQ(1U, navigation_entry_committed_counter_);
821 navigation_entry_committed_counter_ = 0;
822 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
823 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
824 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
827 // Tests navigating to a new URL when there is a pending back/forward
828 // navigation. This will happen if the user hits back, but before that commits,
829 // they navigate somewhere new.
830 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
831 NavigationControllerImpl& controller = controller_impl();
832 TestNotificationTracker notifications;
833 RegisterForAllNavNotifications(&notifications, &controller);
835 // First make some history.
836 const GURL kExistingURL1("http://foo/eh");
837 controller.LoadURL(
838 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
839 int entry_id = controller.GetPendingEntry()->GetUniqueID();
840 main_test_rfh()->PrepareForCommit();
841 main_test_rfh()->SendNavigate(0, entry_id, true, kExistingURL1);
842 EXPECT_EQ(1U, navigation_entry_committed_counter_);
843 navigation_entry_committed_counter_ = 0;
845 const GURL kExistingURL2("http://foo/bee");
846 controller.LoadURL(
847 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
848 entry_id = controller.GetPendingEntry()->GetUniqueID();
849 main_test_rfh()->PrepareForCommit();
850 main_test_rfh()->SendNavigate(1, entry_id, true, kExistingURL2);
851 EXPECT_EQ(1U, navigation_entry_committed_counter_);
852 navigation_entry_committed_counter_ = 0;
854 // Now make a pending back/forward navigation. The zeroth entry should be
855 // pending.
856 controller.GoBack();
857 EXPECT_EQ(0U, notifications.size());
858 EXPECT_EQ(0, controller.GetPendingEntryIndex());
859 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
861 // Before that commits, do a new navigation.
862 const GURL kNewURL("http://foo/see");
863 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL, true);
864 main_test_rfh()->PrepareForCommit();
865 main_test_rfh()->SendNavigate(3, 0, true, kNewURL);
867 // There should no longer be any pending entry, and the new navigation we
868 // just made should be committed.
869 EXPECT_EQ(1U, navigation_entry_committed_counter_);
870 navigation_entry_committed_counter_ = 0;
871 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
872 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
873 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
876 // Tests navigating to a new URL when there is a pending back/forward
877 // navigation to a cross-process, privileged URL. This will happen if the user
878 // hits back, but before that commits, they navigate somewhere new.
879 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
880 NavigationControllerImpl& controller = controller_impl();
881 TestNotificationTracker notifications;
882 RegisterForAllNavNotifications(&notifications, &controller);
884 // First make some history, starting with a privileged URL.
885 const GURL kExistingURL1("http://privileged");
886 controller.LoadURL(
887 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
888 int entry_id = controller.GetPendingEntry()->GetUniqueID();
889 // Pretend it has bindings so we can tell if we incorrectly copy it.
890 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
891 main_test_rfh()->PrepareForCommit();
892 main_test_rfh()->SendNavigate(0, entry_id, true, kExistingURL1);
893 EXPECT_EQ(1U, navigation_entry_committed_counter_);
894 navigation_entry_committed_counter_ = 0;
896 // Navigate cross-process to a second URL.
897 const GURL kExistingURL2("http://foo/eh");
898 controller.LoadURL(
899 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
900 entry_id = controller.GetPendingEntry()->GetUniqueID();
901 main_test_rfh()->PrepareForCommit();
902 TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame();
903 foo_rfh->SendNavigate(1, entry_id, true, kExistingURL2);
904 EXPECT_EQ(1U, navigation_entry_committed_counter_);
905 navigation_entry_committed_counter_ = 0;
907 // Now make a pending back/forward navigation to a privileged entry.
908 // The zeroth entry should be pending.
909 controller.GoBack();
910 foo_rfh->SendBeforeUnloadACK(true);
911 EXPECT_EQ(0U, notifications.size());
912 EXPECT_EQ(0, controller.GetPendingEntryIndex());
913 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
914 EXPECT_EQ(2, controller.GetPendingEntry()->bindings());
916 // Before that commits, do a new navigation.
917 const GURL kNewURL("http://foo/bee");
918 foo_rfh->SendRendererInitiatedNavigationRequest(kNewURL, true);
919 foo_rfh->PrepareForCommit();
920 foo_rfh->SendNavigate(3, 0, true, kNewURL);
922 // There should no longer be any pending entry, and the new navigation we
923 // just made should be committed.
924 EXPECT_EQ(1U, navigation_entry_committed_counter_);
925 navigation_entry_committed_counter_ = 0;
926 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
927 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
928 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
929 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
932 // Tests navigating to an existing URL when there is a pending new navigation.
933 // This will happen if the user enters a URL, but before that commits, the
934 // current page fires history.back().
935 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
936 NavigationControllerImpl& controller = controller_impl();
937 TestNotificationTracker notifications;
938 RegisterForAllNavNotifications(&notifications, &controller);
940 // First make some history.
941 const GURL kExistingURL1("http://foo/eh");
942 controller.LoadURL(
943 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
944 int entry_id = controller.GetPendingEntry()->GetUniqueID();
945 main_test_rfh()->PrepareForCommit();
946 main_test_rfh()->SendNavigate(0, entry_id, true, kExistingURL1);
947 EXPECT_EQ(1U, navigation_entry_committed_counter_);
948 navigation_entry_committed_counter_ = 0;
950 const GURL kExistingURL2("http://foo/bee");
951 controller.LoadURL(
952 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
953 entry_id = controller.GetPendingEntry()->GetUniqueID();
954 main_test_rfh()->PrepareForCommit();
955 main_test_rfh()->SendNavigate(1, entry_id, true, kExistingURL2);
956 EXPECT_EQ(1U, navigation_entry_committed_counter_);
957 navigation_entry_committed_counter_ = 0;
959 // A back navigation comes in from the renderer...
960 controller.GoToOffset(-1);
961 entry_id = controller.GetPendingEntry()->GetUniqueID();
963 // ...while the user tries to navigate to a new page...
964 const GURL kNewURL("http://foo/see");
965 controller.LoadURL(
966 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
967 EXPECT_EQ(0U, notifications.size());
968 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
969 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
971 // ...and the back navigation commits.
972 main_test_rfh()->PrepareForCommit();
973 main_test_rfh()->SendNavigate(0, entry_id, false, kExistingURL1);
975 // There should no longer be any pending entry, and the back navigation should
976 // be committed.
977 EXPECT_EQ(1U, navigation_entry_committed_counter_);
978 navigation_entry_committed_counter_ = 0;
979 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
980 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
981 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
984 // Tests an ignored navigation when there is a pending new navigation.
985 // This will happen if the user enters a URL, but before that commits, the
986 // current blank page reloads. See http://crbug.com/77507.
987 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
988 NavigationControllerImpl& controller = controller_impl();
989 TestNotificationTracker notifications;
990 RegisterForAllNavNotifications(&notifications, &controller);
992 // Set a WebContentsDelegate to listen for state changes.
993 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
994 EXPECT_FALSE(contents()->GetDelegate());
995 contents()->SetDelegate(delegate.get());
997 // Without any navigations, the renderer starts at about:blank.
998 const GURL kExistingURL(url::kAboutBlankURL);
1000 // Now make a pending new navigation.
1001 const GURL kNewURL("http://eh");
1002 controller.LoadURL(
1003 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1004 EXPECT_EQ(0U, notifications.size());
1005 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1006 EXPECT_TRUE(controller.GetPendingEntry());
1007 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1008 EXPECT_EQ(1, delegate->navigation_state_change_count());
1010 // Before that commits, a document.write and location.reload can cause the
1011 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1012 // PlzNavigate: this will stop the old navigation and start a new one.
1013 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL, true);
1014 main_test_rfh()->SendNavigate(-1, 0, false, kExistingURL);
1016 // This should clear the pending entry and notify of a navigation state
1017 // change, so that we do not keep displaying kNewURL.
1018 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1019 EXPECT_FALSE(controller.GetPendingEntry());
1020 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1021 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1022 switches::kEnableBrowserSideNavigation))
1023 EXPECT_EQ(4, delegate->navigation_state_change_count());
1024 else
1025 EXPECT_EQ(2, delegate->navigation_state_change_count());
1027 contents()->SetDelegate(NULL);
1030 // Tests that the pending entry state is correct after an abort.
1031 // We do not want to clear the pending entry, so that the user doesn't
1032 // lose a typed URL. (See http://crbug.com/9682.)
1033 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
1034 NavigationControllerImpl& controller = controller_impl();
1035 TestNotificationTracker notifications;
1036 RegisterForAllNavNotifications(&notifications, &controller);
1038 // Set a WebContentsDelegate to listen for state changes.
1039 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1040 EXPECT_FALSE(contents()->GetDelegate());
1041 contents()->SetDelegate(delegate.get());
1043 // Start with a pending new navigation.
1044 const GURL kNewURL("http://eh");
1045 controller.LoadURL(
1046 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1047 main_test_rfh()->PrepareForCommit();
1048 EXPECT_EQ(0U, notifications.size());
1049 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1050 EXPECT_TRUE(controller.GetPendingEntry());
1051 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1052 EXPECT_EQ(1, delegate->navigation_state_change_count());
1054 // It may abort before committing, if it's a download or due to a stop or
1055 // a new navigation from the user.
1056 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1057 params.error_code = net::ERR_ABORTED;
1058 params.error_description = base::string16();
1059 params.url = kNewURL;
1060 params.showing_repost_interstitial = false;
1061 main_test_rfh()->OnMessageReceived(
1062 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1063 params));
1065 // This should not clear the pending entry or notify of a navigation state
1066 // change, so that we keep displaying kNewURL (until the user clears it).
1067 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1068 EXPECT_TRUE(controller.GetPendingEntry());
1069 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1070 EXPECT_EQ(1, delegate->navigation_state_change_count());
1071 NavigationEntry* pending_entry = controller.GetPendingEntry();
1073 // Ensure that a reload keeps the same pending entry.
1074 controller.Reload(true);
1075 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1076 EXPECT_TRUE(controller.GetPendingEntry());
1077 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
1078 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1080 contents()->SetDelegate(NULL);
1083 // Tests that the pending URL is not visible during a renderer-initiated
1084 // redirect and abort. See http://crbug.com/83031.
1085 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
1086 NavigationControllerImpl& controller = controller_impl();
1087 TestNotificationTracker notifications;
1088 RegisterForAllNavNotifications(&notifications, &controller);
1090 // First make an existing committed entry.
1091 const GURL kExistingURL("http://foo/eh");
1092 controller.LoadURL(kExistingURL, content::Referrer(),
1093 ui::PAGE_TRANSITION_TYPED, std::string());
1094 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1095 main_test_rfh()->PrepareForCommit();
1096 main_test_rfh()->SendNavigate(1, entry_id, true, kExistingURL);
1097 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1098 navigation_entry_committed_counter_ = 0;
1100 // Set a WebContentsDelegate to listen for state changes.
1101 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1102 EXPECT_FALSE(contents()->GetDelegate());
1103 contents()->SetDelegate(delegate.get());
1105 // Now make a pending new navigation, initiated by the renderer.
1106 const GURL kNewURL("http://foo/bee");
1107 NavigationController::LoadURLParams load_url_params(kNewURL);
1108 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
1109 load_url_params.is_renderer_initiated = true;
1110 controller.LoadURLWithParams(load_url_params);
1111 EXPECT_EQ(0U, notifications.size());
1112 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1113 EXPECT_TRUE(controller.GetPendingEntry());
1114 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1115 EXPECT_EQ(0, delegate->navigation_state_change_count());
1117 // The visible entry should be the last committed URL, not the pending one.
1118 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1120 // Now the navigation redirects. (There is no corresponding message here.)
1121 const GURL kRedirectURL("http://foo/see");
1123 // We don't want to change the NavigationEntry's url, in case it cancels.
1124 // Prevents regression of http://crbug.com/77786.
1125 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1127 // It may abort before committing, if it's a download or due to a stop or
1128 // a new navigation from the user.
1129 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1130 params.error_code = net::ERR_ABORTED;
1131 params.error_description = base::string16();
1132 params.url = kRedirectURL;
1133 params.showing_repost_interstitial = false;
1134 main_test_rfh()->OnMessageReceived(
1135 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1136 params));
1138 // Because the pending entry is renderer initiated and not visible, we
1139 // clear it when it fails.
1140 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1141 EXPECT_FALSE(controller.GetPendingEntry());
1142 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1143 EXPECT_EQ(1, delegate->navigation_state_change_count());
1145 // The visible entry should be the last committed URL, not the pending one,
1146 // so that no spoof is possible.
1147 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1149 contents()->SetDelegate(NULL);
1152 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1153 // at the time they committed. http://crbug.com/173672.
1154 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1155 NavigationControllerImpl& controller = controller_impl();
1156 TestNotificationTracker notifications;
1157 RegisterForAllNavNotifications(&notifications, &controller);
1158 std::vector<GURL> url_chain;
1160 const GURL url1("http://foo1");
1161 const GURL url2("http://foo2");
1163 // Navigate to a first, unprivileged URL.
1164 controller.LoadURL(
1165 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1166 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1167 controller.GetPendingEntry()->bindings());
1168 int entry1_id = controller.GetPendingEntry()->GetUniqueID();
1170 // Commit.
1171 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1172 orig_rfh->PrepareForCommit();
1173 orig_rfh->SendNavigate(0, entry1_id, true, url1);
1174 EXPECT_EQ(controller.GetEntryCount(), 1);
1175 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1176 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1177 entry1_id = controller.GetLastCommittedEntry()->GetUniqueID();
1179 // Manually increase the number of active frames in the SiteInstance
1180 // that orig_rfh belongs to, to prevent it from being destroyed when
1181 // it gets swapped out, so that we can reuse orig_rfh when the
1182 // controller goes back.
1183 orig_rfh->GetSiteInstance()->increment_active_frame_count();
1185 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1186 // transition, and set bindings on the pending RenderViewHost to simulate a
1187 // privileged url.
1188 controller.LoadURL(
1189 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1190 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1191 orig_rfh->PrepareForCommit();
1192 TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
1193 new_rfh->GetRenderViewHost()->AllowBindings(1);
1194 new_rfh->SendNavigate(1, entry_id, true, url2);
1196 // The second load should be committed, and bindings should be remembered.
1197 EXPECT_EQ(controller.GetEntryCount(), 2);
1198 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1199 EXPECT_TRUE(controller.CanGoBack());
1200 EXPECT_EQ(1, controller.GetLastCommittedEntry()->bindings());
1202 // Going back, the first entry should still appear unprivileged.
1203 controller.GoBack();
1204 new_rfh->PrepareForCommit();
1205 contents()->GetPendingMainFrame()->SendNavigate(0, entry1_id, false, url1);
1206 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1207 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1210 TEST_F(NavigationControllerTest, Reload) {
1211 NavigationControllerImpl& controller = controller_impl();
1212 TestNotificationTracker notifications;
1213 RegisterForAllNavNotifications(&notifications, &controller);
1215 const GURL url1("http://foo1");
1217 controller.LoadURL(
1218 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1219 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1220 EXPECT_EQ(0U, notifications.size());
1221 main_test_rfh()->PrepareForCommit();
1222 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
1223 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1224 navigation_entry_committed_counter_ = 0;
1225 ASSERT_TRUE(controller.GetVisibleEntry());
1226 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1227 entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
1229 controller.Reload(true);
1230 EXPECT_EQ(0U, notifications.size());
1232 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1233 EXPECT_FALSE(timestamp.is_null());
1235 // The reload is pending.
1236 EXPECT_EQ(controller.GetEntryCount(), 1);
1237 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1238 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1239 EXPECT_TRUE(controller.GetLastCommittedEntry());
1240 EXPECT_TRUE(controller.GetPendingEntry());
1241 EXPECT_FALSE(controller.CanGoBack());
1242 EXPECT_FALSE(controller.CanGoForward());
1243 // Make sure the title has been cleared (will be redrawn just after reload).
1244 // Avoids a stale cached title when the new page being reloaded has no title.
1245 // See http://crbug.com/96041.
1246 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1248 main_test_rfh()->PrepareForCommit();
1249 main_test_rfh()->SendNavigate(0, entry_id, false, url1);
1250 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1251 navigation_entry_committed_counter_ = 0;
1253 // Now the reload is committed.
1254 EXPECT_EQ(controller.GetEntryCount(), 1);
1255 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1256 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1257 EXPECT_TRUE(controller.GetLastCommittedEntry());
1258 EXPECT_FALSE(controller.GetPendingEntry());
1259 EXPECT_FALSE(controller.CanGoBack());
1260 EXPECT_FALSE(controller.CanGoForward());
1262 // The timestamp should have been updated.
1263 ASSERT_TRUE(controller.GetVisibleEntry());
1264 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1267 // Tests what happens when a reload navigation produces a new page.
1268 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1269 NavigationControllerImpl& controller = controller_impl();
1270 TestNotificationTracker notifications;
1271 RegisterForAllNavNotifications(&notifications, &controller);
1273 const GURL url1("http://foo1");
1274 const GURL url2("http://foo2");
1276 controller.LoadURL(
1277 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1278 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1279 main_test_rfh()->PrepareForCommit();
1280 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
1281 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1282 navigation_entry_committed_counter_ = 0;
1283 entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
1285 controller.Reload(true);
1286 EXPECT_EQ(0U, notifications.size());
1288 main_test_rfh()->PrepareForCommitWithServerRedirect(url2);
1289 main_test_rfh()->SendNavigate(1, entry_id, true, url2);
1290 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1291 navigation_entry_committed_counter_ = 0;
1293 // Now the reload is committed.
1294 EXPECT_EQ(controller.GetEntryCount(), 2);
1295 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1296 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1297 EXPECT_TRUE(controller.GetLastCommittedEntry());
1298 EXPECT_FALSE(controller.GetPendingEntry());
1299 EXPECT_TRUE(controller.CanGoBack());
1300 EXPECT_FALSE(controller.CanGoForward());
1303 // This test ensures that when a guest renderer reloads, the reload goes through
1304 // without ending up in the "we have a wrong process for the URL" branch in
1305 // NavigationControllerImpl::ReloadInternal.
1306 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1307 NavigationControllerImpl& controller = controller_impl();
1309 const GURL url1("http://foo1");
1310 controller.LoadURL(
1311 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1312 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1313 main_test_rfh()->PrepareForCommit();
1314 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
1315 ASSERT_TRUE(controller.GetVisibleEntry());
1317 // Make the entry believe its RenderProcessHost is a guest.
1318 NavigationEntryImpl* entry1 = controller.GetVisibleEntry();
1319 reinterpret_cast<MockRenderProcessHost*>(
1320 entry1->site_instance()->GetProcess())->set_is_for_guests_only(true);
1322 // And reload.
1323 controller.Reload(true);
1325 // The reload is pending. Check that the NavigationEntry didn't get replaced
1326 // because of having the wrong process.
1327 EXPECT_EQ(controller.GetEntryCount(), 1);
1328 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1329 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1331 NavigationEntryImpl* entry2 = controller.GetPendingEntry();
1332 EXPECT_EQ(entry1, entry2);
1335 #if !defined(OS_ANDROID) // http://crbug.com/157428
1336 namespace {
1337 void SetOriginalURL(const GURL& url,
1338 FrameHostMsg_DidCommitProvisionalLoad_Params* params) {
1339 params->original_request_url = url;
1343 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1344 NavigationControllerImpl& controller = controller_impl();
1345 TestNotificationTracker notifications;
1346 RegisterForAllNavNotifications(&notifications, &controller);
1348 const GURL original_url("http://foo1");
1349 const GURL final_url("http://foo2");
1350 auto set_original_url_callback = base::Bind(SetOriginalURL, original_url);
1352 // Load up the original URL, but get redirected.
1353 controller.LoadURL(
1354 original_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1355 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1356 EXPECT_EQ(0U, notifications.size());
1357 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
1358 main_test_rfh()->SendNavigateWithModificationCallback(
1359 0, entry_id, true, final_url, set_original_url_callback);
1360 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1361 navigation_entry_committed_counter_ = 0;
1362 entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
1364 // The NavigationEntry should save both the original URL and the final
1365 // redirected URL.
1366 EXPECT_EQ(
1367 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1368 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1370 // Reload using the original URL.
1371 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1372 controller.ReloadOriginalRequestURL(false);
1373 EXPECT_EQ(0U, notifications.size());
1375 // The reload is pending. The request should point to the original URL.
1376 EXPECT_EQ(original_url, navigated_url());
1377 EXPECT_EQ(controller.GetEntryCount(), 1);
1378 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1379 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1380 EXPECT_TRUE(controller.GetLastCommittedEntry());
1381 EXPECT_TRUE(controller.GetPendingEntry());
1382 EXPECT_FALSE(controller.CanGoBack());
1383 EXPECT_FALSE(controller.CanGoForward());
1385 // Make sure the title has been cleared (will be redrawn just after reload).
1386 // Avoids a stale cached title when the new page being reloaded has no title.
1387 // See http://crbug.com/96041.
1388 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1390 // Send that the navigation has proceeded; say it got redirected again.
1391 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
1392 main_test_rfh()->SendNavigate(0, entry_id, false, final_url);
1393 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1394 navigation_entry_committed_counter_ = 0;
1396 // Now the reload is committed.
1397 EXPECT_EQ(controller.GetEntryCount(), 1);
1398 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1399 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1400 EXPECT_TRUE(controller.GetLastCommittedEntry());
1401 EXPECT_FALSE(controller.GetPendingEntry());
1402 EXPECT_FALSE(controller.CanGoBack());
1403 EXPECT_FALSE(controller.CanGoForward());
1406 #endif // !defined(OS_ANDROID)
1408 // Test that certain non-persisted NavigationEntryImpl values get reset after
1409 // commit.
1410 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1411 NavigationControllerImpl& controller = controller_impl();
1413 // The value of "should replace entry" will be tested, but it's an error to
1414 // specify it when there are no entries. Create a simple entry to be replaced.
1415 const GURL url0("http://foo/0");
1416 controller.LoadURL(
1417 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1418 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1419 main_test_rfh()->PrepareForCommit();
1420 main_test_rfh()->SendNavigate(0, entry_id, true, url0);
1422 // Set up the pending entry.
1423 const GURL url1("http://foo/1");
1424 controller.LoadURL(
1425 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1426 entry_id = controller.GetPendingEntry()->GetUniqueID();
1428 // Set up some sample values.
1429 const unsigned char* raw_data =
1430 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1431 const int length = 11;
1432 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1433 scoped_refptr<base::RefCountedBytes> post_data =
1434 base::RefCountedBytes::TakeVector(&post_data_vector);
1435 GlobalRequestID transfer_id(3, 4);
1437 // Set non-persisted values on the pending entry.
1438 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1439 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1440 pending_entry->set_is_renderer_initiated(true);
1441 pending_entry->set_transferred_global_request_id(transfer_id);
1442 pending_entry->set_should_replace_entry(true);
1443 pending_entry->set_should_clear_history_list(true);
1444 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1445 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1446 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1447 EXPECT_TRUE(pending_entry->should_replace_entry());
1448 EXPECT_TRUE(pending_entry->should_clear_history_list());
1450 // Fake a commit response.
1451 main_test_rfh()->PrepareForCommit();
1452 main_test_rfh()->SendNavigate(1, entry_id, true, url1);
1454 // Certain values that are only used for pending entries get reset after
1455 // commit.
1456 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1457 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1458 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1459 EXPECT_EQ(GlobalRequestID(-1, -1),
1460 committed_entry->transferred_global_request_id());
1461 EXPECT_FALSE(committed_entry->should_replace_entry());
1462 EXPECT_FALSE(committed_entry->should_clear_history_list());
1465 namespace {
1466 void SetRedirects(const std::vector<GURL>& redirects,
1467 FrameHostMsg_DidCommitProvisionalLoad_Params* params) {
1468 params->redirects = redirects;
1472 // Test that Redirects are preserved after a commit.
1473 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1474 NavigationControllerImpl& controller = controller_impl();
1475 const GURL url1("http://foo1");
1476 const GURL url2("http://foo2");
1477 controller.LoadURL(
1478 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1479 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1481 // Set up some redirect values.
1482 std::vector<GURL> redirects;
1483 redirects.push_back(url2);
1484 auto set_redirects_callback = base::Bind(SetRedirects, redirects);
1486 // Set redirects on the pending entry.
1487 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1488 pending_entry->SetRedirectChain(redirects);
1489 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1490 EXPECT_EQ(url2, pending_entry->GetRedirectChain()[0]);
1492 // Normal navigation will preserve redirects in the committed entry.
1493 main_test_rfh()->PrepareForCommitWithServerRedirect(url2);
1494 main_test_rfh()->SendNavigateWithModificationCallback(0, entry_id, true, url1,
1495 set_redirects_callback);
1496 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1497 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1498 EXPECT_EQ(url2, committed_entry->GetRedirectChain()[0]);
1501 // Tests what happens when we navigate back successfully
1502 TEST_F(NavigationControllerTest, Back) {
1503 NavigationControllerImpl& controller = controller_impl();
1504 TestNotificationTracker notifications;
1505 RegisterForAllNavNotifications(&notifications, &controller);
1507 const GURL url1("http://foo1");
1508 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1);
1509 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1510 navigation_entry_committed_counter_ = 0;
1512 const GURL url2("http://foo2");
1513 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2);
1514 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1515 navigation_entry_committed_counter_ = 0;
1517 controller.GoBack();
1518 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1519 EXPECT_EQ(0U, notifications.size());
1521 // We should now have a pending navigation to go back.
1522 EXPECT_EQ(controller.GetEntryCount(), 2);
1523 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1524 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1525 EXPECT_TRUE(controller.GetLastCommittedEntry());
1526 EXPECT_TRUE(controller.GetPendingEntry());
1527 EXPECT_FALSE(controller.CanGoBack());
1528 EXPECT_FALSE(controller.CanGoToOffset(-1));
1529 EXPECT_TRUE(controller.CanGoForward());
1530 EXPECT_TRUE(controller.CanGoToOffset(1));
1531 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go forward 2 steps.
1533 // Timestamp for entry 1 should be on or after that of entry 0.
1534 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1535 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1536 controller.GetEntryAtIndex(0)->GetTimestamp());
1538 main_test_rfh()->PrepareForCommit();
1539 main_test_rfh()->SendNavigate(0, entry_id, false, url2);
1540 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1541 navigation_entry_committed_counter_ = 0;
1543 // The back navigation completed successfully.
1544 EXPECT_EQ(controller.GetEntryCount(), 2);
1545 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1546 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1547 EXPECT_TRUE(controller.GetLastCommittedEntry());
1548 EXPECT_FALSE(controller.GetPendingEntry());
1549 EXPECT_FALSE(controller.CanGoBack());
1550 EXPECT_FALSE(controller.CanGoToOffset(-1));
1551 EXPECT_TRUE(controller.CanGoForward());
1552 EXPECT_TRUE(controller.CanGoToOffset(1));
1553 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1555 // Timestamp for entry 0 should be on or after that of entry 1
1556 // (since we went back to it).
1557 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1558 controller.GetEntryAtIndex(1)->GetTimestamp());
1561 // Tests what happens when a back navigation produces a new page.
1562 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1563 NavigationControllerImpl& controller = controller_impl();
1564 TestNotificationTracker notifications;
1565 RegisterForAllNavNotifications(&notifications, &controller);
1567 const GURL url1("http://foo/1");
1568 const GURL url2("http://foo/2");
1569 const GURL url3("http://foo/3");
1571 controller.LoadURL(
1572 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1573 int entry1_id = controller.GetPendingEntry()->GetUniqueID();
1574 main_test_rfh()->PrepareForCommit();
1575 main_test_rfh()->SendNavigate(0, entry1_id, true, url1);
1576 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1577 navigation_entry_committed_counter_ = 0;
1578 entry1_id = controller.GetLastCommittedEntry()->GetUniqueID();
1580 controller.LoadURL(
1581 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1582 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1583 main_test_rfh()->PrepareForCommit();
1584 main_test_rfh()->SendNavigate(1, entry_id, true, url2);
1585 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1586 navigation_entry_committed_counter_ = 0;
1588 controller.GoBack();
1589 EXPECT_EQ(0U, notifications.size());
1591 // We should now have a pending navigation to go back.
1592 EXPECT_EQ(controller.GetEntryCount(), 2);
1593 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1594 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1595 EXPECT_TRUE(controller.GetLastCommittedEntry());
1596 EXPECT_TRUE(controller.GetPendingEntry());
1597 EXPECT_FALSE(controller.CanGoBack());
1598 EXPECT_TRUE(controller.CanGoForward());
1600 main_test_rfh()->PrepareForCommitWithServerRedirect(url3);
1601 main_test_rfh()->SendNavigate(2, entry1_id, true, url3);
1602 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1603 navigation_entry_committed_counter_ = 0;
1605 // The back navigation resulted in a completely new navigation.
1606 // TODO(darin): perhaps this behavior will be confusing to users?
1607 EXPECT_EQ(controller.GetEntryCount(), 3);
1608 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1609 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1610 EXPECT_TRUE(controller.GetLastCommittedEntry());
1611 EXPECT_FALSE(controller.GetPendingEntry());
1612 EXPECT_TRUE(controller.CanGoBack());
1613 EXPECT_FALSE(controller.CanGoForward());
1616 // Receives a back message when there is a new pending navigation entry.
1617 TEST_F(NavigationControllerTest, Back_NewPending) {
1618 NavigationControllerImpl& controller = controller_impl();
1619 TestNotificationTracker notifications;
1620 RegisterForAllNavNotifications(&notifications, &controller);
1622 const GURL kUrl1("http://foo1");
1623 const GURL kUrl2("http://foo2");
1624 const GURL kUrl3("http://foo3");
1626 // First navigate two places so we have some back history.
1627 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1);
1628 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1629 navigation_entry_committed_counter_ = 0;
1631 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1632 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2);
1633 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1634 navigation_entry_committed_counter_ = 0;
1636 // Now start a new pending navigation and go back before it commits.
1637 controller.LoadURL(
1638 kUrl3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1639 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1640 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1641 controller.GoBack();
1643 // The pending navigation should now be the "back" item and the new one
1644 // should be gone.
1645 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1646 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1649 // Tests what happens when we navigate forward successfully.
1650 TEST_F(NavigationControllerTest, Forward) {
1651 NavigationControllerImpl& controller = controller_impl();
1652 TestNotificationTracker notifications;
1653 RegisterForAllNavNotifications(&notifications, &controller);
1655 const GURL url1("http://foo1");
1656 const GURL url2("http://foo2");
1658 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1, true);
1659 main_test_rfh()->PrepareForCommit();
1660 main_test_rfh()->SendNavigate(0, 0, true, url1);
1661 NavigationEntry* entry1 = controller.GetLastCommittedEntry();
1662 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1663 navigation_entry_committed_counter_ = 0;
1665 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2, true);
1666 main_test_rfh()->PrepareForCommit();
1667 main_test_rfh()->SendNavigate(1, 0, true, url2);
1668 NavigationEntry* entry2 = controller.GetLastCommittedEntry();
1669 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1670 navigation_entry_committed_counter_ = 0;
1672 controller.GoBack();
1673 main_test_rfh()->PrepareForCommit();
1674 main_test_rfh()->SendNavigate(0, entry1->GetUniqueID(), false, url1);
1675 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1676 navigation_entry_committed_counter_ = 0;
1678 controller.GoForward();
1680 // We should now have a pending navigation to go forward.
1681 EXPECT_EQ(controller.GetEntryCount(), 2);
1682 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1683 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1684 EXPECT_TRUE(controller.GetLastCommittedEntry());
1685 EXPECT_TRUE(controller.GetPendingEntry());
1686 EXPECT_TRUE(controller.CanGoBack());
1687 EXPECT_TRUE(controller.CanGoToOffset(-1));
1688 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1689 EXPECT_FALSE(controller.CanGoForward());
1690 EXPECT_FALSE(controller.CanGoToOffset(1));
1692 // Timestamp for entry 0 should be on or after that of entry 1
1693 // (since we went back to it).
1694 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1695 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1696 controller.GetEntryAtIndex(1)->GetTimestamp());
1698 main_test_rfh()->PrepareForCommit();
1699 main_test_rfh()->SendNavigate(1, entry2->GetUniqueID(), false, url2);
1700 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1701 navigation_entry_committed_counter_ = 0;
1703 // The forward navigation completed successfully.
1704 EXPECT_EQ(controller.GetEntryCount(), 2);
1705 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1706 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1707 EXPECT_TRUE(controller.GetLastCommittedEntry());
1708 EXPECT_FALSE(controller.GetPendingEntry());
1709 EXPECT_TRUE(controller.CanGoBack());
1710 EXPECT_TRUE(controller.CanGoToOffset(-1));
1711 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1712 EXPECT_FALSE(controller.CanGoForward());
1713 EXPECT_FALSE(controller.CanGoToOffset(1));
1715 // Timestamp for entry 1 should be on or after that of entry 0
1716 // (since we went forward to it).
1717 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1718 controller.GetEntryAtIndex(0)->GetTimestamp());
1721 // Tests what happens when a forward navigation produces a new page.
1722 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1723 NavigationControllerImpl& controller = controller_impl();
1724 TestNotificationTracker notifications;
1725 RegisterForAllNavNotifications(&notifications, &controller);
1727 const GURL url1("http://foo1");
1728 const GURL url2("http://foo2");
1729 const GURL url3("http://foo3");
1731 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1, true);
1732 main_test_rfh()->PrepareForCommit();
1733 main_test_rfh()->SendNavigate(0, 0, true, url1);
1734 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1735 NavigationEntry* entry1 = controller.GetLastCommittedEntry();
1736 navigation_entry_committed_counter_ = 0;
1737 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2, true);
1738 main_test_rfh()->PrepareForCommit();
1739 main_test_rfh()->SendNavigate(1, 0, true, url2);
1740 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1741 NavigationEntry* entry2 = controller.GetLastCommittedEntry();
1742 navigation_entry_committed_counter_ = 0;
1744 controller.GoBack();
1745 main_test_rfh()->PrepareForCommit();
1746 main_test_rfh()->SendNavigate(0, entry1->GetUniqueID(), false, url1);
1747 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1748 navigation_entry_committed_counter_ = 0;
1750 controller.GoForward();
1751 EXPECT_EQ(0U, notifications.size());
1753 // Should now have a pending navigation to go forward.
1754 EXPECT_EQ(controller.GetEntryCount(), 2);
1755 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1756 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1757 EXPECT_TRUE(controller.GetLastCommittedEntry());
1758 EXPECT_TRUE(controller.GetPendingEntry());
1759 EXPECT_TRUE(controller.CanGoBack());
1760 EXPECT_FALSE(controller.CanGoForward());
1762 main_test_rfh()->PrepareForCommit();
1763 main_test_rfh()->SendNavigate(2, entry2->GetUniqueID(), true, url3);
1764 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1765 navigation_entry_committed_counter_ = 0;
1766 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1768 EXPECT_EQ(controller.GetEntryCount(), 2);
1769 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1770 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1771 EXPECT_TRUE(controller.GetLastCommittedEntry());
1772 EXPECT_FALSE(controller.GetPendingEntry());
1773 EXPECT_TRUE(controller.CanGoBack());
1774 EXPECT_FALSE(controller.CanGoForward());
1777 // Two consecutive navigations for the same URL entered in should be considered
1778 // as SAME_PAGE navigation even when we are redirected to some other page.
1779 TEST_F(NavigationControllerTest, Redirect) {
1780 NavigationControllerImpl& controller = controller_impl();
1781 TestNotificationTracker notifications;
1782 RegisterForAllNavNotifications(&notifications, &controller);
1784 const GURL url1("http://foo1");
1785 const GURL url2("http://foo2"); // Redirection target
1787 // First request.
1788 controller.LoadURL(
1789 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1790 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1792 EXPECT_EQ(0U, notifications.size());
1794 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1795 params.page_id = 0;
1796 params.nav_entry_id = entry_id;
1797 params.did_create_new_entry = true;
1798 params.url = url2;
1799 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1800 params.redirects.push_back(GURL("http://foo1"));
1801 params.redirects.push_back(GURL("http://foo2"));
1802 params.should_update_history = false;
1803 params.gesture = NavigationGestureAuto;
1804 params.is_post = false;
1805 params.page_state = PageState::CreateFromURL(url2);
1807 LoadCommittedDetails details;
1809 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1810 &details));
1811 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1812 navigation_entry_committed_counter_ = 0;
1814 // Second request.
1815 controller.LoadURL(
1816 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1817 entry_id = controller.GetPendingEntry()->GetUniqueID();
1819 EXPECT_TRUE(controller.GetPendingEntry());
1820 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1821 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1823 params.nav_entry_id = entry_id;
1824 params.did_create_new_entry = false;
1826 EXPECT_EQ(0U, notifications.size());
1827 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1828 &details));
1829 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1830 navigation_entry_committed_counter_ = 0;
1832 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1833 EXPECT_EQ(controller.GetEntryCount(), 1);
1834 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1835 EXPECT_TRUE(controller.GetLastCommittedEntry());
1836 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1837 EXPECT_FALSE(controller.GetPendingEntry());
1838 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1840 EXPECT_FALSE(controller.CanGoBack());
1841 EXPECT_FALSE(controller.CanGoForward());
1844 // Similar to Redirect above, but the first URL is requested by POST,
1845 // the second URL is requested by GET. NavigationEntry::has_post_data_
1846 // must be cleared. http://crbug.com/21245
1847 TEST_F(NavigationControllerTest, PostThenRedirect) {
1848 NavigationControllerImpl& controller = controller_impl();
1849 TestNotificationTracker notifications;
1850 RegisterForAllNavNotifications(&notifications, &controller);
1852 const GURL url1("http://foo1");
1853 const GURL url2("http://foo2"); // Redirection target
1855 // First request as POST.
1856 controller.LoadURL(
1857 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1858 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1859 controller.GetVisibleEntry()->SetHasPostData(true);
1861 EXPECT_EQ(0U, notifications.size());
1863 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1864 params.page_id = 0;
1865 params.nav_entry_id = entry_id;
1866 params.did_create_new_entry = true;
1867 params.url = url2;
1868 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1869 params.redirects.push_back(GURL("http://foo1"));
1870 params.redirects.push_back(GURL("http://foo2"));
1871 params.should_update_history = false;
1872 params.gesture = NavigationGestureAuto;
1873 params.is_post = true;
1874 params.page_state = PageState::CreateFromURL(url2);
1876 LoadCommittedDetails details;
1878 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1879 &details));
1880 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1881 navigation_entry_committed_counter_ = 0;
1883 // Second request.
1884 controller.LoadURL(
1885 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1886 entry_id = controller.GetPendingEntry()->GetUniqueID();
1888 EXPECT_TRUE(controller.GetPendingEntry());
1889 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1890 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1892 params.nav_entry_id = entry_id;
1893 params.did_create_new_entry = false;
1894 params.is_post = false;
1896 EXPECT_EQ(0U, notifications.size());
1897 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1898 &details));
1899 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1900 navigation_entry_committed_counter_ = 0;
1902 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1903 EXPECT_EQ(controller.GetEntryCount(), 1);
1904 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1905 EXPECT_TRUE(controller.GetLastCommittedEntry());
1906 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1907 EXPECT_FALSE(controller.GetPendingEntry());
1908 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1909 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1911 EXPECT_FALSE(controller.CanGoBack());
1912 EXPECT_FALSE(controller.CanGoForward());
1915 // A redirect right off the bat should be a NEW_PAGE.
1916 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1917 NavigationControllerImpl& controller = controller_impl();
1918 TestNotificationTracker notifications;
1919 RegisterForAllNavNotifications(&notifications, &controller);
1921 const GURL url1("http://foo1");
1922 const GURL url2("http://foo2"); // Redirection target
1924 // First request
1925 controller.LoadURL(
1926 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1927 int entry_id = controller.GetPendingEntry()->GetUniqueID();
1929 EXPECT_TRUE(controller.GetPendingEntry());
1930 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1931 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1933 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1934 params.page_id = 0;
1935 params.nav_entry_id = entry_id;
1936 params.did_create_new_entry = true;
1937 params.url = url2;
1938 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1939 params.redirects.push_back(GURL("http://foo1"));
1940 params.redirects.push_back(GURL("http://foo2"));
1941 params.should_update_history = false;
1942 params.gesture = NavigationGestureAuto;
1943 params.is_post = false;
1944 params.page_state = PageState::CreateFromURL(url2);
1946 LoadCommittedDetails details;
1948 EXPECT_EQ(0U, notifications.size());
1949 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1950 &details));
1951 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1952 navigation_entry_committed_counter_ = 0;
1954 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1955 EXPECT_EQ(controller.GetEntryCount(), 1);
1956 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1957 EXPECT_TRUE(controller.GetLastCommittedEntry());
1958 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1959 EXPECT_FALSE(controller.GetPendingEntry());
1960 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1962 EXPECT_FALSE(controller.CanGoBack());
1963 EXPECT_FALSE(controller.CanGoForward());
1966 // If something is pumping the event loop in the browser process and is loading
1967 // pages rapidly one after the other, there can be a race with two closely-
1968 // spaced load requests. Once the first load request is sent, will the renderer
1969 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
1970 // IPC, and have the browser process handle that IPC before the caller makes
1971 // another load request, replacing the pending entry of the first request?
1973 // This test is about what happens in such a race when that pending entry
1974 // replacement happens. If it happens, and the first load had the same URL as
1975 // the page before it, we must make sure that the replacement of the pending
1976 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
1978 // (This is a unit test rather than a browser test because it's not currently
1979 // possible to force this sequence of events with a browser test.)
1980 TEST_F(NavigationControllerTest,
1981 NavigationTypeClassification_ExistingPageRace) {
1982 NavigationControllerImpl& controller = controller_impl();
1983 const GURL url1("http://foo1");
1984 const GURL url2("http://foo2");
1986 // Start with a loaded page.
1987 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1);
1988 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
1990 // Start a load of the same page again.
1991 controller.LoadURL(
1992 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1993 int entry_id1 = controller.GetPendingEntry()->GetUniqueID();
1995 // Immediately start loading a different page...
1996 controller.LoadURL(
1997 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1998 int entry_id2 = controller.GetPendingEntry()->GetUniqueID();
1999 EXPECT_NE(entry_id1, entry_id2);
2001 // ... and now the renderer sends a commit for the first navigation.
2002 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2003 params.page_id = 0;
2004 params.nav_entry_id = entry_id1;
2005 params.intended_as_new_entry = true;
2006 params.did_create_new_entry = false;
2007 params.url = url1;
2008 params.transition = ui::PAGE_TRANSITION_TYPED;
2009 params.page_state = PageState::CreateFromURL(url1);
2011 LoadCommittedDetails details;
2013 main_test_rfh()->PrepareForCommit();
2014 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2015 &details));
2016 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
2019 // Tests navigation via link click within a subframe. A new navigation entry
2020 // should be created.
2021 TEST_F(NavigationControllerTest, NewSubframe) {
2022 NavigationControllerImpl& controller = controller_impl();
2023 TestNotificationTracker notifications;
2024 RegisterForAllNavNotifications(&notifications, &controller);
2026 const GURL url1("http://foo1");
2027 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
2028 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2029 navigation_entry_committed_counter_ = 0;
2031 // Prereq: add a subframe with an initial auto-subframe navigation.
2032 main_test_rfh()->OnCreateChildFrame(
2033 MSG_ROUTING_NONE, blink::WebTreeScopeType::Document, std::string(),
2034 blink::WebSandboxFlags::None);
2035 RenderFrameHostImpl* subframe =
2036 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2037 const GURL subframe_url("http://foo1/subframe");
2039 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2040 params.page_id = 1;
2041 params.nav_entry_id = 0;
2042 params.did_create_new_entry = false;
2043 params.url = subframe_url;
2044 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2045 params.should_update_history = false;
2046 params.gesture = NavigationGestureUser;
2047 params.is_post = false;
2048 params.page_state = PageState::CreateFromURL(subframe_url);
2050 // Navigating should do nothing.
2051 LoadCommittedDetails details;
2052 EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
2053 EXPECT_EQ(0U, notifications.size());
2056 // Now do a new navigation in the frame.
2057 const GURL url2("http://foo2");
2058 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2059 params.page_id = 2;
2060 params.nav_entry_id = 0;
2061 params.did_create_new_entry = true;
2062 params.url = url2;
2063 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2064 params.should_update_history = false;
2065 params.gesture = NavigationGestureUser;
2066 params.is_post = false;
2067 params.page_state = PageState::CreateFromURL(url2);
2069 LoadCommittedDetails details;
2070 EXPECT_TRUE(controller.RendererDidNavigate(subframe, params, &details));
2071 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2072 navigation_entry_committed_counter_ = 0;
2073 EXPECT_EQ(url1, details.previous_url);
2074 EXPECT_FALSE(details.is_in_page);
2075 EXPECT_FALSE(details.is_main_frame);
2077 // The new entry should be appended.
2078 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
2079 EXPECT_EQ(2, controller.GetEntryCount());
2080 EXPECT_EQ(entry, details.entry);
2082 // New entry should refer to the new page, but the old URL (entries only
2083 // reflect the toplevel URL).
2084 EXPECT_EQ(url1, entry->GetURL());
2085 EXPECT_EQ(params.page_id, entry->GetPageID());
2087 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2088 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2089 // The entry should have a subframe FrameNavigationEntry.
2090 ASSERT_EQ(1U, entry->root_node()->children.size());
2091 EXPECT_EQ(url2, entry->root_node()->children[0]->frame_entry->url());
2092 } else {
2093 // There are no subframe FrameNavigationEntries by default.
2094 EXPECT_EQ(0U, entry->root_node()->children.size());
2098 // Auto subframes are ones the page loads automatically like ads. They should
2099 // not create new navigation entries.
2100 // TODO(creis): Test updating entries for history auto subframe navigations.
2101 TEST_F(NavigationControllerTest, AutoSubframe) {
2102 NavigationControllerImpl& controller = controller_impl();
2103 TestNotificationTracker notifications;
2104 RegisterForAllNavNotifications(&notifications, &controller);
2106 const GURL url1("http://foo/1");
2107 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
2108 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2109 navigation_entry_committed_counter_ = 0;
2111 // Add a subframe and navigate it.
2112 main_test_rfh()->OnCreateChildFrame(
2113 MSG_ROUTING_NONE, blink::WebTreeScopeType::Document, std::string(),
2114 blink::WebSandboxFlags::None);
2115 RenderFrameHostImpl* subframe =
2116 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2117 const GURL url2("http://foo/2");
2119 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2120 params.page_id = 1;
2121 params.nav_entry_id = 0;
2122 params.did_create_new_entry = false;
2123 params.url = url2;
2124 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2125 params.should_update_history = false;
2126 params.gesture = NavigationGestureUser;
2127 params.is_post = false;
2128 params.page_state = PageState::CreateFromURL(url2);
2130 // Navigating should do nothing.
2131 LoadCommittedDetails details;
2132 EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
2133 EXPECT_EQ(0U, notifications.size());
2136 // There should still be only one entry.
2137 EXPECT_EQ(1, controller.GetEntryCount());
2138 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
2139 EXPECT_EQ(url1, entry->GetURL());
2140 EXPECT_EQ(1, entry->GetPageID());
2141 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
2142 EXPECT_EQ(url1, root_entry->url());
2144 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2145 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2146 // The entry should now have a subframe FrameNavigationEntry.
2147 ASSERT_EQ(1U, entry->root_node()->children.size());
2148 FrameNavigationEntry* frame_entry =
2149 entry->root_node()->children[0]->frame_entry.get();
2150 EXPECT_EQ(url2, frame_entry->url());
2151 } else {
2152 // There are no subframe FrameNavigationEntries by default.
2153 EXPECT_EQ(0U, entry->root_node()->children.size());
2156 // Add a second subframe and navigate.
2157 main_test_rfh()->OnCreateChildFrame(
2158 MSG_ROUTING_NONE, blink::WebTreeScopeType::Document, std::string(),
2159 blink::WebSandboxFlags::None);
2160 RenderFrameHostImpl* subframe2 =
2161 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2162 const GURL url3("http://foo/3");
2164 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2165 params.page_id = 1;
2166 params.nav_entry_id = 0;
2167 params.did_create_new_entry = false;
2168 params.url = url3;
2169 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2170 params.should_update_history = false;
2171 params.gesture = NavigationGestureUser;
2172 params.is_post = false;
2173 params.page_state = PageState::CreateFromURL(url3);
2175 // Navigating should do nothing.
2176 LoadCommittedDetails details;
2177 EXPECT_FALSE(controller.RendererDidNavigate(subframe2, params, &details));
2178 EXPECT_EQ(0U, notifications.size());
2181 // There should still be only one entry, mostly unchanged.
2182 EXPECT_EQ(1, controller.GetEntryCount());
2183 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
2184 EXPECT_EQ(url1, entry->GetURL());
2185 EXPECT_EQ(1, entry->GetPageID());
2186 EXPECT_EQ(root_entry, entry->root_node()->frame_entry.get());
2187 EXPECT_EQ(url1, root_entry->url());
2189 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2190 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2191 // The entry should now have 2 subframe FrameNavigationEntries.
2192 ASSERT_EQ(2U, entry->root_node()->children.size());
2193 FrameNavigationEntry* new_frame_entry =
2194 entry->root_node()->children[1]->frame_entry.get();
2195 EXPECT_EQ(url3, new_frame_entry->url());
2196 } else {
2197 // There are no subframe FrameNavigationEntries by default.
2198 EXPECT_EQ(0U, entry->root_node()->children.size());
2201 // Add a nested subframe and navigate.
2202 subframe->OnCreateChildFrame(MSG_ROUTING_NONE,
2203 blink::WebTreeScopeType::Document, std::string(),
2204 blink::WebSandboxFlags::None);
2205 RenderFrameHostImpl* subframe3 = contents()
2206 ->GetFrameTree()
2207 ->root()
2208 ->child_at(0)
2209 ->child_at(0)
2210 ->current_frame_host();
2211 const GURL url4("http://foo/4");
2213 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2214 params.page_id = 1;
2215 params.nav_entry_id = 0;
2216 params.did_create_new_entry = false;
2217 params.url = url4;
2218 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2219 params.should_update_history = false;
2220 params.gesture = NavigationGestureUser;
2221 params.is_post = false;
2222 params.page_state = PageState::CreateFromURL(url4);
2224 // Navigating should do nothing.
2225 LoadCommittedDetails details;
2226 EXPECT_FALSE(controller.RendererDidNavigate(subframe3, params, &details));
2227 EXPECT_EQ(0U, notifications.size());
2230 // There should still be only one entry, mostly unchanged.
2231 EXPECT_EQ(1, controller.GetEntryCount());
2232 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
2233 EXPECT_EQ(url1, entry->GetURL());
2234 EXPECT_EQ(1, entry->GetPageID());
2235 EXPECT_EQ(root_entry, entry->root_node()->frame_entry.get());
2236 EXPECT_EQ(url1, root_entry->url());
2238 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2239 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2240 // The entry should now have a nested FrameNavigationEntry.
2241 EXPECT_EQ(2U, entry->root_node()->children.size());
2242 ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
2243 FrameNavigationEntry* new_frame_entry =
2244 entry->root_node()->children[0]->children[0]->frame_entry.get();
2245 EXPECT_EQ(url4, new_frame_entry->url());
2246 } else {
2247 // There are no subframe FrameNavigationEntries by default.
2248 EXPECT_EQ(0U, entry->root_node()->children.size());
2252 // Tests navigation and then going back to a subframe navigation.
2253 TEST_F(NavigationControllerTest, BackSubframe) {
2254 NavigationControllerImpl& controller = controller_impl();
2255 TestNotificationTracker notifications;
2256 RegisterForAllNavNotifications(&notifications, &controller);
2258 // Main page.
2259 const GURL url1("http://foo1");
2260 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1);
2261 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2262 NavigationEntry* entry1 = controller.GetLastCommittedEntry();
2263 navigation_entry_committed_counter_ = 0;
2265 // Prereq: add a subframe with an initial auto-subframe navigation.
2266 main_test_rfh()->OnCreateChildFrame(
2267 MSG_ROUTING_NONE, blink::WebTreeScopeType::Document, std::string(),
2268 blink::WebSandboxFlags::None);
2269 RenderFrameHostImpl* subframe =
2270 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2271 const GURL subframe_url("http://foo1/subframe");
2273 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2274 params.page_id = 1;
2275 params.nav_entry_id = 0;
2276 params.did_create_new_entry = false;
2277 params.url = subframe_url;
2278 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2279 params.should_update_history = false;
2280 params.gesture = NavigationGestureUser;
2281 params.is_post = false;
2282 params.page_state = PageState::CreateFromURL(subframe_url);
2284 // Navigating should do nothing.
2285 LoadCommittedDetails details;
2286 EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
2287 EXPECT_EQ(0U, notifications.size());
2290 // First manual subframe navigation.
2291 const GURL url2("http://foo2");
2292 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2293 params.page_id = 2;
2294 params.nav_entry_id = 0;
2295 params.did_create_new_entry = true;
2296 params.url = url2;
2297 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2298 params.should_update_history = false;
2299 params.gesture = NavigationGestureUser;
2300 params.is_post = false;
2301 params.page_state = PageState::CreateFromURL(url2);
2303 // This should generate a new entry.
2304 LoadCommittedDetails details;
2305 EXPECT_TRUE(controller.RendererDidNavigate(subframe, params, &details));
2306 NavigationEntryImpl* entry2 = controller.GetLastCommittedEntry();
2307 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2308 navigation_entry_committed_counter_ = 0;
2309 EXPECT_EQ(2, controller.GetEntryCount());
2311 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2312 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2313 // The entry should have a subframe FrameNavigationEntry.
2314 ASSERT_EQ(1U, entry2->root_node()->children.size());
2315 EXPECT_EQ(url2, entry2->root_node()->children[0]->frame_entry->url());
2316 } else {
2317 // There are no subframe FrameNavigationEntries by default.
2318 EXPECT_EQ(0U, entry2->root_node()->children.size());
2321 // Second manual subframe navigation should also make a new entry.
2322 const GURL url3("http://foo3");
2323 params.page_id = 3;
2324 params.nav_entry_id = 0;
2325 params.did_create_new_entry = true;
2326 params.url = url3;
2327 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2328 EXPECT_TRUE(controller.RendererDidNavigate(subframe, params, &details));
2329 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2330 navigation_entry_committed_counter_ = 0;
2331 NavigationEntryImpl* entry3 = controller.GetLastCommittedEntry();
2332 EXPECT_EQ(3, controller.GetEntryCount());
2333 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2335 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2336 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2337 // The entry should have a subframe FrameNavigationEntry.
2338 ASSERT_EQ(1U, entry3->root_node()->children.size());
2339 EXPECT_EQ(url3, entry3->root_node()->children[0]->frame_entry->url());
2340 } else {
2341 // There are no subframe FrameNavigationEntries by default.
2342 EXPECT_EQ(0U, entry3->root_node()->children.size());
2345 // Go back one.
2346 controller.GoBack();
2347 params.page_id = 2;
2348 params.nav_entry_id = entry2->GetUniqueID();
2349 params.did_create_new_entry = false;
2350 params.url = url2;
2351 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2352 EXPECT_TRUE(controller.RendererDidNavigate(subframe, params, &details));
2353 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2354 navigation_entry_committed_counter_ = 0;
2355 EXPECT_EQ(entry2, controller.GetLastCommittedEntry());
2356 EXPECT_EQ(3, controller.GetEntryCount());
2357 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2358 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2359 EXPECT_FALSE(controller.GetPendingEntry());
2361 // Go back one more.
2362 controller.GoBack();
2363 params.page_id = 1;
2364 params.nav_entry_id = entry1->GetUniqueID();
2365 params.did_create_new_entry = false;
2366 params.url = url1;
2367 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2368 EXPECT_TRUE(controller.RendererDidNavigate(subframe, params, &details));
2369 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2370 navigation_entry_committed_counter_ = 0;
2371 EXPECT_EQ(entry1, controller.GetLastCommittedEntry());
2372 EXPECT_EQ(3, controller.GetEntryCount());
2373 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2374 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2375 EXPECT_FALSE(controller.GetPendingEntry());
2378 TEST_F(NavigationControllerTest, LinkClick) {
2379 NavigationControllerImpl& controller = controller_impl();
2380 TestNotificationTracker notifications;
2381 RegisterForAllNavNotifications(&notifications, &controller);
2383 const GURL url1("http://foo1");
2384 const GURL url2("http://foo2");
2386 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1);
2387 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2388 navigation_entry_committed_counter_ = 0;
2390 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2);
2391 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2392 navigation_entry_committed_counter_ = 0;
2394 // Should have produced a new session history entry.
2395 EXPECT_EQ(controller.GetEntryCount(), 2);
2396 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2397 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2398 EXPECT_TRUE(controller.GetLastCommittedEntry());
2399 EXPECT_FALSE(controller.GetPendingEntry());
2400 EXPECT_TRUE(controller.CanGoBack());
2401 EXPECT_FALSE(controller.CanGoForward());
2404 TEST_F(NavigationControllerTest, InPage) {
2405 NavigationControllerImpl& controller = controller_impl();
2406 TestNotificationTracker notifications;
2407 RegisterForAllNavNotifications(&notifications, &controller);
2409 // Main page.
2410 const GURL url1("http://foo");
2411 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1);
2412 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2413 navigation_entry_committed_counter_ = 0;
2415 // Ensure main page navigation to same url respects the was_within_same_page
2416 // hint provided in the params.
2417 FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2418 self_params.page_id = 0;
2419 self_params.nav_entry_id = 0;
2420 self_params.did_create_new_entry = false;
2421 self_params.url = url1;
2422 self_params.transition = ui::PAGE_TRANSITION_LINK;
2423 self_params.should_update_history = false;
2424 self_params.gesture = NavigationGestureUser;
2425 self_params.is_post = false;
2426 self_params.page_state = PageState::CreateFromURL(url1);
2427 self_params.was_within_same_page = true;
2429 LoadCommittedDetails details;
2430 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2431 &details));
2432 NavigationEntry* entry1 = controller.GetLastCommittedEntry();
2433 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2434 navigation_entry_committed_counter_ = 0;
2435 EXPECT_TRUE(details.is_in_page);
2436 EXPECT_TRUE(details.did_replace_entry);
2437 EXPECT_EQ(1, controller.GetEntryCount());
2439 // Fragment navigation to a new page_id.
2440 const GURL url2("http://foo#a");
2441 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2442 params.page_id = 1;
2443 params.nav_entry_id = 0;
2444 params.did_create_new_entry = true;
2445 params.url = url2;
2446 params.transition = ui::PAGE_TRANSITION_LINK;
2447 params.should_update_history = false;
2448 params.gesture = NavigationGestureUser;
2449 params.is_post = false;
2450 params.page_state = PageState::CreateFromURL(url2);
2451 params.was_within_same_page = true;
2453 // This should generate a new entry.
2454 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2455 &details));
2456 NavigationEntry* entry2 = controller.GetLastCommittedEntry();
2457 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2458 navigation_entry_committed_counter_ = 0;
2459 EXPECT_TRUE(details.is_in_page);
2460 EXPECT_FALSE(details.did_replace_entry);
2461 EXPECT_EQ(2, controller.GetEntryCount());
2463 // Go back one.
2464 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2465 controller.GoBack();
2466 back_params.url = url1;
2467 back_params.page_id = 0;
2468 back_params.nav_entry_id = entry1->GetUniqueID();
2469 back_params.did_create_new_entry = false;
2470 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2471 &details));
2472 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2473 navigation_entry_committed_counter_ = 0;
2474 EXPECT_TRUE(details.is_in_page);
2475 EXPECT_EQ(2, controller.GetEntryCount());
2476 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2477 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2479 // Go forward.
2480 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2481 controller.GoForward();
2482 forward_params.url = url2;
2483 forward_params.page_id = 1;
2484 forward_params.nav_entry_id = entry2->GetUniqueID();
2485 forward_params.did_create_new_entry = false;
2486 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2487 &details));
2488 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2489 navigation_entry_committed_counter_ = 0;
2490 EXPECT_TRUE(details.is_in_page);
2491 EXPECT_EQ(2, controller.GetEntryCount());
2492 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2493 EXPECT_EQ(forward_params.url,
2494 controller.GetVisibleEntry()->GetURL());
2496 // Now go back and forward again. This is to work around a bug where we would
2497 // compare the incoming URL with the last committed entry rather than the
2498 // one identified by an existing page ID. This would result in the second URL
2499 // losing the reference fragment when you navigate away from it and then back.
2500 controller.GoBack();
2501 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2502 &details));
2503 controller.GoForward();
2504 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2505 &details));
2506 EXPECT_EQ(forward_params.url,
2507 controller.GetVisibleEntry()->GetURL());
2509 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2510 const GURL url3("http://bar");
2511 params.page_id = 2;
2512 params.nav_entry_id = 0;
2513 params.did_create_new_entry = true;
2514 params.url = url3;
2515 navigation_entry_committed_counter_ = 0;
2516 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2517 &details));
2518 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2519 navigation_entry_committed_counter_ = 0;
2520 EXPECT_FALSE(details.is_in_page);
2521 EXPECT_EQ(3, controller.GetEntryCount());
2522 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2525 TEST_F(NavigationControllerTest, InPage_Replace) {
2526 NavigationControllerImpl& controller = controller_impl();
2527 TestNotificationTracker notifications;
2528 RegisterForAllNavNotifications(&notifications, &controller);
2530 // Main page.
2531 const GURL url1("http://foo");
2532 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1);
2533 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2534 navigation_entry_committed_counter_ = 0;
2536 // First navigation.
2537 const GURL url2("http://foo#a");
2538 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2539 params.page_id = 0; // Same page_id
2540 params.nav_entry_id = 0;
2541 params.did_create_new_entry = false;
2542 params.url = url2;
2543 params.transition = ui::PAGE_TRANSITION_LINK;
2544 params.should_update_history = false;
2545 params.gesture = NavigationGestureUser;
2546 params.is_post = false;
2547 params.page_state = PageState::CreateFromURL(url2);
2548 params.was_within_same_page = true;
2550 // This should NOT generate a new entry, nor prune the list.
2551 LoadCommittedDetails details;
2552 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2553 &details));
2554 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2555 navigation_entry_committed_counter_ = 0;
2556 EXPECT_TRUE(details.is_in_page);
2557 EXPECT_TRUE(details.did_replace_entry);
2558 EXPECT_EQ(1, controller.GetEntryCount());
2561 // Tests for http://crbug.com/40395
2562 // Simulates this:
2563 // <script>
2564 // window.location.replace("#a");
2565 // window.location='http://foo3/';
2566 // </script>
2567 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2568 NavigationControllerImpl& controller = controller_impl();
2569 TestNotificationTracker notifications;
2570 RegisterForAllNavNotifications(&notifications, &controller);
2572 // Load an initial page.
2574 const GURL url("http://foo/");
2575 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url);
2576 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2577 navigation_entry_committed_counter_ = 0;
2580 // Navigate to a new page.
2582 const GURL url("http://foo2/");
2583 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url);
2584 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2585 navigation_entry_committed_counter_ = 0;
2588 // Navigate within the page.
2590 const GURL url("http://foo2/#a");
2591 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2592 params.page_id = 1; // Same page_id
2593 params.nav_entry_id = 0;
2594 params.did_create_new_entry = false;
2595 params.url = url;
2596 params.transition = ui::PAGE_TRANSITION_LINK;
2597 params.redirects.push_back(url);
2598 params.should_update_history = true;
2599 params.gesture = NavigationGestureUnknown;
2600 params.is_post = false;
2601 params.page_state = PageState::CreateFromURL(url);
2602 params.was_within_same_page = true;
2604 // This should NOT generate a new entry, nor prune the list.
2605 LoadCommittedDetails details;
2606 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2607 &details));
2608 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2609 navigation_entry_committed_counter_ = 0;
2610 EXPECT_TRUE(details.is_in_page);
2611 EXPECT_TRUE(details.did_replace_entry);
2612 EXPECT_EQ(2, controller.GetEntryCount());
2615 // Perform a client redirect to a new page.
2617 const GURL url("http://foo3/");
2618 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2619 params.page_id = 2; // New page_id
2620 params.nav_entry_id = 0;
2621 params.did_create_new_entry = true;
2622 params.url = url;
2623 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
2624 params.redirects.push_back(GURL("http://foo2/#a"));
2625 params.redirects.push_back(url);
2626 params.should_update_history = true;
2627 params.gesture = NavigationGestureUnknown;
2628 params.is_post = false;
2629 params.page_state = PageState::CreateFromURL(url);
2631 // This SHOULD generate a new entry.
2632 LoadCommittedDetails details;
2633 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2634 &details));
2635 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2636 navigation_entry_committed_counter_ = 0;
2637 EXPECT_FALSE(details.is_in_page);
2638 EXPECT_EQ(3, controller.GetEntryCount());
2641 // Verify that BACK brings us back to http://foo2/.
2643 const GURL url("http://foo2/");
2644 controller.GoBack();
2645 int entry_id = controller.GetPendingEntry()->GetUniqueID();
2646 main_test_rfh()->PrepareForCommit();
2647 main_test_rfh()->SendNavigate(1, entry_id, false, url);
2648 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2649 navigation_entry_committed_counter_ = 0;
2650 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2654 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
2656 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2657 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2658 GURL url("http://foo");
2659 params.page_id = 1;
2660 params.nav_entry_id = 0;
2661 params.did_create_new_entry = true;
2662 params.url = url;
2663 params.page_state = PageState::CreateFromURL(url);
2664 params.was_within_same_page = true;
2665 main_test_rfh()->SendRendererInitiatedNavigationRequest(url, false);
2666 main_test_rfh()->PrepareForCommit();
2667 contents()->GetMainFrame()->SendNavigateWithParams(&params);
2668 // We pass if we don't crash.
2671 // NotificationObserver implementation used in verifying we've received the
2672 // NOTIFICATION_NAV_LIST_PRUNED method.
2673 class PrunedListener : public NotificationObserver {
2674 public:
2675 explicit PrunedListener(NavigationControllerImpl* controller)
2676 : notification_count_(0) {
2677 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2678 Source<NavigationController>(controller));
2681 void Observe(int type,
2682 const NotificationSource& source,
2683 const NotificationDetails& details) override {
2684 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2685 notification_count_++;
2686 details_ = *(Details<PrunedDetails>(details).ptr());
2690 // Number of times NAV_LIST_PRUNED has been observed.
2691 int notification_count_;
2693 // Details from the last NAV_LIST_PRUNED.
2694 PrunedDetails details_;
2696 private:
2697 NotificationRegistrar registrar_;
2699 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2702 // Tests that we limit the number of navigation entries created correctly.
2703 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2704 NavigationControllerImpl& controller = controller_impl();
2705 size_t original_count = NavigationControllerImpl::max_entry_count();
2706 const int kMaxEntryCount = 5;
2708 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2710 int url_index;
2711 // Load up to the max count, all entries should be there.
2712 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2713 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2714 controller.LoadURL(
2715 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2716 int entry_id = controller.GetPendingEntry()->GetUniqueID();
2717 main_test_rfh()->PrepareForCommit();
2718 main_test_rfh()->SendNavigate(url_index, entry_id, true, url);
2721 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2723 // Created a PrunedListener to observe prune notifications.
2724 PrunedListener listener(&controller);
2726 // Navigate some more.
2727 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2728 controller.LoadURL(
2729 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2730 int entry_id = controller.GetPendingEntry()->GetUniqueID();
2731 main_test_rfh()->PrepareForCommit();
2732 main_test_rfh()->SendNavigate(url_index, entry_id, true, url);
2733 url_index++;
2735 // We should have got a pruned navigation.
2736 EXPECT_EQ(1, listener.notification_count_);
2737 EXPECT_TRUE(listener.details_.from_front);
2738 EXPECT_EQ(1, listener.details_.count);
2740 // We expect http://www.a.com/0 to be gone.
2741 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2742 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2743 GURL("http://www.a.com/1"));
2745 // More navigations.
2746 for (int i = 0; i < 3; i++) {
2747 url = GURL(base::StringPrintf("http://www.a.com/%d", url_index));
2748 controller.LoadURL(
2749 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2750 int entry_id = controller.GetPendingEntry()->GetUniqueID();
2751 main_test_rfh()->PrepareForCommit();
2752 main_test_rfh()->SendNavigate(url_index, entry_id, true, url);
2753 url_index++;
2755 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2756 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2757 GURL("http://www.a.com/4"));
2759 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2762 // Tests that we can do a restore and navigate to the restored entries and
2763 // everything is updated properly. This can be tricky since there is no
2764 // SiteInstance for the entries created initially.
2765 TEST_F(NavigationControllerTest, RestoreNavigate) {
2766 // Create a NavigationController with a restored set of tabs.
2767 GURL url("http://foo");
2768 ScopedVector<NavigationEntry> entries;
2769 scoped_ptr<NavigationEntry> entry =
2770 NavigationControllerImpl::CreateNavigationEntry(
2771 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2772 browser_context());
2773 entry->SetPageID(0);
2774 entry->SetTitle(base::ASCIIToUTF16("Title"));
2775 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2776 const base::Time timestamp = base::Time::Now();
2777 entry->SetTimestamp(timestamp);
2778 entries.push_back(entry.Pass());
2779 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2780 WebContents::Create(WebContents::CreateParams(browser_context()))));
2781 NavigationControllerImpl& our_controller = our_contents->GetController();
2782 our_controller.Restore(
2784 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2785 &entries);
2786 ASSERT_EQ(0u, entries.size());
2788 // Before navigating to the restored entry, it should have a restore_type
2789 // and no SiteInstance.
2790 ASSERT_EQ(1, our_controller.GetEntryCount());
2791 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2792 our_controller.GetEntryAtIndex(0)->restore_type());
2793 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2795 // After navigating, we should have one entry, and it should be "pending".
2796 // It should now have a SiteInstance and no restore_type.
2797 our_controller.GoToIndex(0);
2798 EXPECT_EQ(1, our_controller.GetEntryCount());
2799 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2800 our_controller.GetPendingEntry());
2801 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2802 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2803 our_controller.GetEntryAtIndex(0)->restore_type());
2804 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2806 // Timestamp should remain the same before the navigation finishes.
2807 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2809 // Say we navigated to that entry.
2810 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2811 params.page_id = 0;
2812 params.nav_entry_id = our_controller.GetPendingEntry()->GetUniqueID();
2813 params.did_create_new_entry = false;
2814 params.url = url;
2815 params.transition = ui::PAGE_TRANSITION_LINK;
2816 params.should_update_history = false;
2817 params.gesture = NavigationGestureUser;
2818 params.is_post = false;
2819 params.page_state = PageState::CreateFromURL(url);
2820 LoadCommittedDetails details;
2821 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2822 &details);
2824 // There should be no longer any pending entry and one committed one. This
2825 // means that we were able to locate the entry, assign its site instance, and
2826 // commit it properly.
2827 EXPECT_EQ(1, our_controller.GetEntryCount());
2828 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2829 EXPECT_FALSE(our_controller.GetPendingEntry());
2830 EXPECT_EQ(
2831 url,
2832 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2833 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2834 our_controller.GetEntryAtIndex(0)->restore_type());
2836 // Timestamp should have been updated.
2837 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2840 // Tests that we can still navigate to a restored entry after a different
2841 // navigation fails and clears the pending entry. http://crbug.com/90085
2842 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2843 // Create a NavigationController with a restored set of tabs.
2844 GURL url("http://foo");
2845 ScopedVector<NavigationEntry> entries;
2846 scoped_ptr<NavigationEntry> new_entry =
2847 NavigationControllerImpl::CreateNavigationEntry(
2848 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2849 browser_context());
2850 new_entry->SetPageID(0);
2851 new_entry->SetTitle(base::ASCIIToUTF16("Title"));
2852 new_entry->SetPageState(PageState::CreateFromEncodedData("state"));
2853 entries.push_back(new_entry.Pass());
2854 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2855 WebContents::Create(WebContents::CreateParams(browser_context()))));
2856 NavigationControllerImpl& our_controller = our_contents->GetController();
2857 our_controller.Restore(
2858 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2859 ASSERT_EQ(0u, entries.size());
2861 // Ensure the RenderFrame is initialized before simulating events coming from
2862 // it.
2863 main_test_rfh()->InitializeRenderFrameIfNeeded();
2865 // Before navigating to the restored entry, it should have a restore_type
2866 // and no SiteInstance.
2867 NavigationEntry* entry = our_controller.GetEntryAtIndex(0);
2868 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2869 our_controller.GetEntryAtIndex(0)->restore_type());
2870 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2872 // After navigating, we should have one entry, and it should be "pending".
2873 // It should now have a SiteInstance and no restore_type.
2874 our_controller.GoToIndex(0);
2875 EXPECT_EQ(1, our_controller.GetEntryCount());
2876 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2877 our_controller.GetPendingEntry());
2878 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2879 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2880 our_controller.GetEntryAtIndex(0)->restore_type());
2881 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2883 // This pending navigation may have caused a different navigation to fail,
2884 // which causes the pending entry to be cleared.
2885 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2886 fail_load_params.error_code = net::ERR_ABORTED;
2887 fail_load_params.error_description = base::string16();
2888 fail_load_params.url = url;
2889 fail_load_params.showing_repost_interstitial = false;
2890 main_test_rfh()->InitializeRenderFrameIfNeeded();
2891 main_test_rfh()->OnMessageReceived(
2892 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2893 fail_load_params));
2895 // Now the pending restored entry commits.
2896 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2897 params.page_id = 0;
2898 params.nav_entry_id = entry->GetUniqueID();
2899 params.did_create_new_entry = false;
2900 params.url = url;
2901 params.transition = ui::PAGE_TRANSITION_LINK;
2902 params.should_update_history = false;
2903 params.gesture = NavigationGestureUser;
2904 params.is_post = false;
2905 params.page_state = PageState::CreateFromURL(url);
2906 LoadCommittedDetails details;
2907 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2908 &details);
2910 // There should be no pending entry and one committed one.
2911 EXPECT_EQ(1, our_controller.GetEntryCount());
2912 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2913 EXPECT_FALSE(our_controller.GetPendingEntry());
2914 EXPECT_EQ(
2915 url,
2916 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2917 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2918 our_controller.GetEntryAtIndex(0)->restore_type());
2921 // Make sure that the page type and stuff is correct after an interstitial.
2922 TEST_F(NavigationControllerTest, Interstitial) {
2923 NavigationControllerImpl& controller = controller_impl();
2924 // First navigate somewhere normal.
2925 const GURL url1("http://foo");
2926 controller.LoadURL(
2927 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2928 int entry_id = controller.GetPendingEntry()->GetUniqueID();
2929 main_test_rfh()->PrepareForCommit();
2930 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
2932 // Now navigate somewhere with an interstitial.
2933 const GURL url2("http://bar");
2934 controller.LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_TYPED,
2935 std::string());
2936 entry_id = controller.GetPendingEntry()->GetUniqueID();
2937 controller.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL);
2939 // At this point the interstitial will be displayed and the load will still
2940 // be pending. If the user continues, the load will commit.
2941 main_test_rfh()->PrepareForCommit();
2942 main_test_rfh()->SendNavigate(1, entry_id, true, url2);
2944 // The page should be a normal page again.
2945 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2946 EXPECT_EQ(PAGE_TYPE_NORMAL,
2947 controller.GetLastCommittedEntry()->GetPageType());
2950 TEST_F(NavigationControllerTest, RemoveEntry) {
2951 NavigationControllerImpl& controller = controller_impl();
2952 const GURL url1("http://foo/1");
2953 const GURL url2("http://foo/2");
2954 const GURL url3("http://foo/3");
2955 const GURL url4("http://foo/4");
2956 const GURL url5("http://foo/5");
2957 const GURL pending_url("http://foo/pending");
2958 const GURL default_url("http://foo/default");
2960 controller.LoadURL(
2961 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2962 int entry_id = controller.GetPendingEntry()->GetUniqueID();
2963 main_test_rfh()->PrepareForCommit();
2964 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
2965 controller.LoadURL(
2966 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2967 entry_id = controller.GetPendingEntry()->GetUniqueID();
2968 main_test_rfh()->PrepareForCommit();
2969 main_test_rfh()->SendNavigate(1, entry_id, true, url2);
2970 controller.LoadURL(
2971 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2972 entry_id = controller.GetPendingEntry()->GetUniqueID();
2973 main_test_rfh()->PrepareForCommit();
2974 main_test_rfh()->SendNavigate(2, entry_id, true, url3);
2975 controller.LoadURL(
2976 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2977 entry_id = controller.GetPendingEntry()->GetUniqueID();
2978 main_test_rfh()->PrepareForCommit();
2979 main_test_rfh()->SendNavigate(3, entry_id, true, url4);
2980 controller.LoadURL(
2981 url5, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2982 entry_id = controller.GetPendingEntry()->GetUniqueID();
2983 main_test_rfh()->PrepareForCommit();
2984 main_test_rfh()->SendNavigate(4, entry_id, true, url5);
2986 // Try to remove the last entry. Will fail because it is the current entry.
2987 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2988 EXPECT_EQ(5, controller.GetEntryCount());
2989 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2991 // Go back, but don't commit yet. Check that we can't delete the current
2992 // and pending entries.
2993 controller.GoBack();
2994 entry_id = controller.GetPendingEntry()->GetUniqueID();
2995 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2996 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2998 // Now commit and delete the last entry.
2999 main_test_rfh()->PrepareForCommit();
3000 main_test_rfh()->SendNavigate(3, entry_id, false, url4);
3001 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
3002 EXPECT_EQ(4, controller.GetEntryCount());
3003 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
3004 EXPECT_FALSE(controller.GetPendingEntry());
3006 // Remove an entry which is not the last committed one.
3007 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
3008 EXPECT_EQ(3, controller.GetEntryCount());
3009 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
3010 EXPECT_FALSE(controller.GetPendingEntry());
3012 // Remove the 2 remaining entries.
3013 controller.RemoveEntryAtIndex(1);
3014 controller.RemoveEntryAtIndex(0);
3016 // This should leave us with only the last committed entry.
3017 EXPECT_EQ(1, controller.GetEntryCount());
3018 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
3021 TEST_F(NavigationControllerTest, RemoveEntryWithPending) {
3022 NavigationControllerImpl& controller = controller_impl();
3023 const GURL url1("http://foo/1");
3024 const GURL url2("http://foo/2");
3025 const GURL url3("http://foo/3");
3026 const GURL default_url("http://foo/default");
3028 controller.LoadURL(
3029 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3030 int entry_id = controller.GetPendingEntry()->GetUniqueID();
3031 main_test_rfh()->PrepareForCommit();
3032 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
3033 controller.LoadURL(
3034 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3035 entry_id = controller.GetPendingEntry()->GetUniqueID();
3036 main_test_rfh()->PrepareForCommit();
3037 main_test_rfh()->SendNavigate(1, entry_id, true, url2);
3038 controller.LoadURL(
3039 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3040 entry_id = controller.GetPendingEntry()->GetUniqueID();
3041 main_test_rfh()->PrepareForCommit();
3042 main_test_rfh()->SendNavigate(2, entry_id, true, url3);
3044 // Go back, but don't commit yet. Check that we can't delete the current
3045 // and pending entries.
3046 controller.GoBack();
3047 entry_id = controller.GetPendingEntry()->GetUniqueID();
3048 EXPECT_FALSE(controller.RemoveEntryAtIndex(2));
3049 EXPECT_FALSE(controller.RemoveEntryAtIndex(1));
3051 // Remove the first entry, while there is a pending entry. This is expected
3052 // to discard the pending entry.
3053 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
3054 EXPECT_FALSE(controller.GetPendingEntry());
3055 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3057 // We should update the last committed entry index.
3058 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
3060 // Now commit and ensure we land on the right entry.
3061 main_test_rfh()->PrepareForCommit();
3062 main_test_rfh()->SendNavigate(1, entry_id, false, url2);
3063 EXPECT_EQ(2, controller.GetEntryCount());
3064 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
3065 EXPECT_FALSE(controller.GetPendingEntry());
3068 // Tests the transient entry, making sure it goes away with all navigations.
3069 TEST_F(NavigationControllerTest, TransientEntry) {
3070 NavigationControllerImpl& controller = controller_impl();
3071 TestNotificationTracker notifications;
3072 RegisterForAllNavNotifications(&notifications, &controller);
3074 const GURL url0("http://foo/0");
3075 const GURL url1("http://foo/1");
3076 const GURL url2("http://foo/2");
3077 const GURL url3("http://foo/3");
3078 const GURL url3_ref("http://foo/3#bar");
3079 const GURL url4("http://foo/4");
3080 const GURL transient_url("http://foo/transient");
3082 controller.LoadURL(
3083 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3084 int entry_id = controller.GetPendingEntry()->GetUniqueID();
3085 main_test_rfh()->PrepareForCommit();
3086 main_test_rfh()->SendNavigate(0, entry_id, true, url0);
3087 controller.LoadURL(
3088 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3089 entry_id = controller.GetPendingEntry()->GetUniqueID();
3090 main_test_rfh()->PrepareForCommit();
3091 main_test_rfh()->SendNavigate(1, entry_id, true, url1);
3093 notifications.Reset();
3095 // Adding a transient with no pending entry.
3096 scoped_ptr<NavigationEntry> transient_entry(new NavigationEntryImpl);
3097 transient_entry->SetURL(transient_url);
3098 controller.SetTransientEntry(transient_entry.Pass());
3100 // We should not have received any notifications.
3101 EXPECT_EQ(0U, notifications.size());
3103 // Check our state.
3104 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3105 EXPECT_EQ(controller.GetEntryCount(), 3);
3106 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
3107 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
3108 EXPECT_TRUE(controller.GetLastCommittedEntry());
3109 EXPECT_FALSE(controller.GetPendingEntry());
3110 EXPECT_TRUE(controller.CanGoBack());
3111 EXPECT_FALSE(controller.CanGoForward());
3112 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3114 // Navigate.
3115 controller.LoadURL(
3116 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3117 entry_id = controller.GetPendingEntry()->GetUniqueID();
3118 main_test_rfh()->PrepareForCommit();
3119 main_test_rfh()->SendNavigate(2, entry_id, true, url2);
3121 // We should have navigated, transient entry should be gone.
3122 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3123 EXPECT_EQ(controller.GetEntryCount(), 3);
3125 // Add a transient again, then navigate with no pending entry this time.
3126 transient_entry.reset(new NavigationEntryImpl);
3127 transient_entry->SetURL(transient_url);
3128 controller.SetTransientEntry(transient_entry.Pass());
3129 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3130 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3, true);
3131 main_test_rfh()->PrepareForCommit();
3132 main_test_rfh()->SendNavigate(3, 0, true, url3);
3133 // Transient entry should be gone.
3134 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
3135 EXPECT_EQ(controller.GetEntryCount(), 4);
3137 // Initiate a navigation, add a transient then commit navigation.
3138 controller.LoadURL(
3139 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3140 entry_id = controller.GetPendingEntry()->GetUniqueID();
3141 transient_entry.reset(new NavigationEntryImpl);
3142 transient_entry->SetURL(transient_url);
3143 controller.SetTransientEntry(transient_entry.Pass());
3144 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3145 main_test_rfh()->PrepareForCommit();
3146 main_test_rfh()->SendNavigate(4, entry_id, true, url4);
3147 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
3148 EXPECT_EQ(controller.GetEntryCount(), 5);
3150 // Add a transient and go back. This should simply remove the transient.
3151 transient_entry.reset(new NavigationEntryImpl);
3152 transient_entry->SetURL(transient_url);
3153 controller.SetTransientEntry(transient_entry.Pass());
3154 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3155 EXPECT_TRUE(controller.CanGoBack());
3156 EXPECT_FALSE(controller.CanGoForward());
3157 controller.GoBack();
3158 // Transient entry should be gone.
3159 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
3160 EXPECT_EQ(controller.GetEntryCount(), 5);
3162 // Suppose the page requested a history navigation backward.
3163 controller.GoToOffset(-1);
3164 entry_id = controller.GetPendingEntry()->GetUniqueID();
3165 main_test_rfh()->PrepareForCommit();
3166 main_test_rfh()->SendNavigate(3, entry_id, false, url3);
3168 // Add a transient and go to an entry before the current one.
3169 transient_entry.reset(new NavigationEntryImpl);
3170 transient_entry->SetURL(transient_url);
3171 controller.SetTransientEntry(transient_entry.Pass());
3172 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3173 controller.GoToIndex(1);
3174 entry_id = controller.GetPendingEntry()->GetUniqueID();
3175 // The navigation should have been initiated, transient entry should be gone.
3176 EXPECT_FALSE(controller.GetTransientEntry());
3177 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3178 // Visible entry does not update for history navigations until commit.
3179 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
3180 main_test_rfh()->PrepareForCommit();
3181 main_test_rfh()->SendNavigate(1, entry_id, false, url1);
3182 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3184 // Add a transient and go to an entry after the current one.
3185 transient_entry.reset(new NavigationEntryImpl);
3186 transient_entry->SetURL(transient_url);
3187 controller.SetTransientEntry(transient_entry.Pass());
3188 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3189 controller.GoToIndex(3);
3190 entry_id = controller.GetPendingEntry()->GetUniqueID();
3191 // The navigation should have been initiated, transient entry should be gone.
3192 // Because of the transient entry that is removed, going to index 3 makes us
3193 // land on url2 (which is visible after the commit).
3194 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
3195 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3196 main_test_rfh()->PrepareForCommit();
3197 main_test_rfh()->SendNavigate(2, entry_id, false, url2);
3198 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3200 // Add a transient and go forward.
3201 transient_entry.reset(new NavigationEntryImpl);
3202 transient_entry->SetURL(transient_url);
3203 controller.SetTransientEntry(transient_entry.Pass());
3204 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3205 EXPECT_TRUE(controller.CanGoForward());
3206 controller.GoForward();
3207 entry_id = controller.GetPendingEntry()->GetUniqueID();
3208 // We should have navigated, transient entry should be gone.
3209 EXPECT_FALSE(controller.GetTransientEntry());
3210 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
3211 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3212 main_test_rfh()->PrepareForCommit();
3213 main_test_rfh()->SendNavigate(3, entry_id, false, url3);
3214 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
3216 // Add a transient and do an in-page navigation, replacing the current entry.
3217 transient_entry.reset(new NavigationEntryImpl);
3218 transient_entry->SetURL(transient_url);
3219 controller.SetTransientEntry(transient_entry.Pass());
3220 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3222 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref, false);
3223 main_test_rfh()->PrepareForCommit();
3224 main_test_rfh()->SendNavigate(3, 0, false, url3_ref);
3225 // Transient entry should be gone.
3226 EXPECT_FALSE(controller.GetTransientEntry());
3227 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
3229 // Ensure the URLs are correct.
3230 EXPECT_EQ(controller.GetEntryCount(), 5);
3231 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
3232 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
3233 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
3234 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
3235 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
3238 // Test that Reload initiates a new navigation to a transient entry's URL.
3239 TEST_F(NavigationControllerTest, ReloadTransient) {
3240 NavigationControllerImpl& controller = controller_impl();
3241 const GURL url0("http://foo/0");
3242 const GURL url1("http://foo/1");
3243 const GURL transient_url("http://foo/transient");
3245 // Load |url0|, and start a pending navigation to |url1|.
3246 controller.LoadURL(
3247 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3248 int entry_id = controller.GetPendingEntry()->GetUniqueID();
3249 main_test_rfh()->PrepareForCommit();
3250 main_test_rfh()->SendNavigate(0, entry_id, true, url0);
3251 controller.LoadURL(
3252 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3254 // A transient entry is added, interrupting the navigation.
3255 scoped_ptr<NavigationEntry> transient_entry(new NavigationEntryImpl);
3256 transient_entry->SetURL(transient_url);
3257 controller.SetTransientEntry(transient_entry.Pass());
3258 EXPECT_TRUE(controller.GetTransientEntry());
3259 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3261 // The page is reloaded, which should remove the pending entry for |url1| and
3262 // the transient entry for |transient_url|, and start a navigation to
3263 // |transient_url|.
3264 controller.Reload(true);
3265 entry_id = controller.GetPendingEntry()->GetUniqueID();
3266 EXPECT_FALSE(controller.GetTransientEntry());
3267 EXPECT_TRUE(controller.GetPendingEntry());
3268 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
3269 ASSERT_EQ(controller.GetEntryCount(), 1);
3270 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
3272 // Load of |transient_url| completes.
3273 main_test_rfh()->PrepareForCommit();
3274 main_test_rfh()->SendNavigate(1, entry_id, true, transient_url);
3275 ASSERT_EQ(controller.GetEntryCount(), 2);
3276 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
3277 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
3280 // Ensure that renderer initiated pending entries get replaced, so that we
3281 // don't show a stale virtual URL when a navigation commits.
3282 // See http://crbug.com/266922.
3283 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
3284 NavigationControllerImpl& controller = controller_impl();
3285 Navigator* navigator =
3286 contents()->GetFrameTree()->root()->navigator();
3288 const GURL url1("nonexistent:12121");
3289 const GURL url1_fixed("http://nonexistent:12121/");
3290 const GURL url2("http://foo");
3292 // We create pending entries for renderer-initiated navigations so that we
3293 // can show them in new tabs when it is safe.
3294 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1, false);
3295 main_test_rfh()->PrepareForCommit();
3296 navigator->DidStartProvisionalLoad(main_test_rfh(), url1);
3298 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3299 // the virtual URL to differ from the URL.
3300 controller.GetPendingEntry()->SetURL(url1_fixed);
3301 controller.GetPendingEntry()->SetVirtualURL(url1);
3303 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
3304 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
3305 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3307 // If the user clicks another link, we should replace the pending entry.
3308 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2, false);
3309 main_test_rfh()->PrepareForCommit();
3310 navigator->DidStartProvisionalLoad(main_test_rfh(), url2);
3311 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
3312 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
3314 // Once it commits, the URL and virtual URL should reflect the actual page.
3315 main_test_rfh()->SendNavigate(0, 0, true, url2);
3316 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
3317 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
3319 // We should not replace the pending entry for an error URL.
3320 navigator->DidStartProvisionalLoad(main_test_rfh(), url1);
3321 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3322 navigator->DidStartProvisionalLoad(main_test_rfh(),
3323 GURL(kUnreachableWebDataURL));
3324 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3326 // We should remember if the pending entry will replace the current one.
3327 // http://crbug.com/308444.
3328 navigator->DidStartProvisionalLoad(main_test_rfh(), url1);
3329 controller.GetPendingEntry()->set_should_replace_entry(true);
3331 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2, false);
3332 main_test_rfh()->PrepareForCommit();
3333 navigator->DidStartProvisionalLoad(main_test_rfh(), url2);
3334 EXPECT_TRUE(controller.GetPendingEntry()->should_replace_entry());
3335 main_test_rfh()->SendNavigate(0, 0, false, url2);
3336 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
3339 // Tests that the URLs for renderer-initiated navigations are not displayed to
3340 // the user until the navigation commits, to prevent URL spoof attacks.
3341 // See http://crbug.com/99016.
3342 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
3343 NavigationControllerImpl& controller = controller_impl();
3344 TestNotificationTracker notifications;
3345 RegisterForAllNavNotifications(&notifications, &controller);
3347 const GURL url0("http://foo/0");
3348 const GURL url1("http://foo/1");
3350 // For typed navigations (browser-initiated), both pending and visible entries
3351 // should update before commit.
3352 controller.LoadURL(
3353 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3354 int entry_id = controller.GetPendingEntry()->GetUniqueID();
3355 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
3356 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
3357 main_test_rfh()->PrepareForCommit();
3358 main_test_rfh()->SendNavigate(0, entry_id, true, url0);
3360 // For link clicks (renderer-initiated navigations), the pending entry should
3361 // update before commit but the visible should not.
3362 NavigationController::LoadURLParams load_url_params(url1);
3363 load_url_params.is_renderer_initiated = true;
3364 controller.LoadURLWithParams(load_url_params);
3365 entry_id = controller.GetPendingEntry()->GetUniqueID();
3366 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
3367 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3368 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3370 // After commit, both visible should be updated, there should be no pending
3371 // entry, and we should no longer treat the entry as renderer-initiated.
3372 main_test_rfh()->PrepareForCommit();
3373 main_test_rfh()->SendNavigate(1, entry_id, true, url1);
3374 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3375 EXPECT_FALSE(controller.GetPendingEntry());
3376 EXPECT_FALSE(controller.GetLastCommittedEntry()->is_renderer_initiated());
3378 notifications.Reset();
3381 // Tests that the URLs for renderer-initiated navigations in new tabs are
3382 // displayed to the user before commit, as long as the initial about:blank
3383 // page has not been modified. If so, we must revert to showing about:blank.
3384 // See http://crbug.com/9682.
3385 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
3386 NavigationControllerImpl& controller = controller_impl();
3387 TestNotificationTracker notifications;
3388 RegisterForAllNavNotifications(&notifications, &controller);
3390 const GURL url("http://foo");
3392 // For renderer-initiated navigations in new tabs (with no committed entries),
3393 // we show the pending entry's URL as long as the about:blank page is not
3394 // modified.
3395 NavigationController::LoadURLParams load_url_params(url);
3396 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3397 load_url_params.is_renderer_initiated = true;
3398 controller.LoadURLWithParams(load_url_params);
3399 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3400 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3401 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3402 EXPECT_TRUE(controller.IsInitialNavigation());
3403 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3405 // There should be no title yet.
3406 EXPECT_TRUE(contents()->GetTitle().empty());
3408 // If something else modifies the contents of the about:blank page, then
3409 // we must revert to showing about:blank to avoid a URL spoof.
3410 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3411 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3412 EXPECT_FALSE(controller.GetVisibleEntry());
3413 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3415 notifications.Reset();
3418 // Tests that the URLs for browser-initiated navigations in new tabs are
3419 // displayed to the user even after they fail, as long as the initial
3420 // about:blank page has not been modified. If so, we must revert to showing
3421 // about:blank. See http://crbug.com/355537.
3422 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
3423 NavigationControllerImpl& controller = controller_impl();
3424 TestNotificationTracker notifications;
3425 RegisterForAllNavNotifications(&notifications, &controller);
3427 const GURL url("http://foo");
3429 // For browser-initiated navigations in new tabs (with no committed entries),
3430 // we show the pending entry's URL as long as the about:blank page is not
3431 // modified. This is possible in cases that the user types a URL into a popup
3432 // tab created with a slow URL.
3433 NavigationController::LoadURLParams load_url_params(url);
3434 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
3435 load_url_params.is_renderer_initiated = false;
3436 controller.LoadURLWithParams(load_url_params);
3437 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3438 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3439 EXPECT_FALSE(controller.GetPendingEntry()->is_renderer_initiated());
3440 EXPECT_TRUE(controller.IsInitialNavigation());
3441 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3443 // There should be no title yet.
3444 EXPECT_TRUE(contents()->GetTitle().empty());
3446 // Suppose it aborts before committing, if it's a 204 or download or due to a
3447 // stop or a new navigation from the user. The URL should remain visible.
3448 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3449 params.error_code = net::ERR_ABORTED;
3450 params.error_description = base::string16();
3451 params.url = url;
3452 params.showing_repost_interstitial = false;
3453 main_test_rfh()->OnMessageReceived(
3454 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3455 contents()->SetIsLoading(false, true, NULL);
3456 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3458 // If something else later modifies the contents of the about:blank page, then
3459 // we must revert to showing about:blank to avoid a URL spoof.
3460 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3461 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3462 EXPECT_FALSE(controller.GetVisibleEntry());
3463 EXPECT_FALSE(controller.GetPendingEntry());
3465 notifications.Reset();
3468 // Tests that the URLs for renderer-initiated navigations in new tabs are
3469 // displayed to the user even after they fail, as long as the initial
3470 // about:blank page has not been modified. If so, we must revert to showing
3471 // about:blank. See http://crbug.com/355537.
3472 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
3473 NavigationControllerImpl& controller = controller_impl();
3474 TestNotificationTracker notifications;
3475 RegisterForAllNavNotifications(&notifications, &controller);
3477 const GURL url("http://foo");
3479 // For renderer-initiated navigations in new tabs (with no committed entries),
3480 // we show the pending entry's URL as long as the about:blank page is not
3481 // modified.
3482 NavigationController::LoadURLParams load_url_params(url);
3483 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3484 load_url_params.is_renderer_initiated = true;
3485 controller.LoadURLWithParams(load_url_params);
3486 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3487 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3488 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3489 EXPECT_TRUE(controller.IsInitialNavigation());
3490 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3492 // There should be no title yet.
3493 EXPECT_TRUE(contents()->GetTitle().empty());
3495 // Suppose it aborts before committing, if it's a 204 or download or due to a
3496 // stop or a new navigation from the user. The URL should remain visible.
3497 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3498 params.error_code = net::ERR_ABORTED;
3499 params.error_description = base::string16();
3500 params.url = url;
3501 params.showing_repost_interstitial = false;
3502 main_test_rfh()->OnMessageReceived(
3503 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3504 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3506 // If something else later modifies the contents of the about:blank page, then
3507 // we must revert to showing about:blank to avoid a URL spoof.
3508 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3509 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3510 EXPECT_FALSE(controller.GetVisibleEntry());
3511 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3513 notifications.Reset();
3516 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3517 NavigationControllerImpl& controller = controller_impl();
3518 TestNotificationTracker notifications;
3519 RegisterForAllNavNotifications(&notifications, &controller);
3521 const GURL url1("http://foo/eh");
3522 const GURL url2("http://foo/bee");
3524 // For renderer-initiated navigations in new tabs (with no committed entries),
3525 // we show the pending entry's URL as long as the about:blank page is not
3526 // modified.
3527 NavigationController::LoadURLParams load_url_params(url1);
3528 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3529 load_url_params.is_renderer_initiated = true;
3530 controller.LoadURLWithParams(load_url_params);
3531 int entry_id = controller.GetPendingEntry()->GetUniqueID();
3532 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3533 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3534 EXPECT_TRUE(controller.IsInitialNavigation());
3535 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3537 // Simulate a commit and then starting a new pending navigation.
3538 main_test_rfh()->PrepareForCommit();
3539 main_test_rfh()->SendNavigate(0, entry_id, true, url1);
3540 NavigationController::LoadURLParams load_url2_params(url2);
3541 load_url2_params.transition_type = ui::PAGE_TRANSITION_LINK;
3542 load_url2_params.is_renderer_initiated = true;
3543 controller.LoadURLWithParams(load_url2_params);
3545 // We should not consider this an initial navigation, and thus should
3546 // not show the pending URL.
3547 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3548 EXPECT_FALSE(controller.IsInitialNavigation());
3549 EXPECT_TRUE(controller.GetVisibleEntry());
3550 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3552 notifications.Reset();
3555 // Tests that IsInPageNavigation returns appropriate results. Prevents
3556 // regression for bug 1126349.
3557 TEST_F(NavigationControllerTest, IsInPageNavigation) {
3558 NavigationControllerImpl& controller = controller_impl();
3559 const GURL url("http://www.google.com/home.html");
3561 // If the renderer claims it performed an in-page navigation from
3562 // about:blank, trust the renderer.
3563 // This can happen when an iframe is created and populated via
3564 // document.write(), then tries to perform a fragment navigation.
3565 // TODO(japhet): We should only trust the renderer if the about:blank
3566 // was the first document in the given frame, but we don't have enough
3567 // information to identify that case currently.
3568 const GURL blank_url(url::kAboutBlankURL);
3569 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url);
3570 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3571 main_test_rfh()));
3573 // Navigate to URL with no refs.
3574 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url);
3576 // Reloading the page is not an in-page navigation.
3577 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false, main_test_rfh()));
3578 const GURL other_url("http://www.google.com/add.html");
3579 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3580 main_test_rfh()));
3581 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3582 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3583 main_test_rfh()));
3585 // Navigate to URL with refs.
3586 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref);
3588 // Reloading the page is not an in-page navigation.
3589 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
3590 main_test_rfh()));
3591 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3592 main_test_rfh()));
3593 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3594 main_test_rfh()));
3595 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3596 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
3597 main_test_rfh()));
3599 // Going to the same url again will be considered in-page
3600 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3601 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3602 main_test_rfh()));
3604 // Going back to the non ref url will be considered in-page if the navigation
3605 // type is IN_PAGE.
3606 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3607 main_test_rfh()));
3609 // If the renderer says this is a same-origin in-page navigation, believe it.
3610 // This is the pushState/replaceState case.
3611 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
3612 main_test_rfh()));
3614 // Test allow_universal_access_from_file_urls flag.
3615 const GURL different_origin_url("http://www.example.com");
3616 MockRenderProcessHost* rph = main_test_rfh()->GetProcess();
3617 WebPreferences prefs = test_rvh()->GetWebkitPreferences();
3618 prefs.allow_universal_access_from_file_urls = true;
3619 test_rvh()->UpdateWebkitPreferences(prefs);
3620 prefs = test_rvh()->GetWebkitPreferences();
3621 EXPECT_TRUE(prefs.allow_universal_access_from_file_urls);
3622 // Allow in page navigation if existing URL is file scheme.
3623 const GURL file_url("file:///foo/index.html");
3624 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url);
3625 EXPECT_EQ(0, rph->bad_msg_count());
3626 EXPECT_TRUE(controller.IsURLInPageNavigation(different_origin_url, true,
3627 main_test_rfh()));
3628 EXPECT_EQ(0, rph->bad_msg_count());
3629 // Don't honor allow_universal_access_from_file_urls if existing URL is
3630 // not file scheme.
3631 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url);
3632 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3633 main_test_rfh()));
3634 EXPECT_EQ(1, rph->bad_msg_count());
3636 // Remove allow_universal_access_from_file_urls flag.
3637 prefs.allow_universal_access_from_file_urls = false;
3638 test_rvh()->UpdateWebkitPreferences(prefs);
3639 prefs = test_rvh()->GetWebkitPreferences();
3640 EXPECT_FALSE(prefs.allow_universal_access_from_file_urls);
3642 // Don't believe the renderer if it claims a cross-origin navigation is
3643 // in-page.
3644 EXPECT_EQ(1, rph->bad_msg_count());
3645 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3646 main_test_rfh()));
3647 EXPECT_EQ(2, rph->bad_msg_count());
3650 // Some pages can have subframes with the same base URL (minus the reference) as
3651 // the main page. Even though this is hard, it can happen, and we don't want
3652 // these subframe navigations to affect the toplevel document. They should
3653 // instead be ignored. http://crbug.com/5585
3654 TEST_F(NavigationControllerTest, SameSubframe) {
3655 NavigationControllerImpl& controller = controller_impl();
3656 // Navigate the main frame.
3657 const GURL url("http://www.google.com/");
3658 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url);
3660 // We should be at the first navigation entry.
3661 EXPECT_EQ(controller.GetEntryCount(), 1);
3662 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3664 // Add and navigate a subframe that would normally count as in-page.
3665 main_test_rfh()->OnCreateChildFrame(
3666 MSG_ROUTING_NONE, blink::WebTreeScopeType::Document, std::string(),
3667 blink::WebSandboxFlags::None);
3668 RenderFrameHostImpl* subframe =
3669 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3670 const GURL subframe_url("http://www.google.com/#");
3671 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3672 params.page_id = 0;
3673 params.nav_entry_id = 0;
3674 params.did_create_new_entry = false;
3675 params.url = subframe_url;
3676 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3677 params.should_update_history = false;
3678 params.gesture = NavigationGestureAuto;
3679 params.is_post = false;
3680 params.page_state = PageState::CreateFromURL(subframe_url);
3681 LoadCommittedDetails details;
3682 EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
3684 // Nothing should have changed.
3685 EXPECT_EQ(controller.GetEntryCount(), 1);
3686 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3689 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3690 // false.
3691 TEST_F(NavigationControllerTest, CloneAndGoBack) {
3692 NavigationControllerImpl& controller = controller_impl();
3693 const GURL url1("http://foo1");
3694 const GURL url2("http://foo2");
3695 const base::string16 title(base::ASCIIToUTF16("Title"));
3697 NavigateAndCommit(url1);
3698 controller.GetVisibleEntry()->SetTitle(title);
3699 NavigateAndCommit(url2);
3701 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3703 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3704 EXPECT_TRUE(clone->GetController().NeedsReload());
3705 clone->GetController().GoBack();
3706 // Navigating back should have triggered needs_reload_ to go false.
3707 EXPECT_FALSE(clone->GetController().NeedsReload());
3709 // Ensure that the pending URL and its title are visible.
3710 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3711 EXPECT_EQ(title, clone->GetTitle());
3714 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3715 // See http://crbug.com/234491.
3716 TEST_F(NavigationControllerTest, CloneAndReload) {
3717 NavigationControllerImpl& controller = controller_impl();
3718 const GURL url1("http://foo1");
3719 const GURL url2("http://foo2");
3720 const base::string16 title(base::ASCIIToUTF16("Title"));
3722 NavigateAndCommit(url1);
3723 controller.GetVisibleEntry()->SetTitle(title);
3724 NavigateAndCommit(url2);
3726 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3727 clone->GetController().LoadIfNecessary();
3729 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3730 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3732 clone->GetController().Reload(true);
3733 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3736 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3737 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3738 NavigationControllerImpl& controller = controller_impl();
3739 const GURL url1("http://foo1");
3740 const GURL url2("http://foo2");
3742 NavigateAndCommit(url1);
3743 NavigateAndCommit(url2);
3745 // Add an interstitial entry. Should be deleted with controller.
3746 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3747 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3748 controller.SetTransientEntry(make_scoped_ptr(interstitial_entry));
3750 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3752 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3755 // Test requesting and triggering a lazy reload.
3756 TEST_F(NavigationControllerTest, LazyReload) {
3757 NavigationControllerImpl& controller = controller_impl();
3758 const GURL url("http://foo");
3759 NavigateAndCommit(url);
3760 ASSERT_FALSE(controller.NeedsReload());
3761 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD,
3762 controller.GetLastCommittedEntry()->GetTransitionType());
3764 // Request a reload to happen when the controller becomes active (e.g. after
3765 // the renderer gets killed in background on Android).
3766 controller.SetNeedsReload();
3767 ASSERT_TRUE(controller.NeedsReload());
3768 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3769 controller.GetLastCommittedEntry()->GetTransitionType());
3771 // Set the controller as active, triggering the requested reload.
3772 controller.SetActive(true);
3773 ASSERT_FALSE(controller.NeedsReload());
3774 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3775 controller.GetPendingEntry()->GetTransitionType());
3778 // Test requesting and triggering a lazy reload without any committed entry.
3779 TEST_F(NavigationControllerTest, LazyReloadWithoutCommittedEntry) {
3780 NavigationControllerImpl& controller = controller_impl();
3781 const GURL url("http://foo");
3782 controller.LoadURL(url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3783 ASSERT_FALSE(controller.NeedsReload());
3784 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3785 controller.GetPendingEntry()->GetTransitionType());
3787 // Request a reload to happen when the controller becomes active (e.g. after
3788 // the renderer gets killed in background on Android).
3789 controller.SetNeedsReload();
3790 ASSERT_TRUE(controller.NeedsReload());
3791 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3792 controller.GetPendingEntry()->GetTransitionType());
3794 // Set the controller as active, triggering the requested reload.
3795 controller.SetActive(true);
3796 ASSERT_FALSE(controller.NeedsReload());
3797 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3798 controller.GetPendingEntry()->GetTransitionType());
3801 // Tests a subframe navigation while a toplevel navigation is pending.
3802 // http://crbug.com/43967
3803 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3804 NavigationControllerImpl& controller = controller_impl();
3805 // Load the first page.
3806 const GURL url1("http://foo/");
3807 NavigateAndCommit(url1);
3809 // Now start a pending load to a totally different page, but don't commit it.
3810 const GURL url2("http://bar/");
3811 controller.LoadURL(
3812 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3814 // Send a subframe update from the first page, as if one had just
3815 // automatically loaded. Auto subframes don't increment the page ID.
3816 main_test_rfh()->OnCreateChildFrame(
3817 MSG_ROUTING_NONE, blink::WebTreeScopeType::Document, std::string(),
3818 blink::WebSandboxFlags::None);
3819 RenderFrameHostImpl* subframe =
3820 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3821 const GURL url1_sub("http://foo/subframe");
3822 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3823 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3824 params.nav_entry_id = 0;
3825 params.did_create_new_entry = false;
3826 params.url = url1_sub;
3827 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3828 params.should_update_history = false;
3829 params.gesture = NavigationGestureAuto;
3830 params.is_post = false;
3831 params.page_state = PageState::CreateFromURL(url1_sub);
3832 LoadCommittedDetails details;
3834 // This should return false meaning that nothing was actually updated.
3835 EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
3837 // The notification should have updated the last committed one, and not
3838 // the pending load.
3839 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3841 // The active entry should be unchanged by the subframe load.
3842 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3845 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3846 TEST_F(NavigationControllerTest, CopyStateFrom) {
3847 NavigationControllerImpl& controller = controller_impl();
3848 const GURL url1("http://foo1");
3849 const GURL url2("http://foo2");
3851 NavigateAndCommit(url1);
3852 NavigateAndCommit(url2);
3853 controller.GoBack();
3854 contents()->CommitPendingNavigation();
3856 scoped_ptr<TestWebContents> other_contents(
3857 static_cast<TestWebContents*>(CreateTestWebContents()));
3858 NavigationControllerImpl& other_controller = other_contents->GetController();
3859 other_controller.CopyStateFrom(controller);
3861 // other_controller should now contain 2 urls.
3862 ASSERT_EQ(2, other_controller.GetEntryCount());
3863 // We should be looking at the first one.
3864 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3866 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3867 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3868 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3869 // This is a different site than url1, so the IDs start again at 0.
3870 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3872 // The max page ID map should be copied over and updated with the max page ID
3873 // from the current tab.
3874 SiteInstance* instance1 =
3875 other_controller.GetEntryAtIndex(0)->site_instance();
3876 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3878 // Ensure the SessionStorageNamespaceMaps are the same size and have
3879 // the same partitons loaded.
3881 // TODO(ajwong): We should load a url from a different partition earlier
3882 // to make sure this map has more than one entry.
3883 const SessionStorageNamespaceMap& session_storage_namespace_map =
3884 controller.GetSessionStorageNamespaceMap();
3885 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3886 other_controller.GetSessionStorageNamespaceMap();
3887 EXPECT_EQ(session_storage_namespace_map.size(),
3888 other_session_storage_namespace_map.size());
3889 for (SessionStorageNamespaceMap::const_iterator it =
3890 session_storage_namespace_map.begin();
3891 it != session_storage_namespace_map.end();
3892 ++it) {
3893 SessionStorageNamespaceMap::const_iterator other =
3894 other_session_storage_namespace_map.find(it->first);
3895 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3899 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3900 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3901 NavigationControllerImpl& controller = controller_impl();
3902 const GURL url1("http://foo/1");
3903 const GURL url2("http://foo/2");
3904 const GURL url3("http://foo/3");
3906 NavigateAndCommit(url1);
3907 NavigateAndCommit(url2);
3909 // First two entries should have the same SiteInstance.
3910 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3911 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3912 EXPECT_EQ(instance1, instance2);
3913 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3914 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3915 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3917 scoped_ptr<TestWebContents> other_contents(
3918 static_cast<TestWebContents*>(CreateTestWebContents()));
3919 NavigationControllerImpl& other_controller = other_contents->GetController();
3920 other_contents->NavigateAndCommit(url3);
3921 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3922 other_controller.CopyStateFromAndPrune(&controller, false);
3924 // other_controller should now contain the 3 urls: url1, url2 and url3.
3926 ASSERT_EQ(3, other_controller.GetEntryCount());
3928 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3930 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3931 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3932 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3933 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3934 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3935 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3937 // A new SiteInstance in a different BrowsingInstance should be used for the
3938 // new tab.
3939 SiteInstance* instance3 =
3940 other_controller.GetEntryAtIndex(2)->site_instance();
3941 EXPECT_NE(instance3, instance1);
3942 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3944 // The max page ID map should be copied over and updated with the max page ID
3945 // from the current tab.
3946 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3947 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3950 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3951 // the target.
3952 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3953 NavigationControllerImpl& controller = controller_impl();
3954 const GURL url1("http://foo1");
3955 const GURL url2("http://foo2");
3956 const GURL url3("http://foo3");
3958 NavigateAndCommit(url1);
3959 NavigateAndCommit(url2);
3960 controller.GoBack();
3961 contents()->CommitPendingNavigation();
3963 scoped_ptr<TestWebContents> other_contents(
3964 static_cast<TestWebContents*>(CreateTestWebContents()));
3965 NavigationControllerImpl& other_controller = other_contents->GetController();
3966 other_contents->NavigateAndCommit(url3);
3967 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3968 other_controller.CopyStateFromAndPrune(&controller, false);
3970 // other_controller should now contain: url1, url3
3972 ASSERT_EQ(2, other_controller.GetEntryCount());
3973 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3975 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3976 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3977 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3979 // The max page ID map should be copied over and updated with the max page ID
3980 // from the current tab.
3981 SiteInstance* instance1 =
3982 other_controller.GetEntryAtIndex(1)->site_instance();
3983 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3986 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3987 // the target.
3988 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3989 NavigationControllerImpl& controller = controller_impl();
3990 const GURL url1("http://foo1");
3991 const GURL url2("http://foo2");
3992 const GURL url3("http://foo3");
3993 const GURL url4("http://foo4");
3995 NavigateAndCommit(url1);
3996 NavigateAndCommit(url2);
3998 scoped_ptr<TestWebContents> other_contents(
3999 static_cast<TestWebContents*>(CreateTestWebContents()));
4000 NavigationControllerImpl& other_controller = other_contents->GetController();
4001 other_contents->NavigateAndCommit(url3);
4002 other_contents->NavigateAndCommit(url4);
4003 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
4004 other_controller.CopyStateFromAndPrune(&controller, false);
4006 // other_controller should now contain: url1, url2, url4
4008 ASSERT_EQ(3, other_controller.GetEntryCount());
4009 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
4011 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4012 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
4013 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
4015 // The max page ID map should be copied over and updated with the max page ID
4016 // from the current tab.
4017 SiteInstance* instance1 =
4018 other_controller.GetEntryAtIndex(2)->site_instance();
4019 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
4022 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
4023 // not the last entry selected in the target.
4024 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
4025 NavigationControllerImpl& controller = controller_impl();
4026 const GURL url1("http://foo1");
4027 const GURL url2("http://foo2");
4028 const GURL url3("http://foo3");
4029 const GURL url4("http://foo4");
4031 NavigateAndCommit(url1);
4032 NavigateAndCommit(url2);
4034 scoped_ptr<TestWebContents> other_contents(
4035 static_cast<TestWebContents*>(CreateTestWebContents()));
4036 NavigationControllerImpl& other_controller = other_contents->GetController();
4037 other_contents->NavigateAndCommit(url3);
4038 other_contents->NavigateAndCommit(url4);
4039 other_controller.GoBack();
4040 other_contents->CommitPendingNavigation();
4041 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
4042 other_controller.CopyStateFromAndPrune(&controller, false);
4044 // other_controller should now contain: url1, url2, url3
4046 ASSERT_EQ(3, other_controller.GetEntryCount());
4047 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
4049 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4050 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
4051 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
4053 // The max page ID map should be copied over and updated with the max page ID
4054 // from the current tab.
4055 SiteInstance* instance1 =
4056 other_controller.GetEntryAtIndex(2)->site_instance();
4057 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
4060 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
4061 // a pending entry in the target.
4062 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
4063 NavigationControllerImpl& controller = controller_impl();
4064 const GURL url1("http://foo1");
4065 const GURL url2("http://foo2");
4066 const GURL url3("http://foo3");
4067 const GURL url4("http://foo4");
4069 NavigateAndCommit(url1);
4070 NavigateAndCommit(url2);
4071 controller.GoBack();
4072 contents()->CommitPendingNavigation();
4074 scoped_ptr<TestWebContents> other_contents(
4075 static_cast<TestWebContents*>(CreateTestWebContents()));
4076 NavigationControllerImpl& other_controller = other_contents->GetController();
4077 other_contents->NavigateAndCommit(url3);
4078 other_controller.LoadURL(
4079 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4080 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
4081 other_controller.CopyStateFromAndPrune(&controller, false);
4083 // other_controller should now contain url1, url3, and a pending entry
4084 // for url4.
4086 ASSERT_EQ(2, other_controller.GetEntryCount());
4087 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
4089 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4090 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
4092 // And there should be a pending entry for url4.
4093 ASSERT_TRUE(other_controller.GetPendingEntry());
4094 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
4096 // The max page ID map should be copied over and updated with the max page ID
4097 // from the current tab.
4098 SiteInstance* instance1 =
4099 other_controller.GetEntryAtIndex(0)->site_instance();
4100 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
4103 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
4104 // client redirect entry (with the same page ID) in the target. This used to
4105 // crash because the last committed entry would be pruned but max_page_id
4106 // remembered the page ID (http://crbug.com/234809).
4107 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
4108 NavigationControllerImpl& controller = controller_impl();
4109 const GURL url1("http://foo1");
4110 const GURL url2a("http://foo2/a");
4111 const GURL url2b("http://foo2/b");
4113 NavigateAndCommit(url1);
4115 scoped_ptr<TestWebContents> other_contents(
4116 static_cast<TestWebContents*>(CreateTestWebContents()));
4117 NavigationControllerImpl& other_controller = other_contents->GetController();
4118 other_contents->NavigateAndCommit(url2a);
4119 // Simulate a client redirect, which has the same page ID as entry 2a.
4120 other_controller.LoadURL(
4121 url2b, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
4122 NavigationEntry* entry = other_controller.GetPendingEntry();
4123 entry->SetPageID(other_controller.GetLastCommittedEntry()->GetPageID());
4125 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
4126 other_controller.CopyStateFromAndPrune(&controller, false);
4128 // other_controller should now contain url1, url2a, and a pending entry
4129 // for url2b.
4131 ASSERT_EQ(2, other_controller.GetEntryCount());
4132 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
4134 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4135 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
4137 // And there should be a pending entry for url4.
4138 ASSERT_TRUE(other_controller.GetPendingEntry());
4139 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
4141 // Let the pending entry commit.
4142 other_contents->TestDidNavigate(other_contents->GetMainFrame(),
4143 entry->GetPageID(), 0, false, url2b,
4144 ui::PAGE_TRANSITION_LINK);
4146 // The max page ID map should be copied over and updated with the max page ID
4147 // from the current tab.
4148 SiteInstance* instance1 =
4149 other_controller.GetEntryAtIndex(1)->site_instance();
4150 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
4153 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4154 // source, and 1 entry in the target. The back pending entry should be ignored.
4155 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
4156 NavigationControllerImpl& controller = controller_impl();
4157 const GURL url1("http://foo1");
4158 const GURL url2("http://foo2");
4159 const GURL url3("http://foo3");
4161 NavigateAndCommit(url1);
4162 NavigateAndCommit(url2);
4163 controller.GoBack();
4165 scoped_ptr<TestWebContents> other_contents(
4166 static_cast<TestWebContents*>(CreateTestWebContents()));
4167 NavigationControllerImpl& other_controller = other_contents->GetController();
4168 other_contents->NavigateAndCommit(url3);
4169 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
4170 other_controller.CopyStateFromAndPrune(&controller, false);
4172 // other_controller should now contain: url1, url2, url3
4174 ASSERT_EQ(3, other_controller.GetEntryCount());
4175 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
4177 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4178 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
4179 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
4180 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
4182 // The max page ID map should be copied over and updated with the max page ID
4183 // from the current tab.
4184 SiteInstance* instance1 =
4185 other_controller.GetEntryAtIndex(2)->site_instance();
4186 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
4189 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4190 // when the max entry count is 3. We should prune one entry.
4191 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
4192 NavigationControllerImpl& controller = controller_impl();
4193 size_t original_count = NavigationControllerImpl::max_entry_count();
4194 const int kMaxEntryCount = 3;
4196 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
4198 const GURL url1("http://foo/1");
4199 const GURL url2("http://foo/2");
4200 const GURL url3("http://foo/3");
4201 const GURL url4("http://foo/4");
4203 // Create a PrunedListener to observe prune notifications.
4204 PrunedListener listener(&controller);
4206 NavigateAndCommit(url1);
4207 NavigateAndCommit(url2);
4208 NavigateAndCommit(url3);
4210 scoped_ptr<TestWebContents> other_contents(
4211 static_cast<TestWebContents*>(CreateTestWebContents()));
4212 NavigationControllerImpl& other_controller = other_contents->GetController();
4213 other_contents->NavigateAndCommit(url4);
4214 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
4215 other_controller.CopyStateFromAndPrune(&controller, false);
4217 // We should have received a pruned notification.
4218 EXPECT_EQ(1, listener.notification_count_);
4219 EXPECT_TRUE(listener.details_.from_front);
4220 EXPECT_EQ(1, listener.details_.count);
4222 // other_controller should now contain only 3 urls: url2, url3 and url4.
4224 ASSERT_EQ(3, other_controller.GetEntryCount());
4226 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
4228 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
4229 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
4230 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
4231 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
4232 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
4233 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
4235 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
4238 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4239 // replace_entry set to true.
4240 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
4241 NavigationControllerImpl& controller = controller_impl();
4242 const GURL url1("http://foo/1");
4243 const GURL url2("http://foo/2");
4244 const GURL url3("http://foo/3");
4246 NavigateAndCommit(url1);
4247 NavigateAndCommit(url2);
4249 // First two entries should have the same SiteInstance.
4250 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
4251 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
4252 EXPECT_EQ(instance1, instance2);
4253 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
4254 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
4255 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
4257 scoped_ptr<TestWebContents> other_contents(
4258 static_cast<TestWebContents*>(CreateTestWebContents()));
4259 NavigationControllerImpl& other_controller = other_contents->GetController();
4260 other_contents->NavigateAndCommit(url3);
4261 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
4262 other_controller.CopyStateFromAndPrune(&controller, true);
4264 // other_controller should now contain the 2 urls: url1 and url3.
4266 ASSERT_EQ(2, other_controller.GetEntryCount());
4268 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
4270 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4271 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
4272 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
4273 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
4275 // A new SiteInstance in a different BrowsingInstance should be used for the
4276 // new tab.
4277 SiteInstance* instance3 =
4278 other_controller.GetEntryAtIndex(1)->site_instance();
4279 EXPECT_NE(instance3, instance1);
4280 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
4282 // The max page ID map should be copied over and updated with the max page ID
4283 // from the current tab.
4284 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
4285 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
4288 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4289 // entry count is 3 and replace_entry is true. We should not prune entries.
4290 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
4291 NavigationControllerImpl& controller = controller_impl();
4292 size_t original_count = NavigationControllerImpl::max_entry_count();
4293 const int kMaxEntryCount = 3;
4295 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
4297 const GURL url1("http://foo/1");
4298 const GURL url2("http://foo/2");
4299 const GURL url3("http://foo/3");
4300 const GURL url4("http://foo/4");
4302 // Create a PrunedListener to observe prune notifications.
4303 PrunedListener listener(&controller);
4305 NavigateAndCommit(url1);
4306 NavigateAndCommit(url2);
4307 NavigateAndCommit(url3);
4309 scoped_ptr<TestWebContents> other_contents(
4310 static_cast<TestWebContents*>(CreateTestWebContents()));
4311 NavigationControllerImpl& other_controller = other_contents->GetController();
4312 other_contents->NavigateAndCommit(url4);
4313 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
4314 other_controller.CopyStateFromAndPrune(&controller, true);
4316 // We should have received no pruned notification.
4317 EXPECT_EQ(0, listener.notification_count_);
4319 // other_controller should now contain only 3 urls: url1, url2 and url4.
4321 ASSERT_EQ(3, other_controller.GetEntryCount());
4323 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
4325 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4326 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
4327 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
4328 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
4329 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
4330 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
4332 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
4335 // Tests that we can navigate to the restored entries
4336 // imported by CopyStateFromAndPrune.
4337 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
4338 const GURL kRestoredUrls[] = {
4339 GURL("http://site1.com"),
4340 GURL("http://site2.com"),
4342 const GURL kInitialUrl("http://site3.com");
4344 ScopedVector<NavigationEntry> entries;
4345 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
4346 scoped_ptr<NavigationEntry> entry =
4347 NavigationControllerImpl::CreateNavigationEntry(
4348 kRestoredUrls[i], Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
4349 std::string(), browser_context());
4350 entry->SetPageID(static_cast<int>(i));
4351 entries.push_back(entry.Pass());
4354 // Create a WebContents with restored entries.
4355 scoped_ptr<TestWebContents> source_contents(
4356 static_cast<TestWebContents*>(CreateTestWebContents()));
4357 NavigationControllerImpl& source_controller =
4358 source_contents->GetController();
4359 source_controller.Restore(
4360 entries.size() - 1,
4361 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
4362 &entries);
4363 ASSERT_EQ(0u, entries.size());
4364 source_controller.LoadIfNecessary();
4365 source_contents->CommitPendingNavigation();
4367 // Load a page, then copy state from |source_contents|.
4368 NavigateAndCommit(kInitialUrl);
4369 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4370 controller_impl().CopyStateFromAndPrune(&source_controller, false);
4371 ASSERT_EQ(3, controller_impl().GetEntryCount());
4373 // Go back to the first entry one at a time and
4374 // verify that it works as expected.
4375 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4376 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
4378 controller_impl().GoBack();
4379 contents()->CommitPendingNavigation();
4380 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4381 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
4383 controller_impl().GoBack();
4384 contents()->CommitPendingNavigation();
4385 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4386 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
4389 // Tests that navigations initiated from the page (with the history object)
4390 // work as expected, creating pending entries.
4391 TEST_F(NavigationControllerTest, HistoryNavigate) {
4392 NavigationControllerImpl& controller = controller_impl();
4393 const GURL url1("http://foo/1");
4394 const GURL url2("http://foo/2");
4395 const GURL url3("http://foo/3");
4397 NavigateAndCommit(url1);
4398 NavigateAndCommit(url2);
4399 NavigateAndCommit(url3);
4400 controller.GoBack();
4401 contents()->CommitPendingNavigation();
4402 process()->sink().ClearMessages();
4404 // Simulate the page calling history.back(). It should create a pending entry.
4405 contents()->OnGoToEntryAtOffset(-1);
4406 EXPECT_EQ(0, controller.GetPendingEntryIndex());
4407 // The actual cross-navigation is suspended until the current RVH tells us
4408 // it unloaded, simulate that.
4409 contents()->ProceedWithCrossSiteNavigation();
4410 // Also make sure we told the page to navigate.
4411 GURL nav_url = GetLastNavigationURL();
4412 EXPECT_EQ(url1, nav_url);
4413 contents()->CommitPendingNavigation();
4414 process()->sink().ClearMessages();
4416 // Now test history.forward()
4417 contents()->OnGoToEntryAtOffset(2);
4418 EXPECT_EQ(2, controller.GetPendingEntryIndex());
4419 // The actual cross-navigation is suspended until the current RVH tells us
4420 // it unloaded, simulate that.
4421 contents()->ProceedWithCrossSiteNavigation();
4422 nav_url = GetLastNavigationURL();
4423 EXPECT_EQ(url3, nav_url);
4424 contents()->CommitPendingNavigation();
4425 process()->sink().ClearMessages();
4427 controller.DiscardNonCommittedEntries();
4429 // Make sure an extravagant history.go() doesn't break.
4430 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4431 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4432 EXPECT_FALSE(HasNavigationRequest());
4435 // Test call to PruneAllButLastCommitted for the only entry.
4436 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
4437 NavigationControllerImpl& controller = controller_impl();
4438 const GURL url1("http://foo1");
4439 NavigateAndCommit(url1);
4441 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4443 controller.PruneAllButLastCommitted();
4445 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4446 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4449 // Test call to PruneAllButLastCommitted for first entry.
4450 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
4451 NavigationControllerImpl& controller = controller_impl();
4452 const GURL url1("http://foo/1");
4453 const GURL url2("http://foo/2");
4454 const GURL url3("http://foo/3");
4456 NavigateAndCommit(url1);
4457 NavigateAndCommit(url2);
4458 NavigateAndCommit(url3);
4459 controller.GoBack();
4460 controller.GoBack();
4461 contents()->CommitPendingNavigation();
4463 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4465 controller.PruneAllButLastCommitted();
4467 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4468 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4471 // Test call to PruneAllButLastCommitted for intermediate entry.
4472 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
4473 NavigationControllerImpl& controller = controller_impl();
4474 const GURL url1("http://foo/1");
4475 const GURL url2("http://foo/2");
4476 const GURL url3("http://foo/3");
4478 NavigateAndCommit(url1);
4479 NavigateAndCommit(url2);
4480 NavigateAndCommit(url3);
4481 controller.GoBack();
4482 contents()->CommitPendingNavigation();
4484 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4486 controller.PruneAllButLastCommitted();
4488 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4489 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
4492 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4493 // the list of entries.
4494 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
4495 NavigationControllerImpl& controller = controller_impl();
4496 const GURL url1("http://foo/1");
4497 const GURL url2("http://foo/2");
4498 const GURL url3("http://foo/3");
4500 NavigateAndCommit(url1);
4501 NavigateAndCommit(url2);
4503 // Create a pending entry that is not in the entry list.
4504 controller.LoadURL(
4505 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4506 int entry_id = controller.GetPendingEntry()->GetUniqueID();
4507 EXPECT_TRUE(controller.GetPendingEntry());
4508 EXPECT_EQ(2, controller.GetEntryCount());
4510 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4511 controller.PruneAllButLastCommitted();
4513 // We should only have the last committed and pending entries at this point,
4514 // and the pending entry should still not be in the entry list.
4515 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4516 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
4517 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4518 EXPECT_TRUE(controller.GetPendingEntry());
4519 EXPECT_EQ(1, controller.GetEntryCount());
4521 // Try to commit the pending entry.
4522 main_test_rfh()->PrepareForCommit();
4523 main_test_rfh()->SendNavigate(2, entry_id, true, url3);
4524 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4525 EXPECT_FALSE(controller.GetPendingEntry());
4526 EXPECT_EQ(2, controller.GetEntryCount());
4527 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
4530 // Test to ensure that when we do a history navigation back to the current
4531 // committed page (e.g., going forward to a slow-loading page, then pressing
4532 // the back button), we just stop the navigation to prevent the throbber from
4533 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4534 // start, but WebKit essentially ignores the navigation and never sends a
4535 // message to stop the throbber.
4536 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
4537 NavigationControllerImpl& controller = controller_impl();
4538 const GURL url0("http://foo/0");
4539 const GURL url1("http://foo/1");
4541 NavigateAndCommit(url0);
4542 NavigateAndCommit(url1);
4544 // Go back to the original page, then forward to the slow page, then back
4545 controller.GoBack();
4546 contents()->CommitPendingNavigation();
4548 controller.GoForward();
4549 EXPECT_EQ(1, controller.GetPendingEntryIndex());
4551 controller.GoBack();
4552 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4555 TEST_F(NavigationControllerTest, IsInitialNavigation) {
4556 NavigationControllerImpl& controller = controller_impl();
4557 TestNotificationTracker notifications;
4558 RegisterForAllNavNotifications(&notifications, &controller);
4560 // Initial state.
4561 EXPECT_TRUE(controller.IsInitialNavigation());
4563 // After commit, it stays false.
4564 const GURL url1("http://foo1");
4565 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1);
4566 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4567 navigation_entry_committed_counter_ = 0;
4568 EXPECT_FALSE(controller.IsInitialNavigation());
4570 // After starting a new navigation, it stays false.
4571 const GURL url2("http://foo2");
4572 controller.LoadURL(
4573 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4576 // Check that the favicon is not reused across a client redirect.
4577 // (crbug.com/28515)
4578 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4579 const GURL kPageWithFavicon("http://withfavicon.html");
4580 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4581 const GURL kIconURL("http://withfavicon.ico");
4582 const gfx::Image kDefaultFavicon = FaviconStatus().image;
4584 NavigationControllerImpl& controller = controller_impl();
4585 TestNotificationTracker notifications;
4586 RegisterForAllNavNotifications(&notifications, &controller);
4588 main_test_rfh()->NavigateAndCommitRendererInitiated(
4589 0, true, kPageWithFavicon);
4590 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4591 navigation_entry_committed_counter_ = 0;
4593 NavigationEntry* entry = controller.GetLastCommittedEntry();
4594 EXPECT_TRUE(entry);
4595 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4597 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4598 content::FaviconStatus& favicon_status = entry->GetFavicon();
4599 favicon_status.image = CreateImage(SK_ColorWHITE);
4600 favicon_status.url = kIconURL;
4601 favicon_status.valid = true;
4602 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4604 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon,
4605 false);
4606 main_test_rfh()->PrepareForCommit();
4607 main_test_rfh()->SendNavigateWithTransition(
4608 0, // same page ID.
4609 0, // nav_entry_id
4610 false, // no new entry
4611 kPageWithoutFavicon, ui::PAGE_TRANSITION_CLIENT_REDIRECT);
4612 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4613 navigation_entry_committed_counter_ = 0;
4615 entry = controller.GetLastCommittedEntry();
4616 EXPECT_TRUE(entry);
4617 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4619 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4622 // Check that the favicon is not cleared for NavigationEntries which were
4623 // previously navigated to.
4624 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4625 const GURL kUrl1("http://www.a.com/1");
4626 const GURL kUrl2("http://www.a.com/2");
4627 const GURL kIconURL("http://www.a.com/1/favicon.ico");
4629 NavigationControllerImpl& controller = controller_impl();
4630 TestNotificationTracker notifications;
4631 RegisterForAllNavNotifications(&notifications, &controller);
4633 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1);
4634 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4635 navigation_entry_committed_counter_ = 0;
4637 // Simulate Chromium having set the favicon for |kUrl1|.
4638 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4639 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4640 EXPECT_TRUE(entry);
4641 content::FaviconStatus& favicon_status = entry->GetFavicon();
4642 favicon_status.image = favicon_image;
4643 favicon_status.url = kIconURL;
4644 favicon_status.valid = true;
4646 // Navigate to another page and go back to the original page.
4647 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2);
4648 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4649 navigation_entry_committed_counter_ = 0;
4650 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1, false);
4651 main_test_rfh()->PrepareForCommit();
4652 main_test_rfh()->SendNavigateWithTransition(
4653 0, controller.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1,
4654 ui::PAGE_TRANSITION_FORWARD_BACK);
4655 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4656 navigation_entry_committed_counter_ = 0;
4658 // Verify that the favicon for the page at |kUrl1| was not cleared.
4659 entry = controller.GetEntryAtIndex(0);
4660 EXPECT_TRUE(entry);
4661 EXPECT_EQ(kUrl1, entry->GetURL());
4662 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4665 // The test crashes on android: http://crbug.com/170449
4666 #if defined(OS_ANDROID)
4667 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4668 #else
4669 #define MAYBE_PurgeScreenshot PurgeScreenshot
4670 #endif
4671 // Tests that screenshot are purged correctly.
4672 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4673 NavigationControllerImpl& controller = controller_impl();
4675 NavigationEntryImpl* entry;
4677 // Navigate enough times to make sure that some screenshots are purged.
4678 for (int i = 0; i < 12; ++i) {
4679 const GURL url(base::StringPrintf("http://foo%d/", i));
4680 NavigateAndCommit(url);
4681 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4684 MockScreenshotManager* screenshot_manager =
4685 new MockScreenshotManager(&controller);
4686 controller.SetScreenshotManager(make_scoped_ptr(screenshot_manager));
4687 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4688 entry = controller.GetEntryAtIndex(i);
4689 screenshot_manager->TakeScreenshotFor(entry);
4690 EXPECT_TRUE(entry->screenshot().get());
4693 NavigateAndCommit(GURL("https://foo/"));
4694 EXPECT_EQ(13, controller.GetEntryCount());
4695 entry = controller.GetEntryAtIndex(11);
4696 screenshot_manager->TakeScreenshotFor(entry);
4698 for (int i = 0; i < 2; ++i) {
4699 entry = controller.GetEntryAtIndex(i);
4700 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4701 << " not purged";
4704 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4705 entry = controller.GetEntryAtIndex(i);
4706 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4709 // Navigate to index 5 and then try to assign screenshot to all entries.
4710 controller.GoToIndex(5);
4711 contents()->CommitPendingNavigation();
4712 EXPECT_EQ(5, controller.GetCurrentEntryIndex());
4713 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4714 entry = controller.GetEntryAtIndex(i);
4715 screenshot_manager->TakeScreenshotFor(entry);
4718 for (int i = 10; i <= 12; ++i) {
4719 entry = controller.GetEntryAtIndex(i);
4720 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4721 << " not purged";
4722 screenshot_manager->TakeScreenshotFor(entry);
4725 // Navigate to index 7 and assign screenshot to all entries.
4726 controller.GoToIndex(7);
4727 contents()->CommitPendingNavigation();
4728 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4729 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4730 entry = controller.GetEntryAtIndex(i);
4731 screenshot_manager->TakeScreenshotFor(entry);
4734 for (int i = 0; i < 2; ++i) {
4735 entry = controller.GetEntryAtIndex(i);
4736 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4737 << " not purged";
4740 // Clear all screenshots.
4741 EXPECT_EQ(13, controller.GetEntryCount());
4742 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4743 controller.ClearAllScreenshots();
4744 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4745 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4746 entry = controller.GetEntryAtIndex(i);
4747 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4748 << " not cleared";
4752 TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) {
4753 // Navigate.
4754 main_test_rfh()->NavigateAndCommitRendererInitiated(
4755 1, true, GURL("http://foo"));
4757 // Set title and favicon.
4758 base::string16 title(base::ASCIIToUTF16("Title"));
4759 FaviconStatus favicon;
4760 favicon.valid = true;
4761 favicon.url = GURL("http://foo/favicon.ico");
4762 controller().GetLastCommittedEntry()->SetTitle(title);
4763 controller().GetLastCommittedEntry()->GetFavicon() = favicon;
4765 // history.pushState() is called.
4766 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4767 GURL kUrl2("http://foo#foo");
4768 params.page_id = 2;
4769 params.nav_entry_id = 0;
4770 params.did_create_new_entry = true;
4771 params.url = kUrl2;
4772 params.page_state = PageState::CreateFromURL(kUrl2);
4773 params.was_within_same_page = true;
4774 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, false);
4775 main_test_rfh()->PrepareForCommit();
4776 main_test_rfh()->SendNavigateWithParams(&params);
4778 // The title should immediately be visible on the new NavigationEntry.
4779 base::string16 new_title =
4780 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4781 EXPECT_EQ(title, new_title);
4782 FaviconStatus new_favicon =
4783 controller().GetLastCommittedEntry()->GetFavicon();
4784 EXPECT_EQ(favicon.valid, new_favicon.valid);
4785 EXPECT_EQ(favicon.url, new_favicon.url);
4788 // Test that the navigation controller clears its session history when a
4789 // navigation commits with the clear history list flag set.
4790 TEST_F(NavigationControllerTest, ClearHistoryList) {
4791 const GURL url1("http://foo1");
4792 const GURL url2("http://foo2");
4793 const GURL url3("http://foo3");
4794 const GURL url4("http://foo4");
4796 NavigationControllerImpl& controller = controller_impl();
4798 // Create a session history with three entries, second entry is active.
4799 NavigateAndCommit(url1);
4800 NavigateAndCommit(url2);
4801 NavigateAndCommit(url3);
4802 controller.GoBack();
4803 contents()->CommitPendingNavigation();
4804 EXPECT_EQ(3, controller.GetEntryCount());
4805 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4807 // Create a new pending navigation, and indicate that the session history
4808 // should be cleared.
4809 NavigationController::LoadURLParams params(url4);
4810 params.should_clear_history_list = true;
4811 controller.LoadURLWithParams(params);
4813 // Verify that the pending entry correctly indicates that the session history
4814 // should be cleared.
4815 NavigationEntryImpl* entry = controller.GetPendingEntry();
4816 ASSERT_TRUE(entry);
4817 EXPECT_TRUE(entry->should_clear_history_list());
4819 // Assume that the RenderFrame correctly cleared its history and commit the
4820 // navigation.
4821 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4822 switches::kEnableBrowserSideNavigation)) {
4823 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4825 contents()->GetPendingMainFrame()->
4826 set_simulate_history_list_was_cleared(true);
4827 contents()->CommitPendingNavigation();
4829 // Verify that the NavigationController's session history was correctly
4830 // cleared.
4831 EXPECT_EQ(1, controller.GetEntryCount());
4832 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4833 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4834 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4835 EXPECT_FALSE(controller.CanGoBack());
4836 EXPECT_FALSE(controller.CanGoForward());
4837 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4840 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
4841 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
4842 EXPECT_FALSE(contents()->GetDelegate());
4843 contents()->SetDelegate(delegate.get());
4845 // Submit a form.
4846 GURL url("http://foo");
4847 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4848 params.page_id = 1;
4849 params.nav_entry_id = 0;
4850 params.did_create_new_entry = true;
4851 params.url = url;
4852 params.transition = ui::PAGE_TRANSITION_FORM_SUBMIT;
4853 params.gesture = NavigationGestureUser;
4854 params.page_state = PageState::CreateFromURL(url);
4855 params.was_within_same_page = false;
4856 params.is_post = true;
4857 params.post_id = 2;
4858 main_test_rfh()->SendRendererInitiatedNavigationRequest(url, false);
4859 main_test_rfh()->PrepareForCommit();
4860 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4862 // history.replaceState() is called.
4863 GURL replace_url("http://foo#foo");
4864 params.page_id = 1;
4865 params.nav_entry_id = 0;
4866 params.did_create_new_entry = false;
4867 params.url = replace_url;
4868 params.transition = ui::PAGE_TRANSITION_LINK;
4869 params.gesture = NavigationGestureUser;
4870 params.page_state = PageState::CreateFromURL(replace_url);
4871 params.was_within_same_page = true;
4872 params.is_post = false;
4873 params.post_id = -1;
4874 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url, false);
4875 main_test_rfh()->PrepareForCommit();
4876 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4878 // Now reload. replaceState overrides the POST, so we should not show a
4879 // repost warning dialog.
4880 controller_impl().Reload(true);
4881 EXPECT_EQ(0, delegate->repost_form_warning_count());
4884 TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
4885 GURL url("http://foo");
4886 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4887 params.page_id = 1;
4888 params.nav_entry_id = 0;
4889 params.did_create_new_entry = true;
4890 params.url = url;
4891 params.transition = ui::PAGE_TRANSITION_LINK;
4892 params.gesture = NavigationGestureUser;
4893 params.page_state = PageState::CreateFromURL(url);
4894 params.was_within_same_page = false;
4895 params.is_post = true;
4896 params.post_id = 2;
4897 params.url_is_unreachable = true;
4899 // Navigate to new page.
4901 LoadCommittedDetails details;
4902 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4903 EXPECT_EQ(PAGE_TYPE_ERROR,
4904 controller_impl().GetLastCommittedEntry()->GetPageType());
4905 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details.type);
4908 // Navigate to existing page.
4910 params.did_create_new_entry = false;
4911 LoadCommittedDetails details;
4912 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4913 EXPECT_EQ(PAGE_TYPE_ERROR,
4914 controller_impl().GetLastCommittedEntry()->GetPageType());
4915 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
4918 // Navigate to same page.
4919 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4920 // same-page transition.
4921 controller_impl().LoadURL(
4922 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4923 params.nav_entry_id = controller_impl().GetPendingEntry()->GetUniqueID();
4924 params.transition = ui::PAGE_TRANSITION_TYPED;
4926 LoadCommittedDetails details;
4927 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4928 EXPECT_EQ(PAGE_TYPE_ERROR,
4929 controller_impl().GetLastCommittedEntry()->GetPageType());
4930 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, details.type);
4933 // Navigate in page.
4934 params.url = GURL("http://foo#foo");
4935 params.transition = ui::PAGE_TRANSITION_LINK;
4936 params.was_within_same_page = true;
4938 LoadCommittedDetails details;
4939 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4940 EXPECT_EQ(PAGE_TYPE_ERROR,
4941 controller_impl().GetLastCommittedEntry()->GetPageType());
4942 EXPECT_TRUE(details.is_in_page);
4946 // Tests that if a stale navigation comes back from the renderer, it is properly
4947 // resurrected.
4948 TEST_F(NavigationControllerTest, StaleNavigationsResurrected) {
4949 NavigationControllerImpl& controller = controller_impl();
4950 TestNotificationTracker notifications;
4951 RegisterForAllNavNotifications(&notifications, &controller);
4953 // Start on page A.
4954 const GURL url_a("http://foo.com/a");
4955 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a);
4956 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4957 navigation_entry_committed_counter_ = 0;
4958 EXPECT_EQ(1, controller.GetEntryCount());
4959 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4961 // Go to page B.
4962 const GURL url_b("http://foo.com/b");
4963 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b);
4964 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4965 navigation_entry_committed_counter_ = 0;
4966 EXPECT_EQ(2, controller.GetEntryCount());
4967 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4968 int b_entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
4969 int b_page_id = controller.GetLastCommittedEntry()->GetPageID();
4971 // Back to page A.
4972 controller.GoBack();
4973 contents()->CommitPendingNavigation();
4974 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4975 navigation_entry_committed_counter_ = 0;
4976 EXPECT_EQ(2, controller.GetEntryCount());
4977 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4979 // Start going forward to page B.
4980 controller.GoForward();
4982 // But the renderer unilaterally navigates to page C, pruning B.
4983 const GURL url_c("http://foo.com/c");
4984 main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c);
4985 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4986 navigation_entry_committed_counter_ = 0;
4987 EXPECT_EQ(2, controller.GetEntryCount());
4988 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4989 int c_entry_id = controller.GetLastCommittedEntry()->GetUniqueID();
4990 EXPECT_NE(c_entry_id, b_entry_id);
4992 // And then the navigation to B gets committed.
4993 main_test_rfh()->SendNavigate(b_page_id, b_entry_id, false, url_b);
4994 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4995 navigation_entry_committed_counter_ = 0;
4997 // Even though we were doing a history navigation, because the entry was
4998 // pruned it will end up as a *new* entry at the end of the entry list. This
4999 // means that occasionally a navigation conflict will end up with one entry
5000 // bubbling to the end of the entry list, but that's the least-bad option.
5001 EXPECT_EQ(3, controller.GetEntryCount());
5002 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
5003 EXPECT_EQ(url_a, controller.GetEntryAtIndex(0)->GetURL());
5004 EXPECT_EQ(url_c, controller.GetEntryAtIndex(1)->GetURL());
5005 EXPECT_EQ(url_b, controller.GetEntryAtIndex(2)->GetURL());
5008 // Test that if a renderer provides bogus security info (that fails to
5009 // deserialize properly) when reporting a navigation, the renderer gets
5010 // killed.
5011 TEST_F(NavigationControllerTest, RendererNavigateBogusSecurityInfo) {
5012 GURL url("http://foo.test");
5013 FrameHostMsg_DidCommitProvisionalLoad_Params params;
5014 params.page_id = 0;
5015 params.nav_entry_id = 0;
5016 params.did_create_new_entry = true;
5017 params.url = url;
5018 params.transition = ui::PAGE_TRANSITION_LINK;
5019 params.should_update_history = true;
5020 params.gesture = NavigationGestureUser;
5021 params.is_post = false;
5022 params.page_state = PageState::CreateFromURL(url);
5023 params.was_within_same_page = false;
5024 params.security_info = "bogus security info!";
5026 LoadCommittedDetails details;
5027 EXPECT_EQ(0, main_test_rfh()->GetProcess()->bad_msg_count());
5028 EXPECT_TRUE(
5029 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details));
5031 SSLStatus default_ssl_status;
5032 EXPECT_EQ(default_ssl_status.security_style,
5033 details.ssl_status.security_style);
5034 EXPECT_EQ(default_ssl_status.cert_id, details.ssl_status.cert_id);
5035 EXPECT_EQ(default_ssl_status.cert_status, details.ssl_status.cert_status);
5036 EXPECT_EQ(default_ssl_status.security_bits, details.ssl_status.security_bits);
5037 EXPECT_EQ(default_ssl_status.connection_status,
5038 details.ssl_status.connection_status);
5039 EXPECT_EQ(default_ssl_status.content_status,
5040 details.ssl_status.content_status);
5041 EXPECT_EQ(0u, details.ssl_status.signed_certificate_timestamp_ids.size());
5043 EXPECT_EQ(1, main_test_rfh()->GetProcess()->bad_msg_count());
5046 } // namespace content