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"
7 #include "base/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/path_service.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/navigation_controller_impl.h"
16 #include "content/browser/frame_host/navigation_entry_impl.h"
17 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
18 #include "content/browser/frame_host/navigator.h"
19 #include "content/browser/site_instance_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/frame_messages.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/navigation_details.h"
24 #include "content/public/browser/notification_registrar.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/common/page_state.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/test/mock_render_process_host.h"
32 #include "content/public/test/test_notification_tracker.h"
33 #include "content/public/test/test_utils.h"
34 #include "content/test/test_render_frame_host.h"
35 #include "content/test/test_render_view_host.h"
36 #include "content/test/test_web_contents.h"
37 #include "net/base/net_util.h"
38 #include "skia/ext/platform_canvas.h"
39 #include "testing/gtest/include/gtest/gtest.h"
45 // Creates an image with a 1x1 SkBitmap of the specified |color|.
46 gfx::Image CreateImage(SkColor color) {
48 bitmap.allocN32Pixels(1, 1);
49 bitmap.eraseColor(color);
50 return gfx::Image::CreateFrom1xBitmap(bitmap);
53 // Returns true if images |a| and |b| have the same pixel data.
54 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
55 // Assume that if the 1x bitmaps match, the images match.
56 SkBitmap a_bitmap = a.AsBitmap();
57 SkBitmap b_bitmap = b.AsBitmap();
59 if (a_bitmap.width() != b_bitmap.width() ||
60 a_bitmap.height() != b_bitmap.height()) {
63 SkAutoLockPixels a_bitmap_lock(a_bitmap);
64 SkAutoLockPixels b_bitmap_lock(b_bitmap);
65 return memcmp(a_bitmap.getPixels(),
67 a_bitmap.getSize()) == 0;
70 class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
72 explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
73 : content::NavigationEntryScreenshotManager(owner),
74 encoding_screenshot_in_progress_(false) {
77 virtual ~MockScreenshotManager() {
80 void TakeScreenshotFor(content::NavigationEntryImpl* entry) {
82 bitmap.allocN32Pixels(1, 1);
83 bitmap.eraseARGB(0, 0, 0, 0);
84 encoding_screenshot_in_progress_ = true;
85 OnScreenshotTaken(entry->GetUniqueID(), true, bitmap);
86 WaitUntilScreenshotIsReady();
89 int GetScreenshotCount() {
90 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
93 void WaitUntilScreenshotIsReady() {
94 if (!encoding_screenshot_in_progress_)
96 message_loop_runner_ = new content::MessageLoopRunner;
97 message_loop_runner_->Run();
101 // Overridden from content::NavigationEntryScreenshotManager:
102 virtual void TakeScreenshotImpl(
103 content::RenderViewHost* host,
104 content::NavigationEntryImpl* entry) OVERRIDE {
107 virtual void OnScreenshotSet(content::NavigationEntryImpl* entry) OVERRIDE {
108 encoding_screenshot_in_progress_ = false;
109 NavigationEntryScreenshotManager::OnScreenshotSet(entry);
110 if (message_loop_runner_.get())
111 message_loop_runner_->Quit();
114 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
115 bool encoding_screenshot_in_progress_;
117 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
124 // TimeSmoother tests ----------------------------------------------------------
126 // With no duplicates, GetSmoothedTime should be the identity
128 TEST(TimeSmoother, Basic) {
129 NavigationControllerImpl::TimeSmoother smoother;
130 for (int64 i = 1; i < 1000; ++i) {
131 base::Time t = base::Time::FromInternalValue(i);
132 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
136 // With a single duplicate and timestamps thereafter increasing by one
137 // microsecond, the smoothed time should always be one behind.
138 TEST(TimeSmoother, SingleDuplicate) {
139 NavigationControllerImpl::TimeSmoother smoother;
140 base::Time t = base::Time::FromInternalValue(1);
141 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
142 for (int64 i = 1; i < 1000; ++i) {
143 base::Time expected_t = base::Time::FromInternalValue(i + 1);
144 t = base::Time::FromInternalValue(i);
145 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
149 // With k duplicates and timestamps thereafter increasing by one
150 // microsecond, the smoothed time should always be k behind.
151 TEST(TimeSmoother, ManyDuplicates) {
152 const int64 kNumDuplicates = 100;
153 NavigationControllerImpl::TimeSmoother smoother;
154 base::Time t = base::Time::FromInternalValue(1);
155 for (int64 i = 0; i < kNumDuplicates; ++i) {
156 base::Time expected_t = base::Time::FromInternalValue(i + 1);
157 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
159 for (int64 i = 1; i < 1000; ++i) {
160 base::Time expected_t =
161 base::Time::FromInternalValue(i + kNumDuplicates);
162 t = base::Time::FromInternalValue(i);
163 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
167 // If the clock jumps far back enough after a run of duplicates, it
168 // should immediately jump to that value.
169 TEST(TimeSmoother, ClockBackwardsJump) {
170 const int64 kNumDuplicates = 100;
171 NavigationControllerImpl::TimeSmoother smoother;
172 base::Time t = base::Time::FromInternalValue(1000);
173 for (int64 i = 0; i < kNumDuplicates; ++i) {
174 base::Time expected_t = base::Time::FromInternalValue(i + 1000);
175 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
177 t = base::Time::FromInternalValue(500);
178 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
181 // NavigationControllerTest ----------------------------------------------------
183 class NavigationControllerTest
184 : public RenderViewHostImplTestHarness,
185 public WebContentsObserver {
187 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
190 virtual void SetUp() OVERRIDE {
191 RenderViewHostImplTestHarness::SetUp();
192 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents();
193 ASSERT_TRUE(web_contents); // The WebContents should be created by now.
194 WebContentsObserver::Observe(web_contents);
197 // WebContentsObserver:
198 virtual void DidStartNavigationToPendingEntry(
200 NavigationController::ReloadType reload_type) OVERRIDE {
201 navigated_url_ = url;
204 virtual void NavigationEntryCommitted(
205 const LoadCommittedDetails& load_details) OVERRIDE {
206 navigation_entry_committed_counter_++;
209 const GURL& navigated_url() const {
210 return navigated_url_;
213 NavigationControllerImpl& controller_impl() {
214 return static_cast<NavigationControllerImpl&>(controller());
219 size_t navigation_entry_committed_counter_;
222 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
223 NavigationController* controller) {
224 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED,
225 Source<NavigationController>(controller));
226 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED,
227 Source<NavigationController>(controller));
230 SiteInstance* GetSiteInstanceFromEntry(NavigationEntry* entry) {
231 return NavigationEntryImpl::FromNavigationEntry(entry)->site_instance();
234 class TestWebContentsDelegate : public WebContentsDelegate {
236 explicit TestWebContentsDelegate() :
237 navigation_state_change_count_(0),
238 repost_form_warning_count_(0) {}
240 int navigation_state_change_count() {
241 return navigation_state_change_count_;
244 int repost_form_warning_count() {
245 return repost_form_warning_count_;
248 // Keep track of whether the tab has notified us of a navigation state change.
249 virtual void NavigationStateChanged(const WebContents* source,
250 InvalidateTypes changed_flags) OVERRIDE {
251 navigation_state_change_count_++;
254 virtual void ShowRepostFormWarningDialog(WebContents* source) OVERRIDE {
255 repost_form_warning_count_++;
259 // The number of times NavigationStateChanged has been called.
260 int navigation_state_change_count_;
262 // The number of times ShowRepostFormWarningDialog() was called.
263 int repost_form_warning_count_;
266 // -----------------------------------------------------------------------------
268 TEST_F(NavigationControllerTest, Defaults) {
269 NavigationControllerImpl& controller = controller_impl();
271 EXPECT_FALSE(controller.GetPendingEntry());
272 EXPECT_FALSE(controller.GetVisibleEntry());
273 EXPECT_FALSE(controller.GetLastCommittedEntry());
274 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
275 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
276 EXPECT_EQ(controller.GetEntryCount(), 0);
277 EXPECT_FALSE(controller.CanGoBack());
278 EXPECT_FALSE(controller.CanGoForward());
281 TEST_F(NavigationControllerTest, GoToOffset) {
282 NavigationControllerImpl& controller = controller_impl();
283 TestNotificationTracker notifications;
284 RegisterForAllNavNotifications(¬ifications, &controller);
286 const int kNumUrls = 5;
287 std::vector<GURL> urls(kNumUrls);
288 for (int i = 0; i < kNumUrls; ++i) {
289 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
292 main_test_rfh()->SendNavigate(0, urls[0]);
293 EXPECT_EQ(1U, navigation_entry_committed_counter_);
294 navigation_entry_committed_counter_ = 0;
295 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
296 EXPECT_FALSE(controller.CanGoBack());
297 EXPECT_FALSE(controller.CanGoForward());
298 EXPECT_FALSE(controller.CanGoToOffset(1));
300 for (int i = 1; i <= 4; ++i) {
301 main_test_rfh()->SendNavigate(i, urls[i]);
302 EXPECT_EQ(1U, navigation_entry_committed_counter_);
303 navigation_entry_committed_counter_ = 0;
304 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
305 EXPECT_TRUE(controller.CanGoToOffset(-i));
306 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
307 EXPECT_FALSE(controller.CanGoToOffset(1));
310 // We have loaded 5 pages, and are currently at the last-loaded page.
314 GO_TO_MIDDLE_PAGE = -2,
317 GO_TO_BEGINNING = -2,
322 const int test_offsets[NUM_TESTS] = {
330 for (int test = 0; test < NUM_TESTS; ++test) {
331 int offset = test_offsets[test];
332 controller.GoToOffset(offset);
334 // Check that the GoToOffset will land on the expected page.
335 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
336 main_test_rfh()->SendNavigate(url_index, urls[url_index]);
337 EXPECT_EQ(1U, navigation_entry_committed_counter_);
338 navigation_entry_committed_counter_ = 0;
339 // Check that we can go to any valid offset into the history.
340 for (size_t j = 0; j < urls.size(); ++j)
341 EXPECT_TRUE(controller.CanGoToOffset(j - url_index));
342 // Check that we can't go beyond the beginning or end of the history.
343 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1)));
344 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index));
348 TEST_F(NavigationControllerTest, LoadURL) {
349 NavigationControllerImpl& controller = controller_impl();
350 TestNotificationTracker notifications;
351 RegisterForAllNavNotifications(¬ifications, &controller);
353 const GURL url1("http://foo1");
354 const GURL url2("http://foo2");
356 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
357 // Creating a pending notification should not have issued any of the
358 // notifications we're listening for.
359 EXPECT_EQ(0U, notifications.size());
361 // The load should now be pending.
362 EXPECT_EQ(controller.GetEntryCount(), 0);
363 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
364 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
365 EXPECT_FALSE(controller.GetLastCommittedEntry());
366 ASSERT_TRUE(controller.GetPendingEntry());
367 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
368 EXPECT_FALSE(controller.CanGoBack());
369 EXPECT_FALSE(controller.CanGoForward());
370 EXPECT_EQ(contents()->GetMaxPageID(), -1);
372 // Neither the timestamp nor the status code should have been set yet.
373 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
374 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
376 // We should have gotten no notifications from the preceeding checks.
377 EXPECT_EQ(0U, notifications.size());
379 main_test_rfh()->SendNavigate(0, url1);
380 EXPECT_EQ(1U, navigation_entry_committed_counter_);
381 navigation_entry_committed_counter_ = 0;
383 // The load should now be committed.
384 EXPECT_EQ(controller.GetEntryCount(), 1);
385 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
386 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
387 EXPECT_TRUE(controller.GetLastCommittedEntry());
388 EXPECT_FALSE(controller.GetPendingEntry());
389 ASSERT_TRUE(controller.GetVisibleEntry());
390 EXPECT_FALSE(controller.CanGoBack());
391 EXPECT_FALSE(controller.CanGoForward());
392 EXPECT_EQ(contents()->GetMaxPageID(), 0);
393 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
394 controller.GetLastCommittedEntry())->bindings());
396 // The timestamp should have been set.
397 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
400 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
402 // The load should now be pending.
403 EXPECT_EQ(controller.GetEntryCount(), 1);
404 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
405 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
406 EXPECT_TRUE(controller.GetLastCommittedEntry());
407 ASSERT_TRUE(controller.GetPendingEntry());
408 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
409 // TODO(darin): maybe this should really be true?
410 EXPECT_FALSE(controller.CanGoBack());
411 EXPECT_FALSE(controller.CanGoForward());
412 EXPECT_EQ(contents()->GetMaxPageID(), 0);
414 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
416 // Simulate the beforeunload ack for the cross-site transition, and then the
418 test_rvh()->SendBeforeUnloadACK(true);
419 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
420 EXPECT_EQ(1U, navigation_entry_committed_counter_);
421 navigation_entry_committed_counter_ = 0;
423 // The load should now be committed.
424 EXPECT_EQ(controller.GetEntryCount(), 2);
425 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
426 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
427 EXPECT_TRUE(controller.GetLastCommittedEntry());
428 EXPECT_FALSE(controller.GetPendingEntry());
429 ASSERT_TRUE(controller.GetVisibleEntry());
430 EXPECT_TRUE(controller.CanGoBack());
431 EXPECT_FALSE(controller.CanGoForward());
432 EXPECT_EQ(contents()->GetMaxPageID(), 1);
434 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
439 base::Time GetFixedTime(base::Time time) {
445 TEST_F(NavigationControllerTest, LoadURLSameTime) {
446 NavigationControllerImpl& controller = controller_impl();
447 TestNotificationTracker notifications;
448 RegisterForAllNavNotifications(¬ifications, &controller);
450 // Set the clock to always return a timestamp of 1.
451 controller.SetGetTimestampCallbackForTest(
452 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
454 const GURL url1("http://foo1");
455 const GURL url2("http://foo2");
457 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
459 main_test_rfh()->SendNavigate(0, url1);
460 EXPECT_EQ(1U, navigation_entry_committed_counter_);
461 navigation_entry_committed_counter_ = 0;
464 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
466 // Simulate the beforeunload ack for the cross-site transition, and then the
468 test_rvh()->SendBeforeUnloadACK(true);
469 main_test_rfh()->SendNavigate(1, url2);
470 EXPECT_EQ(1U, navigation_entry_committed_counter_);
471 navigation_entry_committed_counter_ = 0;
473 // The two loads should now be committed.
474 ASSERT_EQ(controller.GetEntryCount(), 2);
476 // Timestamps should be distinct despite the clock returning the
479 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
481 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
484 void CheckNavigationEntryMatchLoadParams(
485 NavigationController::LoadURLParams& load_params,
486 NavigationEntryImpl* entry) {
487 EXPECT_EQ(load_params.url, entry->GetURL());
488 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
489 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
490 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
491 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
493 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
494 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
495 if (!load_params.virtual_url_for_data_url.is_empty()) {
496 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
498 if (NavigationController::UA_OVERRIDE_INHERIT !=
499 load_params.override_user_agent) {
500 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
501 load_params.override_user_agent);
502 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
504 EXPECT_EQ(load_params.browser_initiated_post_data,
505 entry->GetBrowserInitiatedPostData());
506 EXPECT_EQ(load_params.transferred_global_request_id,
507 entry->transferred_global_request_id());
510 TEST_F(NavigationControllerTest, LoadURLWithParams) {
511 NavigationControllerImpl& controller = controller_impl();
513 NavigationController::LoadURLParams load_params(GURL("http://foo"));
514 load_params.referrer =
515 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
516 load_params.transition_type = PAGE_TRANSITION_GENERATED;
517 load_params.extra_headers = "content-type: text/plain";
518 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
519 load_params.is_renderer_initiated = true;
520 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
521 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
523 controller.LoadURLWithParams(load_params);
524 NavigationEntryImpl* entry =
525 NavigationEntryImpl::FromNavigationEntry(
526 controller.GetPendingEntry());
528 // The timestamp should not have been set yet.
530 EXPECT_TRUE(entry->GetTimestamp().is_null());
532 CheckNavigationEntryMatchLoadParams(load_params, entry);
535 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
536 NavigationControllerImpl& controller = controller_impl();
538 NavigationController::LoadURLParams load_params(
539 GURL("data:text/html,dataurl"));
540 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
541 load_params.base_url_for_data_url = GURL("http://foo");
542 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
543 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
545 controller.LoadURLWithParams(load_params);
546 NavigationEntryImpl* entry =
547 NavigationEntryImpl::FromNavigationEntry(
548 controller.GetPendingEntry());
550 CheckNavigationEntryMatchLoadParams(load_params, entry);
553 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
554 NavigationControllerImpl& controller = controller_impl();
556 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
557 load_params.transition_type = PAGE_TRANSITION_TYPED;
558 load_params.load_type =
559 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
560 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
563 const unsigned char* raw_data =
564 reinterpret_cast<const unsigned char*>("d\n\0a2");
565 const int length = 5;
566 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
567 scoped_refptr<base::RefCountedBytes> data =
568 base::RefCountedBytes::TakeVector(&post_data_vector);
569 load_params.browser_initiated_post_data = data.get();
571 controller.LoadURLWithParams(load_params);
572 NavigationEntryImpl* entry =
573 NavigationEntryImpl::FromNavigationEntry(
574 controller.GetPendingEntry());
576 CheckNavigationEntryMatchLoadParams(load_params, entry);
579 // Tests what happens when the same page is loaded again. Should not create a
580 // new session history entry. This is what happens when you press enter in the
581 // URL bar to reload: a pending entry is created and then it is discarded when
582 // the load commits (because WebCore didn't actually make a new entry).
583 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
584 NavigationControllerImpl& controller = controller_impl();
585 TestNotificationTracker notifications;
586 RegisterForAllNavNotifications(¬ifications, &controller);
588 const GURL url1("http://foo1");
590 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
591 EXPECT_EQ(0U, notifications.size());
592 main_test_rfh()->SendNavigate(0, url1);
593 EXPECT_EQ(1U, navigation_entry_committed_counter_);
594 navigation_entry_committed_counter_ = 0;
596 ASSERT_TRUE(controller.GetVisibleEntry());
597 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
598 EXPECT_FALSE(timestamp.is_null());
600 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
601 EXPECT_EQ(0U, notifications.size());
602 main_test_rfh()->SendNavigate(0, url1);
603 EXPECT_EQ(1U, navigation_entry_committed_counter_);
604 navigation_entry_committed_counter_ = 0;
606 // We should not have produced a new session history entry.
607 EXPECT_EQ(controller.GetEntryCount(), 1);
608 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
609 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
610 EXPECT_TRUE(controller.GetLastCommittedEntry());
611 EXPECT_FALSE(controller.GetPendingEntry());
612 ASSERT_TRUE(controller.GetVisibleEntry());
613 EXPECT_FALSE(controller.CanGoBack());
614 EXPECT_FALSE(controller.CanGoForward());
616 // The timestamp should have been updated.
618 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
619 // EXPECT_GT once we guarantee that timestamps are unique.
620 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
623 // Load the same page twice, once as a GET and once as a POST.
624 // We should update the post state on the NavigationEntry.
625 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
626 NavigationControllerImpl& controller = controller_impl();
627 TestNotificationTracker notifications;
628 RegisterForAllNavNotifications(¬ifications, &controller);
630 const GURL url1("http://foo1");
632 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
633 FrameHostMsg_DidCommitProvisionalLoad_Params params;
636 params.transition = PAGE_TRANSITION_TYPED;
637 params.is_post = true;
638 params.post_id = 123;
639 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
640 main_test_rfh()->SendNavigateWithParams(¶ms);
642 // The post data should be visible.
643 NavigationEntry* entry = controller.GetVisibleEntry();
645 EXPECT_TRUE(entry->GetHasPostData());
646 EXPECT_EQ(entry->GetPostID(), 123);
648 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
649 main_test_rfh()->SendNavigate(0, url1);
651 // We should not have produced a new session history entry.
652 ASSERT_EQ(controller.GetVisibleEntry(), entry);
654 // The post data should have been cleared due to the GET.
655 EXPECT_FALSE(entry->GetHasPostData());
656 EXPECT_EQ(entry->GetPostID(), 0);
659 // Tests loading a URL but discarding it before the load commits.
660 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
661 NavigationControllerImpl& controller = controller_impl();
662 TestNotificationTracker notifications;
663 RegisterForAllNavNotifications(¬ifications, &controller);
665 const GURL url1("http://foo1");
666 const GURL url2("http://foo2");
668 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
669 EXPECT_EQ(0U, notifications.size());
670 main_test_rfh()->SendNavigate(0, url1);
671 EXPECT_EQ(1U, navigation_entry_committed_counter_);
672 navigation_entry_committed_counter_ = 0;
674 ASSERT_TRUE(controller.GetVisibleEntry());
675 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
676 EXPECT_FALSE(timestamp.is_null());
678 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
679 controller.DiscardNonCommittedEntries();
680 EXPECT_EQ(0U, notifications.size());
682 // Should not have produced a new session history entry.
683 EXPECT_EQ(controller.GetEntryCount(), 1);
684 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
685 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
686 EXPECT_TRUE(controller.GetLastCommittedEntry());
687 EXPECT_FALSE(controller.GetPendingEntry());
688 ASSERT_TRUE(controller.GetVisibleEntry());
689 EXPECT_FALSE(controller.CanGoBack());
690 EXPECT_FALSE(controller.CanGoForward());
692 // Timestamp should not have changed.
693 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
696 // Tests navigations that come in unrequested. This happens when the user
697 // navigates from the web page, and here we test that there is no pending entry.
698 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
699 NavigationControllerImpl& controller = controller_impl();
700 TestNotificationTracker notifications;
701 RegisterForAllNavNotifications(¬ifications, &controller);
703 // First make an existing committed entry.
704 const GURL kExistingURL1("http://eh");
706 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
707 main_test_rfh()->SendNavigate(0, kExistingURL1);
708 EXPECT_EQ(1U, navigation_entry_committed_counter_);
709 navigation_entry_committed_counter_ = 0;
711 // Do a new navigation without making a pending one.
712 const GURL kNewURL("http://see");
713 main_test_rfh()->SendNavigate(99, kNewURL);
715 // There should no longer be any pending entry, and the third navigation we
716 // just made should be committed.
717 EXPECT_EQ(1U, navigation_entry_committed_counter_);
718 navigation_entry_committed_counter_ = 0;
719 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
720 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
721 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
724 // Tests navigating to a new URL when there is a new pending navigation that is
725 // not the one that just loaded. This will happen if the user types in a URL to
726 // somewhere slow, and then navigates the current page before the typed URL
728 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
729 NavigationControllerImpl& controller = controller_impl();
730 TestNotificationTracker notifications;
731 RegisterForAllNavNotifications(¬ifications, &controller);
733 // First make an existing committed entry.
734 const GURL kExistingURL1("http://eh");
736 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
737 main_test_rfh()->SendNavigate(0, kExistingURL1);
738 EXPECT_EQ(1U, navigation_entry_committed_counter_);
739 navigation_entry_committed_counter_ = 0;
741 // Make a pending entry to somewhere new.
742 const GURL kExistingURL2("http://bee");
744 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
745 EXPECT_EQ(0U, notifications.size());
747 // After the beforeunload but before it commits, do a new navigation.
748 test_rvh()->SendBeforeUnloadACK(true);
749 const GURL kNewURL("http://see");
750 contents()->GetPendingMainFrame()->SendNavigate(3, kNewURL);
752 // There should no longer be any pending entry, and the third navigation we
753 // just made should be committed.
754 EXPECT_EQ(1U, navigation_entry_committed_counter_);
755 navigation_entry_committed_counter_ = 0;
756 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
757 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
758 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
761 // Tests navigating to a new URL when there is a pending back/forward
762 // navigation. This will happen if the user hits back, but before that commits,
763 // they navigate somewhere new.
764 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
765 NavigationControllerImpl& controller = controller_impl();
766 TestNotificationTracker notifications;
767 RegisterForAllNavNotifications(¬ifications, &controller);
769 // First make some history.
770 const GURL kExistingURL1("http://foo/eh");
772 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
773 main_test_rfh()->SendNavigate(0, kExistingURL1);
774 EXPECT_EQ(1U, navigation_entry_committed_counter_);
775 navigation_entry_committed_counter_ = 0;
777 const GURL kExistingURL2("http://foo/bee");
779 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
780 main_test_rfh()->SendNavigate(1, kExistingURL2);
781 EXPECT_EQ(1U, navigation_entry_committed_counter_);
782 navigation_entry_committed_counter_ = 0;
784 // Now make a pending back/forward navigation. The zeroth entry should be
787 EXPECT_EQ(0U, notifications.size());
788 EXPECT_EQ(0, controller.GetPendingEntryIndex());
789 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
791 // Before that commits, do a new navigation.
792 const GURL kNewURL("http://foo/see");
793 LoadCommittedDetails details;
794 main_test_rfh()->SendNavigate(3, kNewURL);
796 // There should no longer be any pending entry, and the third navigation we
797 // just made should be committed.
798 EXPECT_EQ(1U, navigation_entry_committed_counter_);
799 navigation_entry_committed_counter_ = 0;
800 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
801 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
802 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
805 // Tests navigating to a new URL when there is a pending back/forward
806 // navigation to a cross-process, privileged URL. This will happen if the user
807 // hits back, but before that commits, they navigate somewhere new.
808 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
809 NavigationControllerImpl& controller = controller_impl();
810 TestNotificationTracker notifications;
811 RegisterForAllNavNotifications(¬ifications, &controller);
813 // First make some history, starting with a privileged URL.
814 const GURL kExistingURL1("http://privileged");
816 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
817 // Pretend it has bindings so we can tell if we incorrectly copy it.
818 test_rvh()->AllowBindings(2);
819 main_test_rfh()->SendNavigate(0, kExistingURL1);
820 EXPECT_EQ(1U, navigation_entry_committed_counter_);
821 navigation_entry_committed_counter_ = 0;
823 // Navigate cross-process to a second URL.
824 const GURL kExistingURL2("http://foo/eh");
826 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
827 test_rvh()->SendBeforeUnloadACK(true);
828 TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame();
829 foo_rfh->SendNavigate(1, kExistingURL2);
830 EXPECT_EQ(1U, navigation_entry_committed_counter_);
831 navigation_entry_committed_counter_ = 0;
833 // Now make a pending back/forward navigation to a privileged entry.
834 // The zeroth entry should be pending.
836 foo_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
837 EXPECT_EQ(0U, notifications.size());
838 EXPECT_EQ(0, controller.GetPendingEntryIndex());
839 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
840 EXPECT_EQ(2, NavigationEntryImpl::FromNavigationEntry(
841 controller.GetPendingEntry())->bindings());
843 // Before that commits, do a new navigation.
844 const GURL kNewURL("http://foo/bee");
845 LoadCommittedDetails details;
846 foo_rfh->SendNavigate(3, kNewURL);
848 // There should no longer be any pending entry, and the third navigation we
849 // just made should be committed.
850 EXPECT_EQ(1U, navigation_entry_committed_counter_);
851 navigation_entry_committed_counter_ = 0;
852 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
853 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
854 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
855 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
856 controller.GetLastCommittedEntry())->bindings());
859 // Tests navigating to an existing URL when there is a pending new navigation.
860 // This will happen if the user enters a URL, but before that commits, the
861 // current page fires history.back().
862 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
863 NavigationControllerImpl& controller = controller_impl();
864 TestNotificationTracker notifications;
865 RegisterForAllNavNotifications(¬ifications, &controller);
867 // First make some history.
868 const GURL kExistingURL1("http://foo/eh");
870 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
871 main_test_rfh()->SendNavigate(0, kExistingURL1);
872 EXPECT_EQ(1U, navigation_entry_committed_counter_);
873 navigation_entry_committed_counter_ = 0;
875 const GURL kExistingURL2("http://foo/bee");
877 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
878 main_test_rfh()->SendNavigate(1, kExistingURL2);
879 EXPECT_EQ(1U, navigation_entry_committed_counter_);
880 navigation_entry_committed_counter_ = 0;
882 // Now make a pending new navigation.
883 const GURL kNewURL("http://foo/see");
885 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
886 EXPECT_EQ(0U, notifications.size());
887 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
888 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
890 // Before that commits, a back navigation from the renderer commits.
891 main_test_rfh()->SendNavigate(0, kExistingURL1);
893 // There should no longer be any pending entry, and the back navigation we
894 // just made should be committed.
895 EXPECT_EQ(1U, navigation_entry_committed_counter_);
896 navigation_entry_committed_counter_ = 0;
897 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
898 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
899 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
902 // Tests an ignored navigation when there is a pending new navigation.
903 // This will happen if the user enters a URL, but before that commits, the
904 // current blank page reloads. See http://crbug.com/77507.
905 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
906 NavigationControllerImpl& controller = controller_impl();
907 TestNotificationTracker notifications;
908 RegisterForAllNavNotifications(¬ifications, &controller);
910 // Set a WebContentsDelegate to listen for state changes.
911 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
912 EXPECT_FALSE(contents()->GetDelegate());
913 contents()->SetDelegate(delegate.get());
915 // Without any navigations, the renderer starts at about:blank.
916 const GURL kExistingURL(url::kAboutBlankURL);
918 // Now make a pending new navigation.
919 const GURL kNewURL("http://eh");
921 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
922 EXPECT_EQ(0U, notifications.size());
923 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
924 EXPECT_TRUE(controller.GetPendingEntry());
925 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
926 EXPECT_EQ(1, delegate->navigation_state_change_count());
928 // Before that commits, a document.write and location.reload can cause the
929 // renderer to send a FrameNavigate with page_id -1.
930 main_test_rfh()->SendNavigate(-1, kExistingURL);
932 // This should clear the pending entry and notify of a navigation state
933 // change, so that we do not keep displaying kNewURL.
934 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
935 EXPECT_FALSE(controller.GetPendingEntry());
936 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
937 EXPECT_EQ(2, delegate->navigation_state_change_count());
939 contents()->SetDelegate(NULL);
942 // Tests that the pending entry state is correct after an abort.
943 // We do not want to clear the pending entry, so that the user doesn't
944 // lose a typed URL. (See http://crbug.com/9682.)
945 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
946 NavigationControllerImpl& controller = controller_impl();
947 TestNotificationTracker notifications;
948 RegisterForAllNavNotifications(¬ifications, &controller);
950 // Set a WebContentsDelegate to listen for state changes.
951 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
952 EXPECT_FALSE(contents()->GetDelegate());
953 contents()->SetDelegate(delegate.get());
955 // Start with a pending new navigation.
956 const GURL kNewURL("http://eh");
958 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
959 EXPECT_EQ(0U, notifications.size());
960 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
961 EXPECT_TRUE(controller.GetPendingEntry());
962 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
963 EXPECT_EQ(1, delegate->navigation_state_change_count());
965 // It may abort before committing, if it's a download or due to a stop or
966 // a new navigation from the user.
967 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
968 params.error_code = net::ERR_ABORTED;
969 params.error_description = base::string16();
970 params.url = kNewURL;
971 params.showing_repost_interstitial = false;
972 main_test_rfh()->OnMessageReceived(
973 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
976 // This should not clear the pending entry or notify of a navigation state
977 // change, so that we keep displaying kNewURL (until the user clears it).
978 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
979 EXPECT_TRUE(controller.GetPendingEntry());
980 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
981 EXPECT_EQ(1, delegate->navigation_state_change_count());
982 NavigationEntry* pending_entry = controller.GetPendingEntry();
984 // Ensure that a reload keeps the same pending entry.
985 controller.Reload(true);
986 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
987 EXPECT_TRUE(controller.GetPendingEntry());
988 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
989 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
991 contents()->SetDelegate(NULL);
994 // Tests that the pending URL is not visible during a renderer-initiated
995 // redirect and abort. See http://crbug.com/83031.
996 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
997 NavigationControllerImpl& controller = controller_impl();
998 TestNotificationTracker notifications;
999 RegisterForAllNavNotifications(¬ifications, &controller);
1001 // First make an existing committed entry.
1002 const GURL kExistingURL("http://foo/eh");
1003 controller.LoadURL(kExistingURL, content::Referrer(),
1004 content::PAGE_TRANSITION_TYPED, std::string());
1005 main_test_rfh()->SendNavigate(0, kExistingURL);
1006 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1007 navigation_entry_committed_counter_ = 0;
1009 // Set a WebContentsDelegate to listen for state changes.
1010 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1011 EXPECT_FALSE(contents()->GetDelegate());
1012 contents()->SetDelegate(delegate.get());
1014 // Now make a pending new navigation, initiated by the renderer.
1015 const GURL kNewURL("http://foo/bee");
1016 NavigationController::LoadURLParams load_url_params(kNewURL);
1017 load_url_params.transition_type = PAGE_TRANSITION_TYPED;
1018 load_url_params.is_renderer_initiated = true;
1019 controller.LoadURLWithParams(load_url_params);
1020 EXPECT_EQ(0U, notifications.size());
1021 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1022 EXPECT_TRUE(controller.GetPendingEntry());
1023 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1024 EXPECT_EQ(0, delegate->navigation_state_change_count());
1026 // The visible entry should be the last committed URL, not the pending one.
1027 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1029 // Now the navigation redirects.
1030 const GURL kRedirectURL("http://foo/see");
1031 main_test_rfh()->OnMessageReceived(
1032 FrameHostMsg_DidRedirectProvisionalLoad(0, // routing_id
1033 -1, // pending page_id
1035 kRedirectURL)); // new url
1037 // We don't want to change the NavigationEntry's url, in case it cancels.
1038 // Prevents regression of http://crbug.com/77786.
1039 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1041 // It may abort before committing, if it's a download or due to a stop or
1042 // a new navigation from the user.
1043 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1044 params.error_code = net::ERR_ABORTED;
1045 params.error_description = base::string16();
1046 params.url = kRedirectURL;
1047 params.showing_repost_interstitial = false;
1048 main_test_rfh()->OnMessageReceived(
1049 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1052 // Because the pending entry is renderer initiated and not visible, we
1053 // clear it when it fails.
1054 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1055 EXPECT_FALSE(controller.GetPendingEntry());
1056 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1057 EXPECT_EQ(1, delegate->navigation_state_change_count());
1059 // The visible entry should be the last committed URL, not the pending one,
1060 // so that no spoof is possible.
1061 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1063 contents()->SetDelegate(NULL);
1066 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1067 // at the time they committed. http://crbug.com/173672.
1068 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1069 NavigationControllerImpl& controller = controller_impl();
1070 TestNotificationTracker notifications;
1071 RegisterForAllNavNotifications(¬ifications, &controller);
1072 std::vector<GURL> url_chain;
1074 const GURL url1("http://foo1");
1075 const GURL url2("http://foo2");
1077 // Navigate to a first, unprivileged URL.
1078 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1079 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1080 NavigationEntryImpl::FromNavigationEntry(
1081 controller.GetPendingEntry())->bindings());
1084 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1085 orig_rfh->SendNavigate(0, url1);
1086 EXPECT_EQ(controller.GetEntryCount(), 1);
1087 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1088 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
1089 controller.GetLastCommittedEntry())->bindings());
1091 // Manually increase the number of active views in the SiteInstance
1092 // that orig_rfh belongs to, to prevent it from being destroyed when
1093 // it gets swapped out, so that we can reuse orig_rfh when the
1094 // controller goes back.
1095 static_cast<SiteInstanceImpl*>(orig_rfh->GetSiteInstance())->
1096 increment_active_view_count();
1098 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1099 // transition, run the unload handler, and set bindings on the pending
1100 // RenderViewHost to simulate a privileged url.
1101 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1102 orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
1103 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1104 contents()->GetPendingMainFrame(),
1105 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1106 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1107 TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
1108 new_rfh->GetRenderViewHost()->AllowBindings(1);
1109 new_rfh->SendNavigate(1, url2);
1111 // The second load should be committed, and bindings should be remembered.
1112 EXPECT_EQ(controller.GetEntryCount(), 2);
1113 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1114 EXPECT_TRUE(controller.CanGoBack());
1115 EXPECT_EQ(1, NavigationEntryImpl::FromNavigationEntry(
1116 controller.GetLastCommittedEntry())->bindings());
1118 // Going back, the first entry should still appear unprivileged.
1119 controller.GoBack();
1120 new_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
1121 contents()->GetRenderManagerForTesting()->OnCrossSiteResponse(
1122 contents()->GetPendingMainFrame(),
1123 GlobalRequestID(0, 0), scoped_ptr<CrossSiteTransferringRequest>(),
1124 url_chain, Referrer(), PAGE_TRANSITION_TYPED, false);
1125 orig_rfh->SendNavigate(0, url1);
1126 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1127 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
1128 controller.GetLastCommittedEntry())->bindings());
1131 TEST_F(NavigationControllerTest, Reload) {
1132 NavigationControllerImpl& controller = controller_impl();
1133 TestNotificationTracker notifications;
1134 RegisterForAllNavNotifications(¬ifications, &controller);
1136 const GURL url1("http://foo1");
1138 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1139 EXPECT_EQ(0U, notifications.size());
1140 main_test_rfh()->SendNavigate(0, url1);
1141 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1142 navigation_entry_committed_counter_ = 0;
1143 ASSERT_TRUE(controller.GetVisibleEntry());
1144 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1145 controller.Reload(true);
1146 EXPECT_EQ(0U, notifications.size());
1148 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1149 EXPECT_FALSE(timestamp.is_null());
1151 // The reload is pending.
1152 EXPECT_EQ(controller.GetEntryCount(), 1);
1153 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1154 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1155 EXPECT_TRUE(controller.GetLastCommittedEntry());
1156 EXPECT_TRUE(controller.GetPendingEntry());
1157 EXPECT_FALSE(controller.CanGoBack());
1158 EXPECT_FALSE(controller.CanGoForward());
1159 // Make sure the title has been cleared (will be redrawn just after reload).
1160 // Avoids a stale cached title when the new page being reloaded has no title.
1161 // See http://crbug.com/96041.
1162 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1164 main_test_rfh()->SendNavigate(0, url1);
1165 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1166 navigation_entry_committed_counter_ = 0;
1168 // Now the reload is committed.
1169 EXPECT_EQ(controller.GetEntryCount(), 1);
1170 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1171 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1172 EXPECT_TRUE(controller.GetLastCommittedEntry());
1173 EXPECT_FALSE(controller.GetPendingEntry());
1174 EXPECT_FALSE(controller.CanGoBack());
1175 EXPECT_FALSE(controller.CanGoForward());
1177 // The timestamp should have been updated.
1178 ASSERT_TRUE(controller.GetVisibleEntry());
1179 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1182 // Tests what happens when a reload navigation produces a new page.
1183 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1184 NavigationControllerImpl& controller = controller_impl();
1185 TestNotificationTracker notifications;
1186 RegisterForAllNavNotifications(¬ifications, &controller);
1188 const GURL url1("http://foo1");
1189 const GURL url2("http://foo2");
1191 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1192 main_test_rfh()->SendNavigate(0, url1);
1193 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1194 navigation_entry_committed_counter_ = 0;
1196 controller.Reload(true);
1197 EXPECT_EQ(0U, notifications.size());
1199 main_test_rfh()->SendNavigate(1, url2);
1200 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1201 navigation_entry_committed_counter_ = 0;
1203 // Now the reload is committed.
1204 EXPECT_EQ(controller.GetEntryCount(), 2);
1205 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1206 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1207 EXPECT_TRUE(controller.GetLastCommittedEntry());
1208 EXPECT_FALSE(controller.GetPendingEntry());
1209 EXPECT_TRUE(controller.CanGoBack());
1210 EXPECT_FALSE(controller.CanGoForward());
1213 // This test ensures that when a guest renderer reloads, the reload goes through
1214 // without ending up in the "we have a wrong process for the URL" branch in
1215 // NavigationControllerImpl::ReloadInternal.
1216 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1217 NavigationControllerImpl& controller = controller_impl();
1219 const GURL url1("http://foo1");
1220 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1221 main_test_rfh()->SendNavigate(0, url1);
1222 ASSERT_TRUE(controller.GetVisibleEntry());
1224 // Make the entry believe its RenderProcessHost is a guest.
1225 NavigationEntryImpl* entry1 =
1226 NavigationEntryImpl::FromNavigationEntry(controller.GetVisibleEntry());
1227 reinterpret_cast<MockRenderProcessHost*>(
1228 entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
1231 controller.Reload(true);
1233 // The reload is pending. Check that the NavigationEntry didn't get replaced
1234 // because of having the wrong process.
1235 EXPECT_EQ(controller.GetEntryCount(), 1);
1236 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1237 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1239 NavigationEntryImpl* entry2 =
1240 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1241 EXPECT_EQ(entry1, entry2);
1244 #if !defined(OS_ANDROID) // http://crbug.com/157428
1245 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1246 NavigationControllerImpl& controller = controller_impl();
1247 TestNotificationTracker notifications;
1248 RegisterForAllNavNotifications(¬ifications, &controller);
1250 const GURL original_url("http://foo1");
1251 const GURL final_url("http://foo2");
1253 // Load up the original URL, but get redirected.
1255 original_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1256 EXPECT_EQ(0U, notifications.size());
1257 main_test_rfh()->SendNavigateWithOriginalRequestURL(
1258 0, final_url, original_url);
1259 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1260 navigation_entry_committed_counter_ = 0;
1262 // The NavigationEntry should save both the original URL and the final
1265 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1266 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1268 // Reload using the original URL.
1269 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1270 controller.ReloadOriginalRequestURL(false);
1271 EXPECT_EQ(0U, notifications.size());
1273 // The reload is pending. The request should point to the original URL.
1274 EXPECT_EQ(original_url, navigated_url());
1275 EXPECT_EQ(controller.GetEntryCount(), 1);
1276 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1277 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1278 EXPECT_TRUE(controller.GetLastCommittedEntry());
1279 EXPECT_TRUE(controller.GetPendingEntry());
1280 EXPECT_FALSE(controller.CanGoBack());
1281 EXPECT_FALSE(controller.CanGoForward());
1283 // Make sure the title has been cleared (will be redrawn just after reload).
1284 // Avoids a stale cached title when the new page being reloaded has no title.
1285 // See http://crbug.com/96041.
1286 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1288 // Send that the navigation has proceeded; say it got redirected again.
1289 main_test_rfh()->SendNavigate(0, final_url);
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(), 1);
1295 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1296 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1297 EXPECT_TRUE(controller.GetLastCommittedEntry());
1298 EXPECT_FALSE(controller.GetPendingEntry());
1299 EXPECT_FALSE(controller.CanGoBack());
1300 EXPECT_FALSE(controller.CanGoForward());
1303 #endif // !defined(OS_ANDROID)
1305 // Test that certain non-persisted NavigationEntryImpl values get reset after
1307 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1308 NavigationControllerImpl& controller = controller_impl();
1309 const GURL url1("http://foo1");
1310 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1312 // Set up some sample values.
1313 const unsigned char* raw_data =
1314 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1315 const int length = 11;
1316 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1317 scoped_refptr<base::RefCountedBytes> post_data =
1318 base::RefCountedBytes::TakeVector(&post_data_vector);
1319 GlobalRequestID transfer_id(3, 4);
1320 std::vector<GURL> redirects;
1321 redirects.push_back(GURL("http://foo2"));
1323 // Set non-persisted values on the pending entry.
1324 NavigationEntryImpl* pending_entry =
1325 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1326 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1327 pending_entry->set_is_renderer_initiated(true);
1328 pending_entry->set_transferred_global_request_id(transfer_id);
1329 pending_entry->set_should_replace_entry(true);
1330 pending_entry->set_should_clear_history_list(true);
1331 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1332 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1333 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1334 EXPECT_TRUE(pending_entry->should_replace_entry());
1335 EXPECT_TRUE(pending_entry->should_clear_history_list());
1337 main_test_rfh()->SendNavigate(0, url1);
1339 // Certain values that are only used for pending entries get reset after
1341 NavigationEntryImpl* committed_entry =
1342 NavigationEntryImpl::FromNavigationEntry(
1343 controller.GetLastCommittedEntry());
1344 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1345 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1346 EXPECT_EQ(GlobalRequestID(-1, -1),
1347 committed_entry->transferred_global_request_id());
1348 EXPECT_FALSE(committed_entry->should_replace_entry());
1349 EXPECT_FALSE(committed_entry->should_clear_history_list());
1352 // Test that Redirects are preserved after a commit.
1353 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1354 NavigationControllerImpl& controller = controller_impl();
1355 const GURL url1("http://foo1");
1356 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1358 // Set up some redirect values.
1359 std::vector<GURL> redirects;
1360 redirects.push_back(GURL("http://foo2"));
1362 // Set redirects on the pending entry.
1363 NavigationEntryImpl* pending_entry =
1364 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1365 pending_entry->SetRedirectChain(redirects);
1366 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1367 EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
1369 // Normal navigation will preserve redirects in the committed entry.
1370 main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
1371 NavigationEntryImpl* committed_entry =
1372 NavigationEntryImpl::FromNavigationEntry(
1373 controller.GetLastCommittedEntry());
1374 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1375 EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
1378 // Tests what happens when we navigate back successfully
1379 TEST_F(NavigationControllerTest, Back) {
1380 NavigationControllerImpl& controller = controller_impl();
1381 TestNotificationTracker notifications;
1382 RegisterForAllNavNotifications(¬ifications, &controller);
1384 const GURL url1("http://foo1");
1385 main_test_rfh()->SendNavigate(0, url1);
1386 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1387 navigation_entry_committed_counter_ = 0;
1389 const GURL url2("http://foo2");
1390 main_test_rfh()->SendNavigate(1, url2);
1391 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1392 navigation_entry_committed_counter_ = 0;
1394 controller.GoBack();
1395 EXPECT_EQ(0U, notifications.size());
1397 // We should now have a pending navigation to go back.
1398 EXPECT_EQ(controller.GetEntryCount(), 2);
1399 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1400 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1401 EXPECT_TRUE(controller.GetLastCommittedEntry());
1402 EXPECT_TRUE(controller.GetPendingEntry());
1403 EXPECT_FALSE(controller.CanGoBack());
1404 EXPECT_FALSE(controller.CanGoToOffset(-1));
1405 EXPECT_TRUE(controller.CanGoForward());
1406 EXPECT_TRUE(controller.CanGoToOffset(1));
1407 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1409 // Timestamp for entry 1 should be on or after that of entry 0.
1410 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1411 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1412 controller.GetEntryAtIndex(0)->GetTimestamp());
1414 main_test_rfh()->SendNavigate(0, url2);
1415 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1416 navigation_entry_committed_counter_ = 0;
1418 // The back navigation completed successfully.
1419 EXPECT_EQ(controller.GetEntryCount(), 2);
1420 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1421 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1422 EXPECT_TRUE(controller.GetLastCommittedEntry());
1423 EXPECT_FALSE(controller.GetPendingEntry());
1424 EXPECT_FALSE(controller.CanGoBack());
1425 EXPECT_FALSE(controller.CanGoToOffset(-1));
1426 EXPECT_TRUE(controller.CanGoForward());
1427 EXPECT_TRUE(controller.CanGoToOffset(1));
1428 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1430 // Timestamp for entry 0 should be on or after that of entry 1
1431 // (since we went back to it).
1432 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1433 controller.GetEntryAtIndex(1)->GetTimestamp());
1436 // Tests what happens when a back navigation produces a new page.
1437 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1438 NavigationControllerImpl& controller = controller_impl();
1439 TestNotificationTracker notifications;
1440 RegisterForAllNavNotifications(¬ifications, &controller);
1442 const GURL url1("http://foo/1");
1443 const GURL url2("http://foo/2");
1444 const GURL url3("http://foo/3");
1447 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1448 main_test_rfh()->SendNavigate(0, url1);
1449 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1450 navigation_entry_committed_counter_ = 0;
1452 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1453 main_test_rfh()->SendNavigate(1, url2);
1454 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1455 navigation_entry_committed_counter_ = 0;
1457 controller.GoBack();
1458 EXPECT_EQ(0U, notifications.size());
1460 // We should now have a pending navigation to go back.
1461 EXPECT_EQ(controller.GetEntryCount(), 2);
1462 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1463 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1464 EXPECT_TRUE(controller.GetLastCommittedEntry());
1465 EXPECT_TRUE(controller.GetPendingEntry());
1466 EXPECT_FALSE(controller.CanGoBack());
1467 EXPECT_TRUE(controller.CanGoForward());
1469 main_test_rfh()->SendNavigate(2, url3);
1470 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1471 navigation_entry_committed_counter_ = 0;
1473 // The back navigation resulted in a completely new navigation.
1474 // TODO(darin): perhaps this behavior will be confusing to users?
1475 EXPECT_EQ(controller.GetEntryCount(), 3);
1476 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1477 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1478 EXPECT_TRUE(controller.GetLastCommittedEntry());
1479 EXPECT_FALSE(controller.GetPendingEntry());
1480 EXPECT_TRUE(controller.CanGoBack());
1481 EXPECT_FALSE(controller.CanGoForward());
1484 // Receives a back message when there is a new pending navigation entry.
1485 TEST_F(NavigationControllerTest, Back_NewPending) {
1486 NavigationControllerImpl& controller = controller_impl();
1487 TestNotificationTracker notifications;
1488 RegisterForAllNavNotifications(¬ifications, &controller);
1490 const GURL kUrl1("http://foo1");
1491 const GURL kUrl2("http://foo2");
1492 const GURL kUrl3("http://foo3");
1494 // First navigate two places so we have some back history.
1495 main_test_rfh()->SendNavigate(0, kUrl1);
1496 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1497 navigation_entry_committed_counter_ = 0;
1499 // controller.LoadURL(kUrl2, PAGE_TRANSITION_TYPED);
1500 main_test_rfh()->SendNavigate(1, kUrl2);
1501 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1502 navigation_entry_committed_counter_ = 0;
1504 // Now start a new pending navigation and go back before it commits.
1505 controller.LoadURL(kUrl3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1506 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1507 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1508 controller.GoBack();
1510 // The pending navigation should now be the "back" item and the new one
1512 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1513 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1516 // Receives a back message when there is a different renavigation already
1518 TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1519 NavigationControllerImpl& controller = controller_impl();
1520 const GURL kUrl1("http://foo/1");
1521 const GURL kUrl2("http://foo/2");
1522 const GURL kUrl3("http://foo/3");
1524 // First navigate three places so we have some back history.
1525 main_test_rfh()->SendNavigate(0, kUrl1);
1526 main_test_rfh()->SendNavigate(1, kUrl2);
1527 main_test_rfh()->SendNavigate(2, kUrl3);
1529 // With nothing pending, say we get a navigation to the second entry.
1530 main_test_rfh()->SendNavigate(1, kUrl2);
1532 // We know all the entries have the same site instance, so we can just grab
1533 // a random one for looking up other entries.
1534 SiteInstance* site_instance =
1535 NavigationEntryImpl::FromNavigationEntry(
1536 controller.GetLastCommittedEntry())->site_instance();
1538 // That second URL should be the last committed and it should have gotten the
1540 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1541 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1542 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1544 // Now go forward to the last item again and say it was committed.
1545 controller.GoForward();
1546 main_test_rfh()->SendNavigate(2, kUrl3);
1548 // Now start going back one to the second page. It will be pending.
1549 controller.GoBack();
1550 EXPECT_EQ(1, controller.GetPendingEntryIndex());
1551 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1553 // Not synthesize a totally new back event to the first page. This will not
1554 // match the pending one.
1555 main_test_rfh()->SendNavigate(0, kUrl1);
1557 // The committed navigation should clear the pending entry.
1558 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1560 // But the navigated entry should be the last committed.
1561 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1562 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1565 // Tests what happens when we navigate forward successfully.
1566 TEST_F(NavigationControllerTest, Forward) {
1567 NavigationControllerImpl& controller = controller_impl();
1568 TestNotificationTracker notifications;
1569 RegisterForAllNavNotifications(¬ifications, &controller);
1571 const GURL url1("http://foo1");
1572 const GURL url2("http://foo2");
1574 main_test_rfh()->SendNavigate(0, url1);
1575 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1576 navigation_entry_committed_counter_ = 0;
1578 main_test_rfh()->SendNavigate(1, url2);
1579 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1580 navigation_entry_committed_counter_ = 0;
1582 controller.GoBack();
1583 main_test_rfh()->SendNavigate(0, url1);
1584 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1585 navigation_entry_committed_counter_ = 0;
1587 controller.GoForward();
1589 // We should now have a pending navigation to go forward.
1590 EXPECT_EQ(controller.GetEntryCount(), 2);
1591 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1592 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1593 EXPECT_TRUE(controller.GetLastCommittedEntry());
1594 EXPECT_TRUE(controller.GetPendingEntry());
1595 EXPECT_TRUE(controller.CanGoBack());
1596 EXPECT_TRUE(controller.CanGoToOffset(-1));
1597 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1598 EXPECT_FALSE(controller.CanGoForward());
1599 EXPECT_FALSE(controller.CanGoToOffset(1));
1601 // Timestamp for entry 0 should be on or after that of entry 1
1602 // (since we went back to it).
1603 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1604 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1605 controller.GetEntryAtIndex(1)->GetTimestamp());
1607 main_test_rfh()->SendNavigate(1, url2);
1608 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1609 navigation_entry_committed_counter_ = 0;
1611 // The forward navigation completed successfully.
1612 EXPECT_EQ(controller.GetEntryCount(), 2);
1613 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1614 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1615 EXPECT_TRUE(controller.GetLastCommittedEntry());
1616 EXPECT_FALSE(controller.GetPendingEntry());
1617 EXPECT_TRUE(controller.CanGoBack());
1618 EXPECT_TRUE(controller.CanGoToOffset(-1));
1619 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1620 EXPECT_FALSE(controller.CanGoForward());
1621 EXPECT_FALSE(controller.CanGoToOffset(1));
1623 // Timestamp for entry 1 should be on or after that of entry 0
1624 // (since we went forward to it).
1625 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1626 controller.GetEntryAtIndex(0)->GetTimestamp());
1629 // Tests what happens when a forward navigation produces a new page.
1630 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1631 NavigationControllerImpl& controller = controller_impl();
1632 TestNotificationTracker notifications;
1633 RegisterForAllNavNotifications(¬ifications, &controller);
1635 const GURL url1("http://foo1");
1636 const GURL url2("http://foo2");
1637 const GURL url3("http://foo3");
1639 main_test_rfh()->SendNavigate(0, url1);
1640 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1641 navigation_entry_committed_counter_ = 0;
1642 main_test_rfh()->SendNavigate(1, url2);
1643 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1644 navigation_entry_committed_counter_ = 0;
1646 controller.GoBack();
1647 main_test_rfh()->SendNavigate(0, url1);
1648 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1649 navigation_entry_committed_counter_ = 0;
1651 controller.GoForward();
1652 EXPECT_EQ(0U, notifications.size());
1654 // Should now have a pending navigation to go forward.
1655 EXPECT_EQ(controller.GetEntryCount(), 2);
1656 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1657 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1658 EXPECT_TRUE(controller.GetLastCommittedEntry());
1659 EXPECT_TRUE(controller.GetPendingEntry());
1660 EXPECT_TRUE(controller.CanGoBack());
1661 EXPECT_FALSE(controller.CanGoForward());
1663 main_test_rfh()->SendNavigate(2, url3);
1664 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1665 navigation_entry_committed_counter_ = 0;
1666 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1668 EXPECT_EQ(controller.GetEntryCount(), 2);
1669 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1670 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1671 EXPECT_TRUE(controller.GetLastCommittedEntry());
1672 EXPECT_FALSE(controller.GetPendingEntry());
1673 EXPECT_TRUE(controller.CanGoBack());
1674 EXPECT_FALSE(controller.CanGoForward());
1677 // Two consequent navigation for the same URL entered in should be considered
1678 // as SAME_PAGE navigation even when we are redirected to some other page.
1679 TEST_F(NavigationControllerTest, Redirect) {
1680 NavigationControllerImpl& controller = controller_impl();
1681 TestNotificationTracker notifications;
1682 RegisterForAllNavNotifications(¬ifications, &controller);
1684 const GURL url1("http://foo1");
1685 const GURL url2("http://foo2"); // Redirection target
1688 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1690 EXPECT_EQ(0U, notifications.size());
1691 main_test_rfh()->SendNavigate(0, url2);
1692 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1693 navigation_entry_committed_counter_ = 0;
1696 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1698 EXPECT_TRUE(controller.GetPendingEntry());
1699 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1700 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1702 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1705 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1706 params.redirects.push_back(GURL("http://foo1"));
1707 params.redirects.push_back(GURL("http://foo2"));
1708 params.should_update_history = false;
1709 params.gesture = NavigationGestureAuto;
1710 params.is_post = false;
1711 params.page_state = PageState::CreateFromURL(url2);
1713 LoadCommittedDetails details;
1715 EXPECT_EQ(0U, notifications.size());
1716 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1718 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1719 navigation_entry_committed_counter_ = 0;
1721 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1722 EXPECT_EQ(controller.GetEntryCount(), 1);
1723 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1724 EXPECT_TRUE(controller.GetLastCommittedEntry());
1725 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1726 EXPECT_FALSE(controller.GetPendingEntry());
1727 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1729 EXPECT_FALSE(controller.CanGoBack());
1730 EXPECT_FALSE(controller.CanGoForward());
1733 // Similar to Redirect above, but the first URL is requested by POST,
1734 // the second URL is requested by GET. NavigationEntry::has_post_data_
1735 // must be cleared. http://crbug.com/21245
1736 TEST_F(NavigationControllerTest, PostThenRedirect) {
1737 NavigationControllerImpl& controller = controller_impl();
1738 TestNotificationTracker notifications;
1739 RegisterForAllNavNotifications(¬ifications, &controller);
1741 const GURL url1("http://foo1");
1742 const GURL url2("http://foo2"); // Redirection target
1744 // First request as POST
1745 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1746 controller.GetVisibleEntry()->SetHasPostData(true);
1748 EXPECT_EQ(0U, notifications.size());
1749 main_test_rfh()->SendNavigate(0, url2);
1750 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1751 navigation_entry_committed_counter_ = 0;
1754 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1756 EXPECT_TRUE(controller.GetPendingEntry());
1757 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1758 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1760 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1763 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1764 params.redirects.push_back(GURL("http://foo1"));
1765 params.redirects.push_back(GURL("http://foo2"));
1766 params.should_update_history = false;
1767 params.gesture = NavigationGestureAuto;
1768 params.is_post = false;
1769 params.page_state = PageState::CreateFromURL(url2);
1771 LoadCommittedDetails details;
1773 EXPECT_EQ(0U, notifications.size());
1774 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1776 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1777 navigation_entry_committed_counter_ = 0;
1779 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1780 EXPECT_EQ(controller.GetEntryCount(), 1);
1781 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1782 EXPECT_TRUE(controller.GetLastCommittedEntry());
1783 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1784 EXPECT_FALSE(controller.GetPendingEntry());
1785 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1786 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1788 EXPECT_FALSE(controller.CanGoBack());
1789 EXPECT_FALSE(controller.CanGoForward());
1792 // A redirect right off the bat should be a NEW_PAGE.
1793 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1794 NavigationControllerImpl& controller = controller_impl();
1795 TestNotificationTracker notifications;
1796 RegisterForAllNavNotifications(¬ifications, &controller);
1798 const GURL url1("http://foo1");
1799 const GURL url2("http://foo2"); // Redirection target
1802 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1804 EXPECT_TRUE(controller.GetPendingEntry());
1805 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1806 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1808 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1811 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1812 params.redirects.push_back(GURL("http://foo1"));
1813 params.redirects.push_back(GURL("http://foo2"));
1814 params.should_update_history = false;
1815 params.gesture = NavigationGestureAuto;
1816 params.is_post = false;
1817 params.page_state = PageState::CreateFromURL(url2);
1819 LoadCommittedDetails details;
1821 EXPECT_EQ(0U, notifications.size());
1822 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1824 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1825 navigation_entry_committed_counter_ = 0;
1827 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1828 EXPECT_EQ(controller.GetEntryCount(), 1);
1829 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1830 EXPECT_TRUE(controller.GetLastCommittedEntry());
1831 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1832 EXPECT_FALSE(controller.GetPendingEntry());
1833 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1835 EXPECT_FALSE(controller.CanGoBack());
1836 EXPECT_FALSE(controller.CanGoForward());
1839 // Tests navigation via link click within a subframe. A new navigation entry
1840 // should be created.
1841 TEST_F(NavigationControllerTest, NewSubframe) {
1842 NavigationControllerImpl& controller = controller_impl();
1843 TestNotificationTracker notifications;
1844 RegisterForAllNavNotifications(¬ifications, &controller);
1846 const GURL url1("http://foo1");
1847 main_test_rfh()->SendNavigate(0, url1);
1848 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1849 navigation_entry_committed_counter_ = 0;
1851 const GURL url2("http://foo2");
1852 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1855 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1856 params.should_update_history = false;
1857 params.gesture = NavigationGestureUser;
1858 params.is_post = false;
1859 params.page_state = PageState::CreateFromURL(url2);
1861 LoadCommittedDetails details;
1862 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1864 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1865 navigation_entry_committed_counter_ = 0;
1866 EXPECT_EQ(url1, details.previous_url);
1867 EXPECT_FALSE(details.is_in_page);
1868 EXPECT_FALSE(details.is_main_frame);
1870 // The new entry should be appended.
1871 EXPECT_EQ(2, controller.GetEntryCount());
1873 // New entry should refer to the new page, but the old URL (entries only
1874 // reflect the toplevel URL).
1875 EXPECT_EQ(url1, details.entry->GetURL());
1876 EXPECT_EQ(params.page_id, details.entry->GetPageID());
1879 // Some pages create a popup, then write an iframe into it. This causes a
1880 // subframe navigation without having any committed entry. Such navigations
1881 // just get thrown on the ground, but we shouldn't crash.
1882 TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
1883 NavigationControllerImpl& controller = controller_impl();
1884 TestNotificationTracker notifications;
1885 RegisterForAllNavNotifications(¬ifications, &controller);
1887 // Navigation controller currently has no entries.
1888 const GURL url("http://foo2");
1889 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1892 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1893 params.should_update_history = false;
1894 params.gesture = NavigationGestureAuto;
1895 params.is_post = false;
1896 params.page_state = PageState::CreateFromURL(url);
1898 LoadCommittedDetails details;
1899 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1901 EXPECT_EQ(0U, notifications.size());
1904 // Auto subframes are ones the page loads automatically like ads. They should
1905 // not create new navigation entries.
1906 TEST_F(NavigationControllerTest, AutoSubframe) {
1907 NavigationControllerImpl& controller = controller_impl();
1908 TestNotificationTracker notifications;
1909 RegisterForAllNavNotifications(¬ifications, &controller);
1911 const GURL url1("http://foo1");
1912 main_test_rfh()->SendNavigate(0, url1);
1913 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1914 navigation_entry_committed_counter_ = 0;
1916 const GURL url2("http://foo2");
1917 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1920 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1921 params.should_update_history = false;
1922 params.gesture = NavigationGestureUser;
1923 params.is_post = false;
1924 params.page_state = PageState::CreateFromURL(url2);
1926 // Navigating should do nothing.
1927 LoadCommittedDetails details;
1928 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1930 EXPECT_EQ(0U, notifications.size());
1932 // There should still be only one entry.
1933 EXPECT_EQ(1, controller.GetEntryCount());
1936 // Tests navigation and then going back to a subframe navigation.
1937 TEST_F(NavigationControllerTest, BackSubframe) {
1938 NavigationControllerImpl& controller = controller_impl();
1939 TestNotificationTracker notifications;
1940 RegisterForAllNavNotifications(¬ifications, &controller);
1943 const GURL url1("http://foo1");
1944 main_test_rfh()->SendNavigate(0, url1);
1945 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1946 navigation_entry_committed_counter_ = 0;
1948 // First manual subframe navigation.
1949 const GURL url2("http://foo2");
1950 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1953 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1954 params.should_update_history = false;
1955 params.gesture = NavigationGestureUser;
1956 params.is_post = false;
1957 params.page_state = PageState::CreateFromURL(url2);
1959 // This should generate a new entry.
1960 LoadCommittedDetails details;
1961 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1963 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1964 navigation_entry_committed_counter_ = 0;
1965 EXPECT_EQ(2, controller.GetEntryCount());
1967 // Second manual subframe navigation should also make a new entry.
1968 const GURL url3("http://foo3");
1971 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1973 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1974 navigation_entry_committed_counter_ = 0;
1975 EXPECT_EQ(3, controller.GetEntryCount());
1976 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
1979 controller.GoBack();
1982 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1984 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1985 navigation_entry_committed_counter_ = 0;
1986 EXPECT_EQ(3, controller.GetEntryCount());
1987 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
1988 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1989 EXPECT_FALSE(controller.GetPendingEntry());
1991 // Go back one more.
1992 controller.GoBack();
1995 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1997 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1998 navigation_entry_committed_counter_ = 0;
1999 EXPECT_EQ(3, controller.GetEntryCount());
2000 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2001 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2002 EXPECT_FALSE(controller.GetPendingEntry());
2005 TEST_F(NavigationControllerTest, LinkClick) {
2006 NavigationControllerImpl& controller = controller_impl();
2007 TestNotificationTracker notifications;
2008 RegisterForAllNavNotifications(¬ifications, &controller);
2010 const GURL url1("http://foo1");
2011 const GURL url2("http://foo2");
2013 main_test_rfh()->SendNavigate(0, url1);
2014 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2015 navigation_entry_committed_counter_ = 0;
2017 main_test_rfh()->SendNavigate(1, url2);
2018 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2019 navigation_entry_committed_counter_ = 0;
2021 // Should not have produced a new session history entry.
2022 EXPECT_EQ(controller.GetEntryCount(), 2);
2023 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2024 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2025 EXPECT_TRUE(controller.GetLastCommittedEntry());
2026 EXPECT_FALSE(controller.GetPendingEntry());
2027 EXPECT_TRUE(controller.CanGoBack());
2028 EXPECT_FALSE(controller.CanGoForward());
2031 TEST_F(NavigationControllerTest, InPage) {
2032 NavigationControllerImpl& controller = controller_impl();
2033 TestNotificationTracker notifications;
2034 RegisterForAllNavNotifications(¬ifications, &controller);
2037 const GURL url1("http://foo");
2038 main_test_rfh()->SendNavigate(0, url1);
2039 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2040 navigation_entry_committed_counter_ = 0;
2042 // Ensure main page navigation to same url respects the was_within_same_page
2043 // hint provided in the params.
2044 FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2045 self_params.page_id = 0;
2046 self_params.url = url1;
2047 self_params.transition = PAGE_TRANSITION_LINK;
2048 self_params.should_update_history = false;
2049 self_params.gesture = NavigationGestureUser;
2050 self_params.is_post = false;
2051 self_params.page_state = PageState::CreateFromURL(url1);
2052 self_params.was_within_same_page = true;
2054 LoadCommittedDetails details;
2055 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2057 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2058 navigation_entry_committed_counter_ = 0;
2059 EXPECT_TRUE(details.is_in_page);
2060 EXPECT_TRUE(details.did_replace_entry);
2061 EXPECT_EQ(1, controller.GetEntryCount());
2063 // Fragment navigation to a new page_id.
2064 const GURL url2("http://foo#a");
2065 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2068 params.transition = PAGE_TRANSITION_LINK;
2069 params.should_update_history = false;
2070 params.gesture = NavigationGestureUser;
2071 params.is_post = false;
2072 params.page_state = PageState::CreateFromURL(url2);
2073 params.was_within_same_page = true;
2075 // This should generate a new entry.
2076 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2078 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2079 navigation_entry_committed_counter_ = 0;
2080 EXPECT_TRUE(details.is_in_page);
2081 EXPECT_FALSE(details.did_replace_entry);
2082 EXPECT_EQ(2, controller.GetEntryCount());
2085 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2086 controller.GoBack();
2087 back_params.url = url1;
2088 back_params.page_id = 0;
2089 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2091 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2092 navigation_entry_committed_counter_ = 0;
2093 EXPECT_TRUE(details.is_in_page);
2094 EXPECT_EQ(2, controller.GetEntryCount());
2095 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2096 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2099 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2100 controller.GoForward();
2101 forward_params.url = url2;
2102 forward_params.page_id = 1;
2103 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2105 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2106 navigation_entry_committed_counter_ = 0;
2107 EXPECT_TRUE(details.is_in_page);
2108 EXPECT_EQ(2, controller.GetEntryCount());
2109 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2110 EXPECT_EQ(forward_params.url,
2111 controller.GetVisibleEntry()->GetURL());
2113 // Now go back and forward again. This is to work around a bug where we would
2114 // compare the incoming URL with the last committed entry rather than the
2115 // one identified by an existing page ID. This would result in the second URL
2116 // losing the reference fragment when you navigate away from it and then back.
2117 controller.GoBack();
2118 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2120 controller.GoForward();
2121 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2123 EXPECT_EQ(forward_params.url,
2124 controller.GetVisibleEntry()->GetURL());
2126 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2127 const GURL url3("http://bar");
2130 navigation_entry_committed_counter_ = 0;
2131 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2133 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2134 navigation_entry_committed_counter_ = 0;
2135 EXPECT_FALSE(details.is_in_page);
2136 EXPECT_EQ(3, controller.GetEntryCount());
2137 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2140 TEST_F(NavigationControllerTest, InPage_Replace) {
2141 NavigationControllerImpl& controller = controller_impl();
2142 TestNotificationTracker notifications;
2143 RegisterForAllNavNotifications(¬ifications, &controller);
2146 const GURL url1("http://foo");
2147 main_test_rfh()->SendNavigate(0, url1);
2148 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2149 navigation_entry_committed_counter_ = 0;
2151 // First navigation.
2152 const GURL url2("http://foo#a");
2153 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2154 params.page_id = 0; // Same page_id
2156 params.transition = PAGE_TRANSITION_LINK;
2157 params.should_update_history = false;
2158 params.gesture = NavigationGestureUser;
2159 params.is_post = false;
2160 params.page_state = PageState::CreateFromURL(url2);
2161 params.was_within_same_page = true;
2163 // This should NOT generate a new entry, nor prune the list.
2164 LoadCommittedDetails details;
2165 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2167 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2168 navigation_entry_committed_counter_ = 0;
2169 EXPECT_TRUE(details.is_in_page);
2170 EXPECT_TRUE(details.did_replace_entry);
2171 EXPECT_EQ(1, controller.GetEntryCount());
2174 // Tests for http://crbug.com/40395
2177 // window.location.replace("#a");
2178 // window.location='http://foo3/';
2180 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2181 NavigationControllerImpl& controller = controller_impl();
2182 TestNotificationTracker notifications;
2183 RegisterForAllNavNotifications(¬ifications, &controller);
2185 // Load an initial page.
2187 const GURL url("http://foo/");
2188 main_test_rfh()->SendNavigate(0, url);
2189 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2190 navigation_entry_committed_counter_ = 0;
2193 // Navigate to a new page.
2195 const GURL url("http://foo2/");
2196 main_test_rfh()->SendNavigate(1, url);
2197 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2198 navigation_entry_committed_counter_ = 0;
2201 // Navigate within the page.
2203 const GURL url("http://foo2/#a");
2204 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2205 params.page_id = 1; // Same page_id
2207 params.transition = PAGE_TRANSITION_LINK;
2208 params.redirects.push_back(url);
2209 params.should_update_history = true;
2210 params.gesture = NavigationGestureUnknown;
2211 params.is_post = false;
2212 params.page_state = PageState::CreateFromURL(url);
2213 params.was_within_same_page = true;
2215 // This should NOT generate a new entry, nor prune the list.
2216 LoadCommittedDetails details;
2217 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2219 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2220 navigation_entry_committed_counter_ = 0;
2221 EXPECT_TRUE(details.is_in_page);
2222 EXPECT_TRUE(details.did_replace_entry);
2223 EXPECT_EQ(2, controller.GetEntryCount());
2226 // Perform a client redirect to a new page.
2228 const GURL url("http://foo3/");
2229 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2230 params.page_id = 2; // New page_id
2232 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
2233 params.redirects.push_back(GURL("http://foo2/#a"));
2234 params.redirects.push_back(url);
2235 params.should_update_history = true;
2236 params.gesture = NavigationGestureUnknown;
2237 params.is_post = false;
2238 params.page_state = PageState::CreateFromURL(url);
2240 // This SHOULD generate a new entry.
2241 LoadCommittedDetails details;
2242 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2244 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2245 navigation_entry_committed_counter_ = 0;
2246 EXPECT_FALSE(details.is_in_page);
2247 EXPECT_EQ(3, controller.GetEntryCount());
2250 // Verify that BACK brings us back to http://foo2/.
2252 const GURL url("http://foo2/");
2253 controller.GoBack();
2254 main_test_rfh()->SendNavigate(1, url);
2255 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2256 navigation_entry_committed_counter_ = 0;
2257 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2261 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
2263 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2264 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2265 GURL url("http://foo");
2268 params.page_state = PageState::CreateFromURL(url);
2269 params.was_within_same_page = true;
2270 test_rvh()->SendNavigateWithParams(¶ms);
2271 // We pass if we don't crash.
2274 // NotificationObserver implementation used in verifying we've received the
2275 // NOTIFICATION_NAV_LIST_PRUNED method.
2276 class PrunedListener : public NotificationObserver {
2278 explicit PrunedListener(NavigationControllerImpl* controller)
2279 : notification_count_(0) {
2280 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2281 Source<NavigationController>(controller));
2284 virtual void Observe(int type,
2285 const NotificationSource& source,
2286 const NotificationDetails& details) OVERRIDE {
2287 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2288 notification_count_++;
2289 details_ = *(Details<PrunedDetails>(details).ptr());
2293 // Number of times NAV_LIST_PRUNED has been observed.
2294 int notification_count_;
2296 // Details from the last NAV_LIST_PRUNED.
2297 PrunedDetails details_;
2300 NotificationRegistrar registrar_;
2302 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2305 // Tests that we limit the number of navigation entries created correctly.
2306 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2307 NavigationControllerImpl& controller = controller_impl();
2308 size_t original_count = NavigationControllerImpl::max_entry_count();
2309 const int kMaxEntryCount = 5;
2311 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2314 // Load up to the max count, all entries should be there.
2315 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2316 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2318 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2319 main_test_rfh()->SendNavigate(url_index, url);
2322 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2324 // Created a PrunedListener to observe prune notifications.
2325 PrunedListener listener(&controller);
2327 // Navigate some more.
2328 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2330 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2331 main_test_rfh()->SendNavigate(url_index, url);
2334 // We should have got a pruned navigation.
2335 EXPECT_EQ(1, listener.notification_count_);
2336 EXPECT_TRUE(listener.details_.from_front);
2337 EXPECT_EQ(1, listener.details_.count);
2339 // We expect http://www.a.com/0 to be gone.
2340 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2341 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2342 GURL("http:////www.a.com/1"));
2344 // More navigations.
2345 for (int i = 0; i < 3; i++) {
2346 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2348 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2349 main_test_rfh()->SendNavigate(url_index, url);
2352 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2353 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2354 GURL("http:////www.a.com/4"));
2356 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2359 // Tests that we can do a restore and navigate to the restored entries and
2360 // everything is updated properly. This can be tricky since there is no
2361 // SiteInstance for the entries created initially.
2362 TEST_F(NavigationControllerTest, RestoreNavigate) {
2363 // Create a NavigationController with a restored set of tabs.
2364 GURL url("http://foo");
2365 std::vector<NavigationEntry*> entries;
2366 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2367 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2369 entry->SetPageID(0);
2370 entry->SetTitle(base::ASCIIToUTF16("Title"));
2371 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2372 const base::Time timestamp = base::Time::Now();
2373 entry->SetTimestamp(timestamp);
2374 entries.push_back(entry);
2375 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2376 WebContents::Create(WebContents::CreateParams(browser_context()))));
2377 NavigationControllerImpl& our_controller = our_contents->GetController();
2378 our_controller.Restore(
2380 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2382 ASSERT_EQ(0u, entries.size());
2384 // Before navigating to the restored entry, it should have a restore_type
2385 // and no SiteInstance.
2386 ASSERT_EQ(1, our_controller.GetEntryCount());
2387 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2388 NavigationEntryImpl::FromNavigationEntry(
2389 our_controller.GetEntryAtIndex(0))->restore_type());
2390 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2391 our_controller.GetEntryAtIndex(0))->site_instance());
2393 // After navigating, we should have one entry, and it should be "pending".
2394 // It should now have a SiteInstance and no restore_type.
2395 our_controller.GoToIndex(0);
2396 EXPECT_EQ(1, our_controller.GetEntryCount());
2397 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2398 our_controller.GetPendingEntry());
2399 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2400 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2401 NavigationEntryImpl::FromNavigationEntry
2402 (our_controller.GetEntryAtIndex(0))->restore_type());
2403 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2404 our_controller.GetEntryAtIndex(0))->site_instance());
2406 // Timestamp should remain the same before the navigation finishes.
2407 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2409 // Say we navigated to that entry.
2410 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2413 params.transition = PAGE_TRANSITION_LINK;
2414 params.should_update_history = false;
2415 params.gesture = NavigationGestureUser;
2416 params.is_post = false;
2417 params.page_state = PageState::CreateFromURL(url);
2418 LoadCommittedDetails details;
2419 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2422 // There should be no longer any pending entry and one committed one. This
2423 // means that we were able to locate the entry, assign its site instance, and
2424 // commit it properly.
2425 EXPECT_EQ(1, our_controller.GetEntryCount());
2426 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2427 EXPECT_FALSE(our_controller.GetPendingEntry());
2429 NavigationEntryImpl::FromNavigationEntry(
2430 our_controller.GetLastCommittedEntry())->site_instance()->
2432 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2433 NavigationEntryImpl::FromNavigationEntry(
2434 our_controller.GetEntryAtIndex(0))->restore_type());
2436 // Timestamp should have been updated.
2437 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2440 // Tests that we can still navigate to a restored entry after a different
2441 // navigation fails and clears the pending entry. http://crbug.com/90085
2442 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2443 // Create a NavigationController with a restored set of tabs.
2444 GURL url("http://foo");
2445 std::vector<NavigationEntry*> entries;
2446 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2447 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2449 entry->SetPageID(0);
2450 entry->SetTitle(base::ASCIIToUTF16("Title"));
2451 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2452 entries.push_back(entry);
2453 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2454 WebContents::Create(WebContents::CreateParams(browser_context()))));
2455 NavigationControllerImpl& our_controller = our_contents->GetController();
2456 our_controller.Restore(
2457 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2458 ASSERT_EQ(0u, entries.size());
2460 // Before navigating to the restored entry, it should have a restore_type
2461 // and no SiteInstance.
2462 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2463 NavigationEntryImpl::FromNavigationEntry(
2464 our_controller.GetEntryAtIndex(0))->restore_type());
2465 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2466 our_controller.GetEntryAtIndex(0))->site_instance());
2468 // After navigating, we should have one entry, and it should be "pending".
2469 // It should now have a SiteInstance and no restore_type.
2470 our_controller.GoToIndex(0);
2471 EXPECT_EQ(1, our_controller.GetEntryCount());
2472 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2473 our_controller.GetPendingEntry());
2474 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2475 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2476 NavigationEntryImpl::FromNavigationEntry(
2477 our_controller.GetEntryAtIndex(0))->restore_type());
2478 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2479 our_controller.GetEntryAtIndex(0))->site_instance());
2481 // This pending navigation may have caused a different navigation to fail,
2482 // which causes the pending entry to be cleared.
2483 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2484 fail_load_params.error_code = net::ERR_ABORTED;
2485 fail_load_params.error_description = base::string16();
2486 fail_load_params.url = url;
2487 fail_load_params.showing_repost_interstitial = false;
2488 main_test_rfh()->OnMessageReceived(
2489 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2492 // Now the pending restored entry commits.
2493 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2496 params.transition = PAGE_TRANSITION_LINK;
2497 params.should_update_history = false;
2498 params.gesture = NavigationGestureUser;
2499 params.is_post = false;
2500 params.page_state = PageState::CreateFromURL(url);
2501 LoadCommittedDetails details;
2502 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2505 // There should be no pending entry and one committed one.
2506 EXPECT_EQ(1, our_controller.GetEntryCount());
2507 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2508 EXPECT_FALSE(our_controller.GetPendingEntry());
2510 NavigationEntryImpl::FromNavigationEntry(
2511 our_controller.GetLastCommittedEntry())->site_instance()->
2513 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2514 NavigationEntryImpl::FromNavigationEntry(
2515 our_controller.GetEntryAtIndex(0))->restore_type());
2518 // Make sure that the page type and stuff is correct after an interstitial.
2519 TEST_F(NavigationControllerTest, Interstitial) {
2520 NavigationControllerImpl& controller = controller_impl();
2521 // First navigate somewhere normal.
2522 const GURL url1("http://foo");
2524 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2525 main_test_rfh()->SendNavigate(0, url1);
2527 // Now navigate somewhere with an interstitial.
2528 const GURL url2("http://bar");
2530 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2531 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2532 set_page_type(PAGE_TYPE_INTERSTITIAL);
2534 // At this point the interstitial will be displayed and the load will still
2535 // be pending. If the user continues, the load will commit.
2536 main_test_rfh()->SendNavigate(1, url2);
2538 // The page should be a normal page again.
2539 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2540 EXPECT_EQ(PAGE_TYPE_NORMAL,
2541 controller.GetLastCommittedEntry()->GetPageType());
2544 TEST_F(NavigationControllerTest, RemoveEntry) {
2545 NavigationControllerImpl& controller = controller_impl();
2546 const GURL url1("http://foo/1");
2547 const GURL url2("http://foo/2");
2548 const GURL url3("http://foo/3");
2549 const GURL url4("http://foo/4");
2550 const GURL url5("http://foo/5");
2551 const GURL pending_url("http://foo/pending");
2552 const GURL default_url("http://foo/default");
2555 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2556 main_test_rfh()->SendNavigate(0, url1);
2558 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2559 main_test_rfh()->SendNavigate(1, url2);
2561 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2562 main_test_rfh()->SendNavigate(2, url3);
2564 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2565 main_test_rfh()->SendNavigate(3, url4);
2567 url5, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2568 main_test_rfh()->SendNavigate(4, url5);
2570 // Try to remove the last entry. Will fail because it is the current entry.
2571 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2572 EXPECT_EQ(5, controller.GetEntryCount());
2573 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2575 // Go back, but don't commit yet. Check that we can't delete the current
2576 // and pending entries.
2577 controller.GoBack();
2578 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2579 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2581 // Now commit and delete the last entry.
2582 main_test_rfh()->SendNavigate(3, url4);
2583 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2584 EXPECT_EQ(4, controller.GetEntryCount());
2585 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2586 EXPECT_FALSE(controller.GetPendingEntry());
2588 // Remove an entry which is not the last committed one.
2589 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2590 EXPECT_EQ(3, controller.GetEntryCount());
2591 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2592 EXPECT_FALSE(controller.GetPendingEntry());
2594 // Remove the 2 remaining entries.
2595 controller.RemoveEntryAtIndex(1);
2596 controller.RemoveEntryAtIndex(0);
2598 // This should leave us with only the last committed entry.
2599 EXPECT_EQ(1, controller.GetEntryCount());
2600 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2603 // Tests the transient entry, making sure it goes away with all navigations.
2604 TEST_F(NavigationControllerTest, TransientEntry) {
2605 NavigationControllerImpl& controller = controller_impl();
2606 TestNotificationTracker notifications;
2607 RegisterForAllNavNotifications(¬ifications, &controller);
2609 const GURL url0("http://foo/0");
2610 const GURL url1("http://foo/1");
2611 const GURL url2("http://foo/2");
2612 const GURL url3("http://foo/3");
2613 const GURL url3_ref("http://foo/3#bar");
2614 const GURL url4("http://foo/4");
2615 const GURL transient_url("http://foo/transient");
2618 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2619 main_test_rfh()->SendNavigate(0, url0);
2621 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2622 main_test_rfh()->SendNavigate(1, url1);
2624 notifications.Reset();
2626 // Adding a transient with no pending entry.
2627 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2628 transient_entry->SetURL(transient_url);
2629 controller.SetTransientEntry(transient_entry);
2631 // We should not have received any notifications.
2632 EXPECT_EQ(0U, notifications.size());
2635 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2636 EXPECT_EQ(controller.GetEntryCount(), 3);
2637 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2638 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2639 EXPECT_TRUE(controller.GetLastCommittedEntry());
2640 EXPECT_FALSE(controller.GetPendingEntry());
2641 EXPECT_TRUE(controller.CanGoBack());
2642 EXPECT_FALSE(controller.CanGoForward());
2643 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2647 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2648 main_test_rfh()->SendNavigate(2, url2);
2650 // We should have navigated, transient entry should be gone.
2651 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2652 EXPECT_EQ(controller.GetEntryCount(), 3);
2654 // Add a transient again, then navigate with no pending entry this time.
2655 transient_entry = new NavigationEntryImpl;
2656 transient_entry->SetURL(transient_url);
2657 controller.SetTransientEntry(transient_entry);
2658 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2659 main_test_rfh()->SendNavigate(3, url3);
2660 // Transient entry should be gone.
2661 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2662 EXPECT_EQ(controller.GetEntryCount(), 4);
2664 // Initiate a navigation, add a transient then commit navigation.
2666 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2667 transient_entry = new NavigationEntryImpl;
2668 transient_entry->SetURL(transient_url);
2669 controller.SetTransientEntry(transient_entry);
2670 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2671 main_test_rfh()->SendNavigate(4, url4);
2672 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2673 EXPECT_EQ(controller.GetEntryCount(), 5);
2675 // Add a transient and go back. This should simply remove the transient.
2676 transient_entry = new NavigationEntryImpl;
2677 transient_entry->SetURL(transient_url);
2678 controller.SetTransientEntry(transient_entry);
2679 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2680 EXPECT_TRUE(controller.CanGoBack());
2681 EXPECT_FALSE(controller.CanGoForward());
2682 controller.GoBack();
2683 // Transient entry should be gone.
2684 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2685 EXPECT_EQ(controller.GetEntryCount(), 5);
2686 main_test_rfh()->SendNavigate(3, url3);
2688 // Add a transient and go to an entry before the current one.
2689 transient_entry = new NavigationEntryImpl;
2690 transient_entry->SetURL(transient_url);
2691 controller.SetTransientEntry(transient_entry);
2692 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2693 controller.GoToIndex(1);
2694 // The navigation should have been initiated, transient entry should be gone.
2695 EXPECT_FALSE(controller.GetTransientEntry());
2696 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2697 // Visible entry does not update for history navigations until commit.
2698 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2699 main_test_rfh()->SendNavigate(1, url1);
2700 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2702 // Add a transient and go to an entry after the current one.
2703 transient_entry = new NavigationEntryImpl;
2704 transient_entry->SetURL(transient_url);
2705 controller.SetTransientEntry(transient_entry);
2706 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2707 controller.GoToIndex(3);
2708 // The navigation should have been initiated, transient entry should be gone.
2709 // Because of the transient entry that is removed, going to index 3 makes us
2710 // land on url2 (which is visible after the commit).
2711 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2712 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2713 main_test_rfh()->SendNavigate(2, url2);
2714 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2716 // Add a transient and go forward.
2717 transient_entry = new NavigationEntryImpl;
2718 transient_entry->SetURL(transient_url);
2719 controller.SetTransientEntry(transient_entry);
2720 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2721 EXPECT_TRUE(controller.CanGoForward());
2722 controller.GoForward();
2723 // We should have navigated, transient entry should be gone.
2724 EXPECT_FALSE(controller.GetTransientEntry());
2725 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2726 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2727 main_test_rfh()->SendNavigate(3, url3);
2728 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2730 // Add a transient and do an in-page navigation, replacing the current entry.
2731 transient_entry = new NavigationEntryImpl;
2732 transient_entry->SetURL(transient_url);
2733 controller.SetTransientEntry(transient_entry);
2734 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2735 main_test_rfh()->SendNavigate(3, url3_ref);
2736 // Transient entry should be gone.
2737 EXPECT_FALSE(controller.GetTransientEntry());
2738 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2740 // Ensure the URLs are correct.
2741 EXPECT_EQ(controller.GetEntryCount(), 5);
2742 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2743 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2744 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2745 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2746 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2749 // Test that Reload initiates a new navigation to a transient entry's URL.
2750 TEST_F(NavigationControllerTest, ReloadTransient) {
2751 NavigationControllerImpl& controller = controller_impl();
2752 const GURL url0("http://foo/0");
2753 const GURL url1("http://foo/1");
2754 const GURL transient_url("http://foo/transient");
2756 // Load |url0|, and start a pending navigation to |url1|.
2758 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2759 main_test_rfh()->SendNavigate(0, url0);
2761 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2763 // A transient entry is added, interrupting the navigation.
2764 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2765 transient_entry->SetURL(transient_url);
2766 controller.SetTransientEntry(transient_entry);
2767 EXPECT_TRUE(controller.GetTransientEntry());
2768 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2770 // The page is reloaded, which should remove the pending entry for |url1| and
2771 // the transient entry for |transient_url|, and start a navigation to
2773 controller.Reload(true);
2774 EXPECT_FALSE(controller.GetTransientEntry());
2775 EXPECT_TRUE(controller.GetPendingEntry());
2776 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2777 ASSERT_EQ(controller.GetEntryCount(), 1);
2778 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2780 // Load of |transient_url| completes.
2781 main_test_rfh()->SendNavigate(1, transient_url);
2782 ASSERT_EQ(controller.GetEntryCount(), 2);
2783 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2784 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2787 // Ensure that renderer initiated pending entries get replaced, so that we
2788 // don't show a stale virtual URL when a navigation commits.
2789 // See http://crbug.com/266922.
2790 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2791 NavigationControllerImpl& controller = controller_impl();
2792 Navigator* navigator =
2793 contents()->GetFrameTree()->root()->navigator();
2795 const GURL url1("nonexistent:12121");
2796 const GURL url1_fixed("http://nonexistent:12121/");
2797 const GURL url2("http://foo");
2799 // We create pending entries for renderer-initiated navigations so that we
2800 // can show them in new tabs when it is safe.
2801 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2803 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2804 // the virtual URL to differ from the URL.
2805 controller.GetPendingEntry()->SetURL(url1_fixed);
2806 controller.GetPendingEntry()->SetVirtualURL(url1);
2808 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
2809 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
2811 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2812 is_renderer_initiated());
2814 // If the user clicks another link, we should replace the pending entry.
2815 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2816 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2817 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
2819 // Once it commits, the URL and virtual URL should reflect the actual page.
2820 main_test_rfh()->SendNavigate(0, url2);
2821 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2822 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
2824 // We should not replace the pending entry for an error URL.
2825 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2826 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2827 navigator->DidStartProvisionalLoad(main_test_rfh(),
2828 GURL(kUnreachableWebDataURL), false);
2829 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2831 // We should remember if the pending entry will replace the current one.
2832 // http://crbug.com/308444.
2833 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2834 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2835 set_should_replace_entry(true);
2836 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2838 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2839 should_replace_entry());
2840 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2841 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2842 // to the main frame.
2843 main_test_rfh()->SendNavigate(0, url2);
2844 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2847 // Tests that the URLs for renderer-initiated navigations are not displayed to
2848 // the user until the navigation commits, to prevent URL spoof attacks.
2849 // See http://crbug.com/99016.
2850 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
2851 NavigationControllerImpl& controller = controller_impl();
2852 TestNotificationTracker notifications;
2853 RegisterForAllNavNotifications(¬ifications, &controller);
2855 const GURL url0("http://foo/0");
2856 const GURL url1("http://foo/1");
2858 // For typed navigations (browser-initiated), both pending and visible entries
2859 // should update before commit.
2860 controller.LoadURL(url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2861 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
2862 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2863 main_test_rfh()->SendNavigate(0, url0);
2865 // For link clicks (renderer-initiated navigations), the pending entry should
2866 // update before commit but the visible should not.
2867 NavigationController::LoadURLParams load_url_params(url1);
2868 load_url_params.is_renderer_initiated = true;
2869 controller.LoadURLWithParams(load_url_params);
2870 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2871 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2873 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2874 is_renderer_initiated());
2876 // After commit, both visible should be updated, there should be no pending
2877 // entry, and we should no longer treat the entry as renderer-initiated.
2878 main_test_rfh()->SendNavigate(1, url1);
2879 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2880 EXPECT_FALSE(controller.GetPendingEntry());
2882 NavigationEntryImpl::FromNavigationEntry(
2883 controller.GetLastCommittedEntry())->is_renderer_initiated());
2885 notifications.Reset();
2888 // Tests that the URLs for renderer-initiated navigations in new tabs are
2889 // displayed to the user before commit, as long as the initial about:blank
2890 // page has not been modified. If so, we must revert to showing about:blank.
2891 // See http://crbug.com/9682.
2892 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
2893 NavigationControllerImpl& controller = controller_impl();
2894 TestNotificationTracker notifications;
2895 RegisterForAllNavNotifications(¬ifications, &controller);
2897 const GURL url("http://foo");
2899 // For renderer-initiated navigations in new tabs (with no committed entries),
2900 // we show the pending entry's URL as long as the about:blank page is not
2902 NavigationController::LoadURLParams load_url_params(url);
2903 load_url_params.transition_type = PAGE_TRANSITION_LINK;
2904 load_url_params.is_renderer_initiated = true;
2905 controller.LoadURLWithParams(load_url_params);
2906 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2907 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2909 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2910 is_renderer_initiated());
2911 EXPECT_TRUE(controller.IsInitialNavigation());
2912 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2914 // There should be no title yet.
2915 EXPECT_TRUE(contents()->GetTitle().empty());
2917 // If something else modifies the contents of the about:blank page, then
2918 // we must revert to showing about:blank to avoid a URL spoof.
2919 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
2920 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
2921 EXPECT_FALSE(controller.GetVisibleEntry());
2922 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2924 notifications.Reset();
2927 // Tests that the URLs for browser-initiated navigations in new tabs are
2928 // displayed to the user even after they fail, as long as the initial
2929 // about:blank page has not been modified. If so, we must revert to showing
2930 // about:blank. See http://crbug.com/355537.
2931 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
2932 NavigationControllerImpl& controller = controller_impl();
2933 TestNotificationTracker notifications;
2934 RegisterForAllNavNotifications(¬ifications, &controller);
2936 const GURL url("http://foo");
2938 // For browser-initiated navigations in new tabs (with no committed entries),
2939 // we show the pending entry's URL as long as the about:blank page is not
2940 // modified. This is possible in cases that the user types a URL into a popup
2941 // tab created with a slow URL.
2942 NavigationController::LoadURLParams load_url_params(url);
2943 load_url_params.transition_type = PAGE_TRANSITION_TYPED;
2944 load_url_params.is_renderer_initiated = false;
2945 controller.LoadURLWithParams(load_url_params);
2946 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2947 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2949 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2950 is_renderer_initiated());
2951 EXPECT_TRUE(controller.IsInitialNavigation());
2952 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2954 // There should be no title yet.
2955 EXPECT_TRUE(contents()->GetTitle().empty());
2957 // Suppose it aborts before committing, if it's a 204 or download or due to a
2958 // stop or a new navigation from the user. The URL should remain visible.
2959 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
2960 params.error_code = net::ERR_ABORTED;
2961 params.error_description = base::string16();
2963 params.showing_repost_interstitial = false;
2964 main_test_rfh()->OnMessageReceived(
2965 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
2966 contents()->SetIsLoading(test_rvh(), false, true, NULL);
2967 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2969 // If something else later modifies the contents of the about:blank page, then
2970 // we must revert to showing about:blank to avoid a URL spoof.
2971 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
2972 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
2973 EXPECT_FALSE(controller.GetVisibleEntry());
2974 EXPECT_FALSE(controller.GetPendingEntry());
2976 notifications.Reset();
2979 // Tests that the URLs for renderer-initiated navigations in new tabs are
2980 // displayed to the user even after they fail, as long as the initial
2981 // about:blank page has not been modified. If so, we must revert to showing
2982 // about:blank. See http://crbug.com/355537.
2983 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
2984 NavigationControllerImpl& controller = controller_impl();
2985 TestNotificationTracker notifications;
2986 RegisterForAllNavNotifications(¬ifications, &controller);
2988 const GURL url("http://foo");
2990 // For renderer-initiated navigations in new tabs (with no committed entries),
2991 // we show the pending entry's URL as long as the about:blank page is not
2993 NavigationController::LoadURLParams load_url_params(url);
2994 load_url_params.transition_type = PAGE_TRANSITION_LINK;
2995 load_url_params.is_renderer_initiated = true;
2996 controller.LoadURLWithParams(load_url_params);
2997 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2998 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3000 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
3001 is_renderer_initiated());
3002 EXPECT_TRUE(controller.IsInitialNavigation());
3003 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3005 // There should be no title yet.
3006 EXPECT_TRUE(contents()->GetTitle().empty());
3008 // Suppose it aborts before committing, if it's a 204 or download or due to a
3009 // stop or a new navigation from the user. The URL should remain visible.
3010 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3011 params.error_code = net::ERR_ABORTED;
3012 params.error_description = base::string16();
3014 params.showing_repost_interstitial = false;
3015 main_test_rfh()->OnMessageReceived(
3016 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3017 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3019 // If something else later modifies the contents of the about:blank page, then
3020 // we must revert to showing about:blank to avoid a URL spoof.
3021 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3022 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3023 EXPECT_FALSE(controller.GetVisibleEntry());
3024 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3026 notifications.Reset();
3029 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3030 NavigationControllerImpl& controller = controller_impl();
3031 TestNotificationTracker notifications;
3032 RegisterForAllNavNotifications(¬ifications, &controller);
3034 const GURL url1("http://foo/eh");
3035 const GURL url2("http://foo/bee");
3037 // For renderer-initiated navigations in new tabs (with no committed entries),
3038 // we show the pending entry's URL as long as the about:blank page is not
3040 NavigationController::LoadURLParams load_url_params(url1);
3041 load_url_params.transition_type = PAGE_TRANSITION_LINK;
3042 load_url_params.is_renderer_initiated = true;
3043 controller.LoadURLWithParams(load_url_params);
3044 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3046 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
3047 is_renderer_initiated());
3048 EXPECT_TRUE(controller.IsInitialNavigation());
3049 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3051 // Simulate a commit and then starting a new pending navigation.
3052 main_test_rfh()->SendNavigate(0, url1);
3053 NavigationController::LoadURLParams load_url2_params(url2);
3054 load_url2_params.transition_type = PAGE_TRANSITION_LINK;
3055 load_url2_params.is_renderer_initiated = true;
3056 controller.LoadURLWithParams(load_url2_params);
3058 // We should not consider this an initial navigation, and thus should
3059 // not show the pending URL.
3060 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3061 EXPECT_FALSE(controller.IsInitialNavigation());
3062 EXPECT_TRUE(controller.GetVisibleEntry());
3063 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3065 notifications.Reset();
3068 // Tests that IsInPageNavigation returns appropriate results. Prevents
3069 // regression for bug 1126349.
3070 TEST_F(NavigationControllerTest, IsInPageNavigation) {
3071 NavigationControllerImpl& controller = controller_impl();
3072 const GURL url("http://www.google.com/home.html");
3074 // If the renderer claims it performed an in-page navigation from
3075 // about:blank, trust the renderer.
3076 // This can happen when an iframe is created and populated via
3077 // document.write(), then tries to perform a fragment navigation.
3078 // TODO(japhet): We should only trust the renderer if the about:blank
3079 // was the first document in the given frame, but we don't have enough
3080 // information to identify that case currently.
3081 const GURL blank_url(url::kAboutBlankURL);
3082 main_test_rfh()->SendNavigate(0, blank_url);
3083 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3086 // Navigate to URL with no refs.
3087 main_test_rfh()->SendNavigate(0, url);
3089 // Reloading the page is not an in-page navigation.
3090 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3092 const GURL other_url("http://www.google.com/add.html");
3093 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3095 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3096 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3099 // Navigate to URL with refs.
3100 main_test_rfh()->SendNavigate(1, url_with_ref);
3102 // Reloading the page is not an in-page navigation.
3103 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
3105 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3107 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3109 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3110 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
3113 // Going to the same url again will be considered in-page
3114 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3115 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3118 // Going back to the non ref url will be considered in-page if the navigation
3120 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3123 // If the renderer says this is a same-origin in-page navigation, believe it.
3124 // This is the pushState/replaceState case.
3125 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
3128 // Don't believe the renderer if it claims a cross-origin navigation is
3130 const GURL different_origin_url("http://www.example.com");
3131 MockRenderProcessHost* rph =
3132 static_cast<MockRenderProcessHost*>(main_test_rfh()->GetProcess());
3133 EXPECT_EQ(0, rph->bad_msg_count());
3134 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3136 EXPECT_EQ(1, rph->bad_msg_count());
3139 // Some pages can have subframes with the same base URL (minus the reference) as
3140 // the main page. Even though this is hard, it can happen, and we don't want
3141 // these subframe navigations to affect the toplevel document. They should
3142 // instead be ignored. http://crbug.com/5585
3143 TEST_F(NavigationControllerTest, SameSubframe) {
3144 NavigationControllerImpl& controller = controller_impl();
3145 // Navigate the main frame.
3146 const GURL url("http://www.google.com/");
3147 main_test_rfh()->SendNavigate(0, url);
3149 // We should be at the first navigation entry.
3150 EXPECT_EQ(controller.GetEntryCount(), 1);
3151 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3153 // Navigate a subframe that would normally count as in-page.
3154 const GURL subframe("http://www.google.com/#");
3155 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3157 params.url = subframe;
3158 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3159 params.should_update_history = false;
3160 params.gesture = NavigationGestureAuto;
3161 params.is_post = false;
3162 params.page_state = PageState::CreateFromURL(subframe);
3163 LoadCommittedDetails details;
3164 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3167 // Nothing should have changed.
3168 EXPECT_EQ(controller.GetEntryCount(), 1);
3169 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3172 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3174 TEST_F(NavigationControllerTest, CloneAndGoBack) {
3175 NavigationControllerImpl& controller = controller_impl();
3176 const GURL url1("http://foo1");
3177 const GURL url2("http://foo2");
3178 const base::string16 title(base::ASCIIToUTF16("Title"));
3180 NavigateAndCommit(url1);
3181 controller.GetVisibleEntry()->SetTitle(title);
3182 NavigateAndCommit(url2);
3184 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3186 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3187 EXPECT_TRUE(clone->GetController().NeedsReload());
3188 clone->GetController().GoBack();
3189 // Navigating back should have triggered needs_reload_ to go false.
3190 EXPECT_FALSE(clone->GetController().NeedsReload());
3192 // Ensure that the pending URL and its title are visible.
3193 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3194 EXPECT_EQ(title, clone->GetTitle());
3197 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3198 // See http://crbug.com/234491.
3199 TEST_F(NavigationControllerTest, CloneAndReload) {
3200 NavigationControllerImpl& controller = controller_impl();
3201 const GURL url1("http://foo1");
3202 const GURL url2("http://foo2");
3203 const base::string16 title(base::ASCIIToUTF16("Title"));
3205 NavigateAndCommit(url1);
3206 controller.GetVisibleEntry()->SetTitle(title);
3207 NavigateAndCommit(url2);
3209 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3210 clone->GetController().LoadIfNecessary();
3212 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3213 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3215 clone->GetController().Reload(true);
3216 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3219 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3220 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3221 NavigationControllerImpl& controller = controller_impl();
3222 const GURL url1("http://foo1");
3223 const GURL url2("http://foo2");
3225 NavigateAndCommit(url1);
3226 NavigateAndCommit(url2);
3228 // Add an interstitial entry. Should be deleted with controller.
3229 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3230 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3231 controller.SetTransientEntry(interstitial_entry);
3233 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3235 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3238 // Test requesting and triggering a lazy reload.
3239 TEST_F(NavigationControllerTest, LazyReload) {
3240 NavigationControllerImpl& controller = controller_impl();
3241 const GURL url("http://foo");
3242 NavigateAndCommit(url);
3243 ASSERT_FALSE(controller.NeedsReload());
3245 // Request a reload to happen when the controller becomes active (e.g. after
3246 // the renderer gets killed in background on Android).
3247 controller.SetNeedsReload();
3248 ASSERT_TRUE(controller.NeedsReload());
3250 // Set the controller as active, triggering the requested reload.
3251 controller.SetActive(true);
3252 ASSERT_FALSE(controller.NeedsReload());
3255 // Tests a subframe navigation while a toplevel navigation is pending.
3256 // http://crbug.com/43967
3257 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3258 NavigationControllerImpl& controller = controller_impl();
3259 // Load the first page.
3260 const GURL url1("http://foo/");
3261 NavigateAndCommit(url1);
3263 // Now start a pending load to a totally different page, but don't commit it.
3264 const GURL url2("http://bar/");
3266 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3268 // Send a subframe update from the first page, as if one had just
3269 // automatically loaded. Auto subframes don't increment the page ID.
3270 const GURL url1_sub("http://foo/subframe");
3271 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3272 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3273 params.url = url1_sub;
3274 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3275 params.should_update_history = false;
3276 params.gesture = NavigationGestureAuto;
3277 params.is_post = false;
3278 params.page_state = PageState::CreateFromURL(url1_sub);
3279 LoadCommittedDetails details;
3281 // This should return false meaning that nothing was actually updated.
3282 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3285 // The notification should have updated the last committed one, and not
3286 // the pending load.
3287 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3289 // The active entry should be unchanged by the subframe load.
3290 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3293 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3294 TEST_F(NavigationControllerTest, CopyStateFrom) {
3295 NavigationControllerImpl& controller = controller_impl();
3296 const GURL url1("http://foo1");
3297 const GURL url2("http://foo2");
3299 NavigateAndCommit(url1);
3300 NavigateAndCommit(url2);
3301 controller.GoBack();
3302 contents()->CommitPendingNavigation();
3304 scoped_ptr<TestWebContents> other_contents(
3305 static_cast<TestWebContents*>(CreateTestWebContents()));
3306 NavigationControllerImpl& other_controller = other_contents->GetController();
3307 other_controller.CopyStateFrom(controller);
3309 // other_controller should now contain 2 urls.
3310 ASSERT_EQ(2, other_controller.GetEntryCount());
3311 // We should be looking at the first one.
3312 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3314 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3315 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3316 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3317 // This is a different site than url1, so the IDs start again at 0.
3318 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3320 // The max page ID map should be copied over and updated with the max page ID
3321 // from the current tab.
3322 SiteInstance* instance1 =
3323 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3324 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3326 // Ensure the SessionStorageNamespaceMaps are the same size and have
3327 // the same partitons loaded.
3329 // TODO(ajwong): We should load a url from a different partition earlier
3330 // to make sure this map has more than one entry.
3331 const SessionStorageNamespaceMap& session_storage_namespace_map =
3332 controller.GetSessionStorageNamespaceMap();
3333 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3334 other_controller.GetSessionStorageNamespaceMap();
3335 EXPECT_EQ(session_storage_namespace_map.size(),
3336 other_session_storage_namespace_map.size());
3337 for (SessionStorageNamespaceMap::const_iterator it =
3338 session_storage_namespace_map.begin();
3339 it != session_storage_namespace_map.end();
3341 SessionStorageNamespaceMap::const_iterator other =
3342 other_session_storage_namespace_map.find(it->first);
3343 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3347 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3348 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3349 NavigationControllerImpl& controller = controller_impl();
3350 const GURL url1("http://foo/1");
3351 const GURL url2("http://foo/2");
3352 const GURL url3("http://foo/3");
3354 NavigateAndCommit(url1);
3355 NavigateAndCommit(url2);
3357 // First two entries should have the same SiteInstance.
3358 SiteInstance* instance1 =
3359 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3360 SiteInstance* instance2 =
3361 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3362 EXPECT_EQ(instance1, instance2);
3363 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3364 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3365 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3367 scoped_ptr<TestWebContents> other_contents(
3368 static_cast<TestWebContents*>(CreateTestWebContents()));
3369 NavigationControllerImpl& other_controller = other_contents->GetController();
3370 other_contents->NavigateAndCommit(url3);
3371 other_contents->ExpectSetHistoryLengthAndPrune(
3372 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3373 other_controller.GetEntryAtIndex(0)->GetPageID());
3374 other_controller.CopyStateFromAndPrune(&controller, false);
3376 // other_controller should now contain the 3 urls: url1, url2 and url3.
3378 ASSERT_EQ(3, other_controller.GetEntryCount());
3380 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3382 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3383 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3384 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3385 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3386 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3387 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3389 // A new SiteInstance in a different BrowsingInstance should be used for the
3391 SiteInstance* instance3 =
3392 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3393 EXPECT_NE(instance3, instance1);
3394 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3396 // The max page ID map should be copied over and updated with the max page ID
3397 // from the current tab.
3398 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3399 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3402 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3404 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3405 NavigationControllerImpl& controller = controller_impl();
3406 const GURL url1("http://foo1");
3407 const GURL url2("http://foo2");
3408 const GURL url3("http://foo3");
3410 NavigateAndCommit(url1);
3411 NavigateAndCommit(url2);
3412 controller.GoBack();
3413 contents()->CommitPendingNavigation();
3415 scoped_ptr<TestWebContents> other_contents(
3416 static_cast<TestWebContents*>(CreateTestWebContents()));
3417 NavigationControllerImpl& other_controller = other_contents->GetController();
3418 other_contents->NavigateAndCommit(url3);
3419 other_contents->ExpectSetHistoryLengthAndPrune(
3420 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3421 other_controller.GetEntryAtIndex(0)->GetPageID());
3422 other_controller.CopyStateFromAndPrune(&controller, false);
3424 // other_controller should now contain: url1, url3
3426 ASSERT_EQ(2, other_controller.GetEntryCount());
3427 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3429 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3430 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3431 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3433 // The max page ID map should be copied over and updated with the max page ID
3434 // from the current tab.
3435 SiteInstance* instance1 =
3436 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3437 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3440 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3442 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3443 NavigationControllerImpl& controller = controller_impl();
3444 const GURL url1("http://foo1");
3445 const GURL url2("http://foo2");
3446 const GURL url3("http://foo3");
3447 const GURL url4("http://foo4");
3449 NavigateAndCommit(url1);
3450 NavigateAndCommit(url2);
3452 scoped_ptr<TestWebContents> other_contents(
3453 static_cast<TestWebContents*>(CreateTestWebContents()));
3454 NavigationControllerImpl& other_controller = other_contents->GetController();
3455 other_contents->NavigateAndCommit(url3);
3456 other_contents->NavigateAndCommit(url4);
3457 other_contents->ExpectSetHistoryLengthAndPrune(
3458 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)), 2,
3459 other_controller.GetEntryAtIndex(0)->GetPageID());
3460 other_controller.CopyStateFromAndPrune(&controller, false);
3462 // other_controller should now contain: url1, url2, url4
3464 ASSERT_EQ(3, other_controller.GetEntryCount());
3465 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3467 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3468 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3469 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3471 // The max page ID map should be copied over and updated with the max page ID
3472 // from the current tab.
3473 SiteInstance* instance1 =
3474 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3475 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3478 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3479 // not the last entry selected in the target.
3480 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3481 NavigationControllerImpl& controller = controller_impl();
3482 const GURL url1("http://foo1");
3483 const GURL url2("http://foo2");
3484 const GURL url3("http://foo3");
3485 const GURL url4("http://foo4");
3487 NavigateAndCommit(url1);
3488 NavigateAndCommit(url2);
3490 scoped_ptr<TestWebContents> other_contents(
3491 static_cast<TestWebContents*>(CreateTestWebContents()));
3492 NavigationControllerImpl& other_controller = other_contents->GetController();
3493 other_contents->NavigateAndCommit(url3);
3494 other_contents->NavigateAndCommit(url4);
3495 other_controller.GoBack();
3496 other_contents->CommitPendingNavigation();
3497 other_contents->ExpectSetHistoryLengthAndPrune(
3498 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3499 other_controller.GetEntryAtIndex(0)->GetPageID());
3500 other_controller.CopyStateFromAndPrune(&controller, false);
3502 // other_controller should now contain: url1, url2, url3
3504 ASSERT_EQ(3, other_controller.GetEntryCount());
3505 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3507 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3508 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3509 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3511 // The max page ID map should be copied over and updated with the max page ID
3512 // from the current tab.
3513 SiteInstance* instance1 =
3514 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3515 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3518 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3519 // a pending entry in the target.
3520 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3521 NavigationControllerImpl& controller = controller_impl();
3522 const GURL url1("http://foo1");
3523 const GURL url2("http://foo2");
3524 const GURL url3("http://foo3");
3525 const GURL url4("http://foo4");
3527 NavigateAndCommit(url1);
3528 NavigateAndCommit(url2);
3529 controller.GoBack();
3530 contents()->CommitPendingNavigation();
3532 scoped_ptr<TestWebContents> other_contents(
3533 static_cast<TestWebContents*>(CreateTestWebContents()));
3534 NavigationControllerImpl& other_controller = other_contents->GetController();
3535 other_contents->NavigateAndCommit(url3);
3536 other_controller.LoadURL(
3537 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3538 other_contents->ExpectSetHistoryLengthAndPrune(
3539 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3540 other_controller.GetEntryAtIndex(0)->GetPageID());
3541 other_controller.CopyStateFromAndPrune(&controller, false);
3543 // other_controller should now contain url1, url3, and a pending entry
3546 ASSERT_EQ(2, other_controller.GetEntryCount());
3547 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3549 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3550 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3552 // And there should be a pending entry for url4.
3553 ASSERT_TRUE(other_controller.GetPendingEntry());
3554 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3556 // The max page ID map should be copied over and updated with the max page ID
3557 // from the current tab.
3558 SiteInstance* instance1 =
3559 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3560 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3563 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3564 // client redirect entry (with the same page ID) in the target. This used to
3565 // crash because the last committed entry would be pruned but max_page_id
3566 // remembered the page ID (http://crbug.com/234809).
3567 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3568 NavigationControllerImpl& controller = controller_impl();
3569 const GURL url1("http://foo1");
3570 const GURL url2a("http://foo2/a");
3571 const GURL url2b("http://foo2/b");
3573 NavigateAndCommit(url1);
3575 scoped_ptr<TestWebContents> other_contents(
3576 static_cast<TestWebContents*>(CreateTestWebContents()));
3577 NavigationControllerImpl& other_controller = other_contents->GetController();
3578 other_contents->NavigateAndCommit(url2a);
3579 // Simulate a client redirect, which has the same page ID as entry 2a.
3580 other_controller.LoadURL(
3581 url2b, Referrer(), PAGE_TRANSITION_LINK, std::string());
3582 other_controller.GetPendingEntry()->SetPageID(
3583 other_controller.GetLastCommittedEntry()->GetPageID());
3585 other_contents->ExpectSetHistoryLengthAndPrune(
3586 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3587 other_controller.GetEntryAtIndex(0)->GetPageID());
3588 other_controller.CopyStateFromAndPrune(&controller, false);
3590 // other_controller should now contain url1, url2a, and a pending entry
3593 ASSERT_EQ(2, other_controller.GetEntryCount());
3594 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3596 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3597 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3599 // And there should be a pending entry for url4.
3600 ASSERT_TRUE(other_controller.GetPendingEntry());
3601 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3603 // Let the pending entry commit.
3604 other_contents->CommitPendingNavigation();
3606 // The max page ID map should be copied over and updated with the max page ID
3607 // from the current tab.
3608 SiteInstance* instance1 =
3609 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3610 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3613 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3614 // source, and 1 entry in the target. The back pending entry should be ignored.
3615 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3616 NavigationControllerImpl& controller = controller_impl();
3617 const GURL url1("http://foo1");
3618 const GURL url2("http://foo2");
3619 const GURL url3("http://foo3");
3621 NavigateAndCommit(url1);
3622 NavigateAndCommit(url2);
3623 controller.GoBack();
3625 scoped_ptr<TestWebContents> other_contents(
3626 static_cast<TestWebContents*>(CreateTestWebContents()));
3627 NavigationControllerImpl& other_controller = other_contents->GetController();
3628 other_contents->NavigateAndCommit(url3);
3629 other_contents->ExpectSetHistoryLengthAndPrune(
3630 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3631 other_controller.GetEntryAtIndex(0)->GetPageID());
3632 other_controller.CopyStateFromAndPrune(&controller, false);
3634 // other_controller should now contain: url1, url2, url3
3636 ASSERT_EQ(3, other_controller.GetEntryCount());
3637 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3639 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3640 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3641 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3642 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3644 // The max page ID map should be copied over and updated with the max page ID
3645 // from the current tab.
3646 SiteInstance* instance1 =
3647 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3648 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3651 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3652 // when the max entry count is 3. We should prune one entry.
3653 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3654 NavigationControllerImpl& controller = controller_impl();
3655 size_t original_count = NavigationControllerImpl::max_entry_count();
3656 const int kMaxEntryCount = 3;
3658 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3660 const GURL url1("http://foo/1");
3661 const GURL url2("http://foo/2");
3662 const GURL url3("http://foo/3");
3663 const GURL url4("http://foo/4");
3665 // Create a PrunedListener to observe prune notifications.
3666 PrunedListener listener(&controller);
3668 NavigateAndCommit(url1);
3669 NavigateAndCommit(url2);
3670 NavigateAndCommit(url3);
3672 scoped_ptr<TestWebContents> other_contents(
3673 static_cast<TestWebContents*>(CreateTestWebContents()));
3674 NavigationControllerImpl& other_controller = other_contents->GetController();
3675 other_contents->NavigateAndCommit(url4);
3676 other_contents->ExpectSetHistoryLengthAndPrune(
3677 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3678 other_controller.GetEntryAtIndex(0)->GetPageID());
3679 other_controller.CopyStateFromAndPrune(&controller, false);
3681 // We should have received a pruned notification.
3682 EXPECT_EQ(1, listener.notification_count_);
3683 EXPECT_TRUE(listener.details_.from_front);
3684 EXPECT_EQ(1, listener.details_.count);
3686 // other_controller should now contain only 3 urls: url2, url3 and url4.
3688 ASSERT_EQ(3, other_controller.GetEntryCount());
3690 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3692 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3693 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3694 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3695 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3696 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3697 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3699 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3702 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3703 // replace_entry set to true.
3704 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3705 NavigationControllerImpl& controller = controller_impl();
3706 const GURL url1("http://foo/1");
3707 const GURL url2("http://foo/2");
3708 const GURL url3("http://foo/3");
3710 NavigateAndCommit(url1);
3711 NavigateAndCommit(url2);
3713 // First two entries should have the same SiteInstance.
3714 SiteInstance* instance1 =
3715 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3716 SiteInstance* instance2 =
3717 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3718 EXPECT_EQ(instance1, instance2);
3719 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3720 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3721 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3723 scoped_ptr<TestWebContents> other_contents(
3724 static_cast<TestWebContents*>(CreateTestWebContents()));
3725 NavigationControllerImpl& other_controller = other_contents->GetController();
3726 other_contents->NavigateAndCommit(url3);
3727 other_contents->ExpectSetHistoryLengthAndPrune(
3728 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3729 other_controller.GetEntryAtIndex(0)->GetPageID());
3730 other_controller.CopyStateFromAndPrune(&controller, true);
3732 // other_controller should now contain the 2 urls: url1 and url3.
3734 ASSERT_EQ(2, other_controller.GetEntryCount());
3736 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3738 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3739 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3740 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3741 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3743 // A new SiteInstance in a different BrowsingInstance should be used for the
3745 SiteInstance* instance3 =
3746 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3747 EXPECT_NE(instance3, instance1);
3748 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3750 // The max page ID map should be copied over and updated with the max page ID
3751 // from the current tab.
3752 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3753 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3756 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3757 // entry count is 3 and replace_entry is true. We should not prune entries.
3758 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3759 NavigationControllerImpl& controller = controller_impl();
3760 size_t original_count = NavigationControllerImpl::max_entry_count();
3761 const int kMaxEntryCount = 3;
3763 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3765 const GURL url1("http://foo/1");
3766 const GURL url2("http://foo/2");
3767 const GURL url3("http://foo/3");
3768 const GURL url4("http://foo/4");
3770 // Create a PrunedListener to observe prune notifications.
3771 PrunedListener listener(&controller);
3773 NavigateAndCommit(url1);
3774 NavigateAndCommit(url2);
3775 NavigateAndCommit(url3);
3777 scoped_ptr<TestWebContents> other_contents(
3778 static_cast<TestWebContents*>(CreateTestWebContents()));
3779 NavigationControllerImpl& other_controller = other_contents->GetController();
3780 other_contents->NavigateAndCommit(url4);
3781 other_contents->ExpectSetHistoryLengthAndPrune(
3782 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3783 other_controller.GetEntryAtIndex(0)->GetPageID());
3784 other_controller.CopyStateFromAndPrune(&controller, true);
3786 // We should have received no pruned notification.
3787 EXPECT_EQ(0, listener.notification_count_);
3789 // other_controller should now contain only 3 urls: url1, url2 and url4.
3791 ASSERT_EQ(3, other_controller.GetEntryCount());
3793 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3795 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3796 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3797 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3798 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3799 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3800 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3802 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3805 // Tests that we can navigate to the restored entries
3806 // imported by CopyStateFromAndPrune.
3807 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
3808 const GURL kRestoredUrls[] = {
3809 GURL("http://site1.com"),
3810 GURL("http://site2.com"),
3812 const GURL kInitialUrl("http://site3.com");
3814 std::vector<NavigationEntry*> entries;
3815 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
3816 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
3817 kRestoredUrls[i], Referrer(), PAGE_TRANSITION_RELOAD, false,
3818 std::string(), browser_context());
3819 entry->SetPageID(static_cast<int>(i));
3820 entries.push_back(entry);
3823 // Create a WebContents with restored entries.
3824 scoped_ptr<TestWebContents> source_contents(
3825 static_cast<TestWebContents*>(CreateTestWebContents()));
3826 NavigationControllerImpl& source_controller =
3827 source_contents->GetController();
3828 source_controller.Restore(
3830 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
3832 ASSERT_EQ(0u, entries.size());
3833 source_controller.LoadIfNecessary();
3834 source_contents->CommitPendingNavigation();
3836 // Load a page, then copy state from |source_contents|.
3837 NavigateAndCommit(kInitialUrl);
3838 contents()->ExpectSetHistoryLengthAndPrune(
3839 GetSiteInstanceFromEntry(controller_impl().GetEntryAtIndex(0)), 2,
3840 controller_impl().GetEntryAtIndex(0)->GetPageID());
3841 controller_impl().CopyStateFromAndPrune(&source_controller, false);
3842 ASSERT_EQ(3, controller_impl().GetEntryCount());
3844 // Go back to the first entry one at a time and
3845 // verify that it works as expected.
3846 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
3847 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
3849 controller_impl().GoBack();
3850 contents()->CommitPendingNavigation();
3851 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
3852 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
3854 controller_impl().GoBack();
3855 contents()->CommitPendingNavigation();
3856 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
3857 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
3860 // Tests that navigations initiated from the page (with the history object)
3861 // work as expected, creating pending entries.
3862 TEST_F(NavigationControllerTest, HistoryNavigate) {
3863 NavigationControllerImpl& controller = controller_impl();
3864 const GURL url1("http://foo/1");
3865 const GURL url2("http://foo/2");
3866 const GURL url3("http://foo/3");
3868 NavigateAndCommit(url1);
3869 NavigateAndCommit(url2);
3870 NavigateAndCommit(url3);
3871 controller.GoBack();
3872 contents()->CommitPendingNavigation();
3874 // Simulate the page calling history.back(). It should create a pending entry.
3875 contents()->OnGoToEntryAtOffset(-1);
3876 EXPECT_EQ(0, controller.GetPendingEntryIndex());
3877 // The actual cross-navigation is suspended until the current RVH tells us
3878 // it unloaded, simulate that.
3879 contents()->ProceedWithCrossSiteNavigation();
3880 // Also make sure we told the page to navigate.
3881 const IPC::Message* message =
3882 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3883 ASSERT_TRUE(message != NULL);
3884 Tuple1<FrameMsg_Navigate_Params> nav_params;
3885 FrameMsg_Navigate::Read(message, &nav_params);
3886 EXPECT_EQ(url1, nav_params.a.url);
3887 process()->sink().ClearMessages();
3889 // Now test history.forward()
3890 contents()->OnGoToEntryAtOffset(2);
3891 EXPECT_EQ(2, controller.GetPendingEntryIndex());
3892 // The actual cross-navigation is suspended until the current RVH tells us
3893 // it unloaded, simulate that.
3894 contents()->ProceedWithCrossSiteNavigation();
3895 message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3896 ASSERT_TRUE(message != NULL);
3897 FrameMsg_Navigate::Read(message, &nav_params);
3898 EXPECT_EQ(url3, nav_params.a.url);
3899 process()->sink().ClearMessages();
3901 controller.DiscardNonCommittedEntries();
3903 // Make sure an extravagant history.go() doesn't break.
3904 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
3905 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3906 message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3907 EXPECT_TRUE(message == NULL);
3910 // Test call to PruneAllButLastCommitted for the only entry.
3911 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
3912 NavigationControllerImpl& controller = controller_impl();
3913 const GURL url1("http://foo1");
3914 NavigateAndCommit(url1);
3916 contents()->ExpectSetHistoryLengthAndPrune(
3917 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3918 controller.GetEntryAtIndex(0)->GetPageID());
3920 controller.PruneAllButLastCommitted();
3922 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3923 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3926 // Test call to PruneAllButLastCommitted for first entry.
3927 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
3928 NavigationControllerImpl& controller = controller_impl();
3929 const GURL url1("http://foo/1");
3930 const GURL url2("http://foo/2");
3931 const GURL url3("http://foo/3");
3933 NavigateAndCommit(url1);
3934 NavigateAndCommit(url2);
3935 NavigateAndCommit(url3);
3936 controller.GoBack();
3937 controller.GoBack();
3938 contents()->CommitPendingNavigation();
3940 contents()->ExpectSetHistoryLengthAndPrune(
3941 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3942 controller.GetEntryAtIndex(0)->GetPageID());
3944 controller.PruneAllButLastCommitted();
3946 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3947 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3950 // Test call to PruneAllButLastCommitted for intermediate entry.
3951 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
3952 NavigationControllerImpl& controller = controller_impl();
3953 const GURL url1("http://foo/1");
3954 const GURL url2("http://foo/2");
3955 const GURL url3("http://foo/3");
3957 NavigateAndCommit(url1);
3958 NavigateAndCommit(url2);
3959 NavigateAndCommit(url3);
3960 controller.GoBack();
3961 contents()->CommitPendingNavigation();
3963 contents()->ExpectSetHistoryLengthAndPrune(
3964 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)), 0,
3965 controller.GetEntryAtIndex(1)->GetPageID());
3967 controller.PruneAllButLastCommitted();
3969 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3970 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
3973 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
3974 // the list of entries.
3975 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
3976 NavigationControllerImpl& controller = controller_impl();
3977 const GURL url1("http://foo/1");
3978 const GURL url2("http://foo/2");
3979 const GURL url3("http://foo/3");
3981 NavigateAndCommit(url1);
3982 NavigateAndCommit(url2);
3984 // Create a pending entry that is not in the entry list.
3986 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3987 EXPECT_TRUE(controller.GetPendingEntry());
3988 EXPECT_EQ(2, controller.GetEntryCount());
3990 contents()->ExpectSetHistoryLengthAndPrune(
3991 NULL, 0, controller.GetPendingEntry()->GetPageID());
3992 controller.PruneAllButLastCommitted();
3994 // We should only have the last committed and pending entries at this point,
3995 // and the pending entry should still not be in the entry list.
3996 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
3997 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
3998 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3999 EXPECT_TRUE(controller.GetPendingEntry());
4000 EXPECT_EQ(1, controller.GetEntryCount());
4002 // Try to commit the pending entry.
4003 main_test_rfh()->SendNavigate(2, url3);
4004 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4005 EXPECT_FALSE(controller.GetPendingEntry());
4006 EXPECT_EQ(2, controller.GetEntryCount());
4007 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
4010 // Test to ensure that when we do a history navigation back to the current
4011 // committed page (e.g., going forward to a slow-loading page, then pressing
4012 // the back button), we just stop the navigation to prevent the throbber from
4013 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4014 // start, but WebKit essentially ignores the navigation and never sends a
4015 // message to stop the throbber.
4016 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
4017 NavigationControllerImpl& controller = controller_impl();
4018 const GURL url0("http://foo/0");
4019 const GURL url1("http://foo/1");
4021 NavigateAndCommit(url0);
4022 NavigateAndCommit(url1);
4024 // Go back to the original page, then forward to the slow page, then back
4025 controller.GoBack();
4026 contents()->CommitPendingNavigation();
4028 controller.GoForward();
4029 EXPECT_EQ(1, controller.GetPendingEntryIndex());
4031 controller.GoBack();
4032 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4035 TEST_F(NavigationControllerTest, IsInitialNavigation) {
4036 NavigationControllerImpl& controller = controller_impl();
4037 TestNotificationTracker notifications;
4038 RegisterForAllNavNotifications(¬ifications, &controller);
4041 EXPECT_TRUE(controller.IsInitialNavigation());
4043 // After commit, it stays false.
4044 const GURL url1("http://foo1");
4045 main_test_rfh()->SendNavigate(0, url1);
4046 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4047 navigation_entry_committed_counter_ = 0;
4048 EXPECT_FALSE(controller.IsInitialNavigation());
4050 // After starting a new navigation, it stays false.
4051 const GURL url2("http://foo2");
4053 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
4056 // Check that the favicon is not reused across a client redirect.
4057 // (crbug.com/28515)
4058 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4059 const GURL kPageWithFavicon("http://withfavicon.html");
4060 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4061 const GURL kIconURL("http://withfavicon.ico");
4062 const gfx::Image kDefaultFavicon = FaviconStatus().image;
4064 NavigationControllerImpl& controller = controller_impl();
4065 TestNotificationTracker notifications;
4066 RegisterForAllNavNotifications(¬ifications, &controller);
4068 main_test_rfh()->SendNavigate(0, kPageWithFavicon);
4069 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4070 navigation_entry_committed_counter_ = 0;
4072 NavigationEntry* entry = controller.GetLastCommittedEntry();
4074 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4076 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4077 content::FaviconStatus& favicon_status = entry->GetFavicon();
4078 favicon_status.image = CreateImage(SK_ColorWHITE);
4079 favicon_status.url = kIconURL;
4080 favicon_status.valid = true;
4081 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4083 main_test_rfh()->SendNavigateWithTransition(
4085 kPageWithoutFavicon,
4086 PAGE_TRANSITION_CLIENT_REDIRECT);
4087 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4088 navigation_entry_committed_counter_ = 0;
4090 entry = controller.GetLastCommittedEntry();
4092 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4094 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4097 // Check that the favicon is not cleared for NavigationEntries which were
4098 // previously navigated to.
4099 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4100 const GURL kUrl1("http://www.a.com/1");
4101 const GURL kUrl2("http://www.a.com/2");
4102 const GURL kIconURL("http://www.a.com/1/favicon.ico");
4104 NavigationControllerImpl& controller = controller_impl();
4105 TestNotificationTracker notifications;
4106 RegisterForAllNavNotifications(¬ifications, &controller);
4108 main_test_rfh()->SendNavigate(0, kUrl1);
4109 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4110 navigation_entry_committed_counter_ = 0;
4112 // Simulate Chromium having set the favicon for |kUrl1|.
4113 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4114 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4116 content::FaviconStatus& favicon_status = entry->GetFavicon();
4117 favicon_status.image = favicon_image;
4118 favicon_status.url = kIconURL;
4119 favicon_status.valid = true;
4121 // Navigate to another page and go back to the original page.
4122 main_test_rfh()->SendNavigate(1, kUrl2);
4123 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4124 navigation_entry_committed_counter_ = 0;
4125 main_test_rfh()->SendNavigateWithTransition(
4128 PAGE_TRANSITION_FORWARD_BACK);
4129 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4130 navigation_entry_committed_counter_ = 0;
4132 // Verify that the favicon for the page at |kUrl1| was not cleared.
4133 entry = controller.GetEntryAtIndex(0);
4135 EXPECT_EQ(kUrl1, entry->GetURL());
4136 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4139 // The test crashes on android: http://crbug.com/170449
4140 #if defined(OS_ANDROID)
4141 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4143 #define MAYBE_PurgeScreenshot PurgeScreenshot
4145 // Tests that screenshot are purged correctly.
4146 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4147 NavigationControllerImpl& controller = controller_impl();
4149 NavigationEntryImpl* entry;
4151 // Navigate enough times to make sure that some screenshots are purged.
4152 for (int i = 0; i < 12; ++i) {
4153 const GURL url(base::StringPrintf("http://foo%d/", i));
4154 NavigateAndCommit(url);
4155 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4158 MockScreenshotManager* screenshot_manager =
4159 new MockScreenshotManager(&controller);
4160 controller.SetScreenshotManager(screenshot_manager);
4161 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4162 entry = NavigationEntryImpl::FromNavigationEntry(
4163 controller.GetEntryAtIndex(i));
4164 screenshot_manager->TakeScreenshotFor(entry);
4165 EXPECT_TRUE(entry->screenshot().get());
4168 NavigateAndCommit(GURL("https://foo/"));
4169 EXPECT_EQ(13, controller.GetEntryCount());
4170 entry = NavigationEntryImpl::FromNavigationEntry(
4171 controller.GetEntryAtIndex(11));
4172 screenshot_manager->TakeScreenshotFor(entry);
4174 for (int i = 0; i < 2; ++i) {
4175 entry = NavigationEntryImpl::FromNavigationEntry(
4176 controller.GetEntryAtIndex(i));
4177 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4181 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4182 entry = NavigationEntryImpl::FromNavigationEntry(
4183 controller.GetEntryAtIndex(i));
4184 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4187 // Navigate to index 5 and then try to assign screenshot to all entries.
4188 controller.GoToIndex(5);
4189 contents()->CommitPendingNavigation();
4190 EXPECT_EQ(5, controller.GetCurrentEntryIndex());
4191 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4192 entry = NavigationEntryImpl::FromNavigationEntry(
4193 controller.GetEntryAtIndex(i));
4194 screenshot_manager->TakeScreenshotFor(entry);
4197 for (int i = 10; i <= 12; ++i) {
4198 entry = NavigationEntryImpl::FromNavigationEntry(
4199 controller.GetEntryAtIndex(i));
4200 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4202 screenshot_manager->TakeScreenshotFor(entry);
4205 // Navigate to index 7 and assign screenshot to all entries.
4206 controller.GoToIndex(7);
4207 contents()->CommitPendingNavigation();
4208 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4209 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4210 entry = NavigationEntryImpl::FromNavigationEntry(
4211 controller.GetEntryAtIndex(i));
4212 screenshot_manager->TakeScreenshotFor(entry);
4215 for (int i = 0; i < 2; ++i) {
4216 entry = NavigationEntryImpl::FromNavigationEntry(
4217 controller.GetEntryAtIndex(i));
4218 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4222 // Clear all screenshots.
4223 EXPECT_EQ(13, controller.GetEntryCount());
4224 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4225 controller.ClearAllScreenshots();
4226 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4227 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4228 entry = NavigationEntryImpl::FromNavigationEntry(
4229 controller.GetEntryAtIndex(i));
4230 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4235 TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) {
4237 test_rvh()->SendNavigate(1, GURL("http://foo"));
4239 // Set title and favicon.
4240 base::string16 title(base::ASCIIToUTF16("Title"));
4241 FaviconStatus favicon;
4242 favicon.valid = true;
4243 favicon.url = GURL("http://foo/favicon.ico");
4244 controller().GetLastCommittedEntry()->SetTitle(title);
4245 controller().GetLastCommittedEntry()->GetFavicon() = favicon;
4247 // history.pushState() is called.
4248 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4249 GURL url("http://foo#foo");
4252 params.page_state = PageState::CreateFromURL(url);
4253 params.was_within_same_page = true;
4254 test_rvh()->SendNavigateWithParams(¶ms);
4256 // The title should immediately be visible on the new NavigationEntry.
4257 base::string16 new_title =
4258 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4259 EXPECT_EQ(title, new_title);
4260 FaviconStatus new_favicon =
4261 controller().GetLastCommittedEntry()->GetFavicon();
4262 EXPECT_EQ(favicon.valid, new_favicon.valid);
4263 EXPECT_EQ(favicon.url, new_favicon.url);
4266 // Test that the navigation controller clears its session history when a
4267 // navigation commits with the clear history list flag set.
4268 TEST_F(NavigationControllerTest, ClearHistoryList) {
4269 const GURL url1("http://foo1");
4270 const GURL url2("http://foo2");
4271 const GURL url3("http://foo3");
4272 const GURL url4("http://foo4");
4274 NavigationControllerImpl& controller = controller_impl();
4276 // Create a session history with three entries, second entry is active.
4277 NavigateAndCommit(url1);
4278 NavigateAndCommit(url2);
4279 NavigateAndCommit(url3);
4280 controller.GoBack();
4281 contents()->CommitPendingNavigation();
4282 EXPECT_EQ(3, controller.GetEntryCount());
4283 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4285 // Create a new pending navigation, and indicate that the session history
4286 // should be cleared.
4287 NavigationController::LoadURLParams params(url4);
4288 params.should_clear_history_list = true;
4289 controller.LoadURLWithParams(params);
4291 // Verify that the pending entry correctly indicates that the session history
4292 // should be cleared.
4293 NavigationEntryImpl* entry =
4294 NavigationEntryImpl::FromNavigationEntry(
4295 controller.GetPendingEntry());
4297 EXPECT_TRUE(entry->should_clear_history_list());
4299 // Assume that the RV correctly cleared its history and commit the navigation.
4300 contents()->GetPendingMainFrame()->GetRenderViewHost()->
4301 set_simulate_history_list_was_cleared(true);
4302 contents()->CommitPendingNavigation();
4304 // Verify that the NavigationController's session history was correctly
4306 EXPECT_EQ(1, controller.GetEntryCount());
4307 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4308 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4309 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4310 EXPECT_FALSE(controller.CanGoBack());
4311 EXPECT_FALSE(controller.CanGoForward());
4312 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4315 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
4316 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
4317 EXPECT_FALSE(contents()->GetDelegate());
4318 contents()->SetDelegate(delegate.get());
4321 GURL url("http://foo");
4322 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4325 params.transition = PAGE_TRANSITION_FORM_SUBMIT;
4326 params.gesture = NavigationGestureUser;
4327 params.page_state = PageState::CreateFromURL(url);
4328 params.was_within_same_page = false;
4329 params.is_post = true;
4331 test_rvh()->SendNavigateWithParams(¶ms);
4333 // history.replaceState() is called.
4334 GURL replace_url("http://foo#foo");
4336 params.url = replace_url;
4337 params.transition = PAGE_TRANSITION_LINK;
4338 params.gesture = NavigationGestureUser;
4339 params.page_state = PageState::CreateFromURL(replace_url);
4340 params.was_within_same_page = true;
4341 params.is_post = false;
4342 params.post_id = -1;
4343 test_rvh()->SendNavigateWithParams(¶ms);
4345 // Now reload. replaceState overrides the POST, so we should not show a
4346 // repost warning dialog.
4347 controller_impl().Reload(true);
4348 EXPECT_EQ(0, delegate->repost_form_warning_count());
4351 } // namespace content