PlzNavigate: test updates post beforeUnload IPC refactor.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_unittest.cc
blobcb14e671d543d1c87251ababcc7d03d01e5e2e3b
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()->SendRendererInitiatedNavigationRequest(urls[0], true);
320 main_test_rfh()->PrepareForCommit();
321 main_test_rfh()->SendNavigate(0, urls[0]);
322 EXPECT_EQ(1U, navigation_entry_committed_counter_);
323 navigation_entry_committed_counter_ = 0;
324 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
325 EXPECT_FALSE(controller.CanGoBack());
326 EXPECT_FALSE(controller.CanGoForward());
327 EXPECT_FALSE(controller.CanGoToOffset(1));
329 for (int i = 1; i <= 4; ++i) {
330 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls[i], true);
331 main_test_rfh()->PrepareForCommit();
332 main_test_rfh()->SendNavigate(i, urls[i]);
333 EXPECT_EQ(1U, navigation_entry_committed_counter_);
334 navigation_entry_committed_counter_ = 0;
335 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
336 EXPECT_TRUE(controller.CanGoToOffset(-i));
337 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
338 EXPECT_FALSE(controller.CanGoToOffset(1));
341 // We have loaded 5 pages, and are currently at the last-loaded page.
342 int url_index = 4;
344 enum Tests {
345 GO_TO_MIDDLE_PAGE = -2,
346 GO_FORWARDS = 1,
347 GO_BACKWARDS = -1,
348 GO_TO_BEGINNING = -2,
349 GO_TO_END = 4,
350 NUM_TESTS = 5,
353 const int test_offsets[NUM_TESTS] = {
354 GO_TO_MIDDLE_PAGE,
355 GO_FORWARDS,
356 GO_BACKWARDS,
357 GO_TO_BEGINNING,
358 GO_TO_END
361 for (int test = 0; test < NUM_TESTS; ++test) {
362 int offset = test_offsets[test];
363 controller.GoToOffset(offset);
364 url_index += offset;
365 // Check that the GoToOffset will land on the expected page.
366 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
367 main_test_rfh()->PrepareForCommit();
368 main_test_rfh()->SendNavigate(url_index, urls[url_index]);
369 EXPECT_EQ(1U, navigation_entry_committed_counter_);
370 navigation_entry_committed_counter_ = 0;
371 // Check that we can go to any valid offset into the history.
372 for (size_t j = 0; j < urls.size(); ++j)
373 EXPECT_TRUE(controller.CanGoToOffset(j - url_index));
374 // Check that we can't go beyond the beginning or end of the history.
375 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1)));
376 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index));
380 TEST_F(NavigationControllerTest, LoadURL) {
381 NavigationControllerImpl& controller = controller_impl();
382 TestNotificationTracker notifications;
383 RegisterForAllNavNotifications(&notifications, &controller);
385 const GURL url1("http://foo1");
386 const GURL url2("http://foo2");
388 controller.LoadURL(
389 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
390 // Creating a pending notification should not have issued any of the
391 // notifications we're listening for.
392 EXPECT_EQ(0U, notifications.size());
394 // The load should now be pending.
395 EXPECT_EQ(controller.GetEntryCount(), 0);
396 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
397 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
398 EXPECT_FALSE(controller.GetLastCommittedEntry());
399 ASSERT_TRUE(controller.GetPendingEntry());
400 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
401 EXPECT_FALSE(controller.CanGoBack());
402 EXPECT_FALSE(controller.CanGoForward());
403 EXPECT_EQ(contents()->GetMaxPageID(), -1);
405 // Neither the timestamp nor the status code should have been set yet.
406 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
407 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
409 // We should have gotten no notifications from the preceeding checks.
410 EXPECT_EQ(0U, notifications.size());
412 main_test_rfh()->PrepareForCommit();
413 main_test_rfh()->SendNavigate(0, url1);
414 EXPECT_EQ(1U, navigation_entry_committed_counter_);
415 navigation_entry_committed_counter_ = 0;
417 // The load should now be committed.
418 EXPECT_EQ(controller.GetEntryCount(), 1);
419 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
420 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
421 EXPECT_TRUE(controller.GetLastCommittedEntry());
422 EXPECT_FALSE(controller.GetPendingEntry());
423 ASSERT_TRUE(controller.GetVisibleEntry());
424 EXPECT_FALSE(controller.CanGoBack());
425 EXPECT_FALSE(controller.CanGoForward());
426 EXPECT_EQ(contents()->GetMaxPageID(), 0);
427 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
429 // The timestamp should have been set.
430 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
432 // Load another...
433 controller.LoadURL(
434 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
436 // The load should now be pending.
437 EXPECT_EQ(controller.GetEntryCount(), 1);
438 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
439 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
440 EXPECT_TRUE(controller.GetLastCommittedEntry());
441 ASSERT_TRUE(controller.GetPendingEntry());
442 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
443 // TODO(darin): maybe this should really be true?
444 EXPECT_FALSE(controller.CanGoBack());
445 EXPECT_FALSE(controller.CanGoForward());
446 EXPECT_EQ(contents()->GetMaxPageID(), 0);
448 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
450 // Simulate the beforeunload ack for the cross-site transition, and then the
451 // commit.
452 main_test_rfh()->PrepareForCommit();
453 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
454 EXPECT_EQ(1U, navigation_entry_committed_counter_);
455 navigation_entry_committed_counter_ = 0;
457 // The load should now be committed.
458 EXPECT_EQ(controller.GetEntryCount(), 2);
459 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
460 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
461 EXPECT_TRUE(controller.GetLastCommittedEntry());
462 EXPECT_FALSE(controller.GetPendingEntry());
463 ASSERT_TRUE(controller.GetVisibleEntry());
464 EXPECT_TRUE(controller.CanGoBack());
465 EXPECT_FALSE(controller.CanGoForward());
466 EXPECT_EQ(contents()->GetMaxPageID(), 1);
468 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
471 namespace {
473 base::Time GetFixedTime(base::Time time) {
474 return time;
477 } // namespace
479 TEST_F(NavigationControllerTest, LoadURLSameTime) {
480 NavigationControllerImpl& controller = controller_impl();
481 TestNotificationTracker notifications;
482 RegisterForAllNavNotifications(&notifications, &controller);
484 // Set the clock to always return a timestamp of 1.
485 controller.SetGetTimestampCallbackForTest(
486 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
488 const GURL url1("http://foo1");
489 const GURL url2("http://foo2");
491 controller.LoadURL(
492 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
494 main_test_rfh()->PrepareForCommit();
495 main_test_rfh()->SendNavigate(0, url1);
496 EXPECT_EQ(1U, navigation_entry_committed_counter_);
497 navigation_entry_committed_counter_ = 0;
499 // Load another...
500 controller.LoadURL(
501 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
503 // Simulate the beforeunload ack for the cross-site transition, and then the
504 // commit.
505 main_test_rfh()->PrepareForCommit();
506 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
507 EXPECT_EQ(1U, navigation_entry_committed_counter_);
508 navigation_entry_committed_counter_ = 0;
510 // The two loads should now be committed.
511 ASSERT_EQ(controller.GetEntryCount(), 2);
513 // Timestamps should be distinct despite the clock returning the
514 // same value.
515 EXPECT_EQ(1u,
516 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
517 EXPECT_EQ(2u,
518 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
521 void CheckNavigationEntryMatchLoadParams(
522 NavigationController::LoadURLParams& load_params,
523 NavigationEntryImpl* entry) {
524 EXPECT_EQ(load_params.url, entry->GetURL());
525 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
526 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
527 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
528 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
530 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
531 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
532 if (!load_params.virtual_url_for_data_url.is_empty()) {
533 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
535 if (NavigationController::UA_OVERRIDE_INHERIT !=
536 load_params.override_user_agent) {
537 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
538 load_params.override_user_agent);
539 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
541 EXPECT_EQ(load_params.browser_initiated_post_data.get(),
542 entry->GetBrowserInitiatedPostData());
543 EXPECT_EQ(load_params.transferred_global_request_id,
544 entry->transferred_global_request_id());
547 TEST_F(NavigationControllerTest, LoadURLWithParams) {
548 NavigationControllerImpl& controller = controller_impl();
550 NavigationController::LoadURLParams load_params(GURL("http://foo"));
551 load_params.referrer =
552 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
553 load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
554 load_params.extra_headers = "content-type: text/plain";
555 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
556 load_params.is_renderer_initiated = true;
557 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
558 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
560 controller.LoadURLWithParams(load_params);
561 NavigationEntryImpl* entry = controller.GetPendingEntry();
563 // The timestamp should not have been set yet.
564 ASSERT_TRUE(entry);
565 EXPECT_TRUE(entry->GetTimestamp().is_null());
567 CheckNavigationEntryMatchLoadParams(load_params, entry);
570 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
571 NavigationControllerImpl& controller = controller_impl();
573 NavigationController::LoadURLParams load_params(
574 GURL("data:text/html,dataurl"));
575 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
576 load_params.base_url_for_data_url = GURL("http://foo");
577 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
578 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
580 controller.LoadURLWithParams(load_params);
581 NavigationEntryImpl* entry = controller.GetPendingEntry();
583 CheckNavigationEntryMatchLoadParams(load_params, entry);
586 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
587 NavigationControllerImpl& controller = controller_impl();
589 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
590 load_params.transition_type = ui::PAGE_TRANSITION_TYPED;
591 load_params.load_type =
592 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
593 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
596 const unsigned char* raw_data =
597 reinterpret_cast<const unsigned char*>("d\n\0a2");
598 const int length = 5;
599 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
600 scoped_refptr<base::RefCountedBytes> data =
601 base::RefCountedBytes::TakeVector(&post_data_vector);
602 load_params.browser_initiated_post_data = data.get();
604 controller.LoadURLWithParams(load_params);
605 NavigationEntryImpl* entry = controller.GetPendingEntry();
607 CheckNavigationEntryMatchLoadParams(load_params, entry);
610 // Tests what happens when the same page is loaded again. Should not create a
611 // new session history entry. This is what happens when you press enter in the
612 // URL bar to reload: a pending entry is created and then it is discarded when
613 // the load commits (because WebCore didn't actually make a new entry).
614 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
615 NavigationControllerImpl& controller = controller_impl();
616 TestNotificationTracker notifications;
617 RegisterForAllNavNotifications(&notifications, &controller);
619 const GURL url1("http://foo1");
621 controller.LoadURL(
622 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
623 EXPECT_EQ(0U, notifications.size());
624 main_test_rfh()->PrepareForCommit();
625 main_test_rfh()->SendNavigate(0, url1);
626 EXPECT_EQ(1U, navigation_entry_committed_counter_);
627 navigation_entry_committed_counter_ = 0;
629 ASSERT_TRUE(controller.GetVisibleEntry());
630 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
631 EXPECT_FALSE(timestamp.is_null());
633 controller.LoadURL(
634 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
635 EXPECT_EQ(0U, notifications.size());
636 main_test_rfh()->PrepareForCommit();
637 main_test_rfh()->SendNavigate(0, url1);
638 EXPECT_EQ(1U, navigation_entry_committed_counter_);
639 navigation_entry_committed_counter_ = 0;
641 // We should not have produced a new session history entry.
642 EXPECT_EQ(controller.GetEntryCount(), 1);
643 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
644 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
645 EXPECT_TRUE(controller.GetLastCommittedEntry());
646 EXPECT_FALSE(controller.GetPendingEntry());
647 ASSERT_TRUE(controller.GetVisibleEntry());
648 EXPECT_FALSE(controller.CanGoBack());
649 EXPECT_FALSE(controller.CanGoForward());
651 // The timestamp should have been updated.
653 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
654 // EXPECT_GT once we guarantee that timestamps are unique.
655 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
658 // Load the same page twice, once as a GET and once as a POST.
659 // We should update the post state on the NavigationEntry.
660 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
661 NavigationControllerImpl& controller = controller_impl();
662 TestNotificationTracker notifications;
663 RegisterForAllNavNotifications(&notifications, &controller);
665 const GURL url1("http://foo1");
667 controller.LoadURL(
668 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
669 FrameHostMsg_DidCommitProvisionalLoad_Params params;
670 params.page_id = 0;
671 params.url = url1;
672 params.transition = ui::PAGE_TRANSITION_TYPED;
673 params.is_post = true;
674 params.post_id = 123;
675 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
676 main_test_rfh()->PrepareForCommit();
677 main_test_rfh()->SendNavigateWithParams(&params);
679 // The post data should be visible.
680 NavigationEntry* entry = controller.GetVisibleEntry();
681 ASSERT_TRUE(entry);
682 EXPECT_TRUE(entry->GetHasPostData());
683 EXPECT_EQ(entry->GetPostID(), 123);
685 controller.LoadURL(
686 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
687 main_test_rfh()->PrepareForCommit();
688 main_test_rfh()->SendNavigate(0, url1);
690 // We should not have produced a new session history entry.
691 ASSERT_EQ(controller.GetVisibleEntry(), entry);
693 // The post data should have been cleared due to the GET.
694 EXPECT_FALSE(entry->GetHasPostData());
695 EXPECT_EQ(entry->GetPostID(), 0);
698 // Tests loading a URL but discarding it before the load commits.
699 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
700 NavigationControllerImpl& controller = controller_impl();
701 TestNotificationTracker notifications;
702 RegisterForAllNavNotifications(&notifications, &controller);
704 const GURL url1("http://foo1");
705 const GURL url2("http://foo2");
707 controller.LoadURL(
708 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
709 EXPECT_EQ(0U, notifications.size());
710 main_test_rfh()->SendNavigate(0, url1);
711 EXPECT_EQ(1U, navigation_entry_committed_counter_);
712 navigation_entry_committed_counter_ = 0;
714 ASSERT_TRUE(controller.GetVisibleEntry());
715 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
716 EXPECT_FALSE(timestamp.is_null());
718 controller.LoadURL(
719 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
720 controller.DiscardNonCommittedEntries();
721 EXPECT_EQ(0U, notifications.size());
723 // Should not have produced a new session history entry.
724 EXPECT_EQ(controller.GetEntryCount(), 1);
725 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
726 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
727 EXPECT_TRUE(controller.GetLastCommittedEntry());
728 EXPECT_FALSE(controller.GetPendingEntry());
729 ASSERT_TRUE(controller.GetVisibleEntry());
730 EXPECT_FALSE(controller.CanGoBack());
731 EXPECT_FALSE(controller.CanGoForward());
733 // Timestamp should not have changed.
734 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
737 // Tests navigations that come in unrequested. This happens when the user
738 // navigates from the web page, and here we test that there is no pending entry.
739 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
740 NavigationControllerImpl& controller = controller_impl();
741 TestNotificationTracker notifications;
742 RegisterForAllNavNotifications(&notifications, &controller);
744 // First make an existing committed entry.
745 const GURL kExistingURL1("http://eh");
746 controller.LoadURL(
747 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
748 main_test_rfh()->SendNavigate(0, kExistingURL1);
749 EXPECT_EQ(1U, navigation_entry_committed_counter_);
750 navigation_entry_committed_counter_ = 0;
752 // Do a new navigation without making a pending one.
753 const GURL kNewURL("http://see");
754 main_test_rfh()->SendNavigate(99, kNewURL);
756 // There should no longer be any pending entry, and the third navigation we
757 // just made should be committed.
758 EXPECT_EQ(1U, navigation_entry_committed_counter_);
759 navigation_entry_committed_counter_ = 0;
760 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
761 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
762 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
765 // Tests navigating to a new URL when there is a new pending navigation that is
766 // not the one that just loaded. This will happen if the user types in a URL to
767 // somewhere slow, and then navigates the current page before the typed URL
768 // commits.
769 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
770 NavigationControllerImpl& controller = controller_impl();
771 TestNotificationTracker notifications;
772 RegisterForAllNavNotifications(&notifications, &controller);
774 // First make an existing committed entry.
775 const GURL kExistingURL1("http://eh");
776 controller.LoadURL(
777 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
778 main_test_rfh()->PrepareForCommit();
779 main_test_rfh()->SendNavigate(0, kExistingURL1);
780 EXPECT_EQ(1U, navigation_entry_committed_counter_);
781 navigation_entry_committed_counter_ = 0;
783 // Make a pending entry to somewhere new.
784 const GURL kExistingURL2("http://bee");
785 controller.LoadURL(
786 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
787 EXPECT_EQ(0U, notifications.size());
789 // After the beforeunload but before it commits, do a new navigation.
790 main_test_rfh()->PrepareForCommit();
791 const GURL kNewURL("http://see");
792 main_test_rfh()->PrepareForCommit();
793 contents()->GetMainFrame()->SendNavigate(3, kNewURL);
795 // There should no longer be any pending entry, and the third navigation we
796 // just made should be committed.
797 EXPECT_EQ(1U, navigation_entry_committed_counter_);
798 navigation_entry_committed_counter_ = 0;
799 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
800 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
801 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
804 // Tests navigating to a new URL when there is a pending back/forward
805 // navigation. This will happen if the user hits back, but before that commits,
806 // they navigate somewhere new.
807 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
808 NavigationControllerImpl& controller = controller_impl();
809 TestNotificationTracker notifications;
810 RegisterForAllNavNotifications(&notifications, &controller);
812 // First make some history.
813 const GURL kExistingURL1("http://foo/eh");
814 controller.LoadURL(
815 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
816 main_test_rfh()->PrepareForCommit();
817 main_test_rfh()->SendNavigate(0, kExistingURL1);
818 EXPECT_EQ(1U, navigation_entry_committed_counter_);
819 navigation_entry_committed_counter_ = 0;
821 const GURL kExistingURL2("http://foo/bee");
822 controller.LoadURL(
823 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
824 main_test_rfh()->PrepareForCommit();
825 main_test_rfh()->SendNavigate(1, kExistingURL2);
826 EXPECT_EQ(1U, navigation_entry_committed_counter_);
827 navigation_entry_committed_counter_ = 0;
829 // Now make a pending back/forward navigation. The zeroth entry should be
830 // pending.
831 controller.GoBack();
832 EXPECT_EQ(0U, notifications.size());
833 EXPECT_EQ(0, controller.GetPendingEntryIndex());
834 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
836 // Before that commits, do a new navigation.
837 const GURL kNewURL("http://foo/see");
838 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL, true);
839 main_test_rfh()->PrepareForCommit();
840 main_test_rfh()->SendNavigate(3, kNewURL);
842 // There should no longer be any pending entry, and the third navigation we
843 // just made should be committed.
844 EXPECT_EQ(1U, navigation_entry_committed_counter_);
845 navigation_entry_committed_counter_ = 0;
846 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
847 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
848 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
851 // Tests navigating to a new URL when there is a pending back/forward
852 // navigation to a cross-process, privileged URL. This will happen if the user
853 // hits back, but before that commits, they navigate somewhere new.
854 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
855 NavigationControllerImpl& controller = controller_impl();
856 TestNotificationTracker notifications;
857 RegisterForAllNavNotifications(&notifications, &controller);
859 // First make some history, starting with a privileged URL.
860 const GURL kExistingURL1("http://privileged");
861 controller.LoadURL(
862 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
863 // Pretend it has bindings so we can tell if we incorrectly copy it.
864 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
865 main_test_rfh()->PrepareForCommit();
866 main_test_rfh()->SendNavigate(0, kExistingURL1);
867 EXPECT_EQ(1U, navigation_entry_committed_counter_);
868 navigation_entry_committed_counter_ = 0;
870 // Navigate cross-process to a second URL.
871 const GURL kExistingURL2("http://foo/eh");
872 controller.LoadURL(
873 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
874 main_test_rfh()->PrepareForCommit();
875 TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame();
876 foo_rfh->SendNavigate(1, kExistingURL2);
877 EXPECT_EQ(1U, navigation_entry_committed_counter_);
878 navigation_entry_committed_counter_ = 0;
880 // Now make a pending back/forward navigation to a privileged entry.
881 // The zeroth entry should be pending.
882 controller.GoBack();
883 foo_rfh->SendBeforeUnloadACK(true);
884 EXPECT_EQ(0U, notifications.size());
885 EXPECT_EQ(0, controller.GetPendingEntryIndex());
886 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
887 EXPECT_EQ(2, controller.GetPendingEntry()->bindings());
889 // Before that commits, do a new navigation.
890 const GURL kNewURL("http://foo/bee");
891 foo_rfh->SendRendererInitiatedNavigationRequest(kNewURL, true);
892 foo_rfh->PrepareForCommit();
893 foo_rfh->SendNavigate(3, kNewURL);
895 // There should no longer be any pending entry, and the third navigation we
896 // just made should be committed.
897 EXPECT_EQ(1U, navigation_entry_committed_counter_);
898 navigation_entry_committed_counter_ = 0;
899 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
900 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
901 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
902 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
905 // Tests navigating to an existing URL when there is a pending new navigation.
906 // This will happen if the user enters a URL, but before that commits, the
907 // current page fires history.back().
908 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
909 NavigationControllerImpl& controller = controller_impl();
910 TestNotificationTracker notifications;
911 RegisterForAllNavNotifications(&notifications, &controller);
913 // First make some history.
914 const GURL kExistingURL1("http://foo/eh");
915 controller.LoadURL(
916 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
917 main_test_rfh()->PrepareForCommit();
918 main_test_rfh()->SendNavigate(0, kExistingURL1);
919 EXPECT_EQ(1U, navigation_entry_committed_counter_);
920 navigation_entry_committed_counter_ = 0;
922 const GURL kExistingURL2("http://foo/bee");
923 controller.LoadURL(
924 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
925 main_test_rfh()->PrepareForCommit();
926 main_test_rfh()->SendNavigate(1, kExistingURL2);
927 EXPECT_EQ(1U, navigation_entry_committed_counter_);
928 navigation_entry_committed_counter_ = 0;
930 // Now make a pending new navigation.
931 const GURL kNewURL("http://foo/see");
932 controller.LoadURL(
933 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
934 EXPECT_EQ(0U, notifications.size());
935 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
936 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
938 // Before that commits, a back navigation from the renderer commits.
939 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL1, true);
940 main_test_rfh()->PrepareForCommit();
941 main_test_rfh()->SendNavigate(0, kExistingURL1);
943 // There should no longer be any pending entry, and the back navigation we
944 // just made should be committed.
945 EXPECT_EQ(1U, navigation_entry_committed_counter_);
946 navigation_entry_committed_counter_ = 0;
947 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
948 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
949 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
952 // Tests an ignored navigation when there is a pending new navigation.
953 // This will happen if the user enters a URL, but before that commits, the
954 // current blank page reloads. See http://crbug.com/77507.
955 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
956 NavigationControllerImpl& controller = controller_impl();
957 TestNotificationTracker notifications;
958 RegisterForAllNavNotifications(&notifications, &controller);
960 // Set a WebContentsDelegate to listen for state changes.
961 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
962 EXPECT_FALSE(contents()->GetDelegate());
963 contents()->SetDelegate(delegate.get());
965 // Without any navigations, the renderer starts at about:blank.
966 const GURL kExistingURL(url::kAboutBlankURL);
968 // Now make a pending new navigation.
969 const GURL kNewURL("http://eh");
970 controller.LoadURL(
971 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
972 EXPECT_EQ(0U, notifications.size());
973 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
974 EXPECT_TRUE(controller.GetPendingEntry());
975 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
976 EXPECT_EQ(1, delegate->navigation_state_change_count());
978 // Before that commits, a document.write and location.reload can cause the
979 // renderer to send a FrameNavigate with page_id -1.
980 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL, true);
981 main_test_rfh()->PrepareForCommit();
982 main_test_rfh()->SendNavigate(-1, kExistingURL);
984 // This should clear the pending entry and notify of a navigation state
985 // change, so that we do not keep displaying kNewURL.
986 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
987 EXPECT_FALSE(controller.GetPendingEntry());
988 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
989 EXPECT_EQ(2, delegate->navigation_state_change_count());
991 contents()->SetDelegate(NULL);
994 // Tests that the pending entry state is correct after an abort.
995 // We do not want to clear the pending entry, so that the user doesn't
996 // lose a typed URL. (See http://crbug.com/9682.)
997 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
998 NavigationControllerImpl& controller = controller_impl();
999 TestNotificationTracker notifications;
1000 RegisterForAllNavNotifications(&notifications, &controller);
1002 // Set a WebContentsDelegate to listen for state changes.
1003 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1004 EXPECT_FALSE(contents()->GetDelegate());
1005 contents()->SetDelegate(delegate.get());
1007 // Start with a pending new navigation.
1008 const GURL kNewURL("http://eh");
1009 controller.LoadURL(
1010 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1011 EXPECT_EQ(0U, notifications.size());
1012 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1013 EXPECT_TRUE(controller.GetPendingEntry());
1014 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1015 EXPECT_EQ(1, delegate->navigation_state_change_count());
1017 // It may abort before committing, if it's a download or due to a stop or
1018 // a new navigation from the user.
1019 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1020 params.error_code = net::ERR_ABORTED;
1021 params.error_description = base::string16();
1022 params.url = kNewURL;
1023 params.showing_repost_interstitial = false;
1024 main_test_rfh()->OnMessageReceived(
1025 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1026 params));
1028 // This should not clear the pending entry or notify of a navigation state
1029 // change, so that we keep displaying kNewURL (until the user clears it).
1030 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1031 EXPECT_TRUE(controller.GetPendingEntry());
1032 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1033 EXPECT_EQ(1, delegate->navigation_state_change_count());
1034 NavigationEntry* pending_entry = controller.GetPendingEntry();
1036 // Ensure that a reload keeps the same pending entry.
1037 controller.Reload(true);
1038 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1039 EXPECT_TRUE(controller.GetPendingEntry());
1040 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
1041 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1043 contents()->SetDelegate(NULL);
1046 // Tests that the pending URL is not visible during a renderer-initiated
1047 // redirect and abort. See http://crbug.com/83031.
1048 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
1049 NavigationControllerImpl& controller = controller_impl();
1050 TestNotificationTracker notifications;
1051 RegisterForAllNavNotifications(&notifications, &controller);
1053 // First make an existing committed entry.
1054 const GURL kExistingURL("http://foo/eh");
1055 controller.LoadURL(kExistingURL, content::Referrer(),
1056 ui::PAGE_TRANSITION_TYPED, std::string());
1057 main_test_rfh()->SendNavigate(1, kExistingURL);
1058 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1059 navigation_entry_committed_counter_ = 0;
1061 // Set a WebContentsDelegate to listen for state changes.
1062 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1063 EXPECT_FALSE(contents()->GetDelegate());
1064 contents()->SetDelegate(delegate.get());
1066 // Now make a pending new navigation, initiated by the renderer.
1067 const GURL kNewURL("http://foo/bee");
1068 NavigationController::LoadURLParams load_url_params(kNewURL);
1069 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
1070 load_url_params.is_renderer_initiated = true;
1071 controller.LoadURLWithParams(load_url_params);
1072 EXPECT_EQ(0U, notifications.size());
1073 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1074 EXPECT_TRUE(controller.GetPendingEntry());
1075 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1076 EXPECT_EQ(0, delegate->navigation_state_change_count());
1078 // The visible entry should be the last committed URL, not the pending one.
1079 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1081 // Now the navigation redirects. (There is no corresponding message here.)
1082 const GURL kRedirectURL("http://foo/see");
1084 // We don't want to change the NavigationEntry's url, in case it cancels.
1085 // Prevents regression of http://crbug.com/77786.
1086 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1088 // It may abort before committing, if it's a download or due to a stop or
1089 // a new navigation from the user.
1090 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1091 params.error_code = net::ERR_ABORTED;
1092 params.error_description = base::string16();
1093 params.url = kRedirectURL;
1094 params.showing_repost_interstitial = false;
1095 main_test_rfh()->OnMessageReceived(
1096 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1097 params));
1099 // Because the pending entry is renderer initiated and not visible, we
1100 // clear it when it fails.
1101 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1102 EXPECT_FALSE(controller.GetPendingEntry());
1103 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1104 EXPECT_EQ(1, delegate->navigation_state_change_count());
1106 // The visible entry should be the last committed URL, not the pending one,
1107 // so that no spoof is possible.
1108 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1110 contents()->SetDelegate(NULL);
1113 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1114 // at the time they committed. http://crbug.com/173672.
1115 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1116 NavigationControllerImpl& controller = controller_impl();
1117 TestNotificationTracker notifications;
1118 RegisterForAllNavNotifications(&notifications, &controller);
1119 std::vector<GURL> url_chain;
1121 const GURL url1("http://foo1");
1122 const GURL url2("http://foo2");
1124 // Navigate to a first, unprivileged URL.
1125 controller.LoadURL(
1126 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1127 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1128 controller.GetPendingEntry()->bindings());
1130 // Commit.
1131 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1132 orig_rfh->PrepareForCommit();
1133 orig_rfh->SendNavigate(0, url1);
1134 EXPECT_EQ(controller.GetEntryCount(), 1);
1135 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1136 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1138 // Manually increase the number of active frames in the SiteInstance
1139 // that orig_rfh belongs to, to prevent it from being destroyed when
1140 // it gets swapped out, so that we can reuse orig_rfh when the
1141 // controller goes back.
1142 orig_rfh->GetSiteInstance()->increment_active_frame_count();
1144 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1145 // transition, and set bindings on the pending RenderViewHost to simulate a
1146 // privileged url.
1147 controller.LoadURL(
1148 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1149 orig_rfh->PrepareForCommit();
1150 TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
1151 new_rfh->GetRenderViewHost()->AllowBindings(1);
1152 new_rfh->SendNavigate(1, url2);
1154 // The second load should be committed, and bindings should be remembered.
1155 EXPECT_EQ(controller.GetEntryCount(), 2);
1156 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1157 EXPECT_TRUE(controller.CanGoBack());
1158 EXPECT_EQ(1, controller.GetLastCommittedEntry()->bindings());
1160 // Going back, the first entry should still appear unprivileged.
1161 controller.GoBack();
1162 new_rfh->PrepareForCommit();
1163 orig_rfh->SendNavigate(0, url1);
1164 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1165 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1168 TEST_F(NavigationControllerTest, Reload) {
1169 NavigationControllerImpl& controller = controller_impl();
1170 TestNotificationTracker notifications;
1171 RegisterForAllNavNotifications(&notifications, &controller);
1173 const GURL url1("http://foo1");
1175 controller.LoadURL(
1176 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1177 EXPECT_EQ(0U, notifications.size());
1178 main_test_rfh()->PrepareForCommit();
1179 main_test_rfh()->SendNavigate(0, url1);
1180 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1181 navigation_entry_committed_counter_ = 0;
1182 ASSERT_TRUE(controller.GetVisibleEntry());
1183 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1184 controller.Reload(true);
1185 EXPECT_EQ(0U, notifications.size());
1187 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1188 EXPECT_FALSE(timestamp.is_null());
1190 // The reload is pending.
1191 EXPECT_EQ(controller.GetEntryCount(), 1);
1192 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1193 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1194 EXPECT_TRUE(controller.GetLastCommittedEntry());
1195 EXPECT_TRUE(controller.GetPendingEntry());
1196 EXPECT_FALSE(controller.CanGoBack());
1197 EXPECT_FALSE(controller.CanGoForward());
1198 // Make sure the title has been cleared (will be redrawn just after reload).
1199 // Avoids a stale cached title when the new page being reloaded has no title.
1200 // See http://crbug.com/96041.
1201 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1203 main_test_rfh()->PrepareForCommit();
1204 main_test_rfh()->SendNavigate(0, url1);
1205 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1206 navigation_entry_committed_counter_ = 0;
1208 // Now the reload is committed.
1209 EXPECT_EQ(controller.GetEntryCount(), 1);
1210 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1211 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1212 EXPECT_TRUE(controller.GetLastCommittedEntry());
1213 EXPECT_FALSE(controller.GetPendingEntry());
1214 EXPECT_FALSE(controller.CanGoBack());
1215 EXPECT_FALSE(controller.CanGoForward());
1217 // The timestamp should have been updated.
1218 ASSERT_TRUE(controller.GetVisibleEntry());
1219 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1222 // Tests what happens when a reload navigation produces a new page.
1223 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1224 NavigationControllerImpl& controller = controller_impl();
1225 TestNotificationTracker notifications;
1226 RegisterForAllNavNotifications(&notifications, &controller);
1228 const GURL url1("http://foo1");
1229 const GURL url2("http://foo2");
1231 controller.LoadURL(
1232 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1233 main_test_rfh()->PrepareForCommit();
1234 main_test_rfh()->SendNavigate(0, url1);
1235 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1236 navigation_entry_committed_counter_ = 0;
1238 controller.Reload(true);
1239 EXPECT_EQ(0U, notifications.size());
1241 main_test_rfh()->PrepareForCommitWithServerRedirect(url2);
1242 main_test_rfh()->SendNavigate(1, url2);
1243 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1244 navigation_entry_committed_counter_ = 0;
1246 // Now the reload is committed.
1247 EXPECT_EQ(controller.GetEntryCount(), 2);
1248 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1249 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1250 EXPECT_TRUE(controller.GetLastCommittedEntry());
1251 EXPECT_FALSE(controller.GetPendingEntry());
1252 EXPECT_TRUE(controller.CanGoBack());
1253 EXPECT_FALSE(controller.CanGoForward());
1256 // This test ensures that when a guest renderer reloads, the reload goes through
1257 // without ending up in the "we have a wrong process for the URL" branch in
1258 // NavigationControllerImpl::ReloadInternal.
1259 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1260 NavigationControllerImpl& controller = controller_impl();
1262 const GURL url1("http://foo1");
1263 controller.LoadURL(
1264 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1265 main_test_rfh()->SendNavigate(0, url1);
1266 ASSERT_TRUE(controller.GetVisibleEntry());
1268 // Make the entry believe its RenderProcessHost is a guest.
1269 NavigationEntryImpl* entry1 = controller.GetVisibleEntry();
1270 reinterpret_cast<MockRenderProcessHost*>(
1271 entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
1273 // And reload.
1274 controller.Reload(true);
1276 // The reload is pending. Check that the NavigationEntry didn't get replaced
1277 // because of having the wrong process.
1278 EXPECT_EQ(controller.GetEntryCount(), 1);
1279 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1280 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1282 NavigationEntryImpl* entry2 = controller.GetPendingEntry();
1283 EXPECT_EQ(entry1, entry2);
1286 #if !defined(OS_ANDROID) // http://crbug.com/157428
1287 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1288 NavigationControllerImpl& controller = controller_impl();
1289 TestNotificationTracker notifications;
1290 RegisterForAllNavNotifications(&notifications, &controller);
1292 const GURL original_url("http://foo1");
1293 const GURL final_url("http://foo2");
1295 // Load up the original URL, but get redirected.
1296 controller.LoadURL(
1297 original_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1298 EXPECT_EQ(0U, notifications.size());
1299 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
1300 main_test_rfh()->SendNavigateWithOriginalRequestURL(
1301 0, final_url, original_url);
1302 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1303 navigation_entry_committed_counter_ = 0;
1305 // The NavigationEntry should save both the original URL and the final
1306 // redirected URL.
1307 EXPECT_EQ(
1308 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1309 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1311 // Reload using the original URL.
1312 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1313 controller.ReloadOriginalRequestURL(false);
1314 EXPECT_EQ(0U, notifications.size());
1316 // The reload is pending. The request should point to the original URL.
1317 EXPECT_EQ(original_url, navigated_url());
1318 EXPECT_EQ(controller.GetEntryCount(), 1);
1319 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1320 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1321 EXPECT_TRUE(controller.GetLastCommittedEntry());
1322 EXPECT_TRUE(controller.GetPendingEntry());
1323 EXPECT_FALSE(controller.CanGoBack());
1324 EXPECT_FALSE(controller.CanGoForward());
1326 // Make sure the title has been cleared (will be redrawn just after reload).
1327 // Avoids a stale cached title when the new page being reloaded has no title.
1328 // See http://crbug.com/96041.
1329 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1331 // Send that the navigation has proceeded; say it got redirected again.
1332 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
1333 main_test_rfh()->SendNavigate(0, final_url);
1334 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1335 navigation_entry_committed_counter_ = 0;
1337 // Now the reload is committed.
1338 EXPECT_EQ(controller.GetEntryCount(), 1);
1339 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1340 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1341 EXPECT_TRUE(controller.GetLastCommittedEntry());
1342 EXPECT_FALSE(controller.GetPendingEntry());
1343 EXPECT_FALSE(controller.CanGoBack());
1344 EXPECT_FALSE(controller.CanGoForward());
1347 #endif // !defined(OS_ANDROID)
1349 // Test that certain non-persisted NavigationEntryImpl values get reset after
1350 // commit.
1351 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1352 NavigationControllerImpl& controller = controller_impl();
1354 // The value of "should replace entry" will be tested, but it's an error to
1355 // specify it when there are no entries. Create a simple entry to be replaced.
1356 const GURL url0("http://foo/0");
1357 controller.LoadURL(
1358 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1359 main_test_rfh()->SendNavigate(0, url0);
1361 // Set up the pending entry.
1362 const GURL url1("http://foo/1");
1363 controller.LoadURL(
1364 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1366 // Set up some sample values.
1367 const unsigned char* raw_data =
1368 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1369 const int length = 11;
1370 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1371 scoped_refptr<base::RefCountedBytes> post_data =
1372 base::RefCountedBytes::TakeVector(&post_data_vector);
1373 GlobalRequestID transfer_id(3, 4);
1375 // Set non-persisted values on the pending entry.
1376 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1377 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1378 pending_entry->set_is_renderer_initiated(true);
1379 pending_entry->set_transferred_global_request_id(transfer_id);
1380 pending_entry->set_should_replace_entry(true);
1381 pending_entry->set_should_clear_history_list(true);
1382 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1383 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1384 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1385 EXPECT_TRUE(pending_entry->should_replace_entry());
1386 EXPECT_TRUE(pending_entry->should_clear_history_list());
1388 // Fake a commit response.
1389 main_test_rfh()->SendNavigate(1, url1);
1391 // Certain values that are only used for pending entries get reset after
1392 // commit.
1393 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1394 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1395 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1396 EXPECT_EQ(GlobalRequestID(-1, -1),
1397 committed_entry->transferred_global_request_id());
1398 EXPECT_FALSE(committed_entry->should_replace_entry());
1399 EXPECT_FALSE(committed_entry->should_clear_history_list());
1402 // Test that Redirects are preserved after a commit.
1403 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1404 NavigationControllerImpl& controller = controller_impl();
1405 const GURL url1("http://foo1");
1406 controller.LoadURL(
1407 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1409 // Set up some redirect values.
1410 std::vector<GURL> redirects;
1411 redirects.push_back(GURL("http://foo2"));
1413 // Set redirects on the pending entry.
1414 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1415 pending_entry->SetRedirectChain(redirects);
1416 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1417 EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
1419 // Normal navigation will preserve redirects in the committed entry.
1420 main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
1421 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1422 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1423 EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
1426 // Tests what happens when we navigate back successfully
1427 TEST_F(NavigationControllerTest, Back) {
1428 NavigationControllerImpl& controller = controller_impl();
1429 TestNotificationTracker notifications;
1430 RegisterForAllNavNotifications(&notifications, &controller);
1432 const GURL url1("http://foo1");
1433 main_test_rfh()->SendNavigate(0, url1);
1434 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1435 navigation_entry_committed_counter_ = 0;
1437 const GURL url2("http://foo2");
1438 main_test_rfh()->SendNavigate(1, url2);
1439 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1440 navigation_entry_committed_counter_ = 0;
1442 controller.GoBack();
1443 EXPECT_EQ(0U, notifications.size());
1445 // We should now have a pending navigation to go back.
1446 EXPECT_EQ(controller.GetEntryCount(), 2);
1447 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1448 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1449 EXPECT_TRUE(controller.GetLastCommittedEntry());
1450 EXPECT_TRUE(controller.GetPendingEntry());
1451 EXPECT_FALSE(controller.CanGoBack());
1452 EXPECT_FALSE(controller.CanGoToOffset(-1));
1453 EXPECT_TRUE(controller.CanGoForward());
1454 EXPECT_TRUE(controller.CanGoToOffset(1));
1455 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1457 // Timestamp for entry 1 should be on or after that of entry 0.
1458 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1459 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1460 controller.GetEntryAtIndex(0)->GetTimestamp());
1462 main_test_rfh()->SendNavigate(0, url2);
1463 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1464 navigation_entry_committed_counter_ = 0;
1466 // The back navigation completed successfully.
1467 EXPECT_EQ(controller.GetEntryCount(), 2);
1468 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1469 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1470 EXPECT_TRUE(controller.GetLastCommittedEntry());
1471 EXPECT_FALSE(controller.GetPendingEntry());
1472 EXPECT_FALSE(controller.CanGoBack());
1473 EXPECT_FALSE(controller.CanGoToOffset(-1));
1474 EXPECT_TRUE(controller.CanGoForward());
1475 EXPECT_TRUE(controller.CanGoToOffset(1));
1476 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1478 // Timestamp for entry 0 should be on or after that of entry 1
1479 // (since we went back to it).
1480 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1481 controller.GetEntryAtIndex(1)->GetTimestamp());
1484 // Tests what happens when a back navigation produces a new page.
1485 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1486 NavigationControllerImpl& controller = controller_impl();
1487 TestNotificationTracker notifications;
1488 RegisterForAllNavNotifications(&notifications, &controller);
1490 const GURL url1("http://foo/1");
1491 const GURL url2("http://foo/2");
1492 const GURL url3("http://foo/3");
1494 controller.LoadURL(
1495 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1496 main_test_rfh()->PrepareForCommit();
1497 main_test_rfh()->SendNavigate(0, url1);
1498 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1499 navigation_entry_committed_counter_ = 0;
1501 controller.LoadURL(
1502 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1503 main_test_rfh()->PrepareForCommit();
1504 main_test_rfh()->SendNavigate(1, url2);
1505 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1506 navigation_entry_committed_counter_ = 0;
1508 controller.GoBack();
1509 EXPECT_EQ(0U, notifications.size());
1511 // We should now have a pending navigation to go back.
1512 EXPECT_EQ(controller.GetEntryCount(), 2);
1513 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1514 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1515 EXPECT_TRUE(controller.GetLastCommittedEntry());
1516 EXPECT_TRUE(controller.GetPendingEntry());
1517 EXPECT_FALSE(controller.CanGoBack());
1518 EXPECT_TRUE(controller.CanGoForward());
1520 main_test_rfh()->PrepareForCommitWithServerRedirect(url3);
1521 main_test_rfh()->SendNavigate(2, url3);
1522 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1523 navigation_entry_committed_counter_ = 0;
1525 // The back navigation resulted in a completely new navigation.
1526 // TODO(darin): perhaps this behavior will be confusing to users?
1527 EXPECT_EQ(controller.GetEntryCount(), 3);
1528 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1529 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1530 EXPECT_TRUE(controller.GetLastCommittedEntry());
1531 EXPECT_FALSE(controller.GetPendingEntry());
1532 EXPECT_TRUE(controller.CanGoBack());
1533 EXPECT_FALSE(controller.CanGoForward());
1536 // Receives a back message when there is a new pending navigation entry.
1537 TEST_F(NavigationControllerTest, Back_NewPending) {
1538 NavigationControllerImpl& controller = controller_impl();
1539 TestNotificationTracker notifications;
1540 RegisterForAllNavNotifications(&notifications, &controller);
1542 const GURL kUrl1("http://foo1");
1543 const GURL kUrl2("http://foo2");
1544 const GURL kUrl3("http://foo3");
1546 // First navigate two places so we have some back history.
1547 main_test_rfh()->SendNavigate(0, kUrl1);
1548 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1549 navigation_entry_committed_counter_ = 0;
1551 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1552 main_test_rfh()->SendNavigate(1, kUrl2);
1553 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1554 navigation_entry_committed_counter_ = 0;
1556 // Now start a new pending navigation and go back before it commits.
1557 controller.LoadURL(
1558 kUrl3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1559 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1560 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1561 controller.GoBack();
1563 // The pending navigation should now be the "back" item and the new one
1564 // should be gone.
1565 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1566 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1569 // Receives a back message when there is a different renavigation already
1570 // pending.
1571 TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1572 NavigationControllerImpl& controller = controller_impl();
1573 const GURL kUrl1("http://foo/1");
1574 const GURL kUrl2("http://foo/2");
1575 const GURL kUrl3("http://foo/3");
1577 // First navigate three places so we have some back history.
1578 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1, true);
1579 main_test_rfh()->PrepareForCommit();
1580 main_test_rfh()->SendNavigate(0, kUrl1);
1581 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, true);
1582 main_test_rfh()->PrepareForCommit();
1583 main_test_rfh()->SendNavigate(1, kUrl2);
1584 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl3, true);
1585 main_test_rfh()->PrepareForCommit();
1586 main_test_rfh()->SendNavigate(2, kUrl3);
1588 // With nothing pending, say we get a navigation to the second entry.
1589 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, true);
1590 main_test_rfh()->PrepareForCommit();
1591 main_test_rfh()->SendNavigate(1, kUrl2);
1593 // We know all the entries have the same site instance, so we can just grab
1594 // a random one for looking up other entries.
1595 SiteInstance* site_instance =
1596 controller.GetLastCommittedEntry()->site_instance();
1598 // That second URL should be the last committed and it should have gotten the
1599 // new title.
1600 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1601 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1602 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1604 // Now go forward to the last item again and say it was committed.
1605 controller.GoForward();
1606 main_test_rfh()->PrepareForCommit();
1607 main_test_rfh()->SendNavigate(2, kUrl3);
1609 // Now start going back one to the second page. It will be pending.
1610 controller.GoBack();
1611 EXPECT_EQ(1, controller.GetPendingEntryIndex());
1612 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1614 // Now synthesize a totally new back event to the first page. This will not
1615 // match the pending one.
1616 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1, true);
1617 main_test_rfh()->PrepareForCommit();
1618 main_test_rfh()->SendNavigate(0, kUrl1);
1620 // The committed navigation should clear the pending entry.
1621 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1623 // But the navigated entry should be the last committed.
1624 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1625 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1628 // Tests what happens when we navigate forward successfully.
1629 TEST_F(NavigationControllerTest, Forward) {
1630 NavigationControllerImpl& controller = controller_impl();
1631 TestNotificationTracker notifications;
1632 RegisterForAllNavNotifications(&notifications, &controller);
1634 const GURL url1("http://foo1");
1635 const GURL url2("http://foo2");
1637 main_test_rfh()->PrepareForCommit();
1638 main_test_rfh()->SendNavigate(0, url1);
1639 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1640 navigation_entry_committed_counter_ = 0;
1642 main_test_rfh()->PrepareForCommit();
1643 main_test_rfh()->SendNavigate(1, url2);
1644 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1645 navigation_entry_committed_counter_ = 0;
1647 controller.GoBack();
1648 main_test_rfh()->PrepareForCommit();
1649 main_test_rfh()->SendNavigate(0, url1);
1650 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1651 navigation_entry_committed_counter_ = 0;
1653 controller.GoForward();
1655 // We should now have a pending navigation to go forward.
1656 EXPECT_EQ(controller.GetEntryCount(), 2);
1657 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1658 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1659 EXPECT_TRUE(controller.GetLastCommittedEntry());
1660 EXPECT_TRUE(controller.GetPendingEntry());
1661 EXPECT_TRUE(controller.CanGoBack());
1662 EXPECT_TRUE(controller.CanGoToOffset(-1));
1663 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1664 EXPECT_FALSE(controller.CanGoForward());
1665 EXPECT_FALSE(controller.CanGoToOffset(1));
1667 // Timestamp for entry 0 should be on or after that of entry 1
1668 // (since we went back to it).
1669 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1670 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1671 controller.GetEntryAtIndex(1)->GetTimestamp());
1673 main_test_rfh()->PrepareForCommit();
1674 main_test_rfh()->SendNavigate(1, url2);
1675 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1676 navigation_entry_committed_counter_ = 0;
1678 // The forward navigation completed successfully.
1679 EXPECT_EQ(controller.GetEntryCount(), 2);
1680 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1681 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1682 EXPECT_TRUE(controller.GetLastCommittedEntry());
1683 EXPECT_FALSE(controller.GetPendingEntry());
1684 EXPECT_TRUE(controller.CanGoBack());
1685 EXPECT_TRUE(controller.CanGoToOffset(-1));
1686 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1687 EXPECT_FALSE(controller.CanGoForward());
1688 EXPECT_FALSE(controller.CanGoToOffset(1));
1690 // Timestamp for entry 1 should be on or after that of entry 0
1691 // (since we went forward to it).
1692 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1693 controller.GetEntryAtIndex(0)->GetTimestamp());
1696 // Tests what happens when a forward navigation produces a new page.
1697 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1698 NavigationControllerImpl& controller = controller_impl();
1699 TestNotificationTracker notifications;
1700 RegisterForAllNavNotifications(&notifications, &controller);
1702 const GURL url1("http://foo1");
1703 const GURL url2("http://foo2");
1704 const GURL url3("http://foo3");
1706 main_test_rfh()->PrepareForCommit();
1707 main_test_rfh()->SendNavigate(0, url1);
1708 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1709 navigation_entry_committed_counter_ = 0;
1710 main_test_rfh()->PrepareForCommit();
1711 main_test_rfh()->SendNavigate(1, url2);
1712 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1713 navigation_entry_committed_counter_ = 0;
1715 controller.GoBack();
1716 main_test_rfh()->PrepareForCommit();
1717 main_test_rfh()->SendNavigate(0, url1);
1718 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1719 navigation_entry_committed_counter_ = 0;
1721 controller.GoForward();
1722 EXPECT_EQ(0U, notifications.size());
1724 // Should now have a pending navigation to go forward.
1725 EXPECT_EQ(controller.GetEntryCount(), 2);
1726 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1727 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1728 EXPECT_TRUE(controller.GetLastCommittedEntry());
1729 EXPECT_TRUE(controller.GetPendingEntry());
1730 EXPECT_TRUE(controller.CanGoBack());
1731 EXPECT_FALSE(controller.CanGoForward());
1733 main_test_rfh()->PrepareForCommit();
1734 main_test_rfh()->SendNavigate(2, url3);
1735 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1736 navigation_entry_committed_counter_ = 0;
1737 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1739 EXPECT_EQ(controller.GetEntryCount(), 2);
1740 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1741 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1742 EXPECT_TRUE(controller.GetLastCommittedEntry());
1743 EXPECT_FALSE(controller.GetPendingEntry());
1744 EXPECT_TRUE(controller.CanGoBack());
1745 EXPECT_FALSE(controller.CanGoForward());
1748 // Two consequent navigation for the same URL entered in should be considered
1749 // as SAME_PAGE navigation even when we are redirected to some other page.
1750 TEST_F(NavigationControllerTest, Redirect) {
1751 NavigationControllerImpl& controller = controller_impl();
1752 TestNotificationTracker notifications;
1753 RegisterForAllNavNotifications(&notifications, &controller);
1755 const GURL url1("http://foo1");
1756 const GURL url2("http://foo2"); // Redirection target
1758 // First request
1759 controller.LoadURL(
1760 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1762 EXPECT_EQ(0U, notifications.size());
1763 main_test_rfh()->SendNavigate(0, url2);
1764 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1765 navigation_entry_committed_counter_ = 0;
1767 // Second request
1768 controller.LoadURL(
1769 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1771 EXPECT_TRUE(controller.GetPendingEntry());
1772 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1773 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1775 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1776 params.page_id = 0;
1777 params.url = url2;
1778 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1779 params.redirects.push_back(GURL("http://foo1"));
1780 params.redirects.push_back(GURL("http://foo2"));
1781 params.should_update_history = false;
1782 params.gesture = NavigationGestureAuto;
1783 params.is_post = false;
1784 params.page_state = PageState::CreateFromURL(url2);
1786 LoadCommittedDetails details;
1788 EXPECT_EQ(0U, notifications.size());
1789 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1790 &details));
1791 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1792 navigation_entry_committed_counter_ = 0;
1794 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1795 EXPECT_EQ(controller.GetEntryCount(), 1);
1796 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1797 EXPECT_TRUE(controller.GetLastCommittedEntry());
1798 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1799 EXPECT_FALSE(controller.GetPendingEntry());
1800 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1802 EXPECT_FALSE(controller.CanGoBack());
1803 EXPECT_FALSE(controller.CanGoForward());
1806 // Similar to Redirect above, but the first URL is requested by POST,
1807 // the second URL is requested by GET. NavigationEntry::has_post_data_
1808 // must be cleared. http://crbug.com/21245
1809 TEST_F(NavigationControllerTest, PostThenRedirect) {
1810 NavigationControllerImpl& controller = controller_impl();
1811 TestNotificationTracker notifications;
1812 RegisterForAllNavNotifications(&notifications, &controller);
1814 const GURL url1("http://foo1");
1815 const GURL url2("http://foo2"); // Redirection target
1817 // First request as POST
1818 controller.LoadURL(
1819 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1820 controller.GetVisibleEntry()->SetHasPostData(true);
1822 EXPECT_EQ(0U, notifications.size());
1823 main_test_rfh()->SendNavigate(0, url2);
1824 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1825 navigation_entry_committed_counter_ = 0;
1827 // Second request
1828 controller.LoadURL(
1829 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1831 EXPECT_TRUE(controller.GetPendingEntry());
1832 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1833 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1835 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1836 params.page_id = 0;
1837 params.url = url2;
1838 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1839 params.redirects.push_back(GURL("http://foo1"));
1840 params.redirects.push_back(GURL("http://foo2"));
1841 params.should_update_history = false;
1842 params.gesture = NavigationGestureAuto;
1843 params.is_post = false;
1844 params.page_state = PageState::CreateFromURL(url2);
1846 LoadCommittedDetails details;
1848 EXPECT_EQ(0U, notifications.size());
1849 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1850 &details));
1851 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1852 navigation_entry_committed_counter_ = 0;
1854 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1855 EXPECT_EQ(controller.GetEntryCount(), 1);
1856 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1857 EXPECT_TRUE(controller.GetLastCommittedEntry());
1858 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1859 EXPECT_FALSE(controller.GetPendingEntry());
1860 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1861 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1863 EXPECT_FALSE(controller.CanGoBack());
1864 EXPECT_FALSE(controller.CanGoForward());
1867 // A redirect right off the bat should be a NEW_PAGE.
1868 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1869 NavigationControllerImpl& controller = controller_impl();
1870 TestNotificationTracker notifications;
1871 RegisterForAllNavNotifications(&notifications, &controller);
1873 const GURL url1("http://foo1");
1874 const GURL url2("http://foo2"); // Redirection target
1876 // First request
1877 controller.LoadURL(
1878 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1880 EXPECT_TRUE(controller.GetPendingEntry());
1881 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1882 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1884 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1885 params.page_id = 0;
1886 params.url = url2;
1887 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1888 params.redirects.push_back(GURL("http://foo1"));
1889 params.redirects.push_back(GURL("http://foo2"));
1890 params.should_update_history = false;
1891 params.gesture = NavigationGestureAuto;
1892 params.is_post = false;
1893 params.page_state = PageState::CreateFromURL(url2);
1895 LoadCommittedDetails details;
1897 EXPECT_EQ(0U, notifications.size());
1898 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1899 &details));
1900 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1901 navigation_entry_committed_counter_ = 0;
1903 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1904 EXPECT_EQ(controller.GetEntryCount(), 1);
1905 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1906 EXPECT_TRUE(controller.GetLastCommittedEntry());
1907 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1908 EXPECT_FALSE(controller.GetPendingEntry());
1909 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1911 EXPECT_FALSE(controller.CanGoBack());
1912 EXPECT_FALSE(controller.CanGoForward());
1915 // Tests navigation via link click within a subframe. A new navigation entry
1916 // should be created.
1917 TEST_F(NavigationControllerTest, NewSubframe) {
1918 NavigationControllerImpl& controller = controller_impl();
1919 TestNotificationTracker notifications;
1920 RegisterForAllNavNotifications(&notifications, &controller);
1922 const GURL url1("http://foo1");
1923 main_test_rfh()->SendNavigate(0, url1);
1924 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1925 navigation_entry_committed_counter_ = 0;
1927 const GURL url2("http://foo2");
1928 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1929 params.page_id = 1;
1930 params.url = url2;
1931 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
1932 params.should_update_history = false;
1933 params.gesture = NavigationGestureUser;
1934 params.is_post = false;
1935 params.page_state = PageState::CreateFromURL(url2);
1937 LoadCommittedDetails details;
1938 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1939 &details));
1940 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1941 navigation_entry_committed_counter_ = 0;
1942 EXPECT_EQ(url1, details.previous_url);
1943 EXPECT_FALSE(details.is_in_page);
1944 EXPECT_FALSE(details.is_main_frame);
1946 // The new entry should be appended.
1947 EXPECT_EQ(2, controller.GetEntryCount());
1949 // New entry should refer to the new page, but the old URL (entries only
1950 // reflect the toplevel URL).
1951 EXPECT_EQ(url1, details.entry->GetURL());
1952 EXPECT_EQ(params.page_id, details.entry->GetPageID());
1955 // Some pages create a popup, then write an iframe into it. This causes a
1956 // subframe navigation without having any committed entry. Such navigations
1957 // just get thrown on the ground, but we shouldn't crash.
1958 TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
1959 NavigationControllerImpl& controller = controller_impl();
1960 TestNotificationTracker notifications;
1961 RegisterForAllNavNotifications(&notifications, &controller);
1963 // Navigation controller currently has no entries.
1964 const GURL url("http://foo2");
1965 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1966 params.page_id = 1;
1967 params.url = url;
1968 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
1969 params.should_update_history = false;
1970 params.gesture = NavigationGestureAuto;
1971 params.is_post = false;
1972 params.page_state = PageState::CreateFromURL(url);
1974 LoadCommittedDetails details;
1975 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1976 &details));
1977 EXPECT_EQ(0U, notifications.size());
1980 // Auto subframes are ones the page loads automatically like ads. They should
1981 // not create new navigation entries.
1982 TEST_F(NavigationControllerTest, AutoSubframe) {
1983 NavigationControllerImpl& controller = controller_impl();
1984 TestNotificationTracker notifications;
1985 RegisterForAllNavNotifications(&notifications, &controller);
1987 const GURL url1("http://foo1");
1988 main_test_rfh()->SendNavigate(0, url1);
1989 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1990 navigation_entry_committed_counter_ = 0;
1992 const GURL url2("http://foo2");
1993 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1994 params.page_id = 0;
1995 params.url = url2;
1996 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
1997 params.should_update_history = false;
1998 params.gesture = NavigationGestureUser;
1999 params.is_post = false;
2000 params.page_state = PageState::CreateFromURL(url2);
2002 // Navigating should do nothing.
2003 LoadCommittedDetails details;
2004 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
2005 &details));
2006 EXPECT_EQ(0U, notifications.size());
2008 // There should still be only one entry.
2009 EXPECT_EQ(1, controller.GetEntryCount());
2012 // Tests navigation and then going back to a subframe navigation.
2013 TEST_F(NavigationControllerTest, BackSubframe) {
2014 NavigationControllerImpl& controller = controller_impl();
2015 TestNotificationTracker notifications;
2016 RegisterForAllNavNotifications(&notifications, &controller);
2018 // Main page.
2019 const GURL url1("http://foo1");
2020 main_test_rfh()->SendNavigate(0, url1);
2021 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2022 navigation_entry_committed_counter_ = 0;
2024 // First manual subframe navigation.
2025 const GURL url2("http://foo2");
2026 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2027 params.page_id = 1;
2028 params.url = url2;
2029 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2030 params.should_update_history = false;
2031 params.gesture = NavigationGestureUser;
2032 params.is_post = false;
2033 params.page_state = PageState::CreateFromURL(url2);
2035 // This should generate a new entry.
2036 LoadCommittedDetails details;
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(2, controller.GetEntryCount());
2043 // Second manual subframe navigation should also make a new entry.
2044 const GURL url3("http://foo3");
2045 params.page_id = 2;
2046 params.url = url3;
2047 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2048 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2049 &details));
2050 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2051 navigation_entry_committed_counter_ = 0;
2052 EXPECT_EQ(3, controller.GetEntryCount());
2053 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2055 // Go back one.
2056 controller.GoBack();
2057 params.page_id = 1;
2058 params.url = url2;
2059 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2060 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2061 &details));
2062 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2063 navigation_entry_committed_counter_ = 0;
2064 EXPECT_EQ(3, controller.GetEntryCount());
2065 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2066 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2067 EXPECT_FALSE(controller.GetPendingEntry());
2069 // Go back one more.
2070 controller.GoBack();
2071 params.page_id = 0;
2072 params.url = url1;
2073 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2074 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2075 &details));
2076 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2077 navigation_entry_committed_counter_ = 0;
2078 EXPECT_EQ(3, controller.GetEntryCount());
2079 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2080 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2081 EXPECT_FALSE(controller.GetPendingEntry());
2084 TEST_F(NavigationControllerTest, LinkClick) {
2085 NavigationControllerImpl& controller = controller_impl();
2086 TestNotificationTracker notifications;
2087 RegisterForAllNavNotifications(&notifications, &controller);
2089 const GURL url1("http://foo1");
2090 const GURL url2("http://foo2");
2092 main_test_rfh()->SendNavigate(0, url1);
2093 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2094 navigation_entry_committed_counter_ = 0;
2096 main_test_rfh()->SendNavigate(1, url2);
2097 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2098 navigation_entry_committed_counter_ = 0;
2100 // Should not have produced a new session history entry.
2101 EXPECT_EQ(controller.GetEntryCount(), 2);
2102 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2103 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2104 EXPECT_TRUE(controller.GetLastCommittedEntry());
2105 EXPECT_FALSE(controller.GetPendingEntry());
2106 EXPECT_TRUE(controller.CanGoBack());
2107 EXPECT_FALSE(controller.CanGoForward());
2110 TEST_F(NavigationControllerTest, InPage) {
2111 NavigationControllerImpl& controller = controller_impl();
2112 TestNotificationTracker notifications;
2113 RegisterForAllNavNotifications(&notifications, &controller);
2115 // Main page.
2116 const GURL url1("http://foo");
2117 main_test_rfh()->SendNavigate(0, url1);
2118 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2119 navigation_entry_committed_counter_ = 0;
2121 // Ensure main page navigation to same url respects the was_within_same_page
2122 // hint provided in the params.
2123 FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2124 self_params.page_id = 0;
2125 self_params.url = url1;
2126 self_params.transition = ui::PAGE_TRANSITION_LINK;
2127 self_params.should_update_history = false;
2128 self_params.gesture = NavigationGestureUser;
2129 self_params.is_post = false;
2130 self_params.page_state = PageState::CreateFromURL(url1);
2131 self_params.was_within_same_page = true;
2133 LoadCommittedDetails details;
2134 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2135 &details));
2136 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2137 navigation_entry_committed_counter_ = 0;
2138 EXPECT_TRUE(details.is_in_page);
2139 EXPECT_TRUE(details.did_replace_entry);
2140 EXPECT_EQ(1, controller.GetEntryCount());
2142 // Fragment navigation to a new page_id.
2143 const GURL url2("http://foo#a");
2144 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2145 params.page_id = 1;
2146 params.url = url2;
2147 params.transition = ui::PAGE_TRANSITION_LINK;
2148 params.should_update_history = false;
2149 params.gesture = NavigationGestureUser;
2150 params.is_post = false;
2151 params.page_state = PageState::CreateFromURL(url2);
2152 params.was_within_same_page = true;
2154 // This should generate a new entry.
2155 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2156 &details));
2157 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2158 navigation_entry_committed_counter_ = 0;
2159 EXPECT_TRUE(details.is_in_page);
2160 EXPECT_FALSE(details.did_replace_entry);
2161 EXPECT_EQ(2, controller.GetEntryCount());
2163 // Go back one.
2164 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2165 controller.GoBack();
2166 back_params.url = url1;
2167 back_params.page_id = 0;
2168 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2169 &details));
2170 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2171 navigation_entry_committed_counter_ = 0;
2172 EXPECT_TRUE(details.is_in_page);
2173 EXPECT_EQ(2, controller.GetEntryCount());
2174 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2175 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2177 // Go forward
2178 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2179 controller.GoForward();
2180 forward_params.url = url2;
2181 forward_params.page_id = 1;
2182 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2183 &details));
2184 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2185 navigation_entry_committed_counter_ = 0;
2186 EXPECT_TRUE(details.is_in_page);
2187 EXPECT_EQ(2, controller.GetEntryCount());
2188 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2189 EXPECT_EQ(forward_params.url,
2190 controller.GetVisibleEntry()->GetURL());
2192 // Now go back and forward again. This is to work around a bug where we would
2193 // compare the incoming URL with the last committed entry rather than the
2194 // one identified by an existing page ID. This would result in the second URL
2195 // losing the reference fragment when you navigate away from it and then back.
2196 controller.GoBack();
2197 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2198 &details));
2199 controller.GoForward();
2200 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2201 &details));
2202 EXPECT_EQ(forward_params.url,
2203 controller.GetVisibleEntry()->GetURL());
2205 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2206 const GURL url3("http://bar");
2207 params.page_id = 2;
2208 params.url = url3;
2209 navigation_entry_committed_counter_ = 0;
2210 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2211 &details));
2212 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2213 navigation_entry_committed_counter_ = 0;
2214 EXPECT_FALSE(details.is_in_page);
2215 EXPECT_EQ(3, controller.GetEntryCount());
2216 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2219 TEST_F(NavigationControllerTest, InPage_Replace) {
2220 NavigationControllerImpl& controller = controller_impl();
2221 TestNotificationTracker notifications;
2222 RegisterForAllNavNotifications(&notifications, &controller);
2224 // Main page.
2225 const GURL url1("http://foo");
2226 main_test_rfh()->SendNavigate(0, url1);
2227 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2228 navigation_entry_committed_counter_ = 0;
2230 // First navigation.
2231 const GURL url2("http://foo#a");
2232 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2233 params.page_id = 0; // Same page_id
2234 params.url = url2;
2235 params.transition = ui::PAGE_TRANSITION_LINK;
2236 params.should_update_history = false;
2237 params.gesture = NavigationGestureUser;
2238 params.is_post = false;
2239 params.page_state = PageState::CreateFromURL(url2);
2240 params.was_within_same_page = true;
2242 // This should NOT generate a new entry, nor prune the list.
2243 LoadCommittedDetails details;
2244 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2245 &details));
2246 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2247 navigation_entry_committed_counter_ = 0;
2248 EXPECT_TRUE(details.is_in_page);
2249 EXPECT_TRUE(details.did_replace_entry);
2250 EXPECT_EQ(1, controller.GetEntryCount());
2253 // Tests for http://crbug.com/40395
2254 // Simulates this:
2255 // <script>
2256 // window.location.replace("#a");
2257 // window.location='http://foo3/';
2258 // </script>
2259 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2260 NavigationControllerImpl& controller = controller_impl();
2261 TestNotificationTracker notifications;
2262 RegisterForAllNavNotifications(&notifications, &controller);
2264 // Load an initial page.
2266 const GURL url("http://foo/");
2267 main_test_rfh()->SendNavigate(0, url);
2268 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2269 navigation_entry_committed_counter_ = 0;
2272 // Navigate to a new page.
2274 const GURL url("http://foo2/");
2275 main_test_rfh()->SendNavigate(1, url);
2276 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2277 navigation_entry_committed_counter_ = 0;
2280 // Navigate within the page.
2282 const GURL url("http://foo2/#a");
2283 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2284 params.page_id = 1; // Same page_id
2285 params.url = url;
2286 params.transition = ui::PAGE_TRANSITION_LINK;
2287 params.redirects.push_back(url);
2288 params.should_update_history = true;
2289 params.gesture = NavigationGestureUnknown;
2290 params.is_post = false;
2291 params.page_state = PageState::CreateFromURL(url);
2292 params.was_within_same_page = true;
2294 // This should NOT generate a new entry, nor prune the list.
2295 LoadCommittedDetails details;
2296 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2297 &details));
2298 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2299 navigation_entry_committed_counter_ = 0;
2300 EXPECT_TRUE(details.is_in_page);
2301 EXPECT_TRUE(details.did_replace_entry);
2302 EXPECT_EQ(2, controller.GetEntryCount());
2305 // Perform a client redirect to a new page.
2307 const GURL url("http://foo3/");
2308 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2309 params.page_id = 2; // New page_id
2310 params.url = url;
2311 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
2312 params.redirects.push_back(GURL("http://foo2/#a"));
2313 params.redirects.push_back(url);
2314 params.should_update_history = true;
2315 params.gesture = NavigationGestureUnknown;
2316 params.is_post = false;
2317 params.page_state = PageState::CreateFromURL(url);
2319 // This SHOULD generate a new entry.
2320 LoadCommittedDetails details;
2321 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2322 &details));
2323 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2324 navigation_entry_committed_counter_ = 0;
2325 EXPECT_FALSE(details.is_in_page);
2326 EXPECT_EQ(3, controller.GetEntryCount());
2329 // Verify that BACK brings us back to http://foo2/.
2331 const GURL url("http://foo2/");
2332 controller.GoBack();
2333 main_test_rfh()->SendNavigate(1, url);
2334 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2335 navigation_entry_committed_counter_ = 0;
2336 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2340 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
2342 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2343 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2344 GURL url("http://foo");
2345 params.page_id = 1;
2346 params.url = url;
2347 params.page_state = PageState::CreateFromURL(url);
2348 params.was_within_same_page = true;
2349 contents()->GetMainFrame()->SendNavigateWithParams(&params);
2350 // We pass if we don't crash.
2353 // NotificationObserver implementation used in verifying we've received the
2354 // NOTIFICATION_NAV_LIST_PRUNED method.
2355 class PrunedListener : public NotificationObserver {
2356 public:
2357 explicit PrunedListener(NavigationControllerImpl* controller)
2358 : notification_count_(0) {
2359 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2360 Source<NavigationController>(controller));
2363 void Observe(int type,
2364 const NotificationSource& source,
2365 const NotificationDetails& details) override {
2366 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2367 notification_count_++;
2368 details_ = *(Details<PrunedDetails>(details).ptr());
2372 // Number of times NAV_LIST_PRUNED has been observed.
2373 int notification_count_;
2375 // Details from the last NAV_LIST_PRUNED.
2376 PrunedDetails details_;
2378 private:
2379 NotificationRegistrar registrar_;
2381 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2384 // Tests that we limit the number of navigation entries created correctly.
2385 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2386 NavigationControllerImpl& controller = controller_impl();
2387 size_t original_count = NavigationControllerImpl::max_entry_count();
2388 const int kMaxEntryCount = 5;
2390 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2392 int url_index;
2393 // Load up to the max count, all entries should be there.
2394 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2395 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2396 controller.LoadURL(
2397 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2398 main_test_rfh()->PrepareForCommit();
2399 main_test_rfh()->SendNavigate(url_index, url);
2402 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2404 // Created a PrunedListener to observe prune notifications.
2405 PrunedListener listener(&controller);
2407 // Navigate some more.
2408 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2409 controller.LoadURL(
2410 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2411 main_test_rfh()->PrepareForCommit();
2412 main_test_rfh()->SendNavigate(url_index, url);
2413 url_index++;
2415 // We should have got a pruned navigation.
2416 EXPECT_EQ(1, listener.notification_count_);
2417 EXPECT_TRUE(listener.details_.from_front);
2418 EXPECT_EQ(1, listener.details_.count);
2420 // We expect http://www.a.com/0 to be gone.
2421 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2422 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2423 GURL("http:////www.a.com/1"));
2425 // More navigations.
2426 for (int i = 0; i < 3; i++) {
2427 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2428 controller.LoadURL(
2429 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2430 main_test_rfh()->PrepareForCommit();
2431 main_test_rfh()->SendNavigate(url_index, url);
2432 url_index++;
2434 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2435 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2436 GURL("http:////www.a.com/4"));
2438 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2441 // Tests that we can do a restore and navigate to the restored entries and
2442 // everything is updated properly. This can be tricky since there is no
2443 // SiteInstance for the entries created initially.
2444 TEST_F(NavigationControllerTest, RestoreNavigate) {
2445 // Create a NavigationController with a restored set of tabs.
2446 GURL url("http://foo");
2447 std::vector<NavigationEntry*> entries;
2448 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2449 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2450 browser_context());
2451 entry->SetPageID(0);
2452 entry->SetTitle(base::ASCIIToUTF16("Title"));
2453 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2454 const base::Time timestamp = base::Time::Now();
2455 entry->SetTimestamp(timestamp);
2456 entries.push_back(entry);
2457 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2458 WebContents::Create(WebContents::CreateParams(browser_context()))));
2459 NavigationControllerImpl& our_controller = our_contents->GetController();
2460 our_controller.Restore(
2462 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2463 &entries);
2464 ASSERT_EQ(0u, entries.size());
2466 // Before navigating to the restored entry, it should have a restore_type
2467 // and no SiteInstance.
2468 ASSERT_EQ(1, our_controller.GetEntryCount());
2469 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2470 our_controller.GetEntryAtIndex(0)->restore_type());
2471 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2473 // After navigating, we should have one entry, and it should be "pending".
2474 // It should now have a SiteInstance and no restore_type.
2475 our_controller.GoToIndex(0);
2476 EXPECT_EQ(1, our_controller.GetEntryCount());
2477 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2478 our_controller.GetPendingEntry());
2479 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2480 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2481 our_controller.GetEntryAtIndex(0)->restore_type());
2482 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2484 // Timestamp should remain the same before the navigation finishes.
2485 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2487 // Say we navigated to that entry.
2488 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2489 params.page_id = 0;
2490 params.url = url;
2491 params.transition = ui::PAGE_TRANSITION_LINK;
2492 params.should_update_history = false;
2493 params.gesture = NavigationGestureUser;
2494 params.is_post = false;
2495 params.page_state = PageState::CreateFromURL(url);
2496 LoadCommittedDetails details;
2497 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2498 &details);
2500 // There should be no longer any pending entry and one committed one. This
2501 // means that we were able to locate the entry, assign its site instance, and
2502 // commit it properly.
2503 EXPECT_EQ(1, our_controller.GetEntryCount());
2504 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2505 EXPECT_FALSE(our_controller.GetPendingEntry());
2506 EXPECT_EQ(
2507 url,
2508 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2509 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2510 our_controller.GetEntryAtIndex(0)->restore_type());
2512 // Timestamp should have been updated.
2513 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2516 // Tests that we can still navigate to a restored entry after a different
2517 // navigation fails and clears the pending entry. http://crbug.com/90085
2518 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2519 // Create a NavigationController with a restored set of tabs.
2520 GURL url("http://foo");
2521 std::vector<NavigationEntry*> entries;
2522 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2523 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2524 browser_context());
2525 entry->SetPageID(0);
2526 entry->SetTitle(base::ASCIIToUTF16("Title"));
2527 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2528 entries.push_back(entry);
2529 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2530 WebContents::Create(WebContents::CreateParams(browser_context()))));
2531 NavigationControllerImpl& our_controller = our_contents->GetController();
2532 our_controller.Restore(
2533 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2534 ASSERT_EQ(0u, entries.size());
2536 // Before navigating to the restored entry, it should have a restore_type
2537 // and no SiteInstance.
2538 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2539 our_controller.GetEntryAtIndex(0)->restore_type());
2540 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2542 // After navigating, we should have one entry, and it should be "pending".
2543 // It should now have a SiteInstance and no restore_type.
2544 our_controller.GoToIndex(0);
2545 EXPECT_EQ(1, our_controller.GetEntryCount());
2546 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2547 our_controller.GetPendingEntry());
2548 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2549 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2550 our_controller.GetEntryAtIndex(0)->restore_type());
2551 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2553 // This pending navigation may have caused a different navigation to fail,
2554 // which causes the pending entry to be cleared.
2555 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2556 fail_load_params.error_code = net::ERR_ABORTED;
2557 fail_load_params.error_description = base::string16();
2558 fail_load_params.url = url;
2559 fail_load_params.showing_repost_interstitial = false;
2560 main_test_rfh()->OnMessageReceived(
2561 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2562 fail_load_params));
2564 // Now the pending restored entry commits.
2565 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2566 params.page_id = 0;
2567 params.url = url;
2568 params.transition = ui::PAGE_TRANSITION_LINK;
2569 params.should_update_history = false;
2570 params.gesture = NavigationGestureUser;
2571 params.is_post = false;
2572 params.page_state = PageState::CreateFromURL(url);
2573 LoadCommittedDetails details;
2574 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2575 &details);
2577 // There should be no pending entry and one committed one.
2578 EXPECT_EQ(1, our_controller.GetEntryCount());
2579 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2580 EXPECT_FALSE(our_controller.GetPendingEntry());
2581 EXPECT_EQ(
2582 url,
2583 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2584 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2585 our_controller.GetEntryAtIndex(0)->restore_type());
2588 // Make sure that the page type and stuff is correct after an interstitial.
2589 TEST_F(NavigationControllerTest, Interstitial) {
2590 NavigationControllerImpl& controller = controller_impl();
2591 // First navigate somewhere normal.
2592 const GURL url1("http://foo");
2593 controller.LoadURL(
2594 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2595 main_test_rfh()->PrepareForCommit();
2596 main_test_rfh()->SendNavigate(0, url1);
2598 // Now navigate somewhere with an interstitial.
2599 const GURL url2("http://bar");
2600 controller.LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_TYPED,
2601 std::string());
2602 controller.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL);
2604 // At this point the interstitial will be displayed and the load will still
2605 // be pending. If the user continues, the load will commit.
2606 main_test_rfh()->PrepareForCommit();
2607 main_test_rfh()->SendNavigate(1, url2);
2609 // The page should be a normal page again.
2610 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2611 EXPECT_EQ(PAGE_TYPE_NORMAL,
2612 controller.GetLastCommittedEntry()->GetPageType());
2615 TEST_F(NavigationControllerTest, RemoveEntry) {
2616 NavigationControllerImpl& controller = controller_impl();
2617 const GURL url1("http://foo/1");
2618 const GURL url2("http://foo/2");
2619 const GURL url3("http://foo/3");
2620 const GURL url4("http://foo/4");
2621 const GURL url5("http://foo/5");
2622 const GURL pending_url("http://foo/pending");
2623 const GURL default_url("http://foo/default");
2625 controller.LoadURL(
2626 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2627 main_test_rfh()->PrepareForCommit();
2628 main_test_rfh()->SendNavigate(0, url1);
2629 controller.LoadURL(
2630 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2631 main_test_rfh()->PrepareForCommit();
2632 main_test_rfh()->SendNavigate(1, url2);
2633 controller.LoadURL(
2634 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2635 main_test_rfh()->PrepareForCommit();
2636 main_test_rfh()->SendNavigate(2, url3);
2637 controller.LoadURL(
2638 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2639 main_test_rfh()->PrepareForCommit();
2640 main_test_rfh()->SendNavigate(3, url4);
2641 controller.LoadURL(
2642 url5, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2643 main_test_rfh()->PrepareForCommit();
2644 main_test_rfh()->SendNavigate(4, url5);
2646 // Try to remove the last entry. Will fail because it is the current entry.
2647 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2648 EXPECT_EQ(5, controller.GetEntryCount());
2649 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2651 // Go back, but don't commit yet. Check that we can't delete the current
2652 // and pending entries.
2653 controller.GoBack();
2654 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2655 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2657 // Now commit and delete the last entry.
2658 main_test_rfh()->PrepareForCommit();
2659 main_test_rfh()->SendNavigate(3, url4);
2660 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2661 EXPECT_EQ(4, controller.GetEntryCount());
2662 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2663 EXPECT_FALSE(controller.GetPendingEntry());
2665 // Remove an entry which is not the last committed one.
2666 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2667 EXPECT_EQ(3, controller.GetEntryCount());
2668 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2669 EXPECT_FALSE(controller.GetPendingEntry());
2671 // Remove the 2 remaining entries.
2672 controller.RemoveEntryAtIndex(1);
2673 controller.RemoveEntryAtIndex(0);
2675 // This should leave us with only the last committed entry.
2676 EXPECT_EQ(1, controller.GetEntryCount());
2677 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2680 TEST_F(NavigationControllerTest, RemoveEntryWithPending) {
2681 NavigationControllerImpl& controller = controller_impl();
2682 const GURL url1("http://foo/1");
2683 const GURL url2("http://foo/2");
2684 const GURL url3("http://foo/3");
2685 const GURL default_url("http://foo/default");
2687 controller.LoadURL(
2688 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2689 main_test_rfh()->PrepareForCommit();
2690 main_test_rfh()->SendNavigate(0, url1);
2691 controller.LoadURL(
2692 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2693 main_test_rfh()->PrepareForCommit();
2694 main_test_rfh()->SendNavigate(1, url2);
2695 controller.LoadURL(
2696 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2697 main_test_rfh()->PrepareForCommit();
2698 main_test_rfh()->SendNavigate(2, url3);
2700 // Go back, but don't commit yet. Check that we can't delete the current
2701 // and pending entries.
2702 controller.GoBack();
2703 EXPECT_FALSE(controller.RemoveEntryAtIndex(2));
2704 EXPECT_FALSE(controller.RemoveEntryAtIndex(1));
2706 // Remove the first entry, while there is a pending entry. This is expected
2707 // to discard the pending entry.
2708 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2709 EXPECT_FALSE(controller.GetPendingEntry());
2710 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2712 // We should update the last committed entry index.
2713 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
2715 // Now commit and ensure we land on the right entry.
2716 main_test_rfh()->PrepareForCommit();
2717 main_test_rfh()->SendNavigate(1, url2);
2718 EXPECT_EQ(2, controller.GetEntryCount());
2719 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2720 EXPECT_FALSE(controller.GetPendingEntry());
2723 // Tests the transient entry, making sure it goes away with all navigations.
2724 TEST_F(NavigationControllerTest, TransientEntry) {
2725 NavigationControllerImpl& controller = controller_impl();
2726 TestNotificationTracker notifications;
2727 RegisterForAllNavNotifications(&notifications, &controller);
2729 const GURL url0("http://foo/0");
2730 const GURL url1("http://foo/1");
2731 const GURL url2("http://foo/2");
2732 const GURL url3("http://foo/3");
2733 const GURL url3_ref("http://foo/3#bar");
2734 const GURL url4("http://foo/4");
2735 const GURL transient_url("http://foo/transient");
2737 controller.LoadURL(
2738 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2739 main_test_rfh()->PrepareForCommit();
2740 main_test_rfh()->SendNavigate(0, url0);
2741 controller.LoadURL(
2742 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2743 main_test_rfh()->PrepareForCommit();
2744 main_test_rfh()->SendNavigate(1, url1);
2746 notifications.Reset();
2748 // Adding a transient with no pending entry.
2749 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2750 transient_entry->SetURL(transient_url);
2751 controller.SetTransientEntry(transient_entry);
2753 // We should not have received any notifications.
2754 EXPECT_EQ(0U, notifications.size());
2756 // Check our state.
2757 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2758 EXPECT_EQ(controller.GetEntryCount(), 3);
2759 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2760 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2761 EXPECT_TRUE(controller.GetLastCommittedEntry());
2762 EXPECT_FALSE(controller.GetPendingEntry());
2763 EXPECT_TRUE(controller.CanGoBack());
2764 EXPECT_FALSE(controller.CanGoForward());
2765 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2767 // Navigate.
2768 controller.LoadURL(
2769 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2770 main_test_rfh()->PrepareForCommit();
2771 main_test_rfh()->SendNavigate(2, url2);
2773 // We should have navigated, transient entry should be gone.
2774 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2775 EXPECT_EQ(controller.GetEntryCount(), 3);
2777 // Add a transient again, then navigate with no pending entry this time.
2778 transient_entry = new NavigationEntryImpl;
2779 transient_entry->SetURL(transient_url);
2780 controller.SetTransientEntry(transient_entry);
2781 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2782 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3, true);
2783 main_test_rfh()->PrepareForCommit();
2784 main_test_rfh()->SendNavigate(3, url3);
2785 // Transient entry should be gone.
2786 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2787 EXPECT_EQ(controller.GetEntryCount(), 4);
2789 // Initiate a navigation, add a transient then commit navigation.
2790 controller.LoadURL(
2791 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2792 transient_entry = new NavigationEntryImpl;
2793 transient_entry->SetURL(transient_url);
2794 controller.SetTransientEntry(transient_entry);
2795 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2796 main_test_rfh()->PrepareForCommit();
2797 main_test_rfh()->SendNavigate(4, url4);
2798 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2799 EXPECT_EQ(controller.GetEntryCount(), 5);
2801 // Add a transient and go back. This should simply remove the transient.
2802 transient_entry = new NavigationEntryImpl;
2803 transient_entry->SetURL(transient_url);
2804 controller.SetTransientEntry(transient_entry);
2805 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2806 EXPECT_TRUE(controller.CanGoBack());
2807 EXPECT_FALSE(controller.CanGoForward());
2808 controller.GoBack();
2809 // Transient entry should be gone.
2810 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2811 EXPECT_EQ(controller.GetEntryCount(), 5);
2813 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3, false);
2814 main_test_rfh()->PrepareForCommit();
2815 main_test_rfh()->SendNavigate(3, url3);
2817 // Add a transient and go to an entry before the current one.
2818 transient_entry = new NavigationEntryImpl;
2819 transient_entry->SetURL(transient_url);
2820 controller.SetTransientEntry(transient_entry);
2821 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2822 controller.GoToIndex(1);
2823 // The navigation should have been initiated, transient entry should be gone.
2824 EXPECT_FALSE(controller.GetTransientEntry());
2825 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2826 // Visible entry does not update for history navigations until commit.
2827 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2828 main_test_rfh()->PrepareForCommit();
2829 main_test_rfh()->SendNavigate(1, url1);
2830 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2832 // Add a transient and go to an entry after the current one.
2833 transient_entry = new NavigationEntryImpl;
2834 transient_entry->SetURL(transient_url);
2835 controller.SetTransientEntry(transient_entry);
2836 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2837 controller.GoToIndex(3);
2838 // The navigation should have been initiated, transient entry should be gone.
2839 // Because of the transient entry that is removed, going to index 3 makes us
2840 // land on url2 (which is visible after the commit).
2841 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2842 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2843 main_test_rfh()->PrepareForCommit();
2844 main_test_rfh()->SendNavigate(2, url2);
2845 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2847 // Add a transient and go forward.
2848 transient_entry = new NavigationEntryImpl;
2849 transient_entry->SetURL(transient_url);
2850 controller.SetTransientEntry(transient_entry);
2851 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2852 EXPECT_TRUE(controller.CanGoForward());
2853 controller.GoForward();
2854 // We should have navigated, transient entry should be gone.
2855 EXPECT_FALSE(controller.GetTransientEntry());
2856 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2857 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2858 main_test_rfh()->PrepareForCommit();
2859 main_test_rfh()->SendNavigate(3, url3);
2860 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2862 // Add a transient and do an in-page navigation, replacing the current entry.
2863 transient_entry = new NavigationEntryImpl;
2864 transient_entry->SetURL(transient_url);
2865 controller.SetTransientEntry(transient_entry);
2866 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2868 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref, false);
2869 main_test_rfh()->PrepareForCommit();
2870 main_test_rfh()->SendNavigate(3, url3_ref);
2871 // Transient entry should be gone.
2872 EXPECT_FALSE(controller.GetTransientEntry());
2873 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2875 // Ensure the URLs are correct.
2876 EXPECT_EQ(controller.GetEntryCount(), 5);
2877 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2878 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2879 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2880 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2881 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2884 // Test that Reload initiates a new navigation to a transient entry's URL.
2885 TEST_F(NavigationControllerTest, ReloadTransient) {
2886 NavigationControllerImpl& controller = controller_impl();
2887 const GURL url0("http://foo/0");
2888 const GURL url1("http://foo/1");
2889 const GURL transient_url("http://foo/transient");
2891 // Load |url0|, and start a pending navigation to |url1|.
2892 controller.LoadURL(
2893 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2894 main_test_rfh()->PrepareForCommit();
2895 main_test_rfh()->SendNavigate(0, url0);
2896 controller.LoadURL(
2897 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2899 // A transient entry is added, interrupting the navigation.
2900 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2901 transient_entry->SetURL(transient_url);
2902 controller.SetTransientEntry(transient_entry);
2903 EXPECT_TRUE(controller.GetTransientEntry());
2904 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2906 // The page is reloaded, which should remove the pending entry for |url1| and
2907 // the transient entry for |transient_url|, and start a navigation to
2908 // |transient_url|.
2909 controller.Reload(true);
2910 EXPECT_FALSE(controller.GetTransientEntry());
2911 EXPECT_TRUE(controller.GetPendingEntry());
2912 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2913 ASSERT_EQ(controller.GetEntryCount(), 1);
2914 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2916 // Load of |transient_url| completes.
2917 main_test_rfh()->PrepareForCommit();
2918 main_test_rfh()->SendNavigate(1, transient_url);
2919 ASSERT_EQ(controller.GetEntryCount(), 2);
2920 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2921 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2924 // Ensure that renderer initiated pending entries get replaced, so that we
2925 // don't show a stale virtual URL when a navigation commits.
2926 // See http://crbug.com/266922.
2927 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2928 NavigationControllerImpl& controller = controller_impl();
2929 Navigator* navigator =
2930 contents()->GetFrameTree()->root()->navigator();
2932 const GURL url1("nonexistent:12121");
2933 const GURL url1_fixed("http://nonexistent:12121/");
2934 const GURL url2("http://foo");
2936 // We create pending entries for renderer-initiated navigations so that we
2937 // can show them in new tabs when it is safe.
2938 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2940 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2941 // the virtual URL to differ from the URL.
2942 controller.GetPendingEntry()->SetURL(url1_fixed);
2943 controller.GetPendingEntry()->SetVirtualURL(url1);
2945 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
2946 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
2947 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
2949 // If the user clicks another link, we should replace the pending entry.
2950 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2951 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2952 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
2954 // Once it commits, the URL and virtual URL should reflect the actual page.
2955 main_test_rfh()->SendNavigate(0, url2);
2956 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2957 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
2959 // We should not replace the pending entry for an error URL.
2960 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2961 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2962 navigator->DidStartProvisionalLoad(main_test_rfh(),
2963 GURL(kUnreachableWebDataURL), false);
2964 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2966 // We should remember if the pending entry will replace the current one.
2967 // http://crbug.com/308444.
2968 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2969 controller.GetPendingEntry()->set_should_replace_entry(true);
2970 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2971 EXPECT_TRUE(controller.GetPendingEntry()->should_replace_entry());
2972 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2973 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2974 // to the main frame.
2975 main_test_rfh()->SendNavigate(0, url2);
2976 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2979 // Tests that the URLs for renderer-initiated navigations are not displayed to
2980 // the user until the navigation commits, to prevent URL spoof attacks.
2981 // See http://crbug.com/99016.
2982 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
2983 NavigationControllerImpl& controller = controller_impl();
2984 TestNotificationTracker notifications;
2985 RegisterForAllNavNotifications(&notifications, &controller);
2987 const GURL url0("http://foo/0");
2988 const GURL url1("http://foo/1");
2990 // For typed navigations (browser-initiated), both pending and visible entries
2991 // should update before commit.
2992 controller.LoadURL(
2993 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2994 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
2995 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2996 main_test_rfh()->PrepareForCommit();
2997 main_test_rfh()->SendNavigate(0, url0);
2999 // For link clicks (renderer-initiated navigations), the pending entry should
3000 // update before commit but the visible should not.
3001 NavigationController::LoadURLParams load_url_params(url1);
3002 load_url_params.is_renderer_initiated = true;
3003 controller.LoadURLWithParams(load_url_params);
3004 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
3005 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3006 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3008 // After commit, both visible should be updated, there should be no pending
3009 // entry, and we should no longer treat the entry as renderer-initiated.
3010 main_test_rfh()->PrepareForCommit();
3011 main_test_rfh()->SendNavigate(1, url1);
3012 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3013 EXPECT_FALSE(controller.GetPendingEntry());
3014 EXPECT_FALSE(controller.GetLastCommittedEntry()->is_renderer_initiated());
3016 notifications.Reset();
3019 // Tests that the URLs for renderer-initiated navigations in new tabs are
3020 // displayed to the user before commit, as long as the initial about:blank
3021 // page has not been modified. If so, we must revert to showing about:blank.
3022 // See http://crbug.com/9682.
3023 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
3024 NavigationControllerImpl& controller = controller_impl();
3025 TestNotificationTracker notifications;
3026 RegisterForAllNavNotifications(&notifications, &controller);
3028 const GURL url("http://foo");
3030 // For renderer-initiated navigations in new tabs (with no committed entries),
3031 // we show the pending entry's URL as long as the about:blank page is not
3032 // modified.
3033 NavigationController::LoadURLParams load_url_params(url);
3034 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3035 load_url_params.is_renderer_initiated = true;
3036 controller.LoadURLWithParams(load_url_params);
3037 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3038 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3039 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3040 EXPECT_TRUE(controller.IsInitialNavigation());
3041 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3043 // There should be no title yet.
3044 EXPECT_TRUE(contents()->GetTitle().empty());
3046 // If something else modifies the contents of the about:blank page, then
3047 // we must revert to showing about:blank to avoid a URL spoof.
3048 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3049 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3050 EXPECT_FALSE(controller.GetVisibleEntry());
3051 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3053 notifications.Reset();
3056 // Tests that the URLs for browser-initiated navigations in new tabs are
3057 // displayed to the user even after they fail, as long as the initial
3058 // about:blank page has not been modified. If so, we must revert to showing
3059 // about:blank. See http://crbug.com/355537.
3060 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
3061 NavigationControllerImpl& controller = controller_impl();
3062 TestNotificationTracker notifications;
3063 RegisterForAllNavNotifications(&notifications, &controller);
3065 const GURL url("http://foo");
3067 // For browser-initiated navigations in new tabs (with no committed entries),
3068 // we show the pending entry's URL as long as the about:blank page is not
3069 // modified. This is possible in cases that the user types a URL into a popup
3070 // tab created with a slow URL.
3071 NavigationController::LoadURLParams load_url_params(url);
3072 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
3073 load_url_params.is_renderer_initiated = false;
3074 controller.LoadURLWithParams(load_url_params);
3075 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3076 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3077 EXPECT_FALSE(controller.GetPendingEntry()->is_renderer_initiated());
3078 EXPECT_TRUE(controller.IsInitialNavigation());
3079 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3081 // There should be no title yet.
3082 EXPECT_TRUE(contents()->GetTitle().empty());
3084 // Suppose it aborts before committing, if it's a 204 or download or due to a
3085 // stop or a new navigation from the user. The URL should remain visible.
3086 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3087 params.error_code = net::ERR_ABORTED;
3088 params.error_description = base::string16();
3089 params.url = url;
3090 params.showing_repost_interstitial = false;
3091 main_test_rfh()->OnMessageReceived(
3092 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3093 contents()->SetIsLoading(test_rvh(), false, true, NULL);
3094 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3096 // If something else later modifies the contents of the about:blank page, then
3097 // we must revert to showing about:blank to avoid a URL spoof.
3098 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3099 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3100 EXPECT_FALSE(controller.GetVisibleEntry());
3101 EXPECT_FALSE(controller.GetPendingEntry());
3103 notifications.Reset();
3106 // Tests that the URLs for renderer-initiated navigations in new tabs are
3107 // displayed to the user even after they fail, as long as the initial
3108 // about:blank page has not been modified. If so, we must revert to showing
3109 // about:blank. See http://crbug.com/355537.
3110 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
3111 NavigationControllerImpl& controller = controller_impl();
3112 TestNotificationTracker notifications;
3113 RegisterForAllNavNotifications(&notifications, &controller);
3115 const GURL url("http://foo");
3117 // For renderer-initiated navigations in new tabs (with no committed entries),
3118 // we show the pending entry's URL as long as the about:blank page is not
3119 // modified.
3120 NavigationController::LoadURLParams load_url_params(url);
3121 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3122 load_url_params.is_renderer_initiated = true;
3123 controller.LoadURLWithParams(load_url_params);
3124 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3125 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3126 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3127 EXPECT_TRUE(controller.IsInitialNavigation());
3128 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3130 // There should be no title yet.
3131 EXPECT_TRUE(contents()->GetTitle().empty());
3133 // Suppose it aborts before committing, if it's a 204 or download or due to a
3134 // stop or a new navigation from the user. The URL should remain visible.
3135 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3136 params.error_code = net::ERR_ABORTED;
3137 params.error_description = base::string16();
3138 params.url = url;
3139 params.showing_repost_interstitial = false;
3140 main_test_rfh()->OnMessageReceived(
3141 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3142 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3144 // If something else later modifies the contents of the about:blank page, then
3145 // we must revert to showing about:blank to avoid a URL spoof.
3146 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3147 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3148 EXPECT_FALSE(controller.GetVisibleEntry());
3149 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3151 notifications.Reset();
3154 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3155 NavigationControllerImpl& controller = controller_impl();
3156 TestNotificationTracker notifications;
3157 RegisterForAllNavNotifications(&notifications, &controller);
3159 const GURL url1("http://foo/eh");
3160 const GURL url2("http://foo/bee");
3162 // For renderer-initiated navigations in new tabs (with no committed entries),
3163 // we show the pending entry's URL as long as the about:blank page is not
3164 // modified.
3165 NavigationController::LoadURLParams load_url_params(url1);
3166 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3167 load_url_params.is_renderer_initiated = true;
3168 controller.LoadURLWithParams(load_url_params);
3169 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3170 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3171 EXPECT_TRUE(controller.IsInitialNavigation());
3172 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3174 // Simulate a commit and then starting a new pending navigation.
3175 main_test_rfh()->SendNavigate(0, url1);
3176 NavigationController::LoadURLParams load_url2_params(url2);
3177 load_url2_params.transition_type = ui::PAGE_TRANSITION_LINK;
3178 load_url2_params.is_renderer_initiated = true;
3179 controller.LoadURLWithParams(load_url2_params);
3181 // We should not consider this an initial navigation, and thus should
3182 // not show the pending URL.
3183 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3184 EXPECT_FALSE(controller.IsInitialNavigation());
3185 EXPECT_TRUE(controller.GetVisibleEntry());
3186 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3188 notifications.Reset();
3191 // Tests that IsInPageNavigation returns appropriate results. Prevents
3192 // regression for bug 1126349.
3193 TEST_F(NavigationControllerTest, IsInPageNavigation) {
3194 NavigationControllerImpl& controller = controller_impl();
3195 const GURL url("http://www.google.com/home.html");
3197 // If the renderer claims it performed an in-page navigation from
3198 // about:blank, trust the renderer.
3199 // This can happen when an iframe is created and populated via
3200 // document.write(), then tries to perform a fragment navigation.
3201 // TODO(japhet): We should only trust the renderer if the about:blank
3202 // was the first document in the given frame, but we don't have enough
3203 // information to identify that case currently.
3204 const GURL blank_url(url::kAboutBlankURL);
3205 main_test_rfh()->SendNavigate(0, blank_url);
3206 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3207 main_test_rfh()));
3209 // Navigate to URL with no refs.
3210 main_test_rfh()->SendNavigate(0, url);
3212 // Reloading the page is not an in-page navigation.
3213 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3214 main_test_rfh()));
3215 const GURL other_url("http://www.google.com/add.html");
3216 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3217 main_test_rfh()));
3218 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3219 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3220 main_test_rfh()));
3222 // Navigate to URL with refs.
3223 main_test_rfh()->SendNavigate(1, url_with_ref);
3225 // Reloading the page is not an in-page navigation.
3226 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
3227 main_test_rfh()));
3228 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3229 main_test_rfh()));
3230 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3231 main_test_rfh()));
3232 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3233 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
3234 main_test_rfh()));
3236 // Going to the same url again will be considered in-page
3237 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3238 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3239 main_test_rfh()));
3241 // Going back to the non ref url will be considered in-page if the navigation
3242 // type is IN_PAGE.
3243 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3244 main_test_rfh()));
3246 // If the renderer says this is a same-origin in-page navigation, believe it.
3247 // This is the pushState/replaceState case.
3248 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
3249 main_test_rfh()));
3251 // Test allow_universal_access_from_file_urls flag.
3252 const GURL different_origin_url("http://www.example.com");
3253 MockRenderProcessHost* rph =
3254 static_cast<MockRenderProcessHost*>(main_test_rfh()->GetProcess());
3255 WebPreferences prefs = test_rvh()->GetWebkitPreferences();
3256 prefs.allow_universal_access_from_file_urls = true;
3257 test_rvh()->UpdateWebkitPreferences(prefs);
3258 prefs = test_rvh()->GetWebkitPreferences();
3259 EXPECT_TRUE(prefs.allow_universal_access_from_file_urls);
3260 // Allow in page navigation if existing URL is file scheme.
3261 const GURL file_url("file:///foo/index.html");
3262 main_test_rfh()->SendNavigate(0, file_url);
3263 EXPECT_EQ(0, rph->bad_msg_count());
3264 EXPECT_TRUE(controller.IsURLInPageNavigation(different_origin_url, true,
3265 main_test_rfh()));
3266 EXPECT_EQ(0, rph->bad_msg_count());
3267 // Don't honor allow_universal_access_from_file_urls if existing URL is
3268 // not file scheme.
3269 main_test_rfh()->SendNavigate(0, url);
3270 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3271 main_test_rfh()));
3272 EXPECT_EQ(1, rph->bad_msg_count());
3274 // Remove allow_universal_access_from_file_urls flag.
3275 prefs.allow_universal_access_from_file_urls = false;
3276 test_rvh()->UpdateWebkitPreferences(prefs);
3277 prefs = test_rvh()->GetWebkitPreferences();
3278 EXPECT_FALSE(prefs.allow_universal_access_from_file_urls);
3280 // Don't believe the renderer if it claims a cross-origin navigation is
3281 // in-page.
3282 EXPECT_EQ(1, rph->bad_msg_count());
3283 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3284 main_test_rfh()));
3285 EXPECT_EQ(2, rph->bad_msg_count());
3288 // Some pages can have subframes with the same base URL (minus the reference) as
3289 // the main page. Even though this is hard, it can happen, and we don't want
3290 // these subframe navigations to affect the toplevel document. They should
3291 // instead be ignored. http://crbug.com/5585
3292 TEST_F(NavigationControllerTest, SameSubframe) {
3293 NavigationControllerImpl& controller = controller_impl();
3294 // Navigate the main frame.
3295 const GURL url("http://www.google.com/");
3296 main_test_rfh()->SendNavigate(0, url);
3298 // We should be at the first navigation entry.
3299 EXPECT_EQ(controller.GetEntryCount(), 1);
3300 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3302 // Navigate a subframe that would normally count as in-page.
3303 const GURL subframe("http://www.google.com/#");
3304 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3305 params.page_id = 0;
3306 params.url = subframe;
3307 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3308 params.should_update_history = false;
3309 params.gesture = NavigationGestureAuto;
3310 params.is_post = false;
3311 params.page_state = PageState::CreateFromURL(subframe);
3312 LoadCommittedDetails details;
3313 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3314 &details));
3316 // Nothing should have changed.
3317 EXPECT_EQ(controller.GetEntryCount(), 1);
3318 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3321 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3322 // false.
3323 TEST_F(NavigationControllerTest, CloneAndGoBack) {
3324 NavigationControllerImpl& controller = controller_impl();
3325 const GURL url1("http://foo1");
3326 const GURL url2("http://foo2");
3327 const base::string16 title(base::ASCIIToUTF16("Title"));
3329 NavigateAndCommit(url1);
3330 controller.GetVisibleEntry()->SetTitle(title);
3331 NavigateAndCommit(url2);
3333 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3335 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3336 EXPECT_TRUE(clone->GetController().NeedsReload());
3337 clone->GetController().GoBack();
3338 // Navigating back should have triggered needs_reload_ to go false.
3339 EXPECT_FALSE(clone->GetController().NeedsReload());
3341 // Ensure that the pending URL and its title are visible.
3342 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3343 EXPECT_EQ(title, clone->GetTitle());
3346 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3347 // See http://crbug.com/234491.
3348 TEST_F(NavigationControllerTest, CloneAndReload) {
3349 NavigationControllerImpl& controller = controller_impl();
3350 const GURL url1("http://foo1");
3351 const GURL url2("http://foo2");
3352 const base::string16 title(base::ASCIIToUTF16("Title"));
3354 NavigateAndCommit(url1);
3355 controller.GetVisibleEntry()->SetTitle(title);
3356 NavigateAndCommit(url2);
3358 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3359 clone->GetController().LoadIfNecessary();
3361 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3362 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3364 clone->GetController().Reload(true);
3365 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3368 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3369 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3370 NavigationControllerImpl& controller = controller_impl();
3371 const GURL url1("http://foo1");
3372 const GURL url2("http://foo2");
3374 NavigateAndCommit(url1);
3375 NavigateAndCommit(url2);
3377 // Add an interstitial entry. Should be deleted with controller.
3378 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3379 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3380 controller.SetTransientEntry(interstitial_entry);
3382 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3384 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3387 // Test requesting and triggering a lazy reload.
3388 TEST_F(NavigationControllerTest, LazyReload) {
3389 NavigationControllerImpl& controller = controller_impl();
3390 const GURL url("http://foo");
3391 NavigateAndCommit(url);
3392 ASSERT_FALSE(controller.NeedsReload());
3393 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD,
3394 controller.GetLastCommittedEntry()->GetTransitionType());
3396 // Request a reload to happen when the controller becomes active (e.g. after
3397 // the renderer gets killed in background on Android).
3398 controller.SetNeedsReload();
3399 ASSERT_TRUE(controller.NeedsReload());
3400 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3401 controller.GetLastCommittedEntry()->GetTransitionType());
3403 // Set the controller as active, triggering the requested reload.
3404 controller.SetActive(true);
3405 ASSERT_FALSE(controller.NeedsReload());
3406 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3407 controller.GetPendingEntry()->GetTransitionType());
3410 // Test requesting and triggering a lazy reload without any committed entry.
3411 TEST_F(NavigationControllerTest, LazyReloadWithoutCommittedEntry) {
3412 NavigationControllerImpl& controller = controller_impl();
3413 const GURL url("http://foo");
3414 controller.LoadURL(url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3415 ASSERT_FALSE(controller.NeedsReload());
3416 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3417 controller.GetPendingEntry()->GetTransitionType());
3419 // Request a reload to happen when the controller becomes active (e.g. after
3420 // the renderer gets killed in background on Android).
3421 controller.SetNeedsReload();
3422 ASSERT_TRUE(controller.NeedsReload());
3423 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3424 controller.GetPendingEntry()->GetTransitionType());
3426 // Set the controller as active, triggering the requested reload.
3427 controller.SetActive(true);
3428 ASSERT_FALSE(controller.NeedsReload());
3429 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3430 controller.GetPendingEntry()->GetTransitionType());
3433 // Tests a subframe navigation while a toplevel navigation is pending.
3434 // http://crbug.com/43967
3435 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3436 NavigationControllerImpl& controller = controller_impl();
3437 // Load the first page.
3438 const GURL url1("http://foo/");
3439 NavigateAndCommit(url1);
3441 // Now start a pending load to a totally different page, but don't commit it.
3442 const GURL url2("http://bar/");
3443 controller.LoadURL(
3444 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3446 // Send a subframe update from the first page, as if one had just
3447 // automatically loaded. Auto subframes don't increment the page ID.
3448 const GURL url1_sub("http://foo/subframe");
3449 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3450 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3451 params.url = url1_sub;
3452 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3453 params.should_update_history = false;
3454 params.gesture = NavigationGestureAuto;
3455 params.is_post = false;
3456 params.page_state = PageState::CreateFromURL(url1_sub);
3457 LoadCommittedDetails details;
3459 // This should return false meaning that nothing was actually updated.
3460 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3461 &details));
3463 // The notification should have updated the last committed one, and not
3464 // the pending load.
3465 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3467 // The active entry should be unchanged by the subframe load.
3468 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3471 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3472 TEST_F(NavigationControllerTest, CopyStateFrom) {
3473 NavigationControllerImpl& controller = controller_impl();
3474 const GURL url1("http://foo1");
3475 const GURL url2("http://foo2");
3477 NavigateAndCommit(url1);
3478 NavigateAndCommit(url2);
3479 controller.GoBack();
3480 contents()->CommitPendingNavigation();
3482 scoped_ptr<TestWebContents> other_contents(
3483 static_cast<TestWebContents*>(CreateTestWebContents()));
3484 NavigationControllerImpl& other_controller = other_contents->GetController();
3485 other_controller.CopyStateFrom(controller);
3487 // other_controller should now contain 2 urls.
3488 ASSERT_EQ(2, other_controller.GetEntryCount());
3489 // We should be looking at the first one.
3490 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3492 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3493 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3494 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3495 // This is a different site than url1, so the IDs start again at 0.
3496 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3498 // The max page ID map should be copied over and updated with the max page ID
3499 // from the current tab.
3500 SiteInstance* instance1 =
3501 other_controller.GetEntryAtIndex(0)->site_instance();
3502 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3504 // Ensure the SessionStorageNamespaceMaps are the same size and have
3505 // the same partitons loaded.
3507 // TODO(ajwong): We should load a url from a different partition earlier
3508 // to make sure this map has more than one entry.
3509 const SessionStorageNamespaceMap& session_storage_namespace_map =
3510 controller.GetSessionStorageNamespaceMap();
3511 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3512 other_controller.GetSessionStorageNamespaceMap();
3513 EXPECT_EQ(session_storage_namespace_map.size(),
3514 other_session_storage_namespace_map.size());
3515 for (SessionStorageNamespaceMap::const_iterator it =
3516 session_storage_namespace_map.begin();
3517 it != session_storage_namespace_map.end();
3518 ++it) {
3519 SessionStorageNamespaceMap::const_iterator other =
3520 other_session_storage_namespace_map.find(it->first);
3521 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3525 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3526 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3527 NavigationControllerImpl& controller = controller_impl();
3528 const GURL url1("http://foo/1");
3529 const GURL url2("http://foo/2");
3530 const GURL url3("http://foo/3");
3532 NavigateAndCommit(url1);
3533 NavigateAndCommit(url2);
3535 // First two entries should have the same SiteInstance.
3536 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3537 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3538 EXPECT_EQ(instance1, instance2);
3539 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3540 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3541 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3543 scoped_ptr<TestWebContents> other_contents(
3544 static_cast<TestWebContents*>(CreateTestWebContents()));
3545 NavigationControllerImpl& other_controller = other_contents->GetController();
3546 other_contents->NavigateAndCommit(url3);
3547 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3548 other_controller.CopyStateFromAndPrune(&controller, false);
3550 // other_controller should now contain the 3 urls: url1, url2 and url3.
3552 ASSERT_EQ(3, other_controller.GetEntryCount());
3554 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3556 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3557 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3558 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3559 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3560 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3561 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3563 // A new SiteInstance in a different BrowsingInstance should be used for the
3564 // new tab.
3565 SiteInstance* instance3 =
3566 other_controller.GetEntryAtIndex(2)->site_instance();
3567 EXPECT_NE(instance3, instance1);
3568 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3570 // The max page ID map should be copied over and updated with the max page ID
3571 // from the current tab.
3572 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3573 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3576 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3577 // the target.
3578 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3579 NavigationControllerImpl& controller = controller_impl();
3580 const GURL url1("http://foo1");
3581 const GURL url2("http://foo2");
3582 const GURL url3("http://foo3");
3584 NavigateAndCommit(url1);
3585 NavigateAndCommit(url2);
3586 controller.GoBack();
3587 contents()->CommitPendingNavigation();
3589 scoped_ptr<TestWebContents> other_contents(
3590 static_cast<TestWebContents*>(CreateTestWebContents()));
3591 NavigationControllerImpl& other_controller = other_contents->GetController();
3592 other_contents->NavigateAndCommit(url3);
3593 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3594 other_controller.CopyStateFromAndPrune(&controller, false);
3596 // other_controller should now contain: url1, url3
3598 ASSERT_EQ(2, other_controller.GetEntryCount());
3599 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3601 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3602 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3603 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3605 // The max page ID map should be copied over and updated with the max page ID
3606 // from the current tab.
3607 SiteInstance* instance1 =
3608 other_controller.GetEntryAtIndex(1)->site_instance();
3609 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3612 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3613 // the target.
3614 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3615 NavigationControllerImpl& controller = controller_impl();
3616 const GURL url1("http://foo1");
3617 const GURL url2("http://foo2");
3618 const GURL url3("http://foo3");
3619 const GURL url4("http://foo4");
3621 NavigateAndCommit(url1);
3622 NavigateAndCommit(url2);
3624 scoped_ptr<TestWebContents> other_contents(
3625 static_cast<TestWebContents*>(CreateTestWebContents()));
3626 NavigationControllerImpl& other_controller = other_contents->GetController();
3627 other_contents->NavigateAndCommit(url3);
3628 other_contents->NavigateAndCommit(url4);
3629 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3630 other_controller.CopyStateFromAndPrune(&controller, false);
3632 // other_controller should now contain: url1, url2, url4
3634 ASSERT_EQ(3, other_controller.GetEntryCount());
3635 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3637 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3638 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3639 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3641 // The max page ID map should be copied over and updated with the max page ID
3642 // from the current tab.
3643 SiteInstance* instance1 =
3644 other_controller.GetEntryAtIndex(2)->site_instance();
3645 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3648 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3649 // not the last entry selected in the target.
3650 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3651 NavigationControllerImpl& controller = controller_impl();
3652 const GURL url1("http://foo1");
3653 const GURL url2("http://foo2");
3654 const GURL url3("http://foo3");
3655 const GURL url4("http://foo4");
3657 NavigateAndCommit(url1);
3658 NavigateAndCommit(url2);
3660 scoped_ptr<TestWebContents> other_contents(
3661 static_cast<TestWebContents*>(CreateTestWebContents()));
3662 NavigationControllerImpl& other_controller = other_contents->GetController();
3663 other_contents->NavigateAndCommit(url3);
3664 other_contents->NavigateAndCommit(url4);
3665 other_controller.GoBack();
3666 other_contents->CommitPendingNavigation();
3667 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3668 other_controller.CopyStateFromAndPrune(&controller, false);
3670 // other_controller should now contain: url1, url2, url3
3672 ASSERT_EQ(3, other_controller.GetEntryCount());
3673 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3675 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3676 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3677 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3679 // The max page ID map should be copied over and updated with the max page ID
3680 // from the current tab.
3681 SiteInstance* instance1 =
3682 other_controller.GetEntryAtIndex(2)->site_instance();
3683 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3686 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3687 // a pending entry in the target.
3688 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3689 NavigationControllerImpl& controller = controller_impl();
3690 const GURL url1("http://foo1");
3691 const GURL url2("http://foo2");
3692 const GURL url3("http://foo3");
3693 const GURL url4("http://foo4");
3695 NavigateAndCommit(url1);
3696 NavigateAndCommit(url2);
3697 controller.GoBack();
3698 contents()->CommitPendingNavigation();
3700 scoped_ptr<TestWebContents> other_contents(
3701 static_cast<TestWebContents*>(CreateTestWebContents()));
3702 NavigationControllerImpl& other_controller = other_contents->GetController();
3703 other_contents->NavigateAndCommit(url3);
3704 other_controller.LoadURL(
3705 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3706 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3707 other_controller.CopyStateFromAndPrune(&controller, false);
3709 // other_controller should now contain url1, url3, and a pending entry
3710 // for url4.
3712 ASSERT_EQ(2, other_controller.GetEntryCount());
3713 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3715 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3716 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3718 // And there should be a pending entry for url4.
3719 ASSERT_TRUE(other_controller.GetPendingEntry());
3720 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3722 // The max page ID map should be copied over and updated with the max page ID
3723 // from the current tab.
3724 SiteInstance* instance1 =
3725 other_controller.GetEntryAtIndex(0)->site_instance();
3726 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3729 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3730 // client redirect entry (with the same page ID) in the target. This used to
3731 // crash because the last committed entry would be pruned but max_page_id
3732 // remembered the page ID (http://crbug.com/234809).
3733 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3734 NavigationControllerImpl& controller = controller_impl();
3735 const GURL url1("http://foo1");
3736 const GURL url2a("http://foo2/a");
3737 const GURL url2b("http://foo2/b");
3739 NavigateAndCommit(url1);
3741 scoped_ptr<TestWebContents> other_contents(
3742 static_cast<TestWebContents*>(CreateTestWebContents()));
3743 NavigationControllerImpl& other_controller = other_contents->GetController();
3744 other_contents->NavigateAndCommit(url2a);
3745 // Simulate a client redirect, which has the same page ID as entry 2a.
3746 other_controller.LoadURL(
3747 url2b, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
3748 other_controller.GetPendingEntry()->SetPageID(
3749 other_controller.GetLastCommittedEntry()->GetPageID());
3751 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3752 other_controller.CopyStateFromAndPrune(&controller, false);
3754 // other_controller should now contain url1, url2a, and a pending entry
3755 // for url2b.
3757 ASSERT_EQ(2, other_controller.GetEntryCount());
3758 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3760 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3761 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3763 // And there should be a pending entry for url4.
3764 ASSERT_TRUE(other_controller.GetPendingEntry());
3765 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3767 // Let the pending entry commit.
3768 other_contents->CommitPendingNavigation();
3770 // The max page ID map should be copied over and updated with the max page ID
3771 // from the current tab.
3772 SiteInstance* instance1 =
3773 other_controller.GetEntryAtIndex(1)->site_instance();
3774 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3777 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3778 // source, and 1 entry in the target. The back pending entry should be ignored.
3779 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3780 NavigationControllerImpl& controller = controller_impl();
3781 const GURL url1("http://foo1");
3782 const GURL url2("http://foo2");
3783 const GURL url3("http://foo3");
3785 NavigateAndCommit(url1);
3786 NavigateAndCommit(url2);
3787 controller.GoBack();
3789 scoped_ptr<TestWebContents> other_contents(
3790 static_cast<TestWebContents*>(CreateTestWebContents()));
3791 NavigationControllerImpl& other_controller = other_contents->GetController();
3792 other_contents->NavigateAndCommit(url3);
3793 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3794 other_controller.CopyStateFromAndPrune(&controller, false);
3796 // other_controller should now contain: url1, url2, url3
3798 ASSERT_EQ(3, other_controller.GetEntryCount());
3799 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3801 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3802 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3803 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3804 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3806 // The max page ID map should be copied over and updated with the max page ID
3807 // from the current tab.
3808 SiteInstance* instance1 =
3809 other_controller.GetEntryAtIndex(2)->site_instance();
3810 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3813 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3814 // when the max entry count is 3. We should prune one entry.
3815 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3816 NavigationControllerImpl& controller = controller_impl();
3817 size_t original_count = NavigationControllerImpl::max_entry_count();
3818 const int kMaxEntryCount = 3;
3820 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3822 const GURL url1("http://foo/1");
3823 const GURL url2("http://foo/2");
3824 const GURL url3("http://foo/3");
3825 const GURL url4("http://foo/4");
3827 // Create a PrunedListener to observe prune notifications.
3828 PrunedListener listener(&controller);
3830 NavigateAndCommit(url1);
3831 NavigateAndCommit(url2);
3832 NavigateAndCommit(url3);
3834 scoped_ptr<TestWebContents> other_contents(
3835 static_cast<TestWebContents*>(CreateTestWebContents()));
3836 NavigationControllerImpl& other_controller = other_contents->GetController();
3837 other_contents->NavigateAndCommit(url4);
3838 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3839 other_controller.CopyStateFromAndPrune(&controller, false);
3841 // We should have received a pruned notification.
3842 EXPECT_EQ(1, listener.notification_count_);
3843 EXPECT_TRUE(listener.details_.from_front);
3844 EXPECT_EQ(1, listener.details_.count);
3846 // other_controller should now contain only 3 urls: url2, url3 and url4.
3848 ASSERT_EQ(3, other_controller.GetEntryCount());
3850 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3852 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3853 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3854 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3855 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3856 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3857 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3859 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3862 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3863 // replace_entry set to true.
3864 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3865 NavigationControllerImpl& controller = controller_impl();
3866 const GURL url1("http://foo/1");
3867 const GURL url2("http://foo/2");
3868 const GURL url3("http://foo/3");
3870 NavigateAndCommit(url1);
3871 NavigateAndCommit(url2);
3873 // First two entries should have the same SiteInstance.
3874 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3875 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3876 EXPECT_EQ(instance1, instance2);
3877 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3878 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3879 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3881 scoped_ptr<TestWebContents> other_contents(
3882 static_cast<TestWebContents*>(CreateTestWebContents()));
3883 NavigationControllerImpl& other_controller = other_contents->GetController();
3884 other_contents->NavigateAndCommit(url3);
3885 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3886 other_controller.CopyStateFromAndPrune(&controller, true);
3888 // other_controller should now contain the 2 urls: url1 and url3.
3890 ASSERT_EQ(2, other_controller.GetEntryCount());
3892 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3894 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3895 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3896 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3897 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3899 // A new SiteInstance in a different BrowsingInstance should be used for the
3900 // new tab.
3901 SiteInstance* instance3 =
3902 other_controller.GetEntryAtIndex(1)->site_instance();
3903 EXPECT_NE(instance3, instance1);
3904 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3906 // The max page ID map should be copied over and updated with the max page ID
3907 // from the current tab.
3908 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3909 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3912 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3913 // entry count is 3 and replace_entry is true. We should not prune entries.
3914 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3915 NavigationControllerImpl& controller = controller_impl();
3916 size_t original_count = NavigationControllerImpl::max_entry_count();
3917 const int kMaxEntryCount = 3;
3919 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3921 const GURL url1("http://foo/1");
3922 const GURL url2("http://foo/2");
3923 const GURL url3("http://foo/3");
3924 const GURL url4("http://foo/4");
3926 // Create a PrunedListener to observe prune notifications.
3927 PrunedListener listener(&controller);
3929 NavigateAndCommit(url1);
3930 NavigateAndCommit(url2);
3931 NavigateAndCommit(url3);
3933 scoped_ptr<TestWebContents> other_contents(
3934 static_cast<TestWebContents*>(CreateTestWebContents()));
3935 NavigationControllerImpl& other_controller = other_contents->GetController();
3936 other_contents->NavigateAndCommit(url4);
3937 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3938 other_controller.CopyStateFromAndPrune(&controller, true);
3940 // We should have received no pruned notification.
3941 EXPECT_EQ(0, listener.notification_count_);
3943 // other_controller should now contain only 3 urls: url1, url2 and url4.
3945 ASSERT_EQ(3, other_controller.GetEntryCount());
3947 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3949 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3950 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3951 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3952 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3953 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3954 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3956 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3959 // Tests that we can navigate to the restored entries
3960 // imported by CopyStateFromAndPrune.
3961 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
3962 const GURL kRestoredUrls[] = {
3963 GURL("http://site1.com"),
3964 GURL("http://site2.com"),
3966 const GURL kInitialUrl("http://site3.com");
3968 std::vector<NavigationEntry*> entries;
3969 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
3970 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
3971 kRestoredUrls[i], Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
3972 std::string(), browser_context());
3973 entry->SetPageID(static_cast<int>(i));
3974 entries.push_back(entry);
3977 // Create a WebContents with restored entries.
3978 scoped_ptr<TestWebContents> source_contents(
3979 static_cast<TestWebContents*>(CreateTestWebContents()));
3980 NavigationControllerImpl& source_controller =
3981 source_contents->GetController();
3982 source_controller.Restore(
3983 entries.size() - 1,
3984 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
3985 &entries);
3986 ASSERT_EQ(0u, entries.size());
3987 source_controller.LoadIfNecessary();
3988 source_contents->CommitPendingNavigation();
3990 // Load a page, then copy state from |source_contents|.
3991 NavigateAndCommit(kInitialUrl);
3992 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
3993 controller_impl().CopyStateFromAndPrune(&source_controller, false);
3994 ASSERT_EQ(3, controller_impl().GetEntryCount());
3996 // Go back to the first entry one at a time and
3997 // verify that it works as expected.
3998 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
3999 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
4001 controller_impl().GoBack();
4002 contents()->CommitPendingNavigation();
4003 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4004 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
4006 controller_impl().GoBack();
4007 contents()->CommitPendingNavigation();
4008 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4009 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
4012 // Tests that navigations initiated from the page (with the history object)
4013 // work as expected, creating pending entries.
4014 TEST_F(NavigationControllerTest, HistoryNavigate) {
4015 NavigationControllerImpl& controller = controller_impl();
4016 const GURL url1("http://foo/1");
4017 const GURL url2("http://foo/2");
4018 const GURL url3("http://foo/3");
4020 NavigateAndCommit(url1);
4021 NavigateAndCommit(url2);
4022 NavigateAndCommit(url3);
4023 controller.GoBack();
4024 contents()->CommitPendingNavigation();
4025 process()->sink().ClearMessages();
4027 // Simulate the page calling history.back(). It should create a pending entry.
4028 contents()->OnGoToEntryAtOffset(-1);
4029 EXPECT_EQ(0, controller.GetPendingEntryIndex());
4030 // The actual cross-navigation is suspended until the current RVH tells us
4031 // it unloaded, simulate that.
4032 contents()->ProceedWithCrossSiteNavigation();
4033 // Also make sure we told the page to navigate.
4034 GURL nav_url = GetLastNavigationURL();
4035 EXPECT_EQ(url1, nav_url);
4036 contents()->CommitPendingNavigation();
4037 process()->sink().ClearMessages();
4039 // Now test history.forward()
4040 contents()->OnGoToEntryAtOffset(2);
4041 EXPECT_EQ(2, controller.GetPendingEntryIndex());
4042 // The actual cross-navigation is suspended until the current RVH tells us
4043 // it unloaded, simulate that.
4044 contents()->ProceedWithCrossSiteNavigation();
4045 nav_url = GetLastNavigationURL();
4046 EXPECT_EQ(url3, nav_url);
4047 contents()->CommitPendingNavigation();
4048 process()->sink().ClearMessages();
4050 controller.DiscardNonCommittedEntries();
4052 // Make sure an extravagant history.go() doesn't break.
4053 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4054 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4055 EXPECT_FALSE(HasNavigationRequest());
4058 // Test call to PruneAllButLastCommitted for the only entry.
4059 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
4060 NavigationControllerImpl& controller = controller_impl();
4061 const GURL url1("http://foo1");
4062 NavigateAndCommit(url1);
4064 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4066 controller.PruneAllButLastCommitted();
4068 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4069 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4072 // Test call to PruneAllButLastCommitted for first entry.
4073 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
4074 NavigationControllerImpl& controller = controller_impl();
4075 const GURL url1("http://foo/1");
4076 const GURL url2("http://foo/2");
4077 const GURL url3("http://foo/3");
4079 NavigateAndCommit(url1);
4080 NavigateAndCommit(url2);
4081 NavigateAndCommit(url3);
4082 controller.GoBack();
4083 controller.GoBack();
4084 contents()->CommitPendingNavigation();
4086 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4088 controller.PruneAllButLastCommitted();
4090 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4091 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4094 // Test call to PruneAllButLastCommitted for intermediate entry.
4095 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
4096 NavigationControllerImpl& controller = controller_impl();
4097 const GURL url1("http://foo/1");
4098 const GURL url2("http://foo/2");
4099 const GURL url3("http://foo/3");
4101 NavigateAndCommit(url1);
4102 NavigateAndCommit(url2);
4103 NavigateAndCommit(url3);
4104 controller.GoBack();
4105 contents()->CommitPendingNavigation();
4107 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4109 controller.PruneAllButLastCommitted();
4111 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4112 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
4115 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4116 // the list of entries.
4117 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
4118 NavigationControllerImpl& controller = controller_impl();
4119 const GURL url1("http://foo/1");
4120 const GURL url2("http://foo/2");
4121 const GURL url3("http://foo/3");
4123 NavigateAndCommit(url1);
4124 NavigateAndCommit(url2);
4126 // Create a pending entry that is not in the entry list.
4127 controller.LoadURL(
4128 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4129 EXPECT_TRUE(controller.GetPendingEntry());
4130 EXPECT_EQ(2, controller.GetEntryCount());
4132 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4133 controller.PruneAllButLastCommitted();
4135 // We should only have the last committed and pending entries at this point,
4136 // and the pending entry should still not be in the entry list.
4137 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4138 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
4139 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4140 EXPECT_TRUE(controller.GetPendingEntry());
4141 EXPECT_EQ(1, controller.GetEntryCount());
4143 // Try to commit the pending entry.
4144 main_test_rfh()->PrepareForCommit();
4145 main_test_rfh()->SendNavigate(2, url3);
4146 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4147 EXPECT_FALSE(controller.GetPendingEntry());
4148 EXPECT_EQ(2, controller.GetEntryCount());
4149 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
4152 // Test to ensure that when we do a history navigation back to the current
4153 // committed page (e.g., going forward to a slow-loading page, then pressing
4154 // the back button), we just stop the navigation to prevent the throbber from
4155 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4156 // start, but WebKit essentially ignores the navigation and never sends a
4157 // message to stop the throbber.
4158 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
4159 NavigationControllerImpl& controller = controller_impl();
4160 const GURL url0("http://foo/0");
4161 const GURL url1("http://foo/1");
4163 NavigateAndCommit(url0);
4164 NavigateAndCommit(url1);
4166 // Go back to the original page, then forward to the slow page, then back
4167 controller.GoBack();
4168 contents()->CommitPendingNavigation();
4170 controller.GoForward();
4171 EXPECT_EQ(1, controller.GetPendingEntryIndex());
4173 controller.GoBack();
4174 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4177 TEST_F(NavigationControllerTest, IsInitialNavigation) {
4178 NavigationControllerImpl& controller = controller_impl();
4179 TestNotificationTracker notifications;
4180 RegisterForAllNavNotifications(&notifications, &controller);
4182 // Initial state.
4183 EXPECT_TRUE(controller.IsInitialNavigation());
4185 // After commit, it stays false.
4186 const GURL url1("http://foo1");
4187 main_test_rfh()->SendNavigate(0, url1);
4188 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4189 navigation_entry_committed_counter_ = 0;
4190 EXPECT_FALSE(controller.IsInitialNavigation());
4192 // After starting a new navigation, it stays false.
4193 const GURL url2("http://foo2");
4194 controller.LoadURL(
4195 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4198 // Check that the favicon is not reused across a client redirect.
4199 // (crbug.com/28515)
4200 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4201 const GURL kPageWithFavicon("http://withfavicon.html");
4202 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4203 const GURL kIconURL("http://withfavicon.ico");
4204 const gfx::Image kDefaultFavicon = FaviconStatus().image;
4206 NavigationControllerImpl& controller = controller_impl();
4207 TestNotificationTracker notifications;
4208 RegisterForAllNavNotifications(&notifications, &controller);
4210 main_test_rfh()->SendNavigate(0, kPageWithFavicon);
4211 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4212 navigation_entry_committed_counter_ = 0;
4214 NavigationEntry* entry = controller.GetLastCommittedEntry();
4215 EXPECT_TRUE(entry);
4216 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4218 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4219 content::FaviconStatus& favicon_status = entry->GetFavicon();
4220 favicon_status.image = CreateImage(SK_ColorWHITE);
4221 favicon_status.url = kIconURL;
4222 favicon_status.valid = true;
4223 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4225 main_test_rfh()->SendNavigateWithTransition(
4226 0, // same page ID.
4227 kPageWithoutFavicon,
4228 ui::PAGE_TRANSITION_CLIENT_REDIRECT);
4229 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4230 navigation_entry_committed_counter_ = 0;
4232 entry = controller.GetLastCommittedEntry();
4233 EXPECT_TRUE(entry);
4234 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4236 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4239 // Check that the favicon is not cleared for NavigationEntries which were
4240 // previously navigated to.
4241 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4242 const GURL kUrl1("http://www.a.com/1");
4243 const GURL kUrl2("http://www.a.com/2");
4244 const GURL kIconURL("http://www.a.com/1/favicon.ico");
4246 NavigationControllerImpl& controller = controller_impl();
4247 TestNotificationTracker notifications;
4248 RegisterForAllNavNotifications(&notifications, &controller);
4250 main_test_rfh()->SendNavigate(0, kUrl1);
4251 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4252 navigation_entry_committed_counter_ = 0;
4254 // Simulate Chromium having set the favicon for |kUrl1|.
4255 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4256 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4257 EXPECT_TRUE(entry);
4258 content::FaviconStatus& favicon_status = entry->GetFavicon();
4259 favicon_status.image = favicon_image;
4260 favicon_status.url = kIconURL;
4261 favicon_status.valid = true;
4263 // Navigate to another page and go back to the original page.
4264 main_test_rfh()->SendNavigate(1, kUrl2);
4265 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4266 navigation_entry_committed_counter_ = 0;
4267 main_test_rfh()->SendNavigateWithTransition(
4269 kUrl1,
4270 ui::PAGE_TRANSITION_FORWARD_BACK);
4271 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4272 navigation_entry_committed_counter_ = 0;
4274 // Verify that the favicon for the page at |kUrl1| was not cleared.
4275 entry = controller.GetEntryAtIndex(0);
4276 EXPECT_TRUE(entry);
4277 EXPECT_EQ(kUrl1, entry->GetURL());
4278 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4281 // The test crashes on android: http://crbug.com/170449
4282 #if defined(OS_ANDROID)
4283 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4284 #else
4285 #define MAYBE_PurgeScreenshot PurgeScreenshot
4286 #endif
4287 // Tests that screenshot are purged correctly.
4288 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4289 NavigationControllerImpl& controller = controller_impl();
4291 NavigationEntryImpl* entry;
4293 // Navigate enough times to make sure that some screenshots are purged.
4294 for (int i = 0; i < 12; ++i) {
4295 const GURL url(base::StringPrintf("http://foo%d/", i));
4296 NavigateAndCommit(url);
4297 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4300 MockScreenshotManager* screenshot_manager =
4301 new MockScreenshotManager(&controller);
4302 controller.SetScreenshotManager(screenshot_manager);
4303 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4304 entry = controller.GetEntryAtIndex(i);
4305 screenshot_manager->TakeScreenshotFor(entry);
4306 EXPECT_TRUE(entry->screenshot().get());
4309 NavigateAndCommit(GURL("https://foo/"));
4310 EXPECT_EQ(13, controller.GetEntryCount());
4311 entry = controller.GetEntryAtIndex(11);
4312 screenshot_manager->TakeScreenshotFor(entry);
4314 for (int i = 0; i < 2; ++i) {
4315 entry = controller.GetEntryAtIndex(i);
4316 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4317 << " not purged";
4320 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4321 entry = controller.GetEntryAtIndex(i);
4322 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4325 // Navigate to index 5 and then try to assign screenshot to all entries.
4326 controller.GoToIndex(5);
4327 contents()->CommitPendingNavigation();
4328 EXPECT_EQ(5, 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 = 10; i <= 12; ++i) {
4335 entry = controller.GetEntryAtIndex(i);
4336 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4337 << " not purged";
4338 screenshot_manager->TakeScreenshotFor(entry);
4341 // Navigate to index 7 and assign screenshot to all entries.
4342 controller.GoToIndex(7);
4343 contents()->CommitPendingNavigation();
4344 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4345 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4346 entry = controller.GetEntryAtIndex(i);
4347 screenshot_manager->TakeScreenshotFor(entry);
4350 for (int i = 0; i < 2; ++i) {
4351 entry = controller.GetEntryAtIndex(i);
4352 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4353 << " not purged";
4356 // Clear all screenshots.
4357 EXPECT_EQ(13, controller.GetEntryCount());
4358 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4359 controller.ClearAllScreenshots();
4360 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4361 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4362 entry = controller.GetEntryAtIndex(i);
4363 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4364 << " not cleared";
4368 TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) {
4369 // Navigate.
4370 contents()->GetMainFrame()->SendNavigate(1, GURL("http://foo"));
4372 // Set title and favicon.
4373 base::string16 title(base::ASCIIToUTF16("Title"));
4374 FaviconStatus favicon;
4375 favicon.valid = true;
4376 favicon.url = GURL("http://foo/favicon.ico");
4377 controller().GetLastCommittedEntry()->SetTitle(title);
4378 controller().GetLastCommittedEntry()->GetFavicon() = favicon;
4380 // history.pushState() is called.
4381 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4382 GURL url("http://foo#foo");
4383 params.page_id = 2;
4384 params.url = url;
4385 params.page_state = PageState::CreateFromURL(url);
4386 params.was_within_same_page = true;
4387 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4389 // The title should immediately be visible on the new NavigationEntry.
4390 base::string16 new_title =
4391 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4392 EXPECT_EQ(title, new_title);
4393 FaviconStatus new_favicon =
4394 controller().GetLastCommittedEntry()->GetFavicon();
4395 EXPECT_EQ(favicon.valid, new_favicon.valid);
4396 EXPECT_EQ(favicon.url, new_favicon.url);
4399 // Test that the navigation controller clears its session history when a
4400 // navigation commits with the clear history list flag set.
4401 TEST_F(NavigationControllerTest, ClearHistoryList) {
4402 const GURL url1("http://foo1");
4403 const GURL url2("http://foo2");
4404 const GURL url3("http://foo3");
4405 const GURL url4("http://foo4");
4407 NavigationControllerImpl& controller = controller_impl();
4409 // Create a session history with three entries, second entry is active.
4410 NavigateAndCommit(url1);
4411 NavigateAndCommit(url2);
4412 NavigateAndCommit(url3);
4413 controller.GoBack();
4414 contents()->CommitPendingNavigation();
4415 EXPECT_EQ(3, controller.GetEntryCount());
4416 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4418 // Create a new pending navigation, and indicate that the session history
4419 // should be cleared.
4420 NavigationController::LoadURLParams params(url4);
4421 params.should_clear_history_list = true;
4422 controller.LoadURLWithParams(params);
4424 // Verify that the pending entry correctly indicates that the session history
4425 // should be cleared.
4426 NavigationEntryImpl* entry = controller.GetPendingEntry();
4427 ASSERT_TRUE(entry);
4428 EXPECT_TRUE(entry->should_clear_history_list());
4430 // Assume that the RenderFrame correctly cleared its history and commit the
4431 // navigation.
4432 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4433 switches::kEnableBrowserSideNavigation)) {
4434 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4436 contents()->GetPendingMainFrame()->
4437 set_simulate_history_list_was_cleared(true);
4438 contents()->CommitPendingNavigation();
4440 // Verify that the NavigationController's session history was correctly
4441 // cleared.
4442 EXPECT_EQ(1, controller.GetEntryCount());
4443 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4444 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4445 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4446 EXPECT_FALSE(controller.CanGoBack());
4447 EXPECT_FALSE(controller.CanGoForward());
4448 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4451 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
4452 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
4453 EXPECT_FALSE(contents()->GetDelegate());
4454 contents()->SetDelegate(delegate.get());
4456 // Submit a form.
4457 GURL url("http://foo");
4458 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4459 params.page_id = 1;
4460 params.url = url;
4461 params.transition = ui::PAGE_TRANSITION_FORM_SUBMIT;
4462 params.gesture = NavigationGestureUser;
4463 params.page_state = PageState::CreateFromURL(url);
4464 params.was_within_same_page = false;
4465 params.is_post = true;
4466 params.post_id = 2;
4467 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4469 // history.replaceState() is called.
4470 GURL replace_url("http://foo#foo");
4471 params.page_id = 1;
4472 params.url = replace_url;
4473 params.transition = ui::PAGE_TRANSITION_LINK;
4474 params.gesture = NavigationGestureUser;
4475 params.page_state = PageState::CreateFromURL(replace_url);
4476 params.was_within_same_page = true;
4477 params.is_post = false;
4478 params.post_id = -1;
4479 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4481 // Now reload. replaceState overrides the POST, so we should not show a
4482 // repost warning dialog.
4483 controller_impl().Reload(true);
4484 EXPECT_EQ(0, delegate->repost_form_warning_count());
4487 TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
4488 GURL url("http://foo");
4489 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4490 params.page_id = 1;
4491 params.url = url;
4492 params.transition = ui::PAGE_TRANSITION_LINK;
4493 params.gesture = NavigationGestureUser;
4494 params.page_state = PageState::CreateFromURL(url);
4495 params.was_within_same_page = false;
4496 params.is_post = true;
4497 params.post_id = 2;
4498 params.url_is_unreachable = true;
4499 // Navigate to new page
4501 LoadCommittedDetails details;
4502 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4503 EXPECT_EQ(PAGE_TYPE_ERROR,
4504 controller_impl().GetLastCommittedEntry()->GetPageType());
4505 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details.type);
4508 // Navigate to existing page.
4510 LoadCommittedDetails details;
4511 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4512 EXPECT_EQ(PAGE_TYPE_ERROR,
4513 controller_impl().GetLastCommittedEntry()->GetPageType());
4514 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
4517 // Navigate to same page.
4518 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4519 // same-page transition.
4520 controller_impl().LoadURL(
4521 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4522 params.transition = ui::PAGE_TRANSITION_TYPED;
4524 LoadCommittedDetails details;
4525 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4526 EXPECT_EQ(PAGE_TYPE_ERROR,
4527 controller_impl().GetLastCommittedEntry()->GetPageType());
4528 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, details.type);
4531 // Navigate in page.
4532 params.url = GURL("http://foo#foo");
4533 params.transition = ui::PAGE_TRANSITION_LINK;
4534 params.was_within_same_page = true;
4536 LoadCommittedDetails details;
4537 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4538 EXPECT_EQ(PAGE_TYPE_ERROR,
4539 controller_impl().GetLastCommittedEntry()->GetPageType());
4540 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, details.type);
4544 } // namespace content