ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_unittest.cc
blob3492196c00e5244c60c2b447af7a74f9e8abece7
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/cross_site_transferring_request.h"
15 #include "content/browser/frame_host/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/navigation_request.h"
19 #include "content/browser/frame_host/navigator.h"
20 #include "content/browser/frame_host/navigator_impl.h"
21 #include "content/browser/site_instance_impl.h"
22 #include "content/browser/web_contents/web_contents_impl.h"
23 #include "content/common/frame_messages.h"
24 #include "content/common/view_messages.h"
25 #include "content/public/browser/navigation_details.h"
26 #include "content/public/browser/notification_registrar.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/browser/web_contents_delegate.h"
30 #include "content/public/browser/web_contents_observer.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/page_state.h"
33 #include "content/public/common/page_type.h"
34 #include "content/public/common/url_constants.h"
35 #include "content/public/test/mock_render_process_host.h"
36 #include "content/public/test/test_notification_tracker.h"
37 #include "content/public/test/test_utils.h"
38 #include "content/test/test_render_frame_host.h"
39 #include "content/test/test_render_view_host.h"
40 #include "content/test/test_web_contents.h"
41 #include "net/base/net_util.h"
42 #include "skia/ext/platform_canvas.h"
43 #include "testing/gtest/include/gtest/gtest.h"
45 using base::Time;
47 namespace {
49 // Creates an image with a 1x1 SkBitmap of the specified |color|.
50 gfx::Image CreateImage(SkColor color) {
51 SkBitmap bitmap;
52 bitmap.allocN32Pixels(1, 1);
53 bitmap.eraseColor(color);
54 return gfx::Image::CreateFrom1xBitmap(bitmap);
57 // Returns true if images |a| and |b| have the same pixel data.
58 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
59 // Assume that if the 1x bitmaps match, the images match.
60 SkBitmap a_bitmap = a.AsBitmap();
61 SkBitmap b_bitmap = b.AsBitmap();
63 if (a_bitmap.width() != b_bitmap.width() ||
64 a_bitmap.height() != b_bitmap.height()) {
65 return false;
67 SkAutoLockPixels a_bitmap_lock(a_bitmap);
68 SkAutoLockPixels b_bitmap_lock(b_bitmap);
69 return memcmp(a_bitmap.getPixels(),
70 b_bitmap.getPixels(),
71 a_bitmap.getSize()) == 0;
74 class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
75 public:
76 explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
77 : content::NavigationEntryScreenshotManager(owner),
78 encoding_screenshot_in_progress_(false) {
81 ~MockScreenshotManager() override {}
83 void TakeScreenshotFor(content::NavigationEntryImpl* entry) {
84 SkBitmap bitmap;
85 bitmap.allocPixels(SkImageInfo::Make(
86 1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
87 bitmap.eraseARGB(0, 0, 0, 0);
88 encoding_screenshot_in_progress_ = true;
89 OnScreenshotTaken(entry->GetUniqueID(), bitmap, content::READBACK_SUCCESS);
90 WaitUntilScreenshotIsReady();
93 int GetScreenshotCount() {
94 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
97 void WaitUntilScreenshotIsReady() {
98 if (!encoding_screenshot_in_progress_)
99 return;
100 message_loop_runner_ = new content::MessageLoopRunner;
101 message_loop_runner_->Run();
104 private:
105 // Overridden from content::NavigationEntryScreenshotManager:
106 void TakeScreenshotImpl(content::RenderViewHost* host,
107 content::NavigationEntryImpl* entry) override {}
109 void OnScreenshotSet(content::NavigationEntryImpl* entry) override {
110 encoding_screenshot_in_progress_ = false;
111 NavigationEntryScreenshotManager::OnScreenshotSet(entry);
112 if (message_loop_runner_.get())
113 message_loop_runner_->Quit();
116 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
117 bool encoding_screenshot_in_progress_;
119 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
122 } // namespace
124 namespace content {
126 // TimeSmoother tests ----------------------------------------------------------
128 // With no duplicates, GetSmoothedTime should be the identity
129 // function.
130 TEST(TimeSmoother, Basic) {
131 NavigationControllerImpl::TimeSmoother smoother;
132 for (int64 i = 1; i < 1000; ++i) {
133 base::Time t = base::Time::FromInternalValue(i);
134 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
138 // With a single duplicate and timestamps thereafter increasing by one
139 // microsecond, the smoothed time should always be one behind.
140 TEST(TimeSmoother, SingleDuplicate) {
141 NavigationControllerImpl::TimeSmoother smoother;
142 base::Time t = base::Time::FromInternalValue(1);
143 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
144 for (int64 i = 1; i < 1000; ++i) {
145 base::Time expected_t = base::Time::FromInternalValue(i + 1);
146 t = base::Time::FromInternalValue(i);
147 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
151 // With k duplicates and timestamps thereafter increasing by one
152 // microsecond, the smoothed time should always be k behind.
153 TEST(TimeSmoother, ManyDuplicates) {
154 const int64 kNumDuplicates = 100;
155 NavigationControllerImpl::TimeSmoother smoother;
156 base::Time t = base::Time::FromInternalValue(1);
157 for (int64 i = 0; i < kNumDuplicates; ++i) {
158 base::Time expected_t = base::Time::FromInternalValue(i + 1);
159 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
161 for (int64 i = 1; i < 1000; ++i) {
162 base::Time expected_t =
163 base::Time::FromInternalValue(i + kNumDuplicates);
164 t = base::Time::FromInternalValue(i);
165 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
169 // If the clock jumps far back enough after a run of duplicates, it
170 // should immediately jump to that value.
171 TEST(TimeSmoother, ClockBackwardsJump) {
172 const int64 kNumDuplicates = 100;
173 NavigationControllerImpl::TimeSmoother smoother;
174 base::Time t = base::Time::FromInternalValue(1000);
175 for (int64 i = 0; i < kNumDuplicates; ++i) {
176 base::Time expected_t = base::Time::FromInternalValue(i + 1000);
177 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
179 t = base::Time::FromInternalValue(500);
180 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
183 // NavigationControllerTest ----------------------------------------------------
185 class NavigationControllerTest
186 : public RenderViewHostImplTestHarness,
187 public WebContentsObserver {
188 public:
189 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
192 void SetUp() override {
193 RenderViewHostImplTestHarness::SetUp();
194 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents();
195 ASSERT_TRUE(web_contents); // The WebContents should be created by now.
196 WebContentsObserver::Observe(web_contents);
199 // WebContentsObserver:
200 void DidStartNavigationToPendingEntry(
201 const GURL& url,
202 NavigationController::ReloadType reload_type) override {
203 navigated_url_ = url;
206 void NavigationEntryCommitted(
207 const LoadCommittedDetails& load_details) override {
208 navigation_entry_committed_counter_++;
211 const GURL& navigated_url() const {
212 return navigated_url_;
215 NavigationControllerImpl& controller_impl() {
216 return static_cast<NavigationControllerImpl&>(controller());
219 bool HasNavigationRequest() {
220 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
221 switches::kEnableBrowserSideNavigation)) {
222 FrameTreeNode* root = contents()->GetFrameTree()->root();
223 NavigationRequest* navigation_request = static_cast<NavigatorImpl*>(
224 root->navigator())->GetNavigationRequestForNodeForTesting(root);
225 return navigation_request != nullptr;
227 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID)
228 != nullptr;
231 const GURL GetLastNavigationURL() {
232 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
233 switches::kEnableBrowserSideNavigation)) {
234 FrameTreeNode* root = contents()->GetFrameTree()->root();
235 NavigationRequest* navigation_request = static_cast<NavigatorImpl*>(
236 root->navigator())->GetNavigationRequestForNodeForTesting(root);
237 CHECK(navigation_request);
238 return navigation_request->common_params().url;
240 const IPC::Message* message =
241 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
242 CHECK(message);
243 Tuple<FrameMsg_Navigate_Params> nav_params;
244 FrameMsg_Navigate::Read(message, &nav_params);
245 return get<0>(nav_params).common_params.url;
248 protected:
249 GURL navigated_url_;
250 size_t navigation_entry_committed_counter_;
253 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
254 NavigationController* controller) {
255 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED,
256 Source<NavigationController>(controller));
257 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED,
258 Source<NavigationController>(controller));
261 class TestWebContentsDelegate : public WebContentsDelegate {
262 public:
263 explicit TestWebContentsDelegate() :
264 navigation_state_change_count_(0),
265 repost_form_warning_count_(0) {}
267 int navigation_state_change_count() {
268 return navigation_state_change_count_;
271 int repost_form_warning_count() {
272 return repost_form_warning_count_;
275 // Keep track of whether the tab has notified us of a navigation state change.
276 void NavigationStateChanged(WebContents* source,
277 InvalidateTypes changed_flags) override {
278 navigation_state_change_count_++;
281 void ShowRepostFormWarningDialog(WebContents* source) override {
282 repost_form_warning_count_++;
285 private:
286 // The number of times NavigationStateChanged has been called.
287 int navigation_state_change_count_;
289 // The number of times ShowRepostFormWarningDialog() was called.
290 int repost_form_warning_count_;
293 // -----------------------------------------------------------------------------
295 TEST_F(NavigationControllerTest, Defaults) {
296 NavigationControllerImpl& controller = controller_impl();
298 EXPECT_FALSE(controller.GetPendingEntry());
299 EXPECT_FALSE(controller.GetVisibleEntry());
300 EXPECT_FALSE(controller.GetLastCommittedEntry());
301 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
302 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
303 EXPECT_EQ(controller.GetEntryCount(), 0);
304 EXPECT_FALSE(controller.CanGoBack());
305 EXPECT_FALSE(controller.CanGoForward());
308 TEST_F(NavigationControllerTest, GoToOffset) {
309 NavigationControllerImpl& controller = controller_impl();
310 TestNotificationTracker notifications;
311 RegisterForAllNavNotifications(&notifications, &controller);
313 const int kNumUrls = 5;
314 std::vector<GURL> urls(kNumUrls);
315 for (int i = 0; i < kNumUrls; ++i) {
316 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
319 main_test_rfh()->PrepareForCommit(urls[0]);
320 main_test_rfh()->SendNavigate(0, urls[0]);
321 EXPECT_EQ(1U, navigation_entry_committed_counter_);
322 navigation_entry_committed_counter_ = 0;
323 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
324 EXPECT_FALSE(controller.CanGoBack());
325 EXPECT_FALSE(controller.CanGoForward());
326 EXPECT_FALSE(controller.CanGoToOffset(1));
328 for (int i = 1; i <= 4; ++i) {
329 main_test_rfh()->PrepareForCommit(urls[i]);
330 main_test_rfh()->SendNavigate(i, urls[i]);
331 EXPECT_EQ(1U, navigation_entry_committed_counter_);
332 navigation_entry_committed_counter_ = 0;
333 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
334 EXPECT_TRUE(controller.CanGoToOffset(-i));
335 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
336 EXPECT_FALSE(controller.CanGoToOffset(1));
339 // We have loaded 5 pages, and are currently at the last-loaded page.
340 int url_index = 4;
342 enum Tests {
343 GO_TO_MIDDLE_PAGE = -2,
344 GO_FORWARDS = 1,
345 GO_BACKWARDS = -1,
346 GO_TO_BEGINNING = -2,
347 GO_TO_END = 4,
348 NUM_TESTS = 5,
351 const int test_offsets[NUM_TESTS] = {
352 GO_TO_MIDDLE_PAGE,
353 GO_FORWARDS,
354 GO_BACKWARDS,
355 GO_TO_BEGINNING,
356 GO_TO_END
359 for (int test = 0; test < NUM_TESTS; ++test) {
360 int offset = test_offsets[test];
361 controller.GoToOffset(offset);
362 url_index += offset;
363 // Check that the GoToOffset will land on the expected page.
364 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
365 main_test_rfh()->PrepareForCommit(urls[url_index]);
366 main_test_rfh()->SendNavigate(url_index, urls[url_index]);
367 EXPECT_EQ(1U, navigation_entry_committed_counter_);
368 navigation_entry_committed_counter_ = 0;
369 // Check that we can go to any valid offset into the history.
370 for (size_t j = 0; j < urls.size(); ++j)
371 EXPECT_TRUE(controller.CanGoToOffset(j - url_index));
372 // Check that we can't go beyond the beginning or end of the history.
373 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1)));
374 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index));
378 TEST_F(NavigationControllerTest, LoadURL) {
379 NavigationControllerImpl& controller = controller_impl();
380 TestNotificationTracker notifications;
381 RegisterForAllNavNotifications(&notifications, &controller);
383 const GURL url1("http://foo1");
384 const GURL url2("http://foo2");
386 controller.LoadURL(
387 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
388 // Creating a pending notification should not have issued any of the
389 // notifications we're listening for.
390 EXPECT_EQ(0U, notifications.size());
392 // The load should now be pending.
393 EXPECT_EQ(controller.GetEntryCount(), 0);
394 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
395 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
396 EXPECT_FALSE(controller.GetLastCommittedEntry());
397 ASSERT_TRUE(controller.GetPendingEntry());
398 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
399 EXPECT_FALSE(controller.CanGoBack());
400 EXPECT_FALSE(controller.CanGoForward());
401 EXPECT_EQ(contents()->GetMaxPageID(), -1);
403 // Neither the timestamp nor the status code should have been set yet.
404 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
405 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
407 // We should have gotten no notifications from the preceeding checks.
408 EXPECT_EQ(0U, notifications.size());
410 main_test_rfh()->PrepareForCommit(url1);
411 main_test_rfh()->SendNavigate(0, url1);
412 EXPECT_EQ(1U, navigation_entry_committed_counter_);
413 navigation_entry_committed_counter_ = 0;
415 // The load should now be committed.
416 EXPECT_EQ(controller.GetEntryCount(), 1);
417 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
418 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
419 EXPECT_TRUE(controller.GetLastCommittedEntry());
420 EXPECT_FALSE(controller.GetPendingEntry());
421 ASSERT_TRUE(controller.GetVisibleEntry());
422 EXPECT_FALSE(controller.CanGoBack());
423 EXPECT_FALSE(controller.CanGoForward());
424 EXPECT_EQ(contents()->GetMaxPageID(), 0);
425 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
427 // The timestamp should have been set.
428 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
430 // Load another...
431 controller.LoadURL(
432 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
434 // The load should now be pending.
435 EXPECT_EQ(controller.GetEntryCount(), 1);
436 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
437 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
438 EXPECT_TRUE(controller.GetLastCommittedEntry());
439 ASSERT_TRUE(controller.GetPendingEntry());
440 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
441 // TODO(darin): maybe this should really be true?
442 EXPECT_FALSE(controller.CanGoBack());
443 EXPECT_FALSE(controller.CanGoForward());
444 EXPECT_EQ(contents()->GetMaxPageID(), 0);
446 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
448 // Simulate the beforeunload ack for the cross-site transition, and then the
449 // commit.
450 main_test_rfh()->PrepareForCommit(url2);
451 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
452 EXPECT_EQ(1U, navigation_entry_committed_counter_);
453 navigation_entry_committed_counter_ = 0;
455 // The load should now be committed.
456 EXPECT_EQ(controller.GetEntryCount(), 2);
457 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
458 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
459 EXPECT_TRUE(controller.GetLastCommittedEntry());
460 EXPECT_FALSE(controller.GetPendingEntry());
461 ASSERT_TRUE(controller.GetVisibleEntry());
462 EXPECT_TRUE(controller.CanGoBack());
463 EXPECT_FALSE(controller.CanGoForward());
464 EXPECT_EQ(contents()->GetMaxPageID(), 1);
466 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
469 namespace {
471 base::Time GetFixedTime(base::Time time) {
472 return time;
475 } // namespace
477 TEST_F(NavigationControllerTest, LoadURLSameTime) {
478 NavigationControllerImpl& controller = controller_impl();
479 TestNotificationTracker notifications;
480 RegisterForAllNavNotifications(&notifications, &controller);
482 // Set the clock to always return a timestamp of 1.
483 controller.SetGetTimestampCallbackForTest(
484 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
486 const GURL url1("http://foo1");
487 const GURL url2("http://foo2");
489 controller.LoadURL(
490 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
492 main_test_rfh()->PrepareForCommit(url1);
493 main_test_rfh()->SendNavigate(0, url1);
494 EXPECT_EQ(1U, navigation_entry_committed_counter_);
495 navigation_entry_committed_counter_ = 0;
497 // Load another...
498 controller.LoadURL(
499 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
501 // Simulate the beforeunload ack for the cross-site transition, and then the
502 // commit.
503 main_test_rfh()->PrepareForCommit(url2);
504 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
505 EXPECT_EQ(1U, navigation_entry_committed_counter_);
506 navigation_entry_committed_counter_ = 0;
508 // The two loads should now be committed.
509 ASSERT_EQ(controller.GetEntryCount(), 2);
511 // Timestamps should be distinct despite the clock returning the
512 // same value.
513 EXPECT_EQ(1u,
514 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
515 EXPECT_EQ(2u,
516 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
519 void CheckNavigationEntryMatchLoadParams(
520 NavigationController::LoadURLParams& load_params,
521 NavigationEntryImpl* entry) {
522 EXPECT_EQ(load_params.url, entry->GetURL());
523 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
524 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
525 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
526 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
528 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
529 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
530 if (!load_params.virtual_url_for_data_url.is_empty()) {
531 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
533 if (NavigationController::UA_OVERRIDE_INHERIT !=
534 load_params.override_user_agent) {
535 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
536 load_params.override_user_agent);
537 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
539 EXPECT_EQ(load_params.browser_initiated_post_data.get(),
540 entry->GetBrowserInitiatedPostData());
541 EXPECT_EQ(load_params.transferred_global_request_id,
542 entry->transferred_global_request_id());
545 TEST_F(NavigationControllerTest, LoadURLWithParams) {
546 NavigationControllerImpl& controller = controller_impl();
548 NavigationController::LoadURLParams load_params(GURL("http://foo"));
549 load_params.referrer =
550 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
551 load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
552 load_params.extra_headers = "content-type: text/plain";
553 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
554 load_params.is_renderer_initiated = true;
555 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
556 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
558 controller.LoadURLWithParams(load_params);
559 NavigationEntryImpl* entry = controller.GetPendingEntry();
561 // The timestamp should not have been set yet.
562 ASSERT_TRUE(entry);
563 EXPECT_TRUE(entry->GetTimestamp().is_null());
565 CheckNavigationEntryMatchLoadParams(load_params, entry);
568 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
569 NavigationControllerImpl& controller = controller_impl();
571 NavigationController::LoadURLParams load_params(
572 GURL("data:text/html,dataurl"));
573 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
574 load_params.base_url_for_data_url = GURL("http://foo");
575 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
576 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
578 controller.LoadURLWithParams(load_params);
579 NavigationEntryImpl* entry = controller.GetPendingEntry();
581 CheckNavigationEntryMatchLoadParams(load_params, entry);
584 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
585 NavigationControllerImpl& controller = controller_impl();
587 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
588 load_params.transition_type = ui::PAGE_TRANSITION_TYPED;
589 load_params.load_type =
590 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
591 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
594 const unsigned char* raw_data =
595 reinterpret_cast<const unsigned char*>("d\n\0a2");
596 const int length = 5;
597 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
598 scoped_refptr<base::RefCountedBytes> data =
599 base::RefCountedBytes::TakeVector(&post_data_vector);
600 load_params.browser_initiated_post_data = data.get();
602 controller.LoadURLWithParams(load_params);
603 NavigationEntryImpl* entry = controller.GetPendingEntry();
605 CheckNavigationEntryMatchLoadParams(load_params, entry);
608 // Tests what happens when the same page is loaded again. Should not create a
609 // new session history entry. This is what happens when you press enter in the
610 // URL bar to reload: a pending entry is created and then it is discarded when
611 // the load commits (because WebCore didn't actually make a new entry).
612 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
613 NavigationControllerImpl& controller = controller_impl();
614 TestNotificationTracker notifications;
615 RegisterForAllNavNotifications(&notifications, &controller);
617 const GURL url1("http://foo1");
619 controller.LoadURL(
620 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
621 EXPECT_EQ(0U, notifications.size());
622 main_test_rfh()->PrepareForCommit(url1);
623 main_test_rfh()->SendNavigate(0, url1);
624 EXPECT_EQ(1U, navigation_entry_committed_counter_);
625 navigation_entry_committed_counter_ = 0;
627 ASSERT_TRUE(controller.GetVisibleEntry());
628 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
629 EXPECT_FALSE(timestamp.is_null());
631 controller.LoadURL(
632 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
633 EXPECT_EQ(0U, notifications.size());
634 main_test_rfh()->PrepareForCommit(url1);
635 main_test_rfh()->SendNavigate(0, url1);
636 EXPECT_EQ(1U, navigation_entry_committed_counter_);
637 navigation_entry_committed_counter_ = 0;
639 // We should not have produced a new session history entry.
640 EXPECT_EQ(controller.GetEntryCount(), 1);
641 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
642 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
643 EXPECT_TRUE(controller.GetLastCommittedEntry());
644 EXPECT_FALSE(controller.GetPendingEntry());
645 ASSERT_TRUE(controller.GetVisibleEntry());
646 EXPECT_FALSE(controller.CanGoBack());
647 EXPECT_FALSE(controller.CanGoForward());
649 // The timestamp should have been updated.
651 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
652 // EXPECT_GT once we guarantee that timestamps are unique.
653 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
656 // Load the same page twice, once as a GET and once as a POST.
657 // We should update the post state on the NavigationEntry.
658 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
659 NavigationControllerImpl& controller = controller_impl();
660 TestNotificationTracker notifications;
661 RegisterForAllNavNotifications(&notifications, &controller);
663 const GURL url1("http://foo1");
665 controller.LoadURL(
666 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
667 FrameHostMsg_DidCommitProvisionalLoad_Params params;
668 params.page_id = 0;
669 params.url = url1;
670 params.transition = ui::PAGE_TRANSITION_TYPED;
671 params.is_post = true;
672 params.post_id = 123;
673 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
674 main_test_rfh()->PrepareForCommit(url1);
675 main_test_rfh()->SendNavigateWithParams(&params);
677 // The post data should be visible.
678 NavigationEntry* entry = controller.GetVisibleEntry();
679 ASSERT_TRUE(entry);
680 EXPECT_TRUE(entry->GetHasPostData());
681 EXPECT_EQ(entry->GetPostID(), 123);
683 controller.LoadURL(
684 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
685 main_test_rfh()->PrepareForCommit(url1);
686 main_test_rfh()->SendNavigate(0, url1);
688 // We should not have produced a new session history entry.
689 ASSERT_EQ(controller.GetVisibleEntry(), entry);
691 // The post data should have been cleared due to the GET.
692 EXPECT_FALSE(entry->GetHasPostData());
693 EXPECT_EQ(entry->GetPostID(), 0);
696 // Tests loading a URL but discarding it before the load commits.
697 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
698 NavigationControllerImpl& controller = controller_impl();
699 TestNotificationTracker notifications;
700 RegisterForAllNavNotifications(&notifications, &controller);
702 const GURL url1("http://foo1");
703 const GURL url2("http://foo2");
705 controller.LoadURL(
706 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
707 EXPECT_EQ(0U, notifications.size());
708 main_test_rfh()->SendNavigate(0, url1);
709 EXPECT_EQ(1U, navigation_entry_committed_counter_);
710 navigation_entry_committed_counter_ = 0;
712 ASSERT_TRUE(controller.GetVisibleEntry());
713 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
714 EXPECT_FALSE(timestamp.is_null());
716 controller.LoadURL(
717 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
718 controller.DiscardNonCommittedEntries();
719 EXPECT_EQ(0U, notifications.size());
721 // Should not have produced a new session history entry.
722 EXPECT_EQ(controller.GetEntryCount(), 1);
723 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
724 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
725 EXPECT_TRUE(controller.GetLastCommittedEntry());
726 EXPECT_FALSE(controller.GetPendingEntry());
727 ASSERT_TRUE(controller.GetVisibleEntry());
728 EXPECT_FALSE(controller.CanGoBack());
729 EXPECT_FALSE(controller.CanGoForward());
731 // Timestamp should not have changed.
732 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
735 // Tests navigations that come in unrequested. This happens when the user
736 // navigates from the web page, and here we test that there is no pending entry.
737 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
738 NavigationControllerImpl& controller = controller_impl();
739 TestNotificationTracker notifications;
740 RegisterForAllNavNotifications(&notifications, &controller);
742 // First make an existing committed entry.
743 const GURL kExistingURL1("http://eh");
744 controller.LoadURL(
745 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
746 main_test_rfh()->SendNavigate(0, kExistingURL1);
747 EXPECT_EQ(1U, navigation_entry_committed_counter_);
748 navigation_entry_committed_counter_ = 0;
750 // Do a new navigation without making a pending one.
751 const GURL kNewURL("http://see");
752 main_test_rfh()->SendNavigate(99, kNewURL);
754 // There should no longer be any pending entry, and the third navigation we
755 // just made should be committed.
756 EXPECT_EQ(1U, navigation_entry_committed_counter_);
757 navigation_entry_committed_counter_ = 0;
758 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
759 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
760 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
763 // Tests navigating to a new URL when there is a new pending navigation that is
764 // not the one that just loaded. This will happen if the user types in a URL to
765 // somewhere slow, and then navigates the current page before the typed URL
766 // commits.
767 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
768 NavigationControllerImpl& controller = controller_impl();
769 TestNotificationTracker notifications;
770 RegisterForAllNavNotifications(&notifications, &controller);
772 // First make an existing committed entry.
773 const GURL kExistingURL1("http://eh");
774 controller.LoadURL(
775 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
776 main_test_rfh()->PrepareForCommit(kExistingURL1);
777 main_test_rfh()->SendNavigate(0, kExistingURL1);
778 EXPECT_EQ(1U, navigation_entry_committed_counter_);
779 navigation_entry_committed_counter_ = 0;
781 // Make a pending entry to somewhere new.
782 const GURL kExistingURL2("http://bee");
783 controller.LoadURL(
784 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
785 EXPECT_EQ(0U, notifications.size());
787 // After the beforeunload but before it commits, do a new navigation.
788 main_test_rfh()->PrepareForCommit(kExistingURL2);
789 const GURL kNewURL("http://see");
790 main_test_rfh()->PrepareForCommit(kNewURL);
791 contents()->GetMainFrame()->SendNavigate(3, kNewURL);
793 // There should no longer be any pending entry, and the third navigation we
794 // just made should be committed.
795 EXPECT_EQ(1U, navigation_entry_committed_counter_);
796 navigation_entry_committed_counter_ = 0;
797 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
798 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
799 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
802 // Tests navigating to a new URL when there is a pending back/forward
803 // navigation. This will happen if the user hits back, but before that commits,
804 // they navigate somewhere new.
805 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
806 NavigationControllerImpl& controller = controller_impl();
807 TestNotificationTracker notifications;
808 RegisterForAllNavNotifications(&notifications, &controller);
810 // First make some history.
811 const GURL kExistingURL1("http://foo/eh");
812 controller.LoadURL(
813 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
814 main_test_rfh()->PrepareForCommit(kExistingURL1);
815 main_test_rfh()->SendNavigate(0, kExistingURL1);
816 EXPECT_EQ(1U, navigation_entry_committed_counter_);
817 navigation_entry_committed_counter_ = 0;
819 const GURL kExistingURL2("http://foo/bee");
820 controller.LoadURL(
821 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
822 main_test_rfh()->PrepareForCommit(kExistingURL2);
823 main_test_rfh()->SendNavigate(1, kExistingURL2);
824 EXPECT_EQ(1U, navigation_entry_committed_counter_);
825 navigation_entry_committed_counter_ = 0;
827 // Now make a pending back/forward navigation. The zeroth entry should be
828 // pending.
829 controller.GoBack();
830 EXPECT_EQ(0U, notifications.size());
831 EXPECT_EQ(0, controller.GetPendingEntryIndex());
832 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
834 // Before that commits, do a new navigation.
835 const GURL kNewURL("http://foo/see");
836 main_test_rfh()->PrepareForCommit(kNewURL);
837 main_test_rfh()->SendNavigate(3, kNewURL);
839 // There should no longer be any pending entry, and the third navigation we
840 // just made should be committed.
841 EXPECT_EQ(1U, navigation_entry_committed_counter_);
842 navigation_entry_committed_counter_ = 0;
843 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
844 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
845 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
848 // Tests navigating to a new URL when there is a pending back/forward
849 // navigation to a cross-process, privileged URL. This will happen if the user
850 // hits back, but before that commits, they navigate somewhere new.
851 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
852 NavigationControllerImpl& controller = controller_impl();
853 TestNotificationTracker notifications;
854 RegisterForAllNavNotifications(&notifications, &controller);
856 // First make some history, starting with a privileged URL.
857 const GURL kExistingURL1("http://privileged");
858 controller.LoadURL(
859 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
860 // Pretend it has bindings so we can tell if we incorrectly copy it.
861 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
862 main_test_rfh()->PrepareForCommit(kExistingURL1);
863 main_test_rfh()->SendNavigate(0, kExistingURL1);
864 EXPECT_EQ(1U, navigation_entry_committed_counter_);
865 navigation_entry_committed_counter_ = 0;
867 // Navigate cross-process to a second URL.
868 const GURL kExistingURL2("http://foo/eh");
869 controller.LoadURL(
870 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
871 main_test_rfh()->PrepareForCommit(kExistingURL2);
872 TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame();
873 foo_rfh->SendNavigate(1, kExistingURL2);
874 EXPECT_EQ(1U, navigation_entry_committed_counter_);
875 navigation_entry_committed_counter_ = 0;
877 // Now make a pending back/forward navigation to a privileged entry.
878 // The zeroth entry should be pending.
879 controller.GoBack();
880 foo_rfh->SendBeforeUnloadACK(true);
881 EXPECT_EQ(0U, notifications.size());
882 EXPECT_EQ(0, controller.GetPendingEntryIndex());
883 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
884 EXPECT_EQ(2, controller.GetPendingEntry()->bindings());
886 // Before that commits, do a new navigation.
887 const GURL kNewURL("http://foo/bee");
888 foo_rfh->PrepareForCommit(kNewURL);
889 foo_rfh->SendNavigate(3, kNewURL);
891 // There should no longer be any pending entry, and the third navigation we
892 // just made should be committed.
893 EXPECT_EQ(1U, navigation_entry_committed_counter_);
894 navigation_entry_committed_counter_ = 0;
895 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
896 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
897 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
898 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
901 // Tests navigating to an existing URL when there is a pending new navigation.
902 // This will happen if the user enters a URL, but before that commits, the
903 // current page fires history.back().
904 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
905 NavigationControllerImpl& controller = controller_impl();
906 TestNotificationTracker notifications;
907 RegisterForAllNavNotifications(&notifications, &controller);
909 // First make some history.
910 const GURL kExistingURL1("http://foo/eh");
911 controller.LoadURL(
912 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
913 main_test_rfh()->PrepareForCommit(kExistingURL1);
914 main_test_rfh()->SendNavigate(0, kExistingURL1);
915 EXPECT_EQ(1U, navigation_entry_committed_counter_);
916 navigation_entry_committed_counter_ = 0;
918 const GURL kExistingURL2("http://foo/bee");
919 controller.LoadURL(
920 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
921 main_test_rfh()->PrepareForCommit(kExistingURL2);
922 main_test_rfh()->SendNavigate(1, kExistingURL2);
923 EXPECT_EQ(1U, navigation_entry_committed_counter_);
924 navigation_entry_committed_counter_ = 0;
926 // Now make a pending new navigation.
927 const GURL kNewURL("http://foo/see");
928 controller.LoadURL(
929 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
930 EXPECT_EQ(0U, notifications.size());
931 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
932 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
934 // Before that commits, a back navigation from the renderer commits.
935 main_test_rfh()->PrepareForCommit(kExistingURL1);
936 main_test_rfh()->SendNavigate(0, kExistingURL1);
938 // There should no longer be any pending entry, and the back navigation we
939 // just made should be committed.
940 EXPECT_EQ(1U, navigation_entry_committed_counter_);
941 navigation_entry_committed_counter_ = 0;
942 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
943 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
944 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
947 // Tests an ignored navigation when there is a pending new navigation.
948 // This will happen if the user enters a URL, but before that commits, the
949 // current blank page reloads. See http://crbug.com/77507.
950 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
951 NavigationControllerImpl& controller = controller_impl();
952 TestNotificationTracker notifications;
953 RegisterForAllNavNotifications(&notifications, &controller);
955 // Set a WebContentsDelegate to listen for state changes.
956 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
957 EXPECT_FALSE(contents()->GetDelegate());
958 contents()->SetDelegate(delegate.get());
960 // Without any navigations, the renderer starts at about:blank.
961 const GURL kExistingURL(url::kAboutBlankURL);
963 // Now make a pending new navigation.
964 const GURL kNewURL("http://eh");
965 controller.LoadURL(
966 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
967 EXPECT_EQ(0U, notifications.size());
968 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
969 EXPECT_TRUE(controller.GetPendingEntry());
970 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
971 EXPECT_EQ(1, delegate->navigation_state_change_count());
973 // Before that commits, a document.write and location.reload can cause the
974 // renderer to send a FrameNavigate with page_id -1.
975 main_test_rfh()->PrepareForCommit(kExistingURL);
976 main_test_rfh()->SendNavigate(-1, kExistingURL);
978 // This should clear the pending entry and notify of a navigation state
979 // change, so that we do not keep displaying kNewURL.
980 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
981 EXPECT_FALSE(controller.GetPendingEntry());
982 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
983 EXPECT_EQ(2, delegate->navigation_state_change_count());
985 contents()->SetDelegate(NULL);
988 // Tests that the pending entry state is correct after an abort.
989 // We do not want to clear the pending entry, so that the user doesn't
990 // lose a typed URL. (See http://crbug.com/9682.)
991 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
992 NavigationControllerImpl& controller = controller_impl();
993 TestNotificationTracker notifications;
994 RegisterForAllNavNotifications(&notifications, &controller);
996 // Set a WebContentsDelegate to listen for state changes.
997 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
998 EXPECT_FALSE(contents()->GetDelegate());
999 contents()->SetDelegate(delegate.get());
1001 // Start with a pending new navigation.
1002 const GURL kNewURL("http://eh");
1003 controller.LoadURL(
1004 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1005 EXPECT_EQ(0U, notifications.size());
1006 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1007 EXPECT_TRUE(controller.GetPendingEntry());
1008 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1009 EXPECT_EQ(1, delegate->navigation_state_change_count());
1011 // It may abort before committing, if it's a download or due to a stop or
1012 // a new navigation from the user.
1013 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1014 params.error_code = net::ERR_ABORTED;
1015 params.error_description = base::string16();
1016 params.url = kNewURL;
1017 params.showing_repost_interstitial = false;
1018 main_test_rfh()->OnMessageReceived(
1019 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1020 params));
1022 // This should not clear the pending entry or notify of a navigation state
1023 // change, so that we keep displaying kNewURL (until the user clears it).
1024 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1025 EXPECT_TRUE(controller.GetPendingEntry());
1026 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1027 EXPECT_EQ(1, delegate->navigation_state_change_count());
1028 NavigationEntry* pending_entry = controller.GetPendingEntry();
1030 // Ensure that a reload keeps the same pending entry.
1031 controller.Reload(true);
1032 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1033 EXPECT_TRUE(controller.GetPendingEntry());
1034 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
1035 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1037 contents()->SetDelegate(NULL);
1040 // Tests that the pending URL is not visible during a renderer-initiated
1041 // redirect and abort. See http://crbug.com/83031.
1042 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
1043 NavigationControllerImpl& controller = controller_impl();
1044 TestNotificationTracker notifications;
1045 RegisterForAllNavNotifications(&notifications, &controller);
1047 // First make an existing committed entry.
1048 const GURL kExistingURL("http://foo/eh");
1049 controller.LoadURL(kExistingURL, content::Referrer(),
1050 ui::PAGE_TRANSITION_TYPED, std::string());
1051 main_test_rfh()->SendNavigate(1, kExistingURL);
1052 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1053 navigation_entry_committed_counter_ = 0;
1055 // Set a WebContentsDelegate to listen for state changes.
1056 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1057 EXPECT_FALSE(contents()->GetDelegate());
1058 contents()->SetDelegate(delegate.get());
1060 // Now make a pending new navigation, initiated by the renderer.
1061 const GURL kNewURL("http://foo/bee");
1062 NavigationController::LoadURLParams load_url_params(kNewURL);
1063 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
1064 load_url_params.is_renderer_initiated = true;
1065 controller.LoadURLWithParams(load_url_params);
1066 EXPECT_EQ(0U, notifications.size());
1067 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1068 EXPECT_TRUE(controller.GetPendingEntry());
1069 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1070 EXPECT_EQ(0, delegate->navigation_state_change_count());
1072 // The visible entry should be the last committed URL, not the pending one.
1073 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1075 // Now the navigation redirects. (There is no corresponding message here.)
1076 const GURL kRedirectURL("http://foo/see");
1078 // We don't want to change the NavigationEntry's url, in case it cancels.
1079 // Prevents regression of http://crbug.com/77786.
1080 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1082 // It may abort before committing, if it's a download or due to a stop or
1083 // a new navigation from the user.
1084 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1085 params.error_code = net::ERR_ABORTED;
1086 params.error_description = base::string16();
1087 params.url = kRedirectURL;
1088 params.showing_repost_interstitial = false;
1089 main_test_rfh()->OnMessageReceived(
1090 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1091 params));
1093 // Because the pending entry is renderer initiated and not visible, we
1094 // clear it when it fails.
1095 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1096 EXPECT_FALSE(controller.GetPendingEntry());
1097 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1098 EXPECT_EQ(1, delegate->navigation_state_change_count());
1100 // The visible entry should be the last committed URL, not the pending one,
1101 // so that no spoof is possible.
1102 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1104 contents()->SetDelegate(NULL);
1107 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1108 // at the time they committed. http://crbug.com/173672.
1109 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1110 NavigationControllerImpl& controller = controller_impl();
1111 TestNotificationTracker notifications;
1112 RegisterForAllNavNotifications(&notifications, &controller);
1113 std::vector<GURL> url_chain;
1115 const GURL url1("http://foo1");
1116 const GURL url2("http://foo2");
1118 // Navigate to a first, unprivileged URL.
1119 controller.LoadURL(
1120 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1121 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1122 controller.GetPendingEntry()->bindings());
1124 // Commit.
1125 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1126 orig_rfh->PrepareForCommit(url1);
1127 orig_rfh->SendNavigate(0, url1);
1128 EXPECT_EQ(controller.GetEntryCount(), 1);
1129 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1130 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1132 // Manually increase the number of active frames in the SiteInstance
1133 // that orig_rfh belongs to, to prevent it from being destroyed when
1134 // it gets swapped out, so that we can reuse orig_rfh when the
1135 // controller goes back.
1136 orig_rfh->GetSiteInstance()->increment_active_frame_count();
1138 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1139 // transition, and set bindings on the pending RenderViewHost to simulate a
1140 // privileged url.
1141 controller.LoadURL(
1142 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1143 orig_rfh->PrepareForCommit(url2);
1144 TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
1145 new_rfh->GetRenderViewHost()->AllowBindings(1);
1146 new_rfh->SendNavigate(1, url2);
1148 // The second load should be committed, and bindings should be remembered.
1149 EXPECT_EQ(controller.GetEntryCount(), 2);
1150 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1151 EXPECT_TRUE(controller.CanGoBack());
1152 EXPECT_EQ(1, controller.GetLastCommittedEntry()->bindings());
1154 // Going back, the first entry should still appear unprivileged.
1155 controller.GoBack();
1156 new_rfh->PrepareForCommit(url1);
1157 orig_rfh->SendNavigate(0, url1);
1158 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1159 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1162 TEST_F(NavigationControllerTest, Reload) {
1163 NavigationControllerImpl& controller = controller_impl();
1164 TestNotificationTracker notifications;
1165 RegisterForAllNavNotifications(&notifications, &controller);
1167 const GURL url1("http://foo1");
1169 controller.LoadURL(
1170 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1171 EXPECT_EQ(0U, notifications.size());
1172 main_test_rfh()->PrepareForCommit(url1);
1173 main_test_rfh()->SendNavigate(0, url1);
1174 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1175 navigation_entry_committed_counter_ = 0;
1176 ASSERT_TRUE(controller.GetVisibleEntry());
1177 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1178 controller.Reload(true);
1179 EXPECT_EQ(0U, notifications.size());
1181 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1182 EXPECT_FALSE(timestamp.is_null());
1184 // The reload is pending.
1185 EXPECT_EQ(controller.GetEntryCount(), 1);
1186 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1187 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1188 EXPECT_TRUE(controller.GetLastCommittedEntry());
1189 EXPECT_TRUE(controller.GetPendingEntry());
1190 EXPECT_FALSE(controller.CanGoBack());
1191 EXPECT_FALSE(controller.CanGoForward());
1192 // Make sure the title has been cleared (will be redrawn just after reload).
1193 // Avoids a stale cached title when the new page being reloaded has no title.
1194 // See http://crbug.com/96041.
1195 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1197 main_test_rfh()->PrepareForCommit(url1);
1198 main_test_rfh()->SendNavigate(0, url1);
1199 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1200 navigation_entry_committed_counter_ = 0;
1202 // Now the reload is committed.
1203 EXPECT_EQ(controller.GetEntryCount(), 1);
1204 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1205 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1206 EXPECT_TRUE(controller.GetLastCommittedEntry());
1207 EXPECT_FALSE(controller.GetPendingEntry());
1208 EXPECT_FALSE(controller.CanGoBack());
1209 EXPECT_FALSE(controller.CanGoForward());
1211 // The timestamp should have been updated.
1212 ASSERT_TRUE(controller.GetVisibleEntry());
1213 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1216 // Tests what happens when a reload navigation produces a new page.
1217 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1218 NavigationControllerImpl& controller = controller_impl();
1219 TestNotificationTracker notifications;
1220 RegisterForAllNavNotifications(&notifications, &controller);
1222 const GURL url1("http://foo1");
1223 const GURL url2("http://foo2");
1225 controller.LoadURL(
1226 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1227 main_test_rfh()->PrepareForCommit(url1);
1228 main_test_rfh()->SendNavigate(0, url1);
1229 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1230 navigation_entry_committed_counter_ = 0;
1232 controller.Reload(true);
1233 EXPECT_EQ(0U, notifications.size());
1235 main_test_rfh()->PrepareForCommit(url2);
1236 main_test_rfh()->SendNavigate(1, url2);
1237 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1238 navigation_entry_committed_counter_ = 0;
1240 // Now the reload is committed.
1241 EXPECT_EQ(controller.GetEntryCount(), 2);
1242 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1243 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1244 EXPECT_TRUE(controller.GetLastCommittedEntry());
1245 EXPECT_FALSE(controller.GetPendingEntry());
1246 EXPECT_TRUE(controller.CanGoBack());
1247 EXPECT_FALSE(controller.CanGoForward());
1250 // This test ensures that when a guest renderer reloads, the reload goes through
1251 // without ending up in the "we have a wrong process for the URL" branch in
1252 // NavigationControllerImpl::ReloadInternal.
1253 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1254 NavigationControllerImpl& controller = controller_impl();
1256 const GURL url1("http://foo1");
1257 controller.LoadURL(
1258 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1259 main_test_rfh()->SendNavigate(0, url1);
1260 ASSERT_TRUE(controller.GetVisibleEntry());
1262 // Make the entry believe its RenderProcessHost is a guest.
1263 NavigationEntryImpl* entry1 = controller.GetVisibleEntry();
1264 reinterpret_cast<MockRenderProcessHost*>(
1265 entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
1267 // And reload.
1268 controller.Reload(true);
1270 // The reload is pending. Check that the NavigationEntry didn't get replaced
1271 // because of having the wrong process.
1272 EXPECT_EQ(controller.GetEntryCount(), 1);
1273 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1274 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1276 NavigationEntryImpl* entry2 = controller.GetPendingEntry();
1277 EXPECT_EQ(entry1, entry2);
1280 #if !defined(OS_ANDROID) // http://crbug.com/157428
1281 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1282 NavigationControllerImpl& controller = controller_impl();
1283 TestNotificationTracker notifications;
1284 RegisterForAllNavNotifications(&notifications, &controller);
1286 const GURL original_url("http://foo1");
1287 const GURL final_url("http://foo2");
1289 // Load up the original URL, but get redirected.
1290 controller.LoadURL(
1291 original_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1292 EXPECT_EQ(0U, notifications.size());
1293 main_test_rfh()->PrepareForCommit(final_url);
1294 main_test_rfh()->SendNavigateWithOriginalRequestURL(
1295 0, final_url, original_url);
1296 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1297 navigation_entry_committed_counter_ = 0;
1299 // The NavigationEntry should save both the original URL and the final
1300 // redirected URL.
1301 EXPECT_EQ(
1302 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1303 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1305 // Reload using the original URL.
1306 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1307 controller.ReloadOriginalRequestURL(false);
1308 EXPECT_EQ(0U, notifications.size());
1310 // The reload is pending. The request should point to the original URL.
1311 EXPECT_EQ(original_url, navigated_url());
1312 EXPECT_EQ(controller.GetEntryCount(), 1);
1313 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1314 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1315 EXPECT_TRUE(controller.GetLastCommittedEntry());
1316 EXPECT_TRUE(controller.GetPendingEntry());
1317 EXPECT_FALSE(controller.CanGoBack());
1318 EXPECT_FALSE(controller.CanGoForward());
1320 // Make sure the title has been cleared (will be redrawn just after reload).
1321 // Avoids a stale cached title when the new page being reloaded has no title.
1322 // See http://crbug.com/96041.
1323 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1325 // Send that the navigation has proceeded; say it got redirected again.
1326 main_test_rfh()->PrepareForCommit(final_url);
1327 main_test_rfh()->SendNavigate(0, final_url);
1328 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1329 navigation_entry_committed_counter_ = 0;
1331 // Now the reload is committed.
1332 EXPECT_EQ(controller.GetEntryCount(), 1);
1333 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1334 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1335 EXPECT_TRUE(controller.GetLastCommittedEntry());
1336 EXPECT_FALSE(controller.GetPendingEntry());
1337 EXPECT_FALSE(controller.CanGoBack());
1338 EXPECT_FALSE(controller.CanGoForward());
1341 #endif // !defined(OS_ANDROID)
1343 // Test that certain non-persisted NavigationEntryImpl values get reset after
1344 // commit.
1345 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1346 NavigationControllerImpl& controller = controller_impl();
1348 // The value of "should replace entry" will be tested, but it's an error to
1349 // specify it when there are no entries. Create a simple entry to be replaced.
1350 const GURL url0("http://foo/0");
1351 controller.LoadURL(
1352 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1353 main_test_rfh()->SendNavigate(0, url0);
1355 // Set up the pending entry.
1356 const GURL url1("http://foo/1");
1357 controller.LoadURL(
1358 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1360 // Set up some sample values.
1361 const unsigned char* raw_data =
1362 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1363 const int length = 11;
1364 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1365 scoped_refptr<base::RefCountedBytes> post_data =
1366 base::RefCountedBytes::TakeVector(&post_data_vector);
1367 GlobalRequestID transfer_id(3, 4);
1369 // Set non-persisted values on the pending entry.
1370 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1371 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1372 pending_entry->set_is_renderer_initiated(true);
1373 pending_entry->set_transferred_global_request_id(transfer_id);
1374 pending_entry->set_should_replace_entry(true);
1375 pending_entry->set_should_clear_history_list(true);
1376 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1377 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1378 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1379 EXPECT_TRUE(pending_entry->should_replace_entry());
1380 EXPECT_TRUE(pending_entry->should_clear_history_list());
1382 // Fake a commit response.
1383 main_test_rfh()->SendNavigate(1, url1);
1385 // Certain values that are only used for pending entries get reset after
1386 // commit.
1387 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1388 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1389 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1390 EXPECT_EQ(GlobalRequestID(-1, -1),
1391 committed_entry->transferred_global_request_id());
1392 EXPECT_FALSE(committed_entry->should_replace_entry());
1393 EXPECT_FALSE(committed_entry->should_clear_history_list());
1396 // Test that Redirects are preserved after a commit.
1397 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1398 NavigationControllerImpl& controller = controller_impl();
1399 const GURL url1("http://foo1");
1400 controller.LoadURL(
1401 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1403 // Set up some redirect values.
1404 std::vector<GURL> redirects;
1405 redirects.push_back(GURL("http://foo2"));
1407 // Set redirects on the pending entry.
1408 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1409 pending_entry->SetRedirectChain(redirects);
1410 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1411 EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
1413 // Normal navigation will preserve redirects in the committed entry.
1414 main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
1415 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1416 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1417 EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
1420 // Tests what happens when we navigate back successfully
1421 TEST_F(NavigationControllerTest, Back) {
1422 NavigationControllerImpl& controller = controller_impl();
1423 TestNotificationTracker notifications;
1424 RegisterForAllNavNotifications(&notifications, &controller);
1426 const GURL url1("http://foo1");
1427 main_test_rfh()->SendNavigate(0, url1);
1428 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1429 navigation_entry_committed_counter_ = 0;
1431 const GURL url2("http://foo2");
1432 main_test_rfh()->SendNavigate(1, url2);
1433 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1434 navigation_entry_committed_counter_ = 0;
1436 controller.GoBack();
1437 EXPECT_EQ(0U, notifications.size());
1439 // We should now have a pending navigation to go back.
1440 EXPECT_EQ(controller.GetEntryCount(), 2);
1441 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1442 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1443 EXPECT_TRUE(controller.GetLastCommittedEntry());
1444 EXPECT_TRUE(controller.GetPendingEntry());
1445 EXPECT_FALSE(controller.CanGoBack());
1446 EXPECT_FALSE(controller.CanGoToOffset(-1));
1447 EXPECT_TRUE(controller.CanGoForward());
1448 EXPECT_TRUE(controller.CanGoToOffset(1));
1449 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1451 // Timestamp for entry 1 should be on or after that of entry 0.
1452 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1453 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1454 controller.GetEntryAtIndex(0)->GetTimestamp());
1456 main_test_rfh()->SendNavigate(0, url2);
1457 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1458 navigation_entry_committed_counter_ = 0;
1460 // The back navigation completed successfully.
1461 EXPECT_EQ(controller.GetEntryCount(), 2);
1462 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1463 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1464 EXPECT_TRUE(controller.GetLastCommittedEntry());
1465 EXPECT_FALSE(controller.GetPendingEntry());
1466 EXPECT_FALSE(controller.CanGoBack());
1467 EXPECT_FALSE(controller.CanGoToOffset(-1));
1468 EXPECT_TRUE(controller.CanGoForward());
1469 EXPECT_TRUE(controller.CanGoToOffset(1));
1470 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1472 // Timestamp for entry 0 should be on or after that of entry 1
1473 // (since we went back to it).
1474 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1475 controller.GetEntryAtIndex(1)->GetTimestamp());
1478 // Tests what happens when a back navigation produces a new page.
1479 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1480 NavigationControllerImpl& controller = controller_impl();
1481 TestNotificationTracker notifications;
1482 RegisterForAllNavNotifications(&notifications, &controller);
1484 const GURL url1("http://foo/1");
1485 const GURL url2("http://foo/2");
1486 const GURL url3("http://foo/3");
1488 controller.LoadURL(
1489 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1490 main_test_rfh()->PrepareForCommit(url1);
1491 main_test_rfh()->SendNavigate(0, url1);
1492 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1493 navigation_entry_committed_counter_ = 0;
1495 controller.LoadURL(
1496 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1497 main_test_rfh()->PrepareForCommit(url2);
1498 main_test_rfh()->SendNavigate(1, url2);
1499 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1500 navigation_entry_committed_counter_ = 0;
1502 controller.GoBack();
1503 EXPECT_EQ(0U, notifications.size());
1505 // We should now have a pending navigation to go back.
1506 EXPECT_EQ(controller.GetEntryCount(), 2);
1507 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1508 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1509 EXPECT_TRUE(controller.GetLastCommittedEntry());
1510 EXPECT_TRUE(controller.GetPendingEntry());
1511 EXPECT_FALSE(controller.CanGoBack());
1512 EXPECT_TRUE(controller.CanGoForward());
1514 main_test_rfh()->PrepareForCommit(url3);
1515 main_test_rfh()->SendNavigate(2, url3);
1516 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1517 navigation_entry_committed_counter_ = 0;
1519 // The back navigation resulted in a completely new navigation.
1520 // TODO(darin): perhaps this behavior will be confusing to users?
1521 EXPECT_EQ(controller.GetEntryCount(), 3);
1522 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1523 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1524 EXPECT_TRUE(controller.GetLastCommittedEntry());
1525 EXPECT_FALSE(controller.GetPendingEntry());
1526 EXPECT_TRUE(controller.CanGoBack());
1527 EXPECT_FALSE(controller.CanGoForward());
1530 // Receives a back message when there is a new pending navigation entry.
1531 TEST_F(NavigationControllerTest, Back_NewPending) {
1532 NavigationControllerImpl& controller = controller_impl();
1533 TestNotificationTracker notifications;
1534 RegisterForAllNavNotifications(&notifications, &controller);
1536 const GURL kUrl1("http://foo1");
1537 const GURL kUrl2("http://foo2");
1538 const GURL kUrl3("http://foo3");
1540 // First navigate two places so we have some back history.
1541 main_test_rfh()->SendNavigate(0, kUrl1);
1542 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1543 navigation_entry_committed_counter_ = 0;
1545 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1546 main_test_rfh()->SendNavigate(1, kUrl2);
1547 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1548 navigation_entry_committed_counter_ = 0;
1550 // Now start a new pending navigation and go back before it commits.
1551 controller.LoadURL(
1552 kUrl3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1553 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1554 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1555 controller.GoBack();
1557 // The pending navigation should now be the "back" item and the new one
1558 // should be gone.
1559 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1560 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1563 // Receives a back message when there is a different renavigation already
1564 // pending.
1565 TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1566 NavigationControllerImpl& controller = controller_impl();
1567 const GURL kUrl1("http://foo/1");
1568 const GURL kUrl2("http://foo/2");
1569 const GURL kUrl3("http://foo/3");
1571 // First navigate three places so we have some back history.
1572 main_test_rfh()->PrepareForCommit(kUrl1);
1573 main_test_rfh()->SendNavigate(0, kUrl1);
1574 main_test_rfh()->PrepareForCommit(kUrl2);
1575 main_test_rfh()->SendNavigate(1, kUrl2);
1576 main_test_rfh()->PrepareForCommit(kUrl3);
1577 main_test_rfh()->SendNavigate(2, kUrl3);
1579 // With nothing pending, say we get a navigation to the second entry.
1580 main_test_rfh()->PrepareForCommit(kUrl2);
1581 main_test_rfh()->SendNavigate(1, kUrl2);
1583 // We know all the entries have the same site instance, so we can just grab
1584 // a random one for looking up other entries.
1585 SiteInstance* site_instance =
1586 controller.GetLastCommittedEntry()->site_instance();
1588 // That second URL should be the last committed and it should have gotten the
1589 // new title.
1590 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1591 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1592 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1594 // Now go forward to the last item again and say it was committed.
1595 controller.GoForward();
1596 main_test_rfh()->PrepareForCommit(kUrl3);
1597 main_test_rfh()->SendNavigate(2, kUrl3);
1599 // Now start going back one to the second page. It will be pending.
1600 controller.GoBack();
1601 EXPECT_EQ(1, controller.GetPendingEntryIndex());
1602 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1604 // Not synthesize a totally new back event to the first page. This will not
1605 // match the pending one.
1606 main_test_rfh()->PrepareForCommit(kUrl1);
1607 main_test_rfh()->SendNavigate(0, kUrl1);
1609 // The committed navigation should clear the pending entry.
1610 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1612 // But the navigated entry should be the last committed.
1613 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1614 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1617 // Tests what happens when we navigate forward successfully.
1618 TEST_F(NavigationControllerTest, Forward) {
1619 NavigationControllerImpl& controller = controller_impl();
1620 TestNotificationTracker notifications;
1621 RegisterForAllNavNotifications(&notifications, &controller);
1623 const GURL url1("http://foo1");
1624 const GURL url2("http://foo2");
1626 main_test_rfh()->PrepareForCommit(url1);
1627 main_test_rfh()->SendNavigate(0, url1);
1628 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1629 navigation_entry_committed_counter_ = 0;
1631 main_test_rfh()->PrepareForCommit(url2);
1632 main_test_rfh()->SendNavigate(1, url2);
1633 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1634 navigation_entry_committed_counter_ = 0;
1636 controller.GoBack();
1637 main_test_rfh()->PrepareForCommit(url1);
1638 main_test_rfh()->SendNavigate(0, url1);
1639 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1640 navigation_entry_committed_counter_ = 0;
1642 controller.GoForward();
1644 // We should now have a pending navigation to go forward.
1645 EXPECT_EQ(controller.GetEntryCount(), 2);
1646 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1647 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1648 EXPECT_TRUE(controller.GetLastCommittedEntry());
1649 EXPECT_TRUE(controller.GetPendingEntry());
1650 EXPECT_TRUE(controller.CanGoBack());
1651 EXPECT_TRUE(controller.CanGoToOffset(-1));
1652 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1653 EXPECT_FALSE(controller.CanGoForward());
1654 EXPECT_FALSE(controller.CanGoToOffset(1));
1656 // Timestamp for entry 0 should be on or after that of entry 1
1657 // (since we went back to it).
1658 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1659 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1660 controller.GetEntryAtIndex(1)->GetTimestamp());
1662 main_test_rfh()->PrepareForCommit(url2);
1663 main_test_rfh()->SendNavigate(1, url2);
1664 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1665 navigation_entry_committed_counter_ = 0;
1667 // The forward navigation completed successfully.
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_TRUE(controller.CanGoToOffset(-1));
1675 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1676 EXPECT_FALSE(controller.CanGoForward());
1677 EXPECT_FALSE(controller.CanGoToOffset(1));
1679 // Timestamp for entry 1 should be on or after that of entry 0
1680 // (since we went forward to it).
1681 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1682 controller.GetEntryAtIndex(0)->GetTimestamp());
1685 // Tests what happens when a forward navigation produces a new page.
1686 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1687 NavigationControllerImpl& controller = controller_impl();
1688 TestNotificationTracker notifications;
1689 RegisterForAllNavNotifications(&notifications, &controller);
1691 const GURL url1("http://foo1");
1692 const GURL url2("http://foo2");
1693 const GURL url3("http://foo3");
1695 main_test_rfh()->PrepareForCommit(url1);
1696 main_test_rfh()->SendNavigate(0, url1);
1697 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1698 navigation_entry_committed_counter_ = 0;
1699 main_test_rfh()->PrepareForCommit(url2);
1700 main_test_rfh()->SendNavigate(1, url2);
1701 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1702 navigation_entry_committed_counter_ = 0;
1704 controller.GoBack();
1705 main_test_rfh()->PrepareForCommit(url1);
1706 main_test_rfh()->SendNavigate(0, url1);
1707 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1708 navigation_entry_committed_counter_ = 0;
1710 controller.GoForward();
1711 EXPECT_EQ(0U, notifications.size());
1713 // Should now have a pending navigation to go forward.
1714 EXPECT_EQ(controller.GetEntryCount(), 2);
1715 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1716 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1717 EXPECT_TRUE(controller.GetLastCommittedEntry());
1718 EXPECT_TRUE(controller.GetPendingEntry());
1719 EXPECT_TRUE(controller.CanGoBack());
1720 EXPECT_FALSE(controller.CanGoForward());
1722 main_test_rfh()->PrepareForCommit(url3);
1723 main_test_rfh()->SendNavigate(2, url3);
1724 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1725 navigation_entry_committed_counter_ = 0;
1726 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1728 EXPECT_EQ(controller.GetEntryCount(), 2);
1729 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1730 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1731 EXPECT_TRUE(controller.GetLastCommittedEntry());
1732 EXPECT_FALSE(controller.GetPendingEntry());
1733 EXPECT_TRUE(controller.CanGoBack());
1734 EXPECT_FALSE(controller.CanGoForward());
1737 // Two consequent navigation for the same URL entered in should be considered
1738 // as SAME_PAGE navigation even when we are redirected to some other page.
1739 TEST_F(NavigationControllerTest, Redirect) {
1740 NavigationControllerImpl& controller = controller_impl();
1741 TestNotificationTracker notifications;
1742 RegisterForAllNavNotifications(&notifications, &controller);
1744 const GURL url1("http://foo1");
1745 const GURL url2("http://foo2"); // Redirection target
1747 // First request
1748 controller.LoadURL(
1749 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1751 EXPECT_EQ(0U, notifications.size());
1752 main_test_rfh()->SendNavigate(0, url2);
1753 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1754 navigation_entry_committed_counter_ = 0;
1756 // Second request
1757 controller.LoadURL(
1758 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1760 EXPECT_TRUE(controller.GetPendingEntry());
1761 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1762 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1764 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1765 params.page_id = 0;
1766 params.url = url2;
1767 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1768 params.redirects.push_back(GURL("http://foo1"));
1769 params.redirects.push_back(GURL("http://foo2"));
1770 params.should_update_history = false;
1771 params.gesture = NavigationGestureAuto;
1772 params.is_post = false;
1773 params.page_state = PageState::CreateFromURL(url2);
1775 LoadCommittedDetails details;
1777 EXPECT_EQ(0U, notifications.size());
1778 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1779 &details));
1780 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1781 navigation_entry_committed_counter_ = 0;
1783 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1784 EXPECT_EQ(controller.GetEntryCount(), 1);
1785 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1786 EXPECT_TRUE(controller.GetLastCommittedEntry());
1787 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1788 EXPECT_FALSE(controller.GetPendingEntry());
1789 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1791 EXPECT_FALSE(controller.CanGoBack());
1792 EXPECT_FALSE(controller.CanGoForward());
1795 // Similar to Redirect above, but the first URL is requested by POST,
1796 // the second URL is requested by GET. NavigationEntry::has_post_data_
1797 // must be cleared. http://crbug.com/21245
1798 TEST_F(NavigationControllerTest, PostThenRedirect) {
1799 NavigationControllerImpl& controller = controller_impl();
1800 TestNotificationTracker notifications;
1801 RegisterForAllNavNotifications(&notifications, &controller);
1803 const GURL url1("http://foo1");
1804 const GURL url2("http://foo2"); // Redirection target
1806 // First request as POST
1807 controller.LoadURL(
1808 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1809 controller.GetVisibleEntry()->SetHasPostData(true);
1811 EXPECT_EQ(0U, notifications.size());
1812 main_test_rfh()->SendNavigate(0, url2);
1813 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1814 navigation_entry_committed_counter_ = 0;
1816 // Second request
1817 controller.LoadURL(
1818 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1820 EXPECT_TRUE(controller.GetPendingEntry());
1821 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1822 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1824 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1825 params.page_id = 0;
1826 params.url = url2;
1827 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1828 params.redirects.push_back(GURL("http://foo1"));
1829 params.redirects.push_back(GURL("http://foo2"));
1830 params.should_update_history = false;
1831 params.gesture = NavigationGestureAuto;
1832 params.is_post = false;
1833 params.page_state = PageState::CreateFromURL(url2);
1835 LoadCommittedDetails details;
1837 EXPECT_EQ(0U, notifications.size());
1838 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1839 &details));
1840 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1841 navigation_entry_committed_counter_ = 0;
1843 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1844 EXPECT_EQ(controller.GetEntryCount(), 1);
1845 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1846 EXPECT_TRUE(controller.GetLastCommittedEntry());
1847 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1848 EXPECT_FALSE(controller.GetPendingEntry());
1849 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1850 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1852 EXPECT_FALSE(controller.CanGoBack());
1853 EXPECT_FALSE(controller.CanGoForward());
1856 // A redirect right off the bat should be a NEW_PAGE.
1857 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1858 NavigationControllerImpl& controller = controller_impl();
1859 TestNotificationTracker notifications;
1860 RegisterForAllNavNotifications(&notifications, &controller);
1862 const GURL url1("http://foo1");
1863 const GURL url2("http://foo2"); // Redirection target
1865 // First request
1866 controller.LoadURL(
1867 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1869 EXPECT_TRUE(controller.GetPendingEntry());
1870 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1871 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1873 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1874 params.page_id = 0;
1875 params.url = url2;
1876 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1877 params.redirects.push_back(GURL("http://foo1"));
1878 params.redirects.push_back(GURL("http://foo2"));
1879 params.should_update_history = false;
1880 params.gesture = NavigationGestureAuto;
1881 params.is_post = false;
1882 params.page_state = PageState::CreateFromURL(url2);
1884 LoadCommittedDetails details;
1886 EXPECT_EQ(0U, notifications.size());
1887 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1888 &details));
1889 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1890 navigation_entry_committed_counter_ = 0;
1892 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1893 EXPECT_EQ(controller.GetEntryCount(), 1);
1894 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1895 EXPECT_TRUE(controller.GetLastCommittedEntry());
1896 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1897 EXPECT_FALSE(controller.GetPendingEntry());
1898 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1900 EXPECT_FALSE(controller.CanGoBack());
1901 EXPECT_FALSE(controller.CanGoForward());
1904 // Tests navigation via link click within a subframe. A new navigation entry
1905 // should be created.
1906 TEST_F(NavigationControllerTest, NewSubframe) {
1907 NavigationControllerImpl& controller = controller_impl();
1908 TestNotificationTracker notifications;
1909 RegisterForAllNavNotifications(&notifications, &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;
1918 params.page_id = 1;
1919 params.url = url2;
1920 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
1921 params.should_update_history = false;
1922 params.gesture = NavigationGestureUser;
1923 params.is_post = false;
1924 params.page_state = PageState::CreateFromURL(url2);
1926 LoadCommittedDetails details;
1927 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1928 &details));
1929 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1930 navigation_entry_committed_counter_ = 0;
1931 EXPECT_EQ(url1, details.previous_url);
1932 EXPECT_FALSE(details.is_in_page);
1933 EXPECT_FALSE(details.is_main_frame);
1935 // The new entry should be appended.
1936 EXPECT_EQ(2, controller.GetEntryCount());
1938 // New entry should refer to the new page, but the old URL (entries only
1939 // reflect the toplevel URL).
1940 EXPECT_EQ(url1, details.entry->GetURL());
1941 EXPECT_EQ(params.page_id, details.entry->GetPageID());
1944 // Some pages create a popup, then write an iframe into it. This causes a
1945 // subframe navigation without having any committed entry. Such navigations
1946 // just get thrown on the ground, but we shouldn't crash.
1947 TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
1948 NavigationControllerImpl& controller = controller_impl();
1949 TestNotificationTracker notifications;
1950 RegisterForAllNavNotifications(&notifications, &controller);
1952 // Navigation controller currently has no entries.
1953 const GURL url("http://foo2");
1954 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1955 params.page_id = 1;
1956 params.url = url;
1957 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
1958 params.should_update_history = false;
1959 params.gesture = NavigationGestureAuto;
1960 params.is_post = false;
1961 params.page_state = PageState::CreateFromURL(url);
1963 LoadCommittedDetails details;
1964 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1965 &details));
1966 EXPECT_EQ(0U, notifications.size());
1969 // Auto subframes are ones the page loads automatically like ads. They should
1970 // not create new navigation entries.
1971 TEST_F(NavigationControllerTest, AutoSubframe) {
1972 NavigationControllerImpl& controller = controller_impl();
1973 TestNotificationTracker notifications;
1974 RegisterForAllNavNotifications(&notifications, &controller);
1976 const GURL url1("http://foo1");
1977 main_test_rfh()->SendNavigate(0, url1);
1978 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1979 navigation_entry_committed_counter_ = 0;
1981 const GURL url2("http://foo2");
1982 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1983 params.page_id = 0;
1984 params.url = url2;
1985 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
1986 params.should_update_history = false;
1987 params.gesture = NavigationGestureUser;
1988 params.is_post = false;
1989 params.page_state = PageState::CreateFromURL(url2);
1991 // Navigating should do nothing.
1992 LoadCommittedDetails details;
1993 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1994 &details));
1995 EXPECT_EQ(0U, notifications.size());
1997 // There should still be only one entry.
1998 EXPECT_EQ(1, controller.GetEntryCount());
2001 // Tests navigation and then going back to a subframe navigation.
2002 TEST_F(NavigationControllerTest, BackSubframe) {
2003 NavigationControllerImpl& controller = controller_impl();
2004 TestNotificationTracker notifications;
2005 RegisterForAllNavNotifications(&notifications, &controller);
2007 // Main page.
2008 const GURL url1("http://foo1");
2009 main_test_rfh()->SendNavigate(0, url1);
2010 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2011 navigation_entry_committed_counter_ = 0;
2013 // First manual subframe navigation.
2014 const GURL url2("http://foo2");
2015 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2016 params.page_id = 1;
2017 params.url = url2;
2018 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2019 params.should_update_history = false;
2020 params.gesture = NavigationGestureUser;
2021 params.is_post = false;
2022 params.page_state = PageState::CreateFromURL(url2);
2024 // This should generate a new entry.
2025 LoadCommittedDetails details;
2026 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2027 &details));
2028 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2029 navigation_entry_committed_counter_ = 0;
2030 EXPECT_EQ(2, controller.GetEntryCount());
2032 // Second manual subframe navigation should also make a new entry.
2033 const GURL url3("http://foo3");
2034 params.page_id = 2;
2035 params.url = url3;
2036 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2037 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2038 &details));
2039 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2040 navigation_entry_committed_counter_ = 0;
2041 EXPECT_EQ(3, controller.GetEntryCount());
2042 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2044 // Go back one.
2045 controller.GoBack();
2046 params.page_id = 1;
2047 params.url = url2;
2048 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2049 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2050 &details));
2051 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2052 navigation_entry_committed_counter_ = 0;
2053 EXPECT_EQ(3, controller.GetEntryCount());
2054 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2055 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2056 EXPECT_FALSE(controller.GetPendingEntry());
2058 // Go back one more.
2059 controller.GoBack();
2060 params.page_id = 0;
2061 params.url = url1;
2062 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2063 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2064 &details));
2065 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2066 navigation_entry_committed_counter_ = 0;
2067 EXPECT_EQ(3, controller.GetEntryCount());
2068 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2069 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2070 EXPECT_FALSE(controller.GetPendingEntry());
2073 TEST_F(NavigationControllerTest, LinkClick) {
2074 NavigationControllerImpl& controller = controller_impl();
2075 TestNotificationTracker notifications;
2076 RegisterForAllNavNotifications(&notifications, &controller);
2078 const GURL url1("http://foo1");
2079 const GURL url2("http://foo2");
2081 main_test_rfh()->SendNavigate(0, url1);
2082 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2083 navigation_entry_committed_counter_ = 0;
2085 main_test_rfh()->SendNavigate(1, url2);
2086 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2087 navigation_entry_committed_counter_ = 0;
2089 // Should not have produced a new session history entry.
2090 EXPECT_EQ(controller.GetEntryCount(), 2);
2091 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2092 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2093 EXPECT_TRUE(controller.GetLastCommittedEntry());
2094 EXPECT_FALSE(controller.GetPendingEntry());
2095 EXPECT_TRUE(controller.CanGoBack());
2096 EXPECT_FALSE(controller.CanGoForward());
2099 TEST_F(NavigationControllerTest, InPage) {
2100 NavigationControllerImpl& controller = controller_impl();
2101 TestNotificationTracker notifications;
2102 RegisterForAllNavNotifications(&notifications, &controller);
2104 // Main page.
2105 const GURL url1("http://foo");
2106 main_test_rfh()->SendNavigate(0, url1);
2107 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2108 navigation_entry_committed_counter_ = 0;
2110 // Ensure main page navigation to same url respects the was_within_same_page
2111 // hint provided in the params.
2112 FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2113 self_params.page_id = 0;
2114 self_params.url = url1;
2115 self_params.transition = ui::PAGE_TRANSITION_LINK;
2116 self_params.should_update_history = false;
2117 self_params.gesture = NavigationGestureUser;
2118 self_params.is_post = false;
2119 self_params.page_state = PageState::CreateFromURL(url1);
2120 self_params.was_within_same_page = true;
2122 LoadCommittedDetails details;
2123 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2124 &details));
2125 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2126 navigation_entry_committed_counter_ = 0;
2127 EXPECT_TRUE(details.is_in_page);
2128 EXPECT_TRUE(details.did_replace_entry);
2129 EXPECT_EQ(1, controller.GetEntryCount());
2131 // Fragment navigation to a new page_id.
2132 const GURL url2("http://foo#a");
2133 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2134 params.page_id = 1;
2135 params.url = url2;
2136 params.transition = ui::PAGE_TRANSITION_LINK;
2137 params.should_update_history = false;
2138 params.gesture = NavigationGestureUser;
2139 params.is_post = false;
2140 params.page_state = PageState::CreateFromURL(url2);
2141 params.was_within_same_page = true;
2143 // This should generate a new entry.
2144 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2145 &details));
2146 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2147 navigation_entry_committed_counter_ = 0;
2148 EXPECT_TRUE(details.is_in_page);
2149 EXPECT_FALSE(details.did_replace_entry);
2150 EXPECT_EQ(2, controller.GetEntryCount());
2152 // Go back one.
2153 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2154 controller.GoBack();
2155 back_params.url = url1;
2156 back_params.page_id = 0;
2157 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2158 &details));
2159 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2160 navigation_entry_committed_counter_ = 0;
2161 EXPECT_TRUE(details.is_in_page);
2162 EXPECT_EQ(2, controller.GetEntryCount());
2163 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2164 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2166 // Go forward
2167 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2168 controller.GoForward();
2169 forward_params.url = url2;
2170 forward_params.page_id = 1;
2171 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2172 &details));
2173 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2174 navigation_entry_committed_counter_ = 0;
2175 EXPECT_TRUE(details.is_in_page);
2176 EXPECT_EQ(2, controller.GetEntryCount());
2177 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2178 EXPECT_EQ(forward_params.url,
2179 controller.GetVisibleEntry()->GetURL());
2181 // Now go back and forward again. This is to work around a bug where we would
2182 // compare the incoming URL with the last committed entry rather than the
2183 // one identified by an existing page ID. This would result in the second URL
2184 // losing the reference fragment when you navigate away from it and then back.
2185 controller.GoBack();
2186 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2187 &details));
2188 controller.GoForward();
2189 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2190 &details));
2191 EXPECT_EQ(forward_params.url,
2192 controller.GetVisibleEntry()->GetURL());
2194 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2195 const GURL url3("http://bar");
2196 params.page_id = 2;
2197 params.url = url3;
2198 navigation_entry_committed_counter_ = 0;
2199 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2200 &details));
2201 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2202 navigation_entry_committed_counter_ = 0;
2203 EXPECT_FALSE(details.is_in_page);
2204 EXPECT_EQ(3, controller.GetEntryCount());
2205 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2208 TEST_F(NavigationControllerTest, InPage_Replace) {
2209 NavigationControllerImpl& controller = controller_impl();
2210 TestNotificationTracker notifications;
2211 RegisterForAllNavNotifications(&notifications, &controller);
2213 // Main page.
2214 const GURL url1("http://foo");
2215 main_test_rfh()->SendNavigate(0, url1);
2216 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2217 navigation_entry_committed_counter_ = 0;
2219 // First navigation.
2220 const GURL url2("http://foo#a");
2221 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2222 params.page_id = 0; // Same page_id
2223 params.url = url2;
2224 params.transition = ui::PAGE_TRANSITION_LINK;
2225 params.should_update_history = false;
2226 params.gesture = NavigationGestureUser;
2227 params.is_post = false;
2228 params.page_state = PageState::CreateFromURL(url2);
2229 params.was_within_same_page = true;
2231 // This should NOT generate a new entry, nor prune the list.
2232 LoadCommittedDetails details;
2233 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2234 &details));
2235 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2236 navigation_entry_committed_counter_ = 0;
2237 EXPECT_TRUE(details.is_in_page);
2238 EXPECT_TRUE(details.did_replace_entry);
2239 EXPECT_EQ(1, controller.GetEntryCount());
2242 // Tests for http://crbug.com/40395
2243 // Simulates this:
2244 // <script>
2245 // window.location.replace("#a");
2246 // window.location='http://foo3/';
2247 // </script>
2248 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2249 NavigationControllerImpl& controller = controller_impl();
2250 TestNotificationTracker notifications;
2251 RegisterForAllNavNotifications(&notifications, &controller);
2253 // Load an initial page.
2255 const GURL url("http://foo/");
2256 main_test_rfh()->SendNavigate(0, url);
2257 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2258 navigation_entry_committed_counter_ = 0;
2261 // Navigate to a new page.
2263 const GURL url("http://foo2/");
2264 main_test_rfh()->SendNavigate(1, url);
2265 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2266 navigation_entry_committed_counter_ = 0;
2269 // Navigate within the page.
2271 const GURL url("http://foo2/#a");
2272 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2273 params.page_id = 1; // Same page_id
2274 params.url = url;
2275 params.transition = ui::PAGE_TRANSITION_LINK;
2276 params.redirects.push_back(url);
2277 params.should_update_history = true;
2278 params.gesture = NavigationGestureUnknown;
2279 params.is_post = false;
2280 params.page_state = PageState::CreateFromURL(url);
2281 params.was_within_same_page = true;
2283 // This should NOT generate a new entry, nor prune the list.
2284 LoadCommittedDetails details;
2285 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2286 &details));
2287 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2288 navigation_entry_committed_counter_ = 0;
2289 EXPECT_TRUE(details.is_in_page);
2290 EXPECT_TRUE(details.did_replace_entry);
2291 EXPECT_EQ(2, controller.GetEntryCount());
2294 // Perform a client redirect to a new page.
2296 const GURL url("http://foo3/");
2297 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2298 params.page_id = 2; // New page_id
2299 params.url = url;
2300 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
2301 params.redirects.push_back(GURL("http://foo2/#a"));
2302 params.redirects.push_back(url);
2303 params.should_update_history = true;
2304 params.gesture = NavigationGestureUnknown;
2305 params.is_post = false;
2306 params.page_state = PageState::CreateFromURL(url);
2308 // This SHOULD generate a new entry.
2309 LoadCommittedDetails details;
2310 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2311 &details));
2312 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2313 navigation_entry_committed_counter_ = 0;
2314 EXPECT_FALSE(details.is_in_page);
2315 EXPECT_EQ(3, controller.GetEntryCount());
2318 // Verify that BACK brings us back to http://foo2/.
2320 const GURL url("http://foo2/");
2321 controller.GoBack();
2322 main_test_rfh()->SendNavigate(1, url);
2323 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2324 navigation_entry_committed_counter_ = 0;
2325 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2329 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
2331 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2332 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2333 GURL url("http://foo");
2334 params.page_id = 1;
2335 params.url = url;
2336 params.page_state = PageState::CreateFromURL(url);
2337 params.was_within_same_page = true;
2338 contents()->GetMainFrame()->SendNavigateWithParams(&params);
2339 // We pass if we don't crash.
2342 // NotificationObserver implementation used in verifying we've received the
2343 // NOTIFICATION_NAV_LIST_PRUNED method.
2344 class PrunedListener : public NotificationObserver {
2345 public:
2346 explicit PrunedListener(NavigationControllerImpl* controller)
2347 : notification_count_(0) {
2348 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2349 Source<NavigationController>(controller));
2352 void Observe(int type,
2353 const NotificationSource& source,
2354 const NotificationDetails& details) override {
2355 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2356 notification_count_++;
2357 details_ = *(Details<PrunedDetails>(details).ptr());
2361 // Number of times NAV_LIST_PRUNED has been observed.
2362 int notification_count_;
2364 // Details from the last NAV_LIST_PRUNED.
2365 PrunedDetails details_;
2367 private:
2368 NotificationRegistrar registrar_;
2370 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2373 // Tests that we limit the number of navigation entries created correctly.
2374 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2375 NavigationControllerImpl& controller = controller_impl();
2376 size_t original_count = NavigationControllerImpl::max_entry_count();
2377 const int kMaxEntryCount = 5;
2379 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2381 int url_index;
2382 // Load up to the max count, all entries should be there.
2383 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2384 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2385 controller.LoadURL(
2386 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2387 main_test_rfh()->PrepareForCommit(url);
2388 main_test_rfh()->SendNavigate(url_index, url);
2391 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2393 // Created a PrunedListener to observe prune notifications.
2394 PrunedListener listener(&controller);
2396 // Navigate some more.
2397 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2398 controller.LoadURL(
2399 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2400 main_test_rfh()->PrepareForCommit(url);
2401 main_test_rfh()->SendNavigate(url_index, url);
2402 url_index++;
2404 // We should have got a pruned navigation.
2405 EXPECT_EQ(1, listener.notification_count_);
2406 EXPECT_TRUE(listener.details_.from_front);
2407 EXPECT_EQ(1, listener.details_.count);
2409 // We expect http://www.a.com/0 to be gone.
2410 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2411 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2412 GURL("http:////www.a.com/1"));
2414 // More navigations.
2415 for (int i = 0; i < 3; i++) {
2416 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2417 controller.LoadURL(
2418 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2419 main_test_rfh()->PrepareForCommit(url);
2420 main_test_rfh()->SendNavigate(url_index, url);
2421 url_index++;
2423 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2424 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2425 GURL("http:////www.a.com/4"));
2427 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2430 // Tests that we can do a restore and navigate to the restored entries and
2431 // everything is updated properly. This can be tricky since there is no
2432 // SiteInstance for the entries created initially.
2433 TEST_F(NavigationControllerTest, RestoreNavigate) {
2434 // Create a NavigationController with a restored set of tabs.
2435 GURL url("http://foo");
2436 std::vector<NavigationEntry*> entries;
2437 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2438 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2439 browser_context());
2440 entry->SetPageID(0);
2441 entry->SetTitle(base::ASCIIToUTF16("Title"));
2442 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2443 const base::Time timestamp = base::Time::Now();
2444 entry->SetTimestamp(timestamp);
2445 entries.push_back(entry);
2446 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2447 WebContents::Create(WebContents::CreateParams(browser_context()))));
2448 NavigationControllerImpl& our_controller = our_contents->GetController();
2449 our_controller.Restore(
2451 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2452 &entries);
2453 ASSERT_EQ(0u, entries.size());
2455 // Before navigating to the restored entry, it should have a restore_type
2456 // and no SiteInstance.
2457 ASSERT_EQ(1, our_controller.GetEntryCount());
2458 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2459 our_controller.GetEntryAtIndex(0)->restore_type());
2460 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2462 // After navigating, we should have one entry, and it should be "pending".
2463 // It should now have a SiteInstance and no restore_type.
2464 our_controller.GoToIndex(0);
2465 EXPECT_EQ(1, our_controller.GetEntryCount());
2466 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2467 our_controller.GetPendingEntry());
2468 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2469 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2470 our_controller.GetEntryAtIndex(0)->restore_type());
2471 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2473 // Timestamp should remain the same before the navigation finishes.
2474 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2476 // Say we navigated to that entry.
2477 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2478 params.page_id = 0;
2479 params.url = url;
2480 params.transition = ui::PAGE_TRANSITION_LINK;
2481 params.should_update_history = false;
2482 params.gesture = NavigationGestureUser;
2483 params.is_post = false;
2484 params.page_state = PageState::CreateFromURL(url);
2485 LoadCommittedDetails details;
2486 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2487 &details);
2489 // There should be no longer any pending entry and one committed one. This
2490 // means that we were able to locate the entry, assign its site instance, and
2491 // commit it properly.
2492 EXPECT_EQ(1, our_controller.GetEntryCount());
2493 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2494 EXPECT_FALSE(our_controller.GetPendingEntry());
2495 EXPECT_EQ(
2496 url,
2497 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2498 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2499 our_controller.GetEntryAtIndex(0)->restore_type());
2501 // Timestamp should have been updated.
2502 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2505 // Tests that we can still navigate to a restored entry after a different
2506 // navigation fails and clears the pending entry. http://crbug.com/90085
2507 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2508 // Create a NavigationController with a restored set of tabs.
2509 GURL url("http://foo");
2510 std::vector<NavigationEntry*> entries;
2511 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2512 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2513 browser_context());
2514 entry->SetPageID(0);
2515 entry->SetTitle(base::ASCIIToUTF16("Title"));
2516 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2517 entries.push_back(entry);
2518 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2519 WebContents::Create(WebContents::CreateParams(browser_context()))));
2520 NavigationControllerImpl& our_controller = our_contents->GetController();
2521 our_controller.Restore(
2522 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2523 ASSERT_EQ(0u, entries.size());
2525 // Before navigating to the restored entry, it should have a restore_type
2526 // and no SiteInstance.
2527 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2528 our_controller.GetEntryAtIndex(0)->restore_type());
2529 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2531 // After navigating, we should have one entry, and it should be "pending".
2532 // It should now have a SiteInstance and no restore_type.
2533 our_controller.GoToIndex(0);
2534 EXPECT_EQ(1, our_controller.GetEntryCount());
2535 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2536 our_controller.GetPendingEntry());
2537 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2538 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2539 our_controller.GetEntryAtIndex(0)->restore_type());
2540 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2542 // This pending navigation may have caused a different navigation to fail,
2543 // which causes the pending entry to be cleared.
2544 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2545 fail_load_params.error_code = net::ERR_ABORTED;
2546 fail_load_params.error_description = base::string16();
2547 fail_load_params.url = url;
2548 fail_load_params.showing_repost_interstitial = false;
2549 main_test_rfh()->OnMessageReceived(
2550 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2551 fail_load_params));
2553 // Now the pending restored entry commits.
2554 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2555 params.page_id = 0;
2556 params.url = url;
2557 params.transition = ui::PAGE_TRANSITION_LINK;
2558 params.should_update_history = false;
2559 params.gesture = NavigationGestureUser;
2560 params.is_post = false;
2561 params.page_state = PageState::CreateFromURL(url);
2562 LoadCommittedDetails details;
2563 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2564 &details);
2566 // There should be no pending entry and one committed one.
2567 EXPECT_EQ(1, our_controller.GetEntryCount());
2568 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2569 EXPECT_FALSE(our_controller.GetPendingEntry());
2570 EXPECT_EQ(
2571 url,
2572 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2573 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2574 our_controller.GetEntryAtIndex(0)->restore_type());
2577 // Make sure that the page type and stuff is correct after an interstitial.
2578 TEST_F(NavigationControllerTest, Interstitial) {
2579 NavigationControllerImpl& controller = controller_impl();
2580 // First navigate somewhere normal.
2581 const GURL url1("http://foo");
2582 controller.LoadURL(
2583 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2584 main_test_rfh()->PrepareForCommit(url1);
2585 main_test_rfh()->SendNavigate(0, url1);
2587 // Now navigate somewhere with an interstitial.
2588 const GURL url2("http://bar");
2589 controller.LoadURL(
2590 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2591 controller.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL);
2593 // At this point the interstitial will be displayed and the load will still
2594 // be pending. If the user continues, the load will commit.
2595 main_test_rfh()->PrepareForCommit(url2);
2596 main_test_rfh()->SendNavigate(1, url2);
2598 // The page should be a normal page again.
2599 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2600 EXPECT_EQ(PAGE_TYPE_NORMAL,
2601 controller.GetLastCommittedEntry()->GetPageType());
2604 TEST_F(NavigationControllerTest, RemoveEntry) {
2605 NavigationControllerImpl& controller = controller_impl();
2606 const GURL url1("http://foo/1");
2607 const GURL url2("http://foo/2");
2608 const GURL url3("http://foo/3");
2609 const GURL url4("http://foo/4");
2610 const GURL url5("http://foo/5");
2611 const GURL pending_url("http://foo/pending");
2612 const GURL default_url("http://foo/default");
2614 controller.LoadURL(
2615 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2616 main_test_rfh()->PrepareForCommit(url1);
2617 main_test_rfh()->SendNavigate(0, url1);
2618 controller.LoadURL(
2619 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2620 main_test_rfh()->PrepareForCommit(url2);
2621 main_test_rfh()->SendNavigate(1, url2);
2622 controller.LoadURL(
2623 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2624 main_test_rfh()->PrepareForCommit(url3);
2625 main_test_rfh()->SendNavigate(2, url3);
2626 controller.LoadURL(
2627 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2628 main_test_rfh()->PrepareForCommit(url4);
2629 main_test_rfh()->SendNavigate(3, url4);
2630 controller.LoadURL(
2631 url5, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2632 main_test_rfh()->PrepareForCommit(url5);
2633 main_test_rfh()->SendNavigate(4, url5);
2635 // Try to remove the last entry. Will fail because it is the current entry.
2636 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2637 EXPECT_EQ(5, controller.GetEntryCount());
2638 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2640 // Go back, but don't commit yet. Check that we can't delete the current
2641 // and pending entries.
2642 controller.GoBack();
2643 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2644 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2646 // Now commit and delete the last entry.
2647 main_test_rfh()->PrepareForCommit(url4);
2648 main_test_rfh()->SendNavigate(3, url4);
2649 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2650 EXPECT_EQ(4, controller.GetEntryCount());
2651 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2652 EXPECT_FALSE(controller.GetPendingEntry());
2654 // Remove an entry which is not the last committed one.
2655 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2656 EXPECT_EQ(3, controller.GetEntryCount());
2657 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2658 EXPECT_FALSE(controller.GetPendingEntry());
2660 // Remove the 2 remaining entries.
2661 controller.RemoveEntryAtIndex(1);
2662 controller.RemoveEntryAtIndex(0);
2664 // This should leave us with only the last committed entry.
2665 EXPECT_EQ(1, controller.GetEntryCount());
2666 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2669 TEST_F(NavigationControllerTest, RemoveEntryWithPending) {
2670 NavigationControllerImpl& controller = controller_impl();
2671 const GURL url1("http://foo/1");
2672 const GURL url2("http://foo/2");
2673 const GURL url3("http://foo/3");
2674 const GURL default_url("http://foo/default");
2676 controller.LoadURL(
2677 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2678 main_test_rfh()->PrepareForCommit(url1);
2679 main_test_rfh()->SendNavigate(0, url1);
2680 controller.LoadURL(
2681 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2682 main_test_rfh()->PrepareForCommit(url2);
2683 main_test_rfh()->SendNavigate(1, url2);
2684 controller.LoadURL(
2685 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2686 main_test_rfh()->PrepareForCommit(url3);
2687 main_test_rfh()->SendNavigate(2, url3);
2689 // Go back, but don't commit yet. Check that we can't delete the current
2690 // and pending entries.
2691 controller.GoBack();
2692 EXPECT_FALSE(controller.RemoveEntryAtIndex(2));
2693 EXPECT_FALSE(controller.RemoveEntryAtIndex(1));
2695 // Remove the first entry, while there is a pending entry. This is expected
2696 // to discard the pending entry.
2697 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2698 EXPECT_FALSE(controller.GetPendingEntry());
2699 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2701 // We should update the last committed entry index.
2702 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
2704 // Now commit and ensure we land on the right entry.
2705 main_test_rfh()->PrepareForCommit(url2);
2706 main_test_rfh()->SendNavigate(1, url2);
2707 EXPECT_EQ(2, controller.GetEntryCount());
2708 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2709 EXPECT_FALSE(controller.GetPendingEntry());
2712 // Tests the transient entry, making sure it goes away with all navigations.
2713 TEST_F(NavigationControllerTest, TransientEntry) {
2714 NavigationControllerImpl& controller = controller_impl();
2715 TestNotificationTracker notifications;
2716 RegisterForAllNavNotifications(&notifications, &controller);
2718 const GURL url0("http://foo/0");
2719 const GURL url1("http://foo/1");
2720 const GURL url2("http://foo/2");
2721 const GURL url3("http://foo/3");
2722 const GURL url3_ref("http://foo/3#bar");
2723 const GURL url4("http://foo/4");
2724 const GURL transient_url("http://foo/transient");
2726 controller.LoadURL(
2727 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2728 main_test_rfh()->PrepareForCommit(url0);
2729 main_test_rfh()->SendNavigate(0, url0);
2730 controller.LoadURL(
2731 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2732 main_test_rfh()->PrepareForCommit(url1);
2733 main_test_rfh()->SendNavigate(1, url1);
2735 notifications.Reset();
2737 // Adding a transient with no pending entry.
2738 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2739 transient_entry->SetURL(transient_url);
2740 controller.SetTransientEntry(transient_entry);
2742 // We should not have received any notifications.
2743 EXPECT_EQ(0U, notifications.size());
2745 // Check our state.
2746 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2747 EXPECT_EQ(controller.GetEntryCount(), 3);
2748 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2749 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2750 EXPECT_TRUE(controller.GetLastCommittedEntry());
2751 EXPECT_FALSE(controller.GetPendingEntry());
2752 EXPECT_TRUE(controller.CanGoBack());
2753 EXPECT_FALSE(controller.CanGoForward());
2754 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2756 // Navigate.
2757 controller.LoadURL(
2758 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2759 main_test_rfh()->PrepareForCommit(url2);
2760 main_test_rfh()->SendNavigate(2, url2);
2762 // We should have navigated, transient entry should be gone.
2763 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2764 EXPECT_EQ(controller.GetEntryCount(), 3);
2766 // Add a transient again, then navigate with no pending entry this time.
2767 transient_entry = new NavigationEntryImpl;
2768 transient_entry->SetURL(transient_url);
2769 controller.SetTransientEntry(transient_entry);
2770 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2771 main_test_rfh()->PrepareForCommit(url3);
2772 main_test_rfh()->SendNavigate(3, url3);
2773 // Transient entry should be gone.
2774 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2775 EXPECT_EQ(controller.GetEntryCount(), 4);
2777 // Initiate a navigation, add a transient then commit navigation.
2778 controller.LoadURL(
2779 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2780 transient_entry = new NavigationEntryImpl;
2781 transient_entry->SetURL(transient_url);
2782 controller.SetTransientEntry(transient_entry);
2783 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2784 main_test_rfh()->PrepareForCommit(url4);
2785 main_test_rfh()->SendNavigate(4, url4);
2786 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2787 EXPECT_EQ(controller.GetEntryCount(), 5);
2789 // Add a transient and go back. This should simply remove the transient.
2790 transient_entry = new NavigationEntryImpl;
2791 transient_entry->SetURL(transient_url);
2792 controller.SetTransientEntry(transient_entry);
2793 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2794 EXPECT_TRUE(controller.CanGoBack());
2795 EXPECT_FALSE(controller.CanGoForward());
2796 controller.GoBack();
2797 // Transient entry should be gone.
2798 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2799 EXPECT_EQ(controller.GetEntryCount(), 5);
2800 main_test_rfh()->PrepareForCommit(url3);
2801 main_test_rfh()->SendNavigate(3, url3);
2803 // Add a transient and go to an entry before the current one.
2804 transient_entry = new NavigationEntryImpl;
2805 transient_entry->SetURL(transient_url);
2806 controller.SetTransientEntry(transient_entry);
2807 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2808 controller.GoToIndex(1);
2809 // The navigation should have been initiated, transient entry should be gone.
2810 EXPECT_FALSE(controller.GetTransientEntry());
2811 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2812 // Visible entry does not update for history navigations until commit.
2813 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2814 main_test_rfh()->PrepareForCommit(url1);
2815 main_test_rfh()->SendNavigate(1, url1);
2816 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2818 // Add a transient and go to an entry after the current one.
2819 transient_entry = new NavigationEntryImpl;
2820 transient_entry->SetURL(transient_url);
2821 controller.SetTransientEntry(transient_entry);
2822 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2823 controller.GoToIndex(3);
2824 // The navigation should have been initiated, transient entry should be gone.
2825 // Because of the transient entry that is removed, going to index 3 makes us
2826 // land on url2 (which is visible after the commit).
2827 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2828 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2829 main_test_rfh()->PrepareForCommit(url2);
2830 main_test_rfh()->SendNavigate(2, url2);
2831 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2833 // Add a transient and go forward.
2834 transient_entry = new NavigationEntryImpl;
2835 transient_entry->SetURL(transient_url);
2836 controller.SetTransientEntry(transient_entry);
2837 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2838 EXPECT_TRUE(controller.CanGoForward());
2839 controller.GoForward();
2840 // We should have navigated, transient entry should be gone.
2841 EXPECT_FALSE(controller.GetTransientEntry());
2842 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2843 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2844 main_test_rfh()->PrepareForCommit(url3);
2845 main_test_rfh()->SendNavigate(3, url3);
2846 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2848 // Add a transient and do an in-page navigation, replacing the current entry.
2849 transient_entry = new NavigationEntryImpl;
2850 transient_entry->SetURL(transient_url);
2851 controller.SetTransientEntry(transient_entry);
2852 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2853 main_test_rfh()->PrepareForCommit(url3_ref);
2854 main_test_rfh()->SendNavigate(3, url3_ref);
2855 // Transient entry should be gone.
2856 EXPECT_FALSE(controller.GetTransientEntry());
2857 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2859 // Ensure the URLs are correct.
2860 EXPECT_EQ(controller.GetEntryCount(), 5);
2861 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2862 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2863 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2864 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2865 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2868 // Test that Reload initiates a new navigation to a transient entry's URL.
2869 TEST_F(NavigationControllerTest, ReloadTransient) {
2870 NavigationControllerImpl& controller = controller_impl();
2871 const GURL url0("http://foo/0");
2872 const GURL url1("http://foo/1");
2873 const GURL transient_url("http://foo/transient");
2875 // Load |url0|, and start a pending navigation to |url1|.
2876 controller.LoadURL(
2877 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2878 main_test_rfh()->PrepareForCommit(url0);
2879 main_test_rfh()->SendNavigate(0, url0);
2880 controller.LoadURL(
2881 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2883 // A transient entry is added, interrupting the navigation.
2884 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2885 transient_entry->SetURL(transient_url);
2886 controller.SetTransientEntry(transient_entry);
2887 EXPECT_TRUE(controller.GetTransientEntry());
2888 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2890 // The page is reloaded, which should remove the pending entry for |url1| and
2891 // the transient entry for |transient_url|, and start a navigation to
2892 // |transient_url|.
2893 controller.Reload(true);
2894 EXPECT_FALSE(controller.GetTransientEntry());
2895 EXPECT_TRUE(controller.GetPendingEntry());
2896 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2897 ASSERT_EQ(controller.GetEntryCount(), 1);
2898 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2900 // Load of |transient_url| completes.
2901 main_test_rfh()->PrepareForCommit(transient_url);
2902 main_test_rfh()->SendNavigate(1, transient_url);
2903 ASSERT_EQ(controller.GetEntryCount(), 2);
2904 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2905 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2908 // Ensure that renderer initiated pending entries get replaced, so that we
2909 // don't show a stale virtual URL when a navigation commits.
2910 // See http://crbug.com/266922.
2911 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2912 NavigationControllerImpl& controller = controller_impl();
2913 Navigator* navigator =
2914 contents()->GetFrameTree()->root()->navigator();
2916 const GURL url1("nonexistent:12121");
2917 const GURL url1_fixed("http://nonexistent:12121/");
2918 const GURL url2("http://foo");
2920 // We create pending entries for renderer-initiated navigations so that we
2921 // can show them in new tabs when it is safe.
2922 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2924 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2925 // the virtual URL to differ from the URL.
2926 controller.GetPendingEntry()->SetURL(url1_fixed);
2927 controller.GetPendingEntry()->SetVirtualURL(url1);
2929 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
2930 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
2931 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
2933 // If the user clicks another link, we should replace the pending entry.
2934 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2935 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2936 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
2938 // Once it commits, the URL and virtual URL should reflect the actual page.
2939 main_test_rfh()->SendNavigate(0, url2);
2940 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2941 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
2943 // We should not replace the pending entry for an error URL.
2944 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2945 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2946 navigator->DidStartProvisionalLoad(main_test_rfh(),
2947 GURL(kUnreachableWebDataURL), false);
2948 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2950 // We should remember if the pending entry will replace the current one.
2951 // http://crbug.com/308444.
2952 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2953 controller.GetPendingEntry()->set_should_replace_entry(true);
2954 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2955 EXPECT_TRUE(controller.GetPendingEntry()->should_replace_entry());
2956 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2957 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2958 // to the main frame.
2959 main_test_rfh()->SendNavigate(0, url2);
2960 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2963 // Tests that the URLs for renderer-initiated navigations are not displayed to
2964 // the user until the navigation commits, to prevent URL spoof attacks.
2965 // See http://crbug.com/99016.
2966 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
2967 NavigationControllerImpl& controller = controller_impl();
2968 TestNotificationTracker notifications;
2969 RegisterForAllNavNotifications(&notifications, &controller);
2971 const GURL url0("http://foo/0");
2972 const GURL url1("http://foo/1");
2974 // For typed navigations (browser-initiated), both pending and visible entries
2975 // should update before commit.
2976 controller.LoadURL(
2977 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2978 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
2979 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2980 main_test_rfh()->PrepareForCommit(url0);
2981 main_test_rfh()->SendNavigate(0, url0);
2983 // For link clicks (renderer-initiated navigations), the pending entry should
2984 // update before commit but the visible should not.
2985 NavigationController::LoadURLParams load_url_params(url1);
2986 load_url_params.is_renderer_initiated = true;
2987 controller.LoadURLWithParams(load_url_params);
2988 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2989 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2990 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
2992 // After commit, both visible should be updated, there should be no pending
2993 // entry, and we should no longer treat the entry as renderer-initiated.
2994 main_test_rfh()->PrepareForCommit(url1);
2995 main_test_rfh()->SendNavigate(1, url1);
2996 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2997 EXPECT_FALSE(controller.GetPendingEntry());
2998 EXPECT_FALSE(controller.GetLastCommittedEntry()->is_renderer_initiated());
3000 notifications.Reset();
3003 // Tests that the URLs for renderer-initiated navigations in new tabs are
3004 // displayed to the user before commit, as long as the initial about:blank
3005 // page has not been modified. If so, we must revert to showing about:blank.
3006 // See http://crbug.com/9682.
3007 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
3008 NavigationControllerImpl& controller = controller_impl();
3009 TestNotificationTracker notifications;
3010 RegisterForAllNavNotifications(&notifications, &controller);
3012 const GURL url("http://foo");
3014 // For renderer-initiated navigations in new tabs (with no committed entries),
3015 // we show the pending entry's URL as long as the about:blank page is not
3016 // modified.
3017 NavigationController::LoadURLParams load_url_params(url);
3018 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3019 load_url_params.is_renderer_initiated = true;
3020 controller.LoadURLWithParams(load_url_params);
3021 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3022 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3023 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3024 EXPECT_TRUE(controller.IsInitialNavigation());
3025 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3027 // There should be no title yet.
3028 EXPECT_TRUE(contents()->GetTitle().empty());
3030 // If something else modifies the contents of the about:blank page, then
3031 // we must revert to showing about:blank to avoid a URL spoof.
3032 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3033 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3034 EXPECT_FALSE(controller.GetVisibleEntry());
3035 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3037 notifications.Reset();
3040 // Tests that the URLs for browser-initiated navigations in new tabs are
3041 // displayed to the user even after they fail, as long as the initial
3042 // about:blank page has not been modified. If so, we must revert to showing
3043 // about:blank. See http://crbug.com/355537.
3044 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
3045 NavigationControllerImpl& controller = controller_impl();
3046 TestNotificationTracker notifications;
3047 RegisterForAllNavNotifications(&notifications, &controller);
3049 const GURL url("http://foo");
3051 // For browser-initiated navigations in new tabs (with no committed entries),
3052 // we show the pending entry's URL as long as the about:blank page is not
3053 // modified. This is possible in cases that the user types a URL into a popup
3054 // tab created with a slow URL.
3055 NavigationController::LoadURLParams load_url_params(url);
3056 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
3057 load_url_params.is_renderer_initiated = false;
3058 controller.LoadURLWithParams(load_url_params);
3059 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3060 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3061 EXPECT_FALSE(controller.GetPendingEntry()->is_renderer_initiated());
3062 EXPECT_TRUE(controller.IsInitialNavigation());
3063 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3065 // There should be no title yet.
3066 EXPECT_TRUE(contents()->GetTitle().empty());
3068 // Suppose it aborts before committing, if it's a 204 or download or due to a
3069 // stop or a new navigation from the user. The URL should remain visible.
3070 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3071 params.error_code = net::ERR_ABORTED;
3072 params.error_description = base::string16();
3073 params.url = url;
3074 params.showing_repost_interstitial = false;
3075 main_test_rfh()->OnMessageReceived(
3076 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3077 contents()->SetIsLoading(test_rvh(), false, true, NULL);
3078 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3080 // If something else later modifies the contents of the about:blank page, then
3081 // we must revert to showing about:blank to avoid a URL spoof.
3082 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3083 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3084 EXPECT_FALSE(controller.GetVisibleEntry());
3085 EXPECT_FALSE(controller.GetPendingEntry());
3087 notifications.Reset();
3090 // Tests that the URLs for renderer-initiated navigations in new tabs are
3091 // displayed to the user even after they fail, as long as the initial
3092 // about:blank page has not been modified. If so, we must revert to showing
3093 // about:blank. See http://crbug.com/355537.
3094 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
3095 NavigationControllerImpl& controller = controller_impl();
3096 TestNotificationTracker notifications;
3097 RegisterForAllNavNotifications(&notifications, &controller);
3099 const GURL url("http://foo");
3101 // For renderer-initiated navigations in new tabs (with no committed entries),
3102 // we show the pending entry's URL as long as the about:blank page is not
3103 // modified.
3104 NavigationController::LoadURLParams load_url_params(url);
3105 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3106 load_url_params.is_renderer_initiated = true;
3107 controller.LoadURLWithParams(load_url_params);
3108 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3109 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3110 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3111 EXPECT_TRUE(controller.IsInitialNavigation());
3112 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3114 // There should be no title yet.
3115 EXPECT_TRUE(contents()->GetTitle().empty());
3117 // Suppose it aborts before committing, if it's a 204 or download or due to a
3118 // stop or a new navigation from the user. The URL should remain visible.
3119 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3120 params.error_code = net::ERR_ABORTED;
3121 params.error_description = base::string16();
3122 params.url = url;
3123 params.showing_repost_interstitial = false;
3124 main_test_rfh()->OnMessageReceived(
3125 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3126 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3128 // If something else later modifies the contents of the about:blank page, then
3129 // we must revert to showing about:blank to avoid a URL spoof.
3130 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3131 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3132 EXPECT_FALSE(controller.GetVisibleEntry());
3133 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3135 notifications.Reset();
3138 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3139 NavigationControllerImpl& controller = controller_impl();
3140 TestNotificationTracker notifications;
3141 RegisterForAllNavNotifications(&notifications, &controller);
3143 const GURL url1("http://foo/eh");
3144 const GURL url2("http://foo/bee");
3146 // For renderer-initiated navigations in new tabs (with no committed entries),
3147 // we show the pending entry's URL as long as the about:blank page is not
3148 // modified.
3149 NavigationController::LoadURLParams load_url_params(url1);
3150 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3151 load_url_params.is_renderer_initiated = true;
3152 controller.LoadURLWithParams(load_url_params);
3153 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3154 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3155 EXPECT_TRUE(controller.IsInitialNavigation());
3156 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3158 // Simulate a commit and then starting a new pending navigation.
3159 main_test_rfh()->SendNavigate(0, url1);
3160 NavigationController::LoadURLParams load_url2_params(url2);
3161 load_url2_params.transition_type = ui::PAGE_TRANSITION_LINK;
3162 load_url2_params.is_renderer_initiated = true;
3163 controller.LoadURLWithParams(load_url2_params);
3165 // We should not consider this an initial navigation, and thus should
3166 // not show the pending URL.
3167 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3168 EXPECT_FALSE(controller.IsInitialNavigation());
3169 EXPECT_TRUE(controller.GetVisibleEntry());
3170 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3172 notifications.Reset();
3175 // Tests that IsInPageNavigation returns appropriate results. Prevents
3176 // regression for bug 1126349.
3177 TEST_F(NavigationControllerTest, IsInPageNavigation) {
3178 NavigationControllerImpl& controller = controller_impl();
3179 const GURL url("http://www.google.com/home.html");
3181 // If the renderer claims it performed an in-page navigation from
3182 // about:blank, trust the renderer.
3183 // This can happen when an iframe is created and populated via
3184 // document.write(), then tries to perform a fragment navigation.
3185 // TODO(japhet): We should only trust the renderer if the about:blank
3186 // was the first document in the given frame, but we don't have enough
3187 // information to identify that case currently.
3188 const GURL blank_url(url::kAboutBlankURL);
3189 main_test_rfh()->SendNavigate(0, blank_url);
3190 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3191 main_test_rfh()));
3193 // Navigate to URL with no refs.
3194 main_test_rfh()->SendNavigate(0, url);
3196 // Reloading the page is not an in-page navigation.
3197 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3198 main_test_rfh()));
3199 const GURL other_url("http://www.google.com/add.html");
3200 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3201 main_test_rfh()));
3202 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3203 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3204 main_test_rfh()));
3206 // Navigate to URL with refs.
3207 main_test_rfh()->SendNavigate(1, url_with_ref);
3209 // Reloading the page is not an in-page navigation.
3210 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
3211 main_test_rfh()));
3212 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3213 main_test_rfh()));
3214 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3215 main_test_rfh()));
3216 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3217 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
3218 main_test_rfh()));
3220 // Going to the same url again will be considered in-page
3221 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3222 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3223 main_test_rfh()));
3225 // Going back to the non ref url will be considered in-page if the navigation
3226 // type is IN_PAGE.
3227 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3228 main_test_rfh()));
3230 // If the renderer says this is a same-origin in-page navigation, believe it.
3231 // This is the pushState/replaceState case.
3232 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
3233 main_test_rfh()));
3235 // Test allow_universal_access_from_file_urls flag.
3236 const GURL different_origin_url("http://www.example.com");
3237 MockRenderProcessHost* rph =
3238 static_cast<MockRenderProcessHost*>(main_test_rfh()->GetProcess());
3239 WebPreferences prefs = test_rvh()->GetWebkitPreferences();
3240 prefs.allow_universal_access_from_file_urls = true;
3241 test_rvh()->UpdateWebkitPreferences(prefs);
3242 prefs = test_rvh()->GetWebkitPreferences();
3243 EXPECT_TRUE(prefs.allow_universal_access_from_file_urls);
3244 // Allow in page navigation if existing URL is file scheme.
3245 const GURL file_url("file:///foo/index.html");
3246 main_test_rfh()->SendNavigate(0, file_url);
3247 EXPECT_EQ(0, rph->bad_msg_count());
3248 EXPECT_TRUE(controller.IsURLInPageNavigation(different_origin_url, true,
3249 main_test_rfh()));
3250 EXPECT_EQ(0, rph->bad_msg_count());
3251 // Don't honor allow_universal_access_from_file_urls if existing URL is
3252 // not file scheme.
3253 main_test_rfh()->SendNavigate(0, url);
3254 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3255 main_test_rfh()));
3256 EXPECT_EQ(1, rph->bad_msg_count());
3258 // Remove allow_universal_access_from_file_urls flag.
3259 prefs.allow_universal_access_from_file_urls = false;
3260 test_rvh()->UpdateWebkitPreferences(prefs);
3261 prefs = test_rvh()->GetWebkitPreferences();
3262 EXPECT_FALSE(prefs.allow_universal_access_from_file_urls);
3264 // Don't believe the renderer if it claims a cross-origin navigation is
3265 // in-page.
3266 EXPECT_EQ(1, rph->bad_msg_count());
3267 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3268 main_test_rfh()));
3269 EXPECT_EQ(2, rph->bad_msg_count());
3272 // Some pages can have subframes with the same base URL (minus the reference) as
3273 // the main page. Even though this is hard, it can happen, and we don't want
3274 // these subframe navigations to affect the toplevel document. They should
3275 // instead be ignored. http://crbug.com/5585
3276 TEST_F(NavigationControllerTest, SameSubframe) {
3277 NavigationControllerImpl& controller = controller_impl();
3278 // Navigate the main frame.
3279 const GURL url("http://www.google.com/");
3280 main_test_rfh()->SendNavigate(0, url);
3282 // We should be at the first navigation entry.
3283 EXPECT_EQ(controller.GetEntryCount(), 1);
3284 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3286 // Navigate a subframe that would normally count as in-page.
3287 const GURL subframe("http://www.google.com/#");
3288 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3289 params.page_id = 0;
3290 params.url = subframe;
3291 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3292 params.should_update_history = false;
3293 params.gesture = NavigationGestureAuto;
3294 params.is_post = false;
3295 params.page_state = PageState::CreateFromURL(subframe);
3296 LoadCommittedDetails details;
3297 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3298 &details));
3300 // Nothing should have changed.
3301 EXPECT_EQ(controller.GetEntryCount(), 1);
3302 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3305 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3306 // false.
3307 TEST_F(NavigationControllerTest, CloneAndGoBack) {
3308 NavigationControllerImpl& controller = controller_impl();
3309 const GURL url1("http://foo1");
3310 const GURL url2("http://foo2");
3311 const base::string16 title(base::ASCIIToUTF16("Title"));
3313 NavigateAndCommit(url1);
3314 controller.GetVisibleEntry()->SetTitle(title);
3315 NavigateAndCommit(url2);
3317 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3319 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3320 EXPECT_TRUE(clone->GetController().NeedsReload());
3321 clone->GetController().GoBack();
3322 // Navigating back should have triggered needs_reload_ to go false.
3323 EXPECT_FALSE(clone->GetController().NeedsReload());
3325 // Ensure that the pending URL and its title are visible.
3326 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3327 EXPECT_EQ(title, clone->GetTitle());
3330 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3331 // See http://crbug.com/234491.
3332 TEST_F(NavigationControllerTest, CloneAndReload) {
3333 NavigationControllerImpl& controller = controller_impl();
3334 const GURL url1("http://foo1");
3335 const GURL url2("http://foo2");
3336 const base::string16 title(base::ASCIIToUTF16("Title"));
3338 NavigateAndCommit(url1);
3339 controller.GetVisibleEntry()->SetTitle(title);
3340 NavigateAndCommit(url2);
3342 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3343 clone->GetController().LoadIfNecessary();
3345 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3346 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3348 clone->GetController().Reload(true);
3349 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3352 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3353 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3354 NavigationControllerImpl& controller = controller_impl();
3355 const GURL url1("http://foo1");
3356 const GURL url2("http://foo2");
3358 NavigateAndCommit(url1);
3359 NavigateAndCommit(url2);
3361 // Add an interstitial entry. Should be deleted with controller.
3362 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3363 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3364 controller.SetTransientEntry(interstitial_entry);
3366 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3368 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3371 // Test requesting and triggering a lazy reload.
3372 TEST_F(NavigationControllerTest, LazyReload) {
3373 NavigationControllerImpl& controller = controller_impl();
3374 const GURL url("http://foo");
3375 NavigateAndCommit(url);
3376 ASSERT_FALSE(controller.NeedsReload());
3377 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD,
3378 controller.GetLastCommittedEntry()->GetTransitionType());
3380 // Request a reload to happen when the controller becomes active (e.g. after
3381 // the renderer gets killed in background on Android).
3382 controller.SetNeedsReload();
3383 ASSERT_TRUE(controller.NeedsReload());
3384 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3385 controller.GetLastCommittedEntry()->GetTransitionType());
3387 // Set the controller as active, triggering the requested reload.
3388 controller.SetActive(true);
3389 ASSERT_FALSE(controller.NeedsReload());
3390 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3391 controller.GetPendingEntry()->GetTransitionType());
3394 // Test requesting and triggering a lazy reload without any committed entry.
3395 TEST_F(NavigationControllerTest, LazyReloadWithoutCommittedEntry) {
3396 NavigationControllerImpl& controller = controller_impl();
3397 const GURL url("http://foo");
3398 controller.LoadURL(url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3399 ASSERT_FALSE(controller.NeedsReload());
3400 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3401 controller.GetPendingEntry()->GetTransitionType());
3403 // Request a reload to happen when the controller becomes active (e.g. after
3404 // the renderer gets killed in background on Android).
3405 controller.SetNeedsReload();
3406 ASSERT_TRUE(controller.NeedsReload());
3407 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3408 controller.GetPendingEntry()->GetTransitionType());
3410 // Set the controller as active, triggering the requested reload.
3411 controller.SetActive(true);
3412 ASSERT_FALSE(controller.NeedsReload());
3413 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3414 controller.GetPendingEntry()->GetTransitionType());
3417 // Tests a subframe navigation while a toplevel navigation is pending.
3418 // http://crbug.com/43967
3419 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3420 NavigationControllerImpl& controller = controller_impl();
3421 // Load the first page.
3422 const GURL url1("http://foo/");
3423 NavigateAndCommit(url1);
3425 // Now start a pending load to a totally different page, but don't commit it.
3426 const GURL url2("http://bar/");
3427 controller.LoadURL(
3428 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3430 // Send a subframe update from the first page, as if one had just
3431 // automatically loaded. Auto subframes don't increment the page ID.
3432 const GURL url1_sub("http://foo/subframe");
3433 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3434 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3435 params.url = url1_sub;
3436 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3437 params.should_update_history = false;
3438 params.gesture = NavigationGestureAuto;
3439 params.is_post = false;
3440 params.page_state = PageState::CreateFromURL(url1_sub);
3441 LoadCommittedDetails details;
3443 // This should return false meaning that nothing was actually updated.
3444 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3445 &details));
3447 // The notification should have updated the last committed one, and not
3448 // the pending load.
3449 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3451 // The active entry should be unchanged by the subframe load.
3452 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3455 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3456 TEST_F(NavigationControllerTest, CopyStateFrom) {
3457 NavigationControllerImpl& controller = controller_impl();
3458 const GURL url1("http://foo1");
3459 const GURL url2("http://foo2");
3461 NavigateAndCommit(url1);
3462 NavigateAndCommit(url2);
3463 controller.GoBack();
3464 contents()->CommitPendingNavigation();
3466 scoped_ptr<TestWebContents> other_contents(
3467 static_cast<TestWebContents*>(CreateTestWebContents()));
3468 NavigationControllerImpl& other_controller = other_contents->GetController();
3469 other_controller.CopyStateFrom(controller);
3471 // other_controller should now contain 2 urls.
3472 ASSERT_EQ(2, other_controller.GetEntryCount());
3473 // We should be looking at the first one.
3474 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3476 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3477 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3478 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3479 // This is a different site than url1, so the IDs start again at 0.
3480 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3482 // The max page ID map should be copied over and updated with the max page ID
3483 // from the current tab.
3484 SiteInstance* instance1 =
3485 other_controller.GetEntryAtIndex(0)->site_instance();
3486 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3488 // Ensure the SessionStorageNamespaceMaps are the same size and have
3489 // the same partitons loaded.
3491 // TODO(ajwong): We should load a url from a different partition earlier
3492 // to make sure this map has more than one entry.
3493 const SessionStorageNamespaceMap& session_storage_namespace_map =
3494 controller.GetSessionStorageNamespaceMap();
3495 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3496 other_controller.GetSessionStorageNamespaceMap();
3497 EXPECT_EQ(session_storage_namespace_map.size(),
3498 other_session_storage_namespace_map.size());
3499 for (SessionStorageNamespaceMap::const_iterator it =
3500 session_storage_namespace_map.begin();
3501 it != session_storage_namespace_map.end();
3502 ++it) {
3503 SessionStorageNamespaceMap::const_iterator other =
3504 other_session_storage_namespace_map.find(it->first);
3505 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3509 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3510 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3511 NavigationControllerImpl& controller = controller_impl();
3512 const GURL url1("http://foo/1");
3513 const GURL url2("http://foo/2");
3514 const GURL url3("http://foo/3");
3516 NavigateAndCommit(url1);
3517 NavigateAndCommit(url2);
3519 // First two entries should have the same SiteInstance.
3520 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3521 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3522 EXPECT_EQ(instance1, instance2);
3523 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3524 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3525 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3527 scoped_ptr<TestWebContents> other_contents(
3528 static_cast<TestWebContents*>(CreateTestWebContents()));
3529 NavigationControllerImpl& other_controller = other_contents->GetController();
3530 other_contents->NavigateAndCommit(url3);
3531 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3532 other_controller.CopyStateFromAndPrune(&controller, false);
3534 // other_controller should now contain the 3 urls: url1, url2 and url3.
3536 ASSERT_EQ(3, other_controller.GetEntryCount());
3538 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3540 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3541 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3542 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3543 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3544 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3545 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3547 // A new SiteInstance in a different BrowsingInstance should be used for the
3548 // new tab.
3549 SiteInstance* instance3 =
3550 other_controller.GetEntryAtIndex(2)->site_instance();
3551 EXPECT_NE(instance3, instance1);
3552 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3554 // The max page ID map should be copied over and updated with the max page ID
3555 // from the current tab.
3556 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3557 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3560 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3561 // the target.
3562 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3563 NavigationControllerImpl& controller = controller_impl();
3564 const GURL url1("http://foo1");
3565 const GURL url2("http://foo2");
3566 const GURL url3("http://foo3");
3568 NavigateAndCommit(url1);
3569 NavigateAndCommit(url2);
3570 controller.GoBack();
3571 contents()->CommitPendingNavigation();
3573 scoped_ptr<TestWebContents> other_contents(
3574 static_cast<TestWebContents*>(CreateTestWebContents()));
3575 NavigationControllerImpl& other_controller = other_contents->GetController();
3576 other_contents->NavigateAndCommit(url3);
3577 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3578 other_controller.CopyStateFromAndPrune(&controller, false);
3580 // other_controller should now contain: url1, url3
3582 ASSERT_EQ(2, other_controller.GetEntryCount());
3583 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3585 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3586 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3587 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3589 // The max page ID map should be copied over and updated with the max page ID
3590 // from the current tab.
3591 SiteInstance* instance1 =
3592 other_controller.GetEntryAtIndex(1)->site_instance();
3593 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3596 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3597 // the target.
3598 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3599 NavigationControllerImpl& controller = controller_impl();
3600 const GURL url1("http://foo1");
3601 const GURL url2("http://foo2");
3602 const GURL url3("http://foo3");
3603 const GURL url4("http://foo4");
3605 NavigateAndCommit(url1);
3606 NavigateAndCommit(url2);
3608 scoped_ptr<TestWebContents> other_contents(
3609 static_cast<TestWebContents*>(CreateTestWebContents()));
3610 NavigationControllerImpl& other_controller = other_contents->GetController();
3611 other_contents->NavigateAndCommit(url3);
3612 other_contents->NavigateAndCommit(url4);
3613 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3614 other_controller.CopyStateFromAndPrune(&controller, false);
3616 // other_controller should now contain: url1, url2, url4
3618 ASSERT_EQ(3, other_controller.GetEntryCount());
3619 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3621 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3622 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3623 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3625 // The max page ID map should be copied over and updated with the max page ID
3626 // from the current tab.
3627 SiteInstance* instance1 =
3628 other_controller.GetEntryAtIndex(2)->site_instance();
3629 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3632 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3633 // not the last entry selected in the target.
3634 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3635 NavigationControllerImpl& controller = controller_impl();
3636 const GURL url1("http://foo1");
3637 const GURL url2("http://foo2");
3638 const GURL url3("http://foo3");
3639 const GURL url4("http://foo4");
3641 NavigateAndCommit(url1);
3642 NavigateAndCommit(url2);
3644 scoped_ptr<TestWebContents> other_contents(
3645 static_cast<TestWebContents*>(CreateTestWebContents()));
3646 NavigationControllerImpl& other_controller = other_contents->GetController();
3647 other_contents->NavigateAndCommit(url3);
3648 other_contents->NavigateAndCommit(url4);
3649 other_controller.GoBack();
3650 other_contents->CommitPendingNavigation();
3651 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3652 other_controller.CopyStateFromAndPrune(&controller, false);
3654 // other_controller should now contain: url1, url2, url3
3656 ASSERT_EQ(3, other_controller.GetEntryCount());
3657 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3659 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3660 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3661 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3663 // The max page ID map should be copied over and updated with the max page ID
3664 // from the current tab.
3665 SiteInstance* instance1 =
3666 other_controller.GetEntryAtIndex(2)->site_instance();
3667 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3670 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3671 // a pending entry in the target.
3672 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3673 NavigationControllerImpl& controller = controller_impl();
3674 const GURL url1("http://foo1");
3675 const GURL url2("http://foo2");
3676 const GURL url3("http://foo3");
3677 const GURL url4("http://foo4");
3679 NavigateAndCommit(url1);
3680 NavigateAndCommit(url2);
3681 controller.GoBack();
3682 contents()->CommitPendingNavigation();
3684 scoped_ptr<TestWebContents> other_contents(
3685 static_cast<TestWebContents*>(CreateTestWebContents()));
3686 NavigationControllerImpl& other_controller = other_contents->GetController();
3687 other_contents->NavigateAndCommit(url3);
3688 other_controller.LoadURL(
3689 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3690 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3691 other_controller.CopyStateFromAndPrune(&controller, false);
3693 // other_controller should now contain url1, url3, and a pending entry
3694 // for url4.
3696 ASSERT_EQ(2, other_controller.GetEntryCount());
3697 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3699 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3700 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3702 // And there should be a pending entry for url4.
3703 ASSERT_TRUE(other_controller.GetPendingEntry());
3704 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3706 // The max page ID map should be copied over and updated with the max page ID
3707 // from the current tab.
3708 SiteInstance* instance1 =
3709 other_controller.GetEntryAtIndex(0)->site_instance();
3710 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3713 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3714 // client redirect entry (with the same page ID) in the target. This used to
3715 // crash because the last committed entry would be pruned but max_page_id
3716 // remembered the page ID (http://crbug.com/234809).
3717 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3718 NavigationControllerImpl& controller = controller_impl();
3719 const GURL url1("http://foo1");
3720 const GURL url2a("http://foo2/a");
3721 const GURL url2b("http://foo2/b");
3723 NavigateAndCommit(url1);
3725 scoped_ptr<TestWebContents> other_contents(
3726 static_cast<TestWebContents*>(CreateTestWebContents()));
3727 NavigationControllerImpl& other_controller = other_contents->GetController();
3728 other_contents->NavigateAndCommit(url2a);
3729 // Simulate a client redirect, which has the same page ID as entry 2a.
3730 other_controller.LoadURL(
3731 url2b, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
3732 other_controller.GetPendingEntry()->SetPageID(
3733 other_controller.GetLastCommittedEntry()->GetPageID());
3735 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3736 other_controller.CopyStateFromAndPrune(&controller, false);
3738 // other_controller should now contain url1, url2a, and a pending entry
3739 // for url2b.
3741 ASSERT_EQ(2, other_controller.GetEntryCount());
3742 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3744 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3745 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3747 // And there should be a pending entry for url4.
3748 ASSERT_TRUE(other_controller.GetPendingEntry());
3749 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3751 // Let the pending entry commit.
3752 other_contents->CommitPendingNavigation();
3754 // The max page ID map should be copied over and updated with the max page ID
3755 // from the current tab.
3756 SiteInstance* instance1 =
3757 other_controller.GetEntryAtIndex(1)->site_instance();
3758 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3761 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3762 // source, and 1 entry in the target. The back pending entry should be ignored.
3763 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3764 NavigationControllerImpl& controller = controller_impl();
3765 const GURL url1("http://foo1");
3766 const GURL url2("http://foo2");
3767 const GURL url3("http://foo3");
3769 NavigateAndCommit(url1);
3770 NavigateAndCommit(url2);
3771 controller.GoBack();
3773 scoped_ptr<TestWebContents> other_contents(
3774 static_cast<TestWebContents*>(CreateTestWebContents()));
3775 NavigationControllerImpl& other_controller = other_contents->GetController();
3776 other_contents->NavigateAndCommit(url3);
3777 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3778 other_controller.CopyStateFromAndPrune(&controller, false);
3780 // other_controller should now contain: url1, url2, url3
3782 ASSERT_EQ(3, other_controller.GetEntryCount());
3783 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3785 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3786 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3787 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3788 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3790 // The max page ID map should be copied over and updated with the max page ID
3791 // from the current tab.
3792 SiteInstance* instance1 =
3793 other_controller.GetEntryAtIndex(2)->site_instance();
3794 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3797 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3798 // when the max entry count is 3. We should prune one entry.
3799 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3800 NavigationControllerImpl& controller = controller_impl();
3801 size_t original_count = NavigationControllerImpl::max_entry_count();
3802 const int kMaxEntryCount = 3;
3804 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3806 const GURL url1("http://foo/1");
3807 const GURL url2("http://foo/2");
3808 const GURL url3("http://foo/3");
3809 const GURL url4("http://foo/4");
3811 // Create a PrunedListener to observe prune notifications.
3812 PrunedListener listener(&controller);
3814 NavigateAndCommit(url1);
3815 NavigateAndCommit(url2);
3816 NavigateAndCommit(url3);
3818 scoped_ptr<TestWebContents> other_contents(
3819 static_cast<TestWebContents*>(CreateTestWebContents()));
3820 NavigationControllerImpl& other_controller = other_contents->GetController();
3821 other_contents->NavigateAndCommit(url4);
3822 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3823 other_controller.CopyStateFromAndPrune(&controller, false);
3825 // We should have received a pruned notification.
3826 EXPECT_EQ(1, listener.notification_count_);
3827 EXPECT_TRUE(listener.details_.from_front);
3828 EXPECT_EQ(1, listener.details_.count);
3830 // other_controller should now contain only 3 urls: url2, url3 and url4.
3832 ASSERT_EQ(3, other_controller.GetEntryCount());
3834 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3836 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3837 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3838 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3839 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3840 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3841 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3843 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3846 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3847 // replace_entry set to true.
3848 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3849 NavigationControllerImpl& controller = controller_impl();
3850 const GURL url1("http://foo/1");
3851 const GURL url2("http://foo/2");
3852 const GURL url3("http://foo/3");
3854 NavigateAndCommit(url1);
3855 NavigateAndCommit(url2);
3857 // First two entries should have the same SiteInstance.
3858 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3859 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3860 EXPECT_EQ(instance1, instance2);
3861 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3862 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3863 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3865 scoped_ptr<TestWebContents> other_contents(
3866 static_cast<TestWebContents*>(CreateTestWebContents()));
3867 NavigationControllerImpl& other_controller = other_contents->GetController();
3868 other_contents->NavigateAndCommit(url3);
3869 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3870 other_controller.CopyStateFromAndPrune(&controller, true);
3872 // other_controller should now contain the 2 urls: url1 and url3.
3874 ASSERT_EQ(2, other_controller.GetEntryCount());
3876 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3878 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3879 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3880 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3881 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3883 // A new SiteInstance in a different BrowsingInstance should be used for the
3884 // new tab.
3885 SiteInstance* instance3 =
3886 other_controller.GetEntryAtIndex(1)->site_instance();
3887 EXPECT_NE(instance3, instance1);
3888 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3890 // The max page ID map should be copied over and updated with the max page ID
3891 // from the current tab.
3892 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3893 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3896 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3897 // entry count is 3 and replace_entry is true. We should not prune entries.
3898 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3899 NavigationControllerImpl& controller = controller_impl();
3900 size_t original_count = NavigationControllerImpl::max_entry_count();
3901 const int kMaxEntryCount = 3;
3903 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3905 const GURL url1("http://foo/1");
3906 const GURL url2("http://foo/2");
3907 const GURL url3("http://foo/3");
3908 const GURL url4("http://foo/4");
3910 // Create a PrunedListener to observe prune notifications.
3911 PrunedListener listener(&controller);
3913 NavigateAndCommit(url1);
3914 NavigateAndCommit(url2);
3915 NavigateAndCommit(url3);
3917 scoped_ptr<TestWebContents> other_contents(
3918 static_cast<TestWebContents*>(CreateTestWebContents()));
3919 NavigationControllerImpl& other_controller = other_contents->GetController();
3920 other_contents->NavigateAndCommit(url4);
3921 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3922 other_controller.CopyStateFromAndPrune(&controller, true);
3924 // We should have received no pruned notification.
3925 EXPECT_EQ(0, listener.notification_count_);
3927 // other_controller should now contain only 3 urls: url1, url2 and url4.
3929 ASSERT_EQ(3, other_controller.GetEntryCount());
3931 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3933 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3934 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3935 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3936 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3937 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3938 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3940 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3943 // Tests that we can navigate to the restored entries
3944 // imported by CopyStateFromAndPrune.
3945 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
3946 const GURL kRestoredUrls[] = {
3947 GURL("http://site1.com"),
3948 GURL("http://site2.com"),
3950 const GURL kInitialUrl("http://site3.com");
3952 std::vector<NavigationEntry*> entries;
3953 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
3954 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
3955 kRestoredUrls[i], Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
3956 std::string(), browser_context());
3957 entry->SetPageID(static_cast<int>(i));
3958 entries.push_back(entry);
3961 // Create a WebContents with restored entries.
3962 scoped_ptr<TestWebContents> source_contents(
3963 static_cast<TestWebContents*>(CreateTestWebContents()));
3964 NavigationControllerImpl& source_controller =
3965 source_contents->GetController();
3966 source_controller.Restore(
3967 entries.size() - 1,
3968 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
3969 &entries);
3970 ASSERT_EQ(0u, entries.size());
3971 source_controller.LoadIfNecessary();
3972 source_contents->CommitPendingNavigation();
3974 // Load a page, then copy state from |source_contents|.
3975 NavigateAndCommit(kInitialUrl);
3976 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
3977 controller_impl().CopyStateFromAndPrune(&source_controller, false);
3978 ASSERT_EQ(3, controller_impl().GetEntryCount());
3980 // Go back to the first entry one at a time and
3981 // verify that it works as expected.
3982 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
3983 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
3985 controller_impl().GoBack();
3986 contents()->CommitPendingNavigation();
3987 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
3988 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
3990 controller_impl().GoBack();
3991 contents()->CommitPendingNavigation();
3992 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
3993 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
3996 // Tests that navigations initiated from the page (with the history object)
3997 // work as expected, creating pending entries.
3998 TEST_F(NavigationControllerTest, HistoryNavigate) {
3999 NavigationControllerImpl& controller = controller_impl();
4000 const GURL url1("http://foo/1");
4001 const GURL url2("http://foo/2");
4002 const GURL url3("http://foo/3");
4004 NavigateAndCommit(url1);
4005 NavigateAndCommit(url2);
4006 NavigateAndCommit(url3);
4007 controller.GoBack();
4008 contents()->CommitPendingNavigation();
4009 process()->sink().ClearMessages();
4011 // Simulate the page calling history.back(). It should create a pending entry.
4012 contents()->OnGoToEntryAtOffset(-1);
4013 EXPECT_EQ(0, controller.GetPendingEntryIndex());
4014 // The actual cross-navigation is suspended until the current RVH tells us
4015 // it unloaded, simulate that.
4016 contents()->ProceedWithCrossSiteNavigation();
4017 // Also make sure we told the page to navigate.
4018 GURL nav_url = GetLastNavigationURL();
4019 EXPECT_EQ(url1, nav_url);
4020 contents()->CommitPendingNavigation();
4021 process()->sink().ClearMessages();
4023 // Now test history.forward()
4024 contents()->OnGoToEntryAtOffset(2);
4025 EXPECT_EQ(2, controller.GetPendingEntryIndex());
4026 // The actual cross-navigation is suspended until the current RVH tells us
4027 // it unloaded, simulate that.
4028 contents()->ProceedWithCrossSiteNavigation();
4029 nav_url = GetLastNavigationURL();
4030 EXPECT_EQ(url3, nav_url);
4031 contents()->CommitPendingNavigation();
4032 process()->sink().ClearMessages();
4034 controller.DiscardNonCommittedEntries();
4036 // Make sure an extravagant history.go() doesn't break.
4037 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4038 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4039 EXPECT_FALSE(HasNavigationRequest());
4042 // Test call to PruneAllButLastCommitted for the only entry.
4043 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
4044 NavigationControllerImpl& controller = controller_impl();
4045 const GURL url1("http://foo1");
4046 NavigateAndCommit(url1);
4048 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4050 controller.PruneAllButLastCommitted();
4052 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4053 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4056 // Test call to PruneAllButLastCommitted for first entry.
4057 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
4058 NavigationControllerImpl& controller = controller_impl();
4059 const GURL url1("http://foo/1");
4060 const GURL url2("http://foo/2");
4061 const GURL url3("http://foo/3");
4063 NavigateAndCommit(url1);
4064 NavigateAndCommit(url2);
4065 NavigateAndCommit(url3);
4066 controller.GoBack();
4067 controller.GoBack();
4068 contents()->CommitPendingNavigation();
4070 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4072 controller.PruneAllButLastCommitted();
4074 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4075 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4078 // Test call to PruneAllButLastCommitted for intermediate entry.
4079 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
4080 NavigationControllerImpl& controller = controller_impl();
4081 const GURL url1("http://foo/1");
4082 const GURL url2("http://foo/2");
4083 const GURL url3("http://foo/3");
4085 NavigateAndCommit(url1);
4086 NavigateAndCommit(url2);
4087 NavigateAndCommit(url3);
4088 controller.GoBack();
4089 contents()->CommitPendingNavigation();
4091 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4093 controller.PruneAllButLastCommitted();
4095 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4096 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
4099 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4100 // the list of entries.
4101 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
4102 NavigationControllerImpl& controller = controller_impl();
4103 const GURL url1("http://foo/1");
4104 const GURL url2("http://foo/2");
4105 const GURL url3("http://foo/3");
4107 NavigateAndCommit(url1);
4108 NavigateAndCommit(url2);
4110 // Create a pending entry that is not in the entry list.
4111 controller.LoadURL(
4112 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4113 EXPECT_TRUE(controller.GetPendingEntry());
4114 EXPECT_EQ(2, controller.GetEntryCount());
4116 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4117 controller.PruneAllButLastCommitted();
4119 // We should only have the last committed and pending entries at this point,
4120 // and the pending entry should still not be in the entry list.
4121 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4122 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
4123 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4124 EXPECT_TRUE(controller.GetPendingEntry());
4125 EXPECT_EQ(1, controller.GetEntryCount());
4127 // Try to commit the pending entry.
4128 main_test_rfh()->PrepareForCommit(url3);
4129 main_test_rfh()->SendNavigate(2, url3);
4130 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4131 EXPECT_FALSE(controller.GetPendingEntry());
4132 EXPECT_EQ(2, controller.GetEntryCount());
4133 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
4136 // Test to ensure that when we do a history navigation back to the current
4137 // committed page (e.g., going forward to a slow-loading page, then pressing
4138 // the back button), we just stop the navigation to prevent the throbber from
4139 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4140 // start, but WebKit essentially ignores the navigation and never sends a
4141 // message to stop the throbber.
4142 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
4143 NavigationControllerImpl& controller = controller_impl();
4144 const GURL url0("http://foo/0");
4145 const GURL url1("http://foo/1");
4147 NavigateAndCommit(url0);
4148 NavigateAndCommit(url1);
4150 // Go back to the original page, then forward to the slow page, then back
4151 controller.GoBack();
4152 contents()->CommitPendingNavigation();
4154 controller.GoForward();
4155 EXPECT_EQ(1, controller.GetPendingEntryIndex());
4157 controller.GoBack();
4158 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4161 TEST_F(NavigationControllerTest, IsInitialNavigation) {
4162 NavigationControllerImpl& controller = controller_impl();
4163 TestNotificationTracker notifications;
4164 RegisterForAllNavNotifications(&notifications, &controller);
4166 // Initial state.
4167 EXPECT_TRUE(controller.IsInitialNavigation());
4169 // After commit, it stays false.
4170 const GURL url1("http://foo1");
4171 main_test_rfh()->SendNavigate(0, url1);
4172 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4173 navigation_entry_committed_counter_ = 0;
4174 EXPECT_FALSE(controller.IsInitialNavigation());
4176 // After starting a new navigation, it stays false.
4177 const GURL url2("http://foo2");
4178 controller.LoadURL(
4179 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4182 // Check that the favicon is not reused across a client redirect.
4183 // (crbug.com/28515)
4184 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4185 const GURL kPageWithFavicon("http://withfavicon.html");
4186 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4187 const GURL kIconURL("http://withfavicon.ico");
4188 const gfx::Image kDefaultFavicon = FaviconStatus().image;
4190 NavigationControllerImpl& controller = controller_impl();
4191 TestNotificationTracker notifications;
4192 RegisterForAllNavNotifications(&notifications, &controller);
4194 main_test_rfh()->SendNavigate(0, kPageWithFavicon);
4195 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4196 navigation_entry_committed_counter_ = 0;
4198 NavigationEntry* entry = controller.GetLastCommittedEntry();
4199 EXPECT_TRUE(entry);
4200 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4202 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4203 content::FaviconStatus& favicon_status = entry->GetFavicon();
4204 favicon_status.image = CreateImage(SK_ColorWHITE);
4205 favicon_status.url = kIconURL;
4206 favicon_status.valid = true;
4207 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4209 main_test_rfh()->SendNavigateWithTransition(
4210 0, // same page ID.
4211 kPageWithoutFavicon,
4212 ui::PAGE_TRANSITION_CLIENT_REDIRECT);
4213 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4214 navigation_entry_committed_counter_ = 0;
4216 entry = controller.GetLastCommittedEntry();
4217 EXPECT_TRUE(entry);
4218 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4220 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4223 // Check that the favicon is not cleared for NavigationEntries which were
4224 // previously navigated to.
4225 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4226 const GURL kUrl1("http://www.a.com/1");
4227 const GURL kUrl2("http://www.a.com/2");
4228 const GURL kIconURL("http://www.a.com/1/favicon.ico");
4230 NavigationControllerImpl& controller = controller_impl();
4231 TestNotificationTracker notifications;
4232 RegisterForAllNavNotifications(&notifications, &controller);
4234 main_test_rfh()->SendNavigate(0, kUrl1);
4235 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4236 navigation_entry_committed_counter_ = 0;
4238 // Simulate Chromium having set the favicon for |kUrl1|.
4239 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4240 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4241 EXPECT_TRUE(entry);
4242 content::FaviconStatus& favicon_status = entry->GetFavicon();
4243 favicon_status.image = favicon_image;
4244 favicon_status.url = kIconURL;
4245 favicon_status.valid = true;
4247 // Navigate to another page and go back to the original page.
4248 main_test_rfh()->SendNavigate(1, kUrl2);
4249 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4250 navigation_entry_committed_counter_ = 0;
4251 main_test_rfh()->SendNavigateWithTransition(
4253 kUrl1,
4254 ui::PAGE_TRANSITION_FORWARD_BACK);
4255 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4256 navigation_entry_committed_counter_ = 0;
4258 // Verify that the favicon for the page at |kUrl1| was not cleared.
4259 entry = controller.GetEntryAtIndex(0);
4260 EXPECT_TRUE(entry);
4261 EXPECT_EQ(kUrl1, entry->GetURL());
4262 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4265 // The test crashes on android: http://crbug.com/170449
4266 #if defined(OS_ANDROID)
4267 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4268 #else
4269 #define MAYBE_PurgeScreenshot PurgeScreenshot
4270 #endif
4271 // Tests that screenshot are purged correctly.
4272 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4273 NavigationControllerImpl& controller = controller_impl();
4275 NavigationEntryImpl* entry;
4277 // Navigate enough times to make sure that some screenshots are purged.
4278 for (int i = 0; i < 12; ++i) {
4279 const GURL url(base::StringPrintf("http://foo%d/", i));
4280 NavigateAndCommit(url);
4281 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4284 MockScreenshotManager* screenshot_manager =
4285 new MockScreenshotManager(&controller);
4286 controller.SetScreenshotManager(screenshot_manager);
4287 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4288 entry = controller.GetEntryAtIndex(i);
4289 screenshot_manager->TakeScreenshotFor(entry);
4290 EXPECT_TRUE(entry->screenshot().get());
4293 NavigateAndCommit(GURL("https://foo/"));
4294 EXPECT_EQ(13, controller.GetEntryCount());
4295 entry = controller.GetEntryAtIndex(11);
4296 screenshot_manager->TakeScreenshotFor(entry);
4298 for (int i = 0; i < 2; ++i) {
4299 entry = controller.GetEntryAtIndex(i);
4300 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4301 << " not purged";
4304 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4305 entry = controller.GetEntryAtIndex(i);
4306 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4309 // Navigate to index 5 and then try to assign screenshot to all entries.
4310 controller.GoToIndex(5);
4311 contents()->CommitPendingNavigation();
4312 EXPECT_EQ(5, controller.GetCurrentEntryIndex());
4313 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4314 entry = controller.GetEntryAtIndex(i);
4315 screenshot_manager->TakeScreenshotFor(entry);
4318 for (int i = 10; i <= 12; ++i) {
4319 entry = controller.GetEntryAtIndex(i);
4320 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4321 << " not purged";
4322 screenshot_manager->TakeScreenshotFor(entry);
4325 // Navigate to index 7 and assign screenshot to all entries.
4326 controller.GoToIndex(7);
4327 contents()->CommitPendingNavigation();
4328 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4329 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4330 entry = controller.GetEntryAtIndex(i);
4331 screenshot_manager->TakeScreenshotFor(entry);
4334 for (int i = 0; i < 2; ++i) {
4335 entry = controller.GetEntryAtIndex(i);
4336 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4337 << " not purged";
4340 // Clear all screenshots.
4341 EXPECT_EQ(13, controller.GetEntryCount());
4342 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4343 controller.ClearAllScreenshots();
4344 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4345 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4346 entry = controller.GetEntryAtIndex(i);
4347 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4348 << " not cleared";
4352 TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) {
4353 // Navigate.
4354 contents()->GetMainFrame()->SendNavigate(1, GURL("http://foo"));
4356 // Set title and favicon.
4357 base::string16 title(base::ASCIIToUTF16("Title"));
4358 FaviconStatus favicon;
4359 favicon.valid = true;
4360 favicon.url = GURL("http://foo/favicon.ico");
4361 controller().GetLastCommittedEntry()->SetTitle(title);
4362 controller().GetLastCommittedEntry()->GetFavicon() = favicon;
4364 // history.pushState() is called.
4365 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4366 GURL url("http://foo#foo");
4367 params.page_id = 2;
4368 params.url = url;
4369 params.page_state = PageState::CreateFromURL(url);
4370 params.was_within_same_page = true;
4371 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4373 // The title should immediately be visible on the new NavigationEntry.
4374 base::string16 new_title =
4375 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4376 EXPECT_EQ(title, new_title);
4377 FaviconStatus new_favicon =
4378 controller().GetLastCommittedEntry()->GetFavicon();
4379 EXPECT_EQ(favicon.valid, new_favicon.valid);
4380 EXPECT_EQ(favicon.url, new_favicon.url);
4383 // Test that the navigation controller clears its session history when a
4384 // navigation commits with the clear history list flag set.
4385 TEST_F(NavigationControllerTest, ClearHistoryList) {
4386 const GURL url1("http://foo1");
4387 const GURL url2("http://foo2");
4388 const GURL url3("http://foo3");
4389 const GURL url4("http://foo4");
4391 NavigationControllerImpl& controller = controller_impl();
4393 // Create a session history with three entries, second entry is active.
4394 NavigateAndCommit(url1);
4395 NavigateAndCommit(url2);
4396 NavigateAndCommit(url3);
4397 controller.GoBack();
4398 contents()->CommitPendingNavigation();
4399 EXPECT_EQ(3, controller.GetEntryCount());
4400 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4402 // Create a new pending navigation, and indicate that the session history
4403 // should be cleared.
4404 NavigationController::LoadURLParams params(url4);
4405 params.should_clear_history_list = true;
4406 controller.LoadURLWithParams(params);
4408 // Verify that the pending entry correctly indicates that the session history
4409 // should be cleared.
4410 NavigationEntryImpl* entry = controller.GetPendingEntry();
4411 ASSERT_TRUE(entry);
4412 EXPECT_TRUE(entry->should_clear_history_list());
4414 // Assume that the RF correctly cleared its history and commit the navigation.
4415 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4416 switches::kEnableBrowserSideNavigation)) {
4417 contents()->GetMainFrame()->PrepareForCommit(entry->GetURL());
4419 contents()->GetPendingMainFrame()->
4420 set_simulate_history_list_was_cleared(true);
4421 contents()->CommitPendingNavigation();
4423 // Verify that the NavigationController's session history was correctly
4424 // cleared.
4425 EXPECT_EQ(1, controller.GetEntryCount());
4426 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4427 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4428 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4429 EXPECT_FALSE(controller.CanGoBack());
4430 EXPECT_FALSE(controller.CanGoForward());
4431 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4434 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
4435 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
4436 EXPECT_FALSE(contents()->GetDelegate());
4437 contents()->SetDelegate(delegate.get());
4439 // Submit a form.
4440 GURL url("http://foo");
4441 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4442 params.page_id = 1;
4443 params.url = url;
4444 params.transition = ui::PAGE_TRANSITION_FORM_SUBMIT;
4445 params.gesture = NavigationGestureUser;
4446 params.page_state = PageState::CreateFromURL(url);
4447 params.was_within_same_page = false;
4448 params.is_post = true;
4449 params.post_id = 2;
4450 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4452 // history.replaceState() is called.
4453 GURL replace_url("http://foo#foo");
4454 params.page_id = 1;
4455 params.url = replace_url;
4456 params.transition = ui::PAGE_TRANSITION_LINK;
4457 params.gesture = NavigationGestureUser;
4458 params.page_state = PageState::CreateFromURL(replace_url);
4459 params.was_within_same_page = true;
4460 params.is_post = false;
4461 params.post_id = -1;
4462 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4464 // Now reload. replaceState overrides the POST, so we should not show a
4465 // repost warning dialog.
4466 controller_impl().Reload(true);
4467 EXPECT_EQ(0, delegate->repost_form_warning_count());
4470 TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
4471 GURL url("http://foo");
4472 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4473 params.page_id = 1;
4474 params.url = url;
4475 params.transition = ui::PAGE_TRANSITION_LINK;
4476 params.gesture = NavigationGestureUser;
4477 params.page_state = PageState::CreateFromURL(url);
4478 params.was_within_same_page = false;
4479 params.is_post = true;
4480 params.post_id = 2;
4481 params.url_is_unreachable = true;
4482 // Navigate to new page
4484 LoadCommittedDetails details;
4485 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4486 EXPECT_EQ(PAGE_TYPE_ERROR,
4487 controller_impl().GetLastCommittedEntry()->GetPageType());
4488 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details.type);
4491 // Navigate to existing page.
4493 LoadCommittedDetails details;
4494 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4495 EXPECT_EQ(PAGE_TYPE_ERROR,
4496 controller_impl().GetLastCommittedEntry()->GetPageType());
4497 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
4500 // Navigate to same page.
4501 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4502 // same-page transition.
4503 controller_impl().LoadURL(
4504 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4505 params.transition = ui::PAGE_TRANSITION_TYPED;
4507 LoadCommittedDetails details;
4508 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4509 EXPECT_EQ(PAGE_TYPE_ERROR,
4510 controller_impl().GetLastCommittedEntry()->GetPageType());
4511 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, details.type);
4514 // Navigate in page.
4515 params.url = GURL("http://foo#foo");
4516 params.transition = ui::PAGE_TRANSITION_LINK;
4517 params.was_within_same_page = true;
4519 LoadCommittedDetails details;
4520 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4521 EXPECT_EQ(PAGE_TYPE_ERROR,
4522 controller_impl().GetLastCommittedEntry()->GetPageType());
4523 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, details.type);
4527 } // namespace content