IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_unittest.cc
blobdb0417a48dd534fc95679f72869d335225b1d33e
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/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/path_service.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/navigation_controller_impl.h"
15 #include "content/browser/frame_host/navigation_entry_impl.h"
16 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
17 #include "content/browser/frame_host/navigator.h"
18 #include "content/browser/site_instance_impl.h"
19 #include "content/browser/web_contents/web_contents_impl.h"
20 #include "content/common/frame_messages.h"
21 #include "content/common/view_messages.h"
22 #include "content/public/browser/navigation_details.h"
23 #include "content/public/browser/notification_registrar.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/web_contents_delegate.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/common/page_state.h"
29 #include "content/public/common/url_constants.h"
30 #include "content/public/test/mock_render_process_host.h"
31 #include "content/public/test/test_notification_tracker.h"
32 #include "content/public/test/test_utils.h"
33 #include "content/test/test_render_view_host.h"
34 #include "content/test/test_web_contents.h"
35 #include "net/base/net_util.h"
36 #include "skia/ext/platform_canvas.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 using base::Time;
41 namespace {
43 // Creates an image with a 1x1 SkBitmap of the specified |color|.
44 gfx::Image CreateImage(SkColor color) {
45 SkBitmap bitmap;
46 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
47 bitmap.allocPixels();
48 bitmap.eraseColor(color);
49 return gfx::Image::CreateFrom1xBitmap(bitmap);
52 // Returns true if images |a| and |b| have the same pixel data.
53 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
54 // Assume that if the 1x bitmaps match, the images match.
55 SkBitmap a_bitmap = a.AsBitmap();
56 SkBitmap b_bitmap = b.AsBitmap();
58 if (a_bitmap.width() != b_bitmap.width() ||
59 a_bitmap.height() != b_bitmap.height()) {
60 return false;
62 SkAutoLockPixels a_bitmap_lock(a_bitmap);
63 SkAutoLockPixels b_bitmap_lock(b_bitmap);
64 return memcmp(a_bitmap.getPixels(),
65 b_bitmap.getPixels(),
66 a_bitmap.getSize()) == 0;
69 class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
70 public:
71 explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
72 : content::NavigationEntryScreenshotManager(owner),
73 encoding_screenshot_in_progress_(false) {
76 virtual ~MockScreenshotManager() {
79 void TakeScreenshotFor(content::NavigationEntryImpl* entry) {
80 SkBitmap bitmap;
81 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
82 bitmap.allocPixels();
83 bitmap.eraseRGB(0, 0, 0);
84 encoding_screenshot_in_progress_ = true;
85 OnScreenshotTaken(entry->GetUniqueID(), true, bitmap);
86 WaitUntilScreenshotIsReady();
89 int GetScreenshotCount() {
90 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
93 void WaitUntilScreenshotIsReady() {
94 if (!encoding_screenshot_in_progress_)
95 return;
96 message_loop_runner_ = new content::MessageLoopRunner;
97 message_loop_runner_->Run();
100 private:
101 // Overridden from content::NavigationEntryScreenshotManager:
102 virtual void TakeScreenshotImpl(
103 content::RenderViewHost* host,
104 content::NavigationEntryImpl* entry) OVERRIDE {
107 virtual void OnScreenshotSet(content::NavigationEntryImpl* entry) OVERRIDE {
108 encoding_screenshot_in_progress_ = false;
109 NavigationEntryScreenshotManager::OnScreenshotSet(entry);
110 if (message_loop_runner_.get())
111 message_loop_runner_->Quit();
114 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
115 bool encoding_screenshot_in_progress_;
117 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
120 } // namespace
122 namespace content {
124 // TimeSmoother tests ----------------------------------------------------------
126 // With no duplicates, GetSmoothedTime should be the identity
127 // function.
128 TEST(TimeSmoother, Basic) {
129 NavigationControllerImpl::TimeSmoother smoother;
130 for (int64 i = 1; i < 1000; ++i) {
131 base::Time t = base::Time::FromInternalValue(i);
132 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
136 // With a single duplicate and timestamps thereafter increasing by one
137 // microsecond, the smoothed time should always be one behind.
138 TEST(TimeSmoother, SingleDuplicate) {
139 NavigationControllerImpl::TimeSmoother smoother;
140 base::Time t = base::Time::FromInternalValue(1);
141 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
142 for (int64 i = 1; i < 1000; ++i) {
143 base::Time expected_t = base::Time::FromInternalValue(i + 1);
144 t = base::Time::FromInternalValue(i);
145 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
149 // With k duplicates and timestamps thereafter increasing by one
150 // microsecond, the smoothed time should always be k behind.
151 TEST(TimeSmoother, ManyDuplicates) {
152 const int64 kNumDuplicates = 100;
153 NavigationControllerImpl::TimeSmoother smoother;
154 base::Time t = base::Time::FromInternalValue(1);
155 for (int64 i = 0; i < kNumDuplicates; ++i) {
156 base::Time expected_t = base::Time::FromInternalValue(i + 1);
157 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
159 for (int64 i = 1; i < 1000; ++i) {
160 base::Time expected_t =
161 base::Time::FromInternalValue(i + kNumDuplicates);
162 t = base::Time::FromInternalValue(i);
163 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
167 // If the clock jumps far back enough after a run of duplicates, it
168 // should immediately jump to that value.
169 TEST(TimeSmoother, ClockBackwardsJump) {
170 const int64 kNumDuplicates = 100;
171 NavigationControllerImpl::TimeSmoother smoother;
172 base::Time t = base::Time::FromInternalValue(1000);
173 for (int64 i = 0; i < kNumDuplicates; ++i) {
174 base::Time expected_t = base::Time::FromInternalValue(i + 1000);
175 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
177 t = base::Time::FromInternalValue(500);
178 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
181 // NavigationControllerTest ----------------------------------------------------
183 class NavigationControllerTest
184 : public RenderViewHostImplTestHarness,
185 public WebContentsObserver {
186 public:
187 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
190 virtual void SetUp() OVERRIDE {
191 RenderViewHostImplTestHarness::SetUp();
192 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents();
193 ASSERT_TRUE(web_contents); // The WebContents should be created by now.
194 WebContentsObserver::Observe(web_contents);
197 // WebContentsObserver:
198 virtual void DidStartNavigationToPendingEntry(
199 const GURL& url,
200 NavigationController::ReloadType reload_type) OVERRIDE {
201 navigated_url_ = url;
204 virtual void NavigationEntryCommitted(
205 const LoadCommittedDetails& load_details) OVERRIDE {
206 navigation_entry_committed_counter_++;
209 const GURL& navigated_url() const {
210 return navigated_url_;
213 NavigationControllerImpl& controller_impl() {
214 return static_cast<NavigationControllerImpl&>(controller());
217 protected:
218 GURL navigated_url_;
219 size_t navigation_entry_committed_counter_;
222 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
223 NavigationController* controller) {
224 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED,
225 Source<NavigationController>(controller));
226 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED,
227 Source<NavigationController>(controller));
230 SiteInstance* GetSiteInstanceFromEntry(NavigationEntry* entry) {
231 return NavigationEntryImpl::FromNavigationEntry(entry)->site_instance();
234 class TestWebContentsDelegate : public WebContentsDelegate {
235 public:
236 explicit TestWebContentsDelegate() :
237 navigation_state_change_count_(0) {}
239 int navigation_state_change_count() {
240 return navigation_state_change_count_;
243 // Keep track of whether the tab has notified us of a navigation state change.
244 virtual void NavigationStateChanged(const WebContents* source,
245 unsigned changed_flags) OVERRIDE {
246 navigation_state_change_count_++;
249 private:
250 // The number of times NavigationStateChanged has been called.
251 int navigation_state_change_count_;
254 // -----------------------------------------------------------------------------
256 TEST_F(NavigationControllerTest, Defaults) {
257 NavigationControllerImpl& controller = controller_impl();
259 EXPECT_FALSE(controller.GetPendingEntry());
260 EXPECT_FALSE(controller.GetVisibleEntry());
261 EXPECT_FALSE(controller.GetLastCommittedEntry());
262 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
263 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
264 EXPECT_EQ(controller.GetEntryCount(), 0);
265 EXPECT_FALSE(controller.CanGoBack());
266 EXPECT_FALSE(controller.CanGoForward());
269 TEST_F(NavigationControllerTest, GoToOffset) {
270 NavigationControllerImpl& controller = controller_impl();
271 TestNotificationTracker notifications;
272 RegisterForAllNavNotifications(&notifications, &controller);
274 const int kNumUrls = 5;
275 std::vector<GURL> urls(kNumUrls);
276 for (int i = 0; i < kNumUrls; ++i) {
277 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
280 test_rvh()->SendNavigate(0, urls[0]);
281 EXPECT_EQ(1U, navigation_entry_committed_counter_);
282 navigation_entry_committed_counter_ = 0;
283 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
284 EXPECT_FALSE(controller.CanGoBack());
285 EXPECT_FALSE(controller.CanGoForward());
286 EXPECT_FALSE(controller.CanGoToOffset(1));
288 for (int i = 1; i <= 4; ++i) {
289 test_rvh()->SendNavigate(i, urls[i]);
290 EXPECT_EQ(1U, navigation_entry_committed_counter_);
291 navigation_entry_committed_counter_ = 0;
292 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
293 EXPECT_TRUE(controller.CanGoToOffset(-i));
294 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
295 EXPECT_FALSE(controller.CanGoToOffset(1));
298 // We have loaded 5 pages, and are currently at the last-loaded page.
299 int url_index = 4;
301 enum Tests {
302 GO_TO_MIDDLE_PAGE = -2,
303 GO_FORWARDS = 1,
304 GO_BACKWARDS = -1,
305 GO_TO_BEGINNING = -2,
306 GO_TO_END = 4,
307 NUM_TESTS = 5,
310 const int test_offsets[NUM_TESTS] = {
311 GO_TO_MIDDLE_PAGE,
312 GO_FORWARDS,
313 GO_BACKWARDS,
314 GO_TO_BEGINNING,
315 GO_TO_END
318 for (int test = 0; test < NUM_TESTS; ++test) {
319 int offset = test_offsets[test];
320 controller.GoToOffset(offset);
321 url_index += offset;
322 // Check that the GoToOffset will land on the expected page.
323 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
324 test_rvh()->SendNavigate(url_index, urls[url_index]);
325 EXPECT_EQ(1U, navigation_entry_committed_counter_);
326 navigation_entry_committed_counter_ = 0;
327 // Check that we can go to any valid offset into the history.
328 for (size_t j = 0; j < urls.size(); ++j)
329 EXPECT_TRUE(controller.CanGoToOffset(j - url_index));
330 // Check that we can't go beyond the beginning or end of the history.
331 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1)));
332 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index));
336 TEST_F(NavigationControllerTest, LoadURL) {
337 NavigationControllerImpl& controller = controller_impl();
338 TestNotificationTracker notifications;
339 RegisterForAllNavNotifications(&notifications, &controller);
341 const GURL url1("http://foo1");
342 const GURL url2("http://foo2");
344 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
345 // Creating a pending notification should not have issued any of the
346 // notifications we're listening for.
347 EXPECT_EQ(0U, notifications.size());
349 // The load should now be pending.
350 EXPECT_EQ(controller.GetEntryCount(), 0);
351 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
352 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
353 EXPECT_FALSE(controller.GetLastCommittedEntry());
354 ASSERT_TRUE(controller.GetPendingEntry());
355 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
356 EXPECT_FALSE(controller.CanGoBack());
357 EXPECT_FALSE(controller.CanGoForward());
358 EXPECT_EQ(contents()->GetMaxPageID(), -1);
360 // Neither the timestamp nor the status code should have been set yet.
361 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
362 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
364 // We should have gotten no notifications from the preceeding checks.
365 EXPECT_EQ(0U, notifications.size());
367 test_rvh()->SendNavigate(0, url1);
368 EXPECT_EQ(1U, navigation_entry_committed_counter_);
369 navigation_entry_committed_counter_ = 0;
371 // The load should now be committed.
372 EXPECT_EQ(controller.GetEntryCount(), 1);
373 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
374 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
375 EXPECT_TRUE(controller.GetLastCommittedEntry());
376 EXPECT_FALSE(controller.GetPendingEntry());
377 ASSERT_TRUE(controller.GetVisibleEntry());
378 EXPECT_FALSE(controller.CanGoBack());
379 EXPECT_FALSE(controller.CanGoForward());
380 EXPECT_EQ(contents()->GetMaxPageID(), 0);
381 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
382 controller.GetLastCommittedEntry())->bindings());
384 // The timestamp should have been set.
385 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
387 // Load another...
388 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
390 // The load should now be pending.
391 EXPECT_EQ(controller.GetEntryCount(), 1);
392 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
393 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
394 EXPECT_TRUE(controller.GetLastCommittedEntry());
395 ASSERT_TRUE(controller.GetPendingEntry());
396 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
397 // TODO(darin): maybe this should really be true?
398 EXPECT_FALSE(controller.CanGoBack());
399 EXPECT_FALSE(controller.CanGoForward());
400 EXPECT_EQ(contents()->GetMaxPageID(), 0);
402 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
404 // Simulate the beforeunload ack for the cross-site transition, and then the
405 // commit.
406 test_rvh()->SendShouldCloseACK(true);
407 static_cast<TestRenderViewHost*>(
408 contents()->GetPendingRenderViewHost())->SendNavigate(1, url2);
409 EXPECT_EQ(1U, navigation_entry_committed_counter_);
410 navigation_entry_committed_counter_ = 0;
412 // The load should now be committed.
413 EXPECT_EQ(controller.GetEntryCount(), 2);
414 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
415 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
416 EXPECT_TRUE(controller.GetLastCommittedEntry());
417 EXPECT_FALSE(controller.GetPendingEntry());
418 ASSERT_TRUE(controller.GetVisibleEntry());
419 EXPECT_TRUE(controller.CanGoBack());
420 EXPECT_FALSE(controller.CanGoForward());
421 EXPECT_EQ(contents()->GetMaxPageID(), 1);
423 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
426 namespace {
428 base::Time GetFixedTime(base::Time time) {
429 return time;
432 } // namespace
434 TEST_F(NavigationControllerTest, LoadURLSameTime) {
435 NavigationControllerImpl& controller = controller_impl();
436 TestNotificationTracker notifications;
437 RegisterForAllNavNotifications(&notifications, &controller);
439 // Set the clock to always return a timestamp of 1.
440 controller.SetGetTimestampCallbackForTest(
441 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
443 const GURL url1("http://foo1");
444 const GURL url2("http://foo2");
446 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
448 test_rvh()->SendNavigate(0, url1);
449 EXPECT_EQ(1U, navigation_entry_committed_counter_);
450 navigation_entry_committed_counter_ = 0;
452 // Load another...
453 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
455 // Simulate the beforeunload ack for the cross-site transition, and then the
456 // commit.
457 test_rvh()->SendShouldCloseACK(true);
458 test_rvh()->SendNavigate(1, url2);
459 EXPECT_EQ(1U, navigation_entry_committed_counter_);
460 navigation_entry_committed_counter_ = 0;
462 // The two loads should now be committed.
463 ASSERT_EQ(controller.GetEntryCount(), 2);
465 // Timestamps should be distinct despite the clock returning the
466 // same value.
467 EXPECT_EQ(1u,
468 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
469 EXPECT_EQ(2u,
470 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
473 void CheckNavigationEntryMatchLoadParams(
474 NavigationController::LoadURLParams& load_params,
475 NavigationEntryImpl* entry) {
476 EXPECT_EQ(load_params.url, entry->GetURL());
477 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
478 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
479 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
480 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
482 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
483 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
484 if (!load_params.virtual_url_for_data_url.is_empty()) {
485 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
487 if (NavigationController::UA_OVERRIDE_INHERIT !=
488 load_params.override_user_agent) {
489 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
490 load_params.override_user_agent);
491 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
493 EXPECT_EQ(load_params.browser_initiated_post_data,
494 entry->GetBrowserInitiatedPostData());
495 EXPECT_EQ(load_params.transferred_global_request_id,
496 entry->transferred_global_request_id());
499 TEST_F(NavigationControllerTest, LoadURLWithParams) {
500 NavigationControllerImpl& controller = controller_impl();
502 NavigationController::LoadURLParams load_params(GURL("http://foo"));
503 load_params.referrer =
504 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
505 load_params.transition_type = PAGE_TRANSITION_GENERATED;
506 load_params.extra_headers = "content-type: text/plain";
507 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
508 load_params.is_renderer_initiated = true;
509 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
510 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
512 controller.LoadURLWithParams(load_params);
513 NavigationEntryImpl* entry =
514 NavigationEntryImpl::FromNavigationEntry(
515 controller.GetPendingEntry());
517 // The timestamp should not have been set yet.
518 ASSERT_TRUE(entry);
519 EXPECT_TRUE(entry->GetTimestamp().is_null());
521 CheckNavigationEntryMatchLoadParams(load_params, entry);
524 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
525 NavigationControllerImpl& controller = controller_impl();
527 NavigationController::LoadURLParams load_params(
528 GURL("data:text/html,dataurl"));
529 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
530 load_params.base_url_for_data_url = GURL("http://foo");
531 load_params.virtual_url_for_data_url = GURL(kAboutBlankURL);
532 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
534 controller.LoadURLWithParams(load_params);
535 NavigationEntryImpl* entry =
536 NavigationEntryImpl::FromNavigationEntry(
537 controller.GetPendingEntry());
539 CheckNavigationEntryMatchLoadParams(load_params, entry);
542 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
543 NavigationControllerImpl& controller = controller_impl();
545 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
546 load_params.transition_type = PAGE_TRANSITION_TYPED;
547 load_params.load_type =
548 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
549 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
552 const unsigned char* raw_data =
553 reinterpret_cast<const unsigned char*>("d\n\0a2");
554 const int length = 5;
555 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
556 scoped_refptr<base::RefCountedBytes> data =
557 base::RefCountedBytes::TakeVector(&post_data_vector);
558 load_params.browser_initiated_post_data = data.get();
560 controller.LoadURLWithParams(load_params);
561 NavigationEntryImpl* entry =
562 NavigationEntryImpl::FromNavigationEntry(
563 controller.GetPendingEntry());
565 CheckNavigationEntryMatchLoadParams(load_params, entry);
568 // Tests what happens when the same page is loaded again. Should not create a
569 // new session history entry. This is what happens when you press enter in the
570 // URL bar to reload: a pending entry is created and then it is discarded when
571 // the load commits (because WebCore didn't actually make a new entry).
572 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
573 NavigationControllerImpl& controller = controller_impl();
574 TestNotificationTracker notifications;
575 RegisterForAllNavNotifications(&notifications, &controller);
577 const GURL url1("http://foo1");
579 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
580 EXPECT_EQ(0U, notifications.size());
581 test_rvh()->SendNavigate(0, url1);
582 EXPECT_EQ(1U, navigation_entry_committed_counter_);
583 navigation_entry_committed_counter_ = 0;
585 ASSERT_TRUE(controller.GetVisibleEntry());
586 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
587 EXPECT_FALSE(timestamp.is_null());
589 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
590 EXPECT_EQ(0U, notifications.size());
591 test_rvh()->SendNavigate(0, url1);
592 EXPECT_EQ(1U, navigation_entry_committed_counter_);
593 navigation_entry_committed_counter_ = 0;
595 // We should not have produced a new session history entry.
596 EXPECT_EQ(controller.GetEntryCount(), 1);
597 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
598 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
599 EXPECT_TRUE(controller.GetLastCommittedEntry());
600 EXPECT_FALSE(controller.GetPendingEntry());
601 ASSERT_TRUE(controller.GetVisibleEntry());
602 EXPECT_FALSE(controller.CanGoBack());
603 EXPECT_FALSE(controller.CanGoForward());
605 // The timestamp should have been updated.
607 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
608 // EXPECT_GT once we guarantee that timestamps are unique.
609 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
612 // Load the same page twice, once as a GET and once as a POST.
613 // We should update the post state on the NavigationEntry.
614 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
615 NavigationControllerImpl& controller = controller_impl();
616 TestNotificationTracker notifications;
617 RegisterForAllNavNotifications(&notifications, &controller);
619 const GURL url1("http://foo1");
621 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
622 ViewHostMsg_FrameNavigate_Params params;
623 params.page_id = 0;
624 params.url = url1;
625 params.transition = PAGE_TRANSITION_TYPED;
626 params.is_post = true;
627 params.post_id = 123;
628 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
629 test_rvh()->SendNavigateWithParams(&params);
631 // The post data should be visible.
632 NavigationEntry* entry = controller.GetVisibleEntry();
633 ASSERT_TRUE(entry);
634 EXPECT_TRUE(entry->GetHasPostData());
635 EXPECT_EQ(entry->GetPostID(), 123);
637 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
638 test_rvh()->SendNavigate(0, url1);
640 // We should not have produced a new session history entry.
641 ASSERT_EQ(controller.GetVisibleEntry(), entry);
643 // The post data should have been cleared due to the GET.
644 EXPECT_FALSE(entry->GetHasPostData());
645 EXPECT_EQ(entry->GetPostID(), 0);
648 // Tests loading a URL but discarding it before the load commits.
649 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
650 NavigationControllerImpl& controller = controller_impl();
651 TestNotificationTracker notifications;
652 RegisterForAllNavNotifications(&notifications, &controller);
654 const GURL url1("http://foo1");
655 const GURL url2("http://foo2");
657 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
658 EXPECT_EQ(0U, notifications.size());
659 test_rvh()->SendNavigate(0, url1);
660 EXPECT_EQ(1U, navigation_entry_committed_counter_);
661 navigation_entry_committed_counter_ = 0;
663 ASSERT_TRUE(controller.GetVisibleEntry());
664 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
665 EXPECT_FALSE(timestamp.is_null());
667 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
668 controller.DiscardNonCommittedEntries();
669 EXPECT_EQ(0U, notifications.size());
671 // Should not have produced a new session history entry.
672 EXPECT_EQ(controller.GetEntryCount(), 1);
673 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
674 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
675 EXPECT_TRUE(controller.GetLastCommittedEntry());
676 EXPECT_FALSE(controller.GetPendingEntry());
677 ASSERT_TRUE(controller.GetVisibleEntry());
678 EXPECT_FALSE(controller.CanGoBack());
679 EXPECT_FALSE(controller.CanGoForward());
681 // Timestamp should not have changed.
682 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
685 // Tests navigations that come in unrequested. This happens when the user
686 // navigates from the web page, and here we test that there is no pending entry.
687 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
688 NavigationControllerImpl& controller = controller_impl();
689 TestNotificationTracker notifications;
690 RegisterForAllNavNotifications(&notifications, &controller);
692 // First make an existing committed entry.
693 const GURL kExistingURL1("http://eh");
694 controller.LoadURL(
695 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
696 test_rvh()->SendNavigate(0, kExistingURL1);
697 EXPECT_EQ(1U, navigation_entry_committed_counter_);
698 navigation_entry_committed_counter_ = 0;
700 // Do a new navigation without making a pending one.
701 const GURL kNewURL("http://see");
702 test_rvh()->SendNavigate(99, kNewURL);
704 // There should no longer be any pending entry, and the third navigation we
705 // just made should be committed.
706 EXPECT_EQ(1U, navigation_entry_committed_counter_);
707 navigation_entry_committed_counter_ = 0;
708 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
709 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
710 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
713 // Tests navigating to a new URL when there is a new pending navigation that is
714 // not the one that just loaded. This will happen if the user types in a URL to
715 // somewhere slow, and then navigates the current page before the typed URL
716 // commits.
717 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
718 NavigationControllerImpl& controller = controller_impl();
719 TestNotificationTracker notifications;
720 RegisterForAllNavNotifications(&notifications, &controller);
722 // First make an existing committed entry.
723 const GURL kExistingURL1("http://eh");
724 controller.LoadURL(
725 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
726 test_rvh()->SendNavigate(0, kExistingURL1);
727 EXPECT_EQ(1U, navigation_entry_committed_counter_);
728 navigation_entry_committed_counter_ = 0;
730 // Make a pending entry to somewhere new.
731 const GURL kExistingURL2("http://bee");
732 controller.LoadURL(
733 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
734 EXPECT_EQ(0U, notifications.size());
736 // After the beforeunload but before it commits, do a new navigation.
737 test_rvh()->SendShouldCloseACK(true);
738 const GURL kNewURL("http://see");
739 static_cast<TestRenderViewHost*>(
740 contents()->GetPendingRenderViewHost())->SendNavigate(3, kNewURL);
742 // There should no longer be any pending entry, and the third navigation we
743 // just made should be committed.
744 EXPECT_EQ(1U, navigation_entry_committed_counter_);
745 navigation_entry_committed_counter_ = 0;
746 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
747 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
748 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
751 // Tests navigating to a new URL when there is a pending back/forward
752 // navigation. This will happen if the user hits back, but before that commits,
753 // they navigate somewhere new.
754 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
755 NavigationControllerImpl& controller = controller_impl();
756 TestNotificationTracker notifications;
757 RegisterForAllNavNotifications(&notifications, &controller);
759 // First make some history.
760 const GURL kExistingURL1("http://foo/eh");
761 controller.LoadURL(
762 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
763 test_rvh()->SendNavigate(0, kExistingURL1);
764 EXPECT_EQ(1U, navigation_entry_committed_counter_);
765 navigation_entry_committed_counter_ = 0;
767 const GURL kExistingURL2("http://foo/bee");
768 controller.LoadURL(
769 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
770 test_rvh()->SendNavigate(1, kExistingURL2);
771 EXPECT_EQ(1U, navigation_entry_committed_counter_);
772 navigation_entry_committed_counter_ = 0;
774 // Now make a pending back/forward navigation. The zeroth entry should be
775 // pending.
776 controller.GoBack();
777 EXPECT_EQ(0U, notifications.size());
778 EXPECT_EQ(0, controller.GetPendingEntryIndex());
779 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
781 // Before that commits, do a new navigation.
782 const GURL kNewURL("http://foo/see");
783 LoadCommittedDetails details;
784 test_rvh()->SendNavigate(3, kNewURL);
786 // There should no longer be any pending entry, and the third navigation we
787 // just made should be committed.
788 EXPECT_EQ(1U, navigation_entry_committed_counter_);
789 navigation_entry_committed_counter_ = 0;
790 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
791 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
792 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
795 // Tests navigating to a new URL when there is a pending back/forward
796 // navigation to a cross-process, privileged URL. This will happen if the user
797 // hits back, but before that commits, they navigate somewhere new.
798 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
799 NavigationControllerImpl& controller = controller_impl();
800 TestNotificationTracker notifications;
801 RegisterForAllNavNotifications(&notifications, &controller);
803 // First make some history, starting with a privileged URL.
804 const GURL kExistingURL1("http://privileged");
805 controller.LoadURL(
806 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
807 // Pretend it has bindings so we can tell if we incorrectly copy it.
808 test_rvh()->AllowBindings(2);
809 test_rvh()->SendNavigate(0, kExistingURL1);
810 EXPECT_EQ(1U, navigation_entry_committed_counter_);
811 navigation_entry_committed_counter_ = 0;
813 // Navigate cross-process to a second URL.
814 const GURL kExistingURL2("http://foo/eh");
815 controller.LoadURL(
816 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
817 test_rvh()->SendShouldCloseACK(true);
818 TestRenderViewHost* foo_rvh = static_cast<TestRenderViewHost*>(
819 contents()->GetPendingRenderViewHost());
820 foo_rvh->SendNavigate(1, kExistingURL2);
821 EXPECT_EQ(1U, navigation_entry_committed_counter_);
822 navigation_entry_committed_counter_ = 0;
824 // Now make a pending back/forward navigation to a privileged entry.
825 // The zeroth entry should be pending.
826 controller.GoBack();
827 foo_rvh->SendShouldCloseACK(true);
828 EXPECT_EQ(0U, notifications.size());
829 EXPECT_EQ(0, controller.GetPendingEntryIndex());
830 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
831 EXPECT_EQ(2, NavigationEntryImpl::FromNavigationEntry(
832 controller.GetPendingEntry())->bindings());
834 // Before that commits, do a new navigation.
835 const GURL kNewURL("http://foo/bee");
836 LoadCommittedDetails details;
837 foo_rvh->SendNavigate(3, kNewURL);
839 // There should no longer be any pending entry, and the third navigation we
840 // just made should be committed.
841 EXPECT_EQ(1U, navigation_entry_committed_counter_);
842 navigation_entry_committed_counter_ = 0;
843 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
844 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
845 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
846 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
847 controller.GetLastCommittedEntry())->bindings());
850 // Tests navigating to an existing URL when there is a pending new navigation.
851 // This will happen if the user enters a URL, but before that commits, the
852 // current page fires history.back().
853 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
854 NavigationControllerImpl& controller = controller_impl();
855 TestNotificationTracker notifications;
856 RegisterForAllNavNotifications(&notifications, &controller);
858 // First make some history.
859 const GURL kExistingURL1("http://foo/eh");
860 controller.LoadURL(
861 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
862 test_rvh()->SendNavigate(0, kExistingURL1);
863 EXPECT_EQ(1U, navigation_entry_committed_counter_);
864 navigation_entry_committed_counter_ = 0;
866 const GURL kExistingURL2("http://foo/bee");
867 controller.LoadURL(
868 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
869 test_rvh()->SendNavigate(1, kExistingURL2);
870 EXPECT_EQ(1U, navigation_entry_committed_counter_);
871 navigation_entry_committed_counter_ = 0;
873 // Now make a pending new navigation.
874 const GURL kNewURL("http://foo/see");
875 controller.LoadURL(
876 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
877 EXPECT_EQ(0U, notifications.size());
878 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
879 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
881 // Before that commits, a back navigation from the renderer commits.
882 test_rvh()->SendNavigate(0, kExistingURL1);
884 // There should no longer be any pending entry, and the back navigation we
885 // just made should be committed.
886 EXPECT_EQ(1U, navigation_entry_committed_counter_);
887 navigation_entry_committed_counter_ = 0;
888 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
889 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
890 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
893 // Tests an ignored navigation when there is a pending new navigation.
894 // This will happen if the user enters a URL, but before that commits, the
895 // current blank page reloads. See http://crbug.com/77507.
896 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
897 NavigationControllerImpl& controller = controller_impl();
898 TestNotificationTracker notifications;
899 RegisterForAllNavNotifications(&notifications, &controller);
901 // Set a WebContentsDelegate to listen for state changes.
902 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
903 EXPECT_FALSE(contents()->GetDelegate());
904 contents()->SetDelegate(delegate.get());
906 // Without any navigations, the renderer starts at about:blank.
907 const GURL kExistingURL(kAboutBlankURL);
909 // Now make a pending new navigation.
910 const GURL kNewURL("http://eh");
911 controller.LoadURL(
912 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
913 EXPECT_EQ(0U, notifications.size());
914 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
915 EXPECT_TRUE(controller.GetPendingEntry());
916 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
917 EXPECT_EQ(1, delegate->navigation_state_change_count());
919 // Before that commits, a document.write and location.reload can cause the
920 // renderer to send a FrameNavigate with page_id -1.
921 test_rvh()->SendNavigate(-1, kExistingURL);
923 // This should clear the pending entry and notify of a navigation state
924 // change, so that we do not keep displaying kNewURL.
925 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
926 EXPECT_FALSE(controller.GetPendingEntry());
927 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
928 EXPECT_EQ(2, delegate->navigation_state_change_count());
930 contents()->SetDelegate(NULL);
933 // Tests that the pending entry state is correct after an abort.
934 // We do not want to clear the pending entry, so that the user doesn't
935 // lose a typed URL. (See http://crbug.com/9682.)
936 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
937 NavigationControllerImpl& controller = controller_impl();
938 TestNotificationTracker notifications;
939 RegisterForAllNavNotifications(&notifications, &controller);
941 // Set a WebContentsDelegate to listen for state changes.
942 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
943 EXPECT_FALSE(contents()->GetDelegate());
944 contents()->SetDelegate(delegate.get());
946 // Start with a pending new navigation.
947 const GURL kNewURL("http://eh");
948 controller.LoadURL(
949 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
950 EXPECT_EQ(0U, notifications.size());
951 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
952 EXPECT_TRUE(controller.GetPendingEntry());
953 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
954 EXPECT_EQ(1, delegate->navigation_state_change_count());
956 // It may abort before committing, if it's a download or due to a stop or
957 // a new navigation from the user.
958 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
959 params.frame_id = 1;
960 params.is_main_frame = true;
961 params.error_code = net::ERR_ABORTED;
962 params.error_description = base::string16();
963 params.url = kNewURL;
964 params.showing_repost_interstitial = false;
965 main_test_rfh()->OnMessageReceived(
966 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
967 params));
969 // This should not clear the pending entry or notify of a navigation state
970 // change, so that we keep displaying kNewURL (until the user clears it).
971 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
972 EXPECT_TRUE(controller.GetPendingEntry());
973 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
974 EXPECT_EQ(1, delegate->navigation_state_change_count());
975 NavigationEntry* pending_entry = controller.GetPendingEntry();
977 // Ensure that a reload keeps the same pending entry.
978 controller.Reload(true);
979 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
980 EXPECT_TRUE(controller.GetPendingEntry());
981 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
982 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
984 contents()->SetDelegate(NULL);
987 // Tests that the pending URL is not visible during a renderer-initiated
988 // redirect and abort. See http://crbug.com/83031.
989 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
990 NavigationControllerImpl& controller = controller_impl();
991 TestNotificationTracker notifications;
992 RegisterForAllNavNotifications(&notifications, &controller);
994 // First make an existing committed entry.
995 const GURL kExistingURL("http://foo/eh");
996 controller.LoadURL(kExistingURL, content::Referrer(),
997 content::PAGE_TRANSITION_TYPED, std::string());
998 test_rvh()->SendNavigate(0, kExistingURL);
999 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1000 navigation_entry_committed_counter_ = 0;
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 // Now make a pending new navigation, initiated by the renderer.
1008 const GURL kNewURL("http://foo/bee");
1009 NavigationController::LoadURLParams load_url_params(kNewURL);
1010 load_url_params.transition_type = PAGE_TRANSITION_TYPED;
1011 load_url_params.is_renderer_initiated = true;
1012 controller.LoadURLWithParams(load_url_params);
1013 EXPECT_EQ(0U, notifications.size());
1014 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1015 EXPECT_TRUE(controller.GetPendingEntry());
1016 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1017 EXPECT_EQ(0, delegate->navigation_state_change_count());
1019 // The visible entry should be the last committed URL, not the pending one.
1020 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1022 // Now the navigation redirects.
1023 const GURL kRedirectURL("http://foo/see");
1024 test_rvh()->OnMessageReceived(
1025 ViewHostMsg_DidRedirectProvisionalLoad(0, // routing_id
1026 -1, // pending page_id
1027 kNewURL, // old url
1028 kRedirectURL)); // new url
1030 // We don't want to change the NavigationEntry's url, in case it cancels.
1031 // Prevents regression of http://crbug.com/77786.
1032 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1034 // It may abort before committing, if it's a download or due to a stop or
1035 // a new navigation from the user.
1036 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1037 params.frame_id = 1;
1038 params.is_main_frame = true;
1039 params.error_code = net::ERR_ABORTED;
1040 params.error_description = base::string16();
1041 params.url = kRedirectURL;
1042 params.showing_repost_interstitial = false;
1043 main_test_rfh()->OnMessageReceived(
1044 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1045 params));
1047 // Because the pending entry is renderer initiated and not visible, we
1048 // clear it when it fails.
1049 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1050 EXPECT_FALSE(controller.GetPendingEntry());
1051 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1052 EXPECT_EQ(0, delegate->navigation_state_change_count());
1054 // The visible entry should be the last committed URL, not the pending one,
1055 // so that no spoof is possible.
1056 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1058 contents()->SetDelegate(NULL);
1061 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1062 // at the time they committed. http://crbug.com/173672.
1063 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1064 NavigationControllerImpl& controller = controller_impl();
1065 TestNotificationTracker notifications;
1066 RegisterForAllNavNotifications(&notifications, &controller);
1068 const GURL url1("http://foo1");
1069 const GURL url2("http://foo2");
1071 // Navigate to a first, unprivileged URL.
1072 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1073 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1074 NavigationEntryImpl::FromNavigationEntry(
1075 controller.GetPendingEntry())->bindings());
1077 // Commit.
1078 TestRenderViewHost* orig_rvh = static_cast<TestRenderViewHost*>(test_rvh());
1079 orig_rvh->SendNavigate(0, url1);
1080 EXPECT_EQ(controller.GetEntryCount(), 1);
1081 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1082 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
1083 controller.GetLastCommittedEntry())->bindings());
1085 // Manually increase the number of active views in the SiteInstance
1086 // that orig_rvh belongs to, to prevent it from being destroyed when
1087 // it gets swapped out, so that we can reuse orig_rvh when the
1088 // controller goes back.
1089 static_cast<SiteInstanceImpl*>(orig_rvh->GetSiteInstance())->
1090 increment_active_view_count();
1092 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1093 // transition, and set bindings on the pending RenderViewHost to simulate a
1094 // privileged url.
1095 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1096 orig_rvh->SendShouldCloseACK(true);
1097 contents()->GetPendingRenderViewHost()->AllowBindings(1);
1098 static_cast<TestRenderViewHost*>(
1099 contents()->GetPendingRenderViewHost())->SendNavigate(1, url2);
1101 // The second load should be committed, and bindings should be remembered.
1102 EXPECT_EQ(controller.GetEntryCount(), 2);
1103 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1104 EXPECT_TRUE(controller.CanGoBack());
1105 EXPECT_EQ(1, NavigationEntryImpl::FromNavigationEntry(
1106 controller.GetLastCommittedEntry())->bindings());
1108 // Going back, the first entry should still appear unprivileged.
1109 controller.GoBack();
1110 orig_rvh->SendNavigate(0, url1);
1111 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1112 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
1113 controller.GetLastCommittedEntry())->bindings());
1116 TEST_F(NavigationControllerTest, Reload) {
1117 NavigationControllerImpl& controller = controller_impl();
1118 TestNotificationTracker notifications;
1119 RegisterForAllNavNotifications(&notifications, &controller);
1121 const GURL url1("http://foo1");
1123 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1124 EXPECT_EQ(0U, notifications.size());
1125 test_rvh()->SendNavigate(0, url1);
1126 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1127 navigation_entry_committed_counter_ = 0;
1128 ASSERT_TRUE(controller.GetVisibleEntry());
1129 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1130 controller.Reload(true);
1131 EXPECT_EQ(0U, notifications.size());
1133 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1134 EXPECT_FALSE(timestamp.is_null());
1136 // The reload is pending.
1137 EXPECT_EQ(controller.GetEntryCount(), 1);
1138 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1139 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1140 EXPECT_TRUE(controller.GetLastCommittedEntry());
1141 EXPECT_TRUE(controller.GetPendingEntry());
1142 EXPECT_FALSE(controller.CanGoBack());
1143 EXPECT_FALSE(controller.CanGoForward());
1144 // Make sure the title has been cleared (will be redrawn just after reload).
1145 // Avoids a stale cached title when the new page being reloaded has no title.
1146 // See http://crbug.com/96041.
1147 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1149 test_rvh()->SendNavigate(0, url1);
1150 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1151 navigation_entry_committed_counter_ = 0;
1153 // Now the reload is committed.
1154 EXPECT_EQ(controller.GetEntryCount(), 1);
1155 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1156 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1157 EXPECT_TRUE(controller.GetLastCommittedEntry());
1158 EXPECT_FALSE(controller.GetPendingEntry());
1159 EXPECT_FALSE(controller.CanGoBack());
1160 EXPECT_FALSE(controller.CanGoForward());
1162 // The timestamp should have been updated.
1163 ASSERT_TRUE(controller.GetVisibleEntry());
1164 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1167 // Tests what happens when a reload navigation produces a new page.
1168 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1169 NavigationControllerImpl& controller = controller_impl();
1170 TestNotificationTracker notifications;
1171 RegisterForAllNavNotifications(&notifications, &controller);
1173 const GURL url1("http://foo1");
1174 const GURL url2("http://foo2");
1176 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1177 test_rvh()->SendNavigate(0, url1);
1178 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1179 navigation_entry_committed_counter_ = 0;
1181 controller.Reload(true);
1182 EXPECT_EQ(0U, notifications.size());
1184 test_rvh()->SendNavigate(1, url2);
1185 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1186 navigation_entry_committed_counter_ = 0;
1188 // Now the reload is committed.
1189 EXPECT_EQ(controller.GetEntryCount(), 2);
1190 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1191 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1192 EXPECT_TRUE(controller.GetLastCommittedEntry());
1193 EXPECT_FALSE(controller.GetPendingEntry());
1194 EXPECT_TRUE(controller.CanGoBack());
1195 EXPECT_FALSE(controller.CanGoForward());
1198 // This test ensures that when a guest renderer reloads, the reload goes through
1199 // without ending up in the "we have a wrong process for the URL" branch in
1200 // NavigationControllerImpl::ReloadInternal.
1201 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1202 NavigationControllerImpl& controller = controller_impl();
1204 const GURL url1("http://foo1");
1205 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1206 test_rvh()->SendNavigate(0, url1);
1207 ASSERT_TRUE(controller.GetVisibleEntry());
1209 // Make the entry believe its RenderProcessHost is a guest.
1210 NavigationEntryImpl* entry1 =
1211 NavigationEntryImpl::FromNavigationEntry(controller.GetVisibleEntry());
1212 reinterpret_cast<MockRenderProcessHost*>(
1213 entry1->site_instance()->GetProcess())->SetIsGuest(true);
1215 // And reload.
1216 controller.Reload(true);
1218 // The reload is pending. Check that the NavigationEntry didn't get replaced
1219 // because of having the wrong process.
1220 EXPECT_EQ(controller.GetEntryCount(), 1);
1221 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1222 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1224 NavigationEntryImpl* entry2 =
1225 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1226 EXPECT_EQ(entry1, entry2);
1229 #if !defined(OS_ANDROID) // http://crbug.com/157428
1230 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1231 NavigationControllerImpl& controller = controller_impl();
1232 TestNotificationTracker notifications;
1233 RegisterForAllNavNotifications(&notifications, &controller);
1235 const GURL original_url("http://foo1");
1236 const GURL final_url("http://foo2");
1238 // Load up the original URL, but get redirected.
1239 controller.LoadURL(
1240 original_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1241 EXPECT_EQ(0U, notifications.size());
1242 test_rvh()->SendNavigateWithOriginalRequestURL(0, final_url, original_url);
1243 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1244 navigation_entry_committed_counter_ = 0;
1246 // The NavigationEntry should save both the original URL and the final
1247 // redirected URL.
1248 EXPECT_EQ(
1249 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1250 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1252 // Reload using the original URL.
1253 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1254 controller.ReloadOriginalRequestURL(false);
1255 EXPECT_EQ(0U, notifications.size());
1257 // The reload is pending. The request should point to the original URL.
1258 EXPECT_EQ(original_url, navigated_url());
1259 EXPECT_EQ(controller.GetEntryCount(), 1);
1260 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1261 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1262 EXPECT_TRUE(controller.GetLastCommittedEntry());
1263 EXPECT_TRUE(controller.GetPendingEntry());
1264 EXPECT_FALSE(controller.CanGoBack());
1265 EXPECT_FALSE(controller.CanGoForward());
1267 // Make sure the title has been cleared (will be redrawn just after reload).
1268 // Avoids a stale cached title when the new page being reloaded has no title.
1269 // See http://crbug.com/96041.
1270 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1272 // Send that the navigation has proceeded; say it got redirected again.
1273 test_rvh()->SendNavigate(0, final_url);
1274 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1275 navigation_entry_committed_counter_ = 0;
1277 // Now the reload is committed.
1278 EXPECT_EQ(controller.GetEntryCount(), 1);
1279 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1280 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1281 EXPECT_TRUE(controller.GetLastCommittedEntry());
1282 EXPECT_FALSE(controller.GetPendingEntry());
1283 EXPECT_FALSE(controller.CanGoBack());
1284 EXPECT_FALSE(controller.CanGoForward());
1287 #endif // !defined(OS_ANDROID)
1289 // Test that certain non-persisted NavigationEntryImpl values get reset after
1290 // commit.
1291 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1292 NavigationControllerImpl& controller = controller_impl();
1293 const GURL url1("http://foo1");
1294 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1296 // Set up some sample values.
1297 const unsigned char* raw_data =
1298 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1299 const int length = 11;
1300 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1301 scoped_refptr<base::RefCountedBytes> post_data =
1302 base::RefCountedBytes::TakeVector(&post_data_vector);
1303 GlobalRequestID transfer_id(3, 4);
1304 std::vector<GURL> redirects;
1305 redirects.push_back(GURL("http://foo2"));
1307 // Set non-persisted values on the pending entry.
1308 NavigationEntryImpl* pending_entry =
1309 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1310 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1311 pending_entry->set_is_renderer_initiated(true);
1312 pending_entry->set_transferred_global_request_id(transfer_id);
1313 pending_entry->set_should_replace_entry(true);
1314 pending_entry->set_redirect_chain(redirects);
1315 pending_entry->set_should_clear_history_list(true);
1316 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1317 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1318 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1319 EXPECT_TRUE(pending_entry->should_replace_entry());
1320 EXPECT_EQ(1U, pending_entry->redirect_chain().size());
1321 EXPECT_TRUE(pending_entry->should_clear_history_list());
1323 test_rvh()->SendNavigate(0, url1);
1325 // Certain values that are only used for pending entries get reset after
1326 // commit.
1327 NavigationEntryImpl* committed_entry =
1328 NavigationEntryImpl::FromNavigationEntry(
1329 controller.GetLastCommittedEntry());
1330 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1331 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1332 EXPECT_EQ(GlobalRequestID(-1, -1),
1333 committed_entry->transferred_global_request_id());
1334 EXPECT_FALSE(committed_entry->should_replace_entry());
1335 EXPECT_EQ(0U, committed_entry->redirect_chain().size());
1336 EXPECT_FALSE(committed_entry->should_clear_history_list());
1339 // Tests what happens when we navigate back successfully
1340 TEST_F(NavigationControllerTest, Back) {
1341 NavigationControllerImpl& controller = controller_impl();
1342 TestNotificationTracker notifications;
1343 RegisterForAllNavNotifications(&notifications, &controller);
1345 const GURL url1("http://foo1");
1346 test_rvh()->SendNavigate(0, url1);
1347 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1348 navigation_entry_committed_counter_ = 0;
1350 const GURL url2("http://foo2");
1351 test_rvh()->SendNavigate(1, url2);
1352 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1353 navigation_entry_committed_counter_ = 0;
1355 controller.GoBack();
1356 EXPECT_EQ(0U, notifications.size());
1358 // We should now have a pending navigation to go back.
1359 EXPECT_EQ(controller.GetEntryCount(), 2);
1360 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1361 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1362 EXPECT_TRUE(controller.GetLastCommittedEntry());
1363 EXPECT_TRUE(controller.GetPendingEntry());
1364 EXPECT_FALSE(controller.CanGoBack());
1365 EXPECT_FALSE(controller.CanGoToOffset(-1));
1366 EXPECT_TRUE(controller.CanGoForward());
1367 EXPECT_TRUE(controller.CanGoToOffset(1));
1368 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1370 // Timestamp for entry 1 should be on or after that of entry 0.
1371 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1372 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1373 controller.GetEntryAtIndex(0)->GetTimestamp());
1375 test_rvh()->SendNavigate(0, url2);
1376 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1377 navigation_entry_committed_counter_ = 0;
1379 // The back navigation completed successfully.
1380 EXPECT_EQ(controller.GetEntryCount(), 2);
1381 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1382 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1383 EXPECT_TRUE(controller.GetLastCommittedEntry());
1384 EXPECT_FALSE(controller.GetPendingEntry());
1385 EXPECT_FALSE(controller.CanGoBack());
1386 EXPECT_FALSE(controller.CanGoToOffset(-1));
1387 EXPECT_TRUE(controller.CanGoForward());
1388 EXPECT_TRUE(controller.CanGoToOffset(1));
1389 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1391 // Timestamp for entry 0 should be on or after that of entry 1
1392 // (since we went back to it).
1393 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1394 controller.GetEntryAtIndex(1)->GetTimestamp());
1397 // Tests what happens when a back navigation produces a new page.
1398 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1399 NavigationControllerImpl& controller = controller_impl();
1400 TestNotificationTracker notifications;
1401 RegisterForAllNavNotifications(&notifications, &controller);
1403 const GURL url1("http://foo/1");
1404 const GURL url2("http://foo/2");
1405 const GURL url3("http://foo/3");
1407 controller.LoadURL(
1408 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1409 test_rvh()->SendNavigate(0, url1);
1410 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1411 navigation_entry_committed_counter_ = 0;
1413 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1414 test_rvh()->SendNavigate(1, url2);
1415 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1416 navigation_entry_committed_counter_ = 0;
1418 controller.GoBack();
1419 EXPECT_EQ(0U, notifications.size());
1421 // We should now have a pending navigation to go back.
1422 EXPECT_EQ(controller.GetEntryCount(), 2);
1423 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1424 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1425 EXPECT_TRUE(controller.GetLastCommittedEntry());
1426 EXPECT_TRUE(controller.GetPendingEntry());
1427 EXPECT_FALSE(controller.CanGoBack());
1428 EXPECT_TRUE(controller.CanGoForward());
1430 test_rvh()->SendNavigate(2, url3);
1431 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1432 navigation_entry_committed_counter_ = 0;
1434 // The back navigation resulted in a completely new navigation.
1435 // TODO(darin): perhaps this behavior will be confusing to users?
1436 EXPECT_EQ(controller.GetEntryCount(), 3);
1437 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1438 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1439 EXPECT_TRUE(controller.GetLastCommittedEntry());
1440 EXPECT_FALSE(controller.GetPendingEntry());
1441 EXPECT_TRUE(controller.CanGoBack());
1442 EXPECT_FALSE(controller.CanGoForward());
1445 // Receives a back message when there is a new pending navigation entry.
1446 TEST_F(NavigationControllerTest, Back_NewPending) {
1447 NavigationControllerImpl& controller = controller_impl();
1448 TestNotificationTracker notifications;
1449 RegisterForAllNavNotifications(&notifications, &controller);
1451 const GURL kUrl1("http://foo1");
1452 const GURL kUrl2("http://foo2");
1453 const GURL kUrl3("http://foo3");
1455 // First navigate two places so we have some back history.
1456 test_rvh()->SendNavigate(0, kUrl1);
1457 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1458 navigation_entry_committed_counter_ = 0;
1460 // controller.LoadURL(kUrl2, PAGE_TRANSITION_TYPED);
1461 test_rvh()->SendNavigate(1, kUrl2);
1462 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1463 navigation_entry_committed_counter_ = 0;
1465 // Now start a new pending navigation and go back before it commits.
1466 controller.LoadURL(kUrl3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1467 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1468 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1469 controller.GoBack();
1471 // The pending navigation should now be the "back" item and the new one
1472 // should be gone.
1473 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1474 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1477 // Receives a back message when there is a different renavigation already
1478 // pending.
1479 TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1480 NavigationControllerImpl& controller = controller_impl();
1481 const GURL kUrl1("http://foo/1");
1482 const GURL kUrl2("http://foo/2");
1483 const GURL kUrl3("http://foo/3");
1485 // First navigate three places so we have some back history.
1486 test_rvh()->SendNavigate(0, kUrl1);
1487 test_rvh()->SendNavigate(1, kUrl2);
1488 test_rvh()->SendNavigate(2, kUrl3);
1490 // With nothing pending, say we get a navigation to the second entry.
1491 test_rvh()->SendNavigate(1, kUrl2);
1493 // We know all the entries have the same site instance, so we can just grab
1494 // a random one for looking up other entries.
1495 SiteInstance* site_instance =
1496 NavigationEntryImpl::FromNavigationEntry(
1497 controller.GetLastCommittedEntry())->site_instance();
1499 // That second URL should be the last committed and it should have gotten the
1500 // new title.
1501 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1502 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1503 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1505 // Now go forward to the last item again and say it was committed.
1506 controller.GoForward();
1507 test_rvh()->SendNavigate(2, kUrl3);
1509 // Now start going back one to the second page. It will be pending.
1510 controller.GoBack();
1511 EXPECT_EQ(1, controller.GetPendingEntryIndex());
1512 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1514 // Not synthesize a totally new back event to the first page. This will not
1515 // match the pending one.
1516 test_rvh()->SendNavigate(0, kUrl1);
1518 // The committed navigation should clear the pending entry.
1519 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1521 // But the navigated entry should be the last committed.
1522 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1523 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1526 // Tests what happens when we navigate forward successfully.
1527 TEST_F(NavigationControllerTest, Forward) {
1528 NavigationControllerImpl& controller = controller_impl();
1529 TestNotificationTracker notifications;
1530 RegisterForAllNavNotifications(&notifications, &controller);
1532 const GURL url1("http://foo1");
1533 const GURL url2("http://foo2");
1535 test_rvh()->SendNavigate(0, url1);
1536 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1537 navigation_entry_committed_counter_ = 0;
1539 test_rvh()->SendNavigate(1, url2);
1540 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1541 navigation_entry_committed_counter_ = 0;
1543 controller.GoBack();
1544 test_rvh()->SendNavigate(0, url1);
1545 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1546 navigation_entry_committed_counter_ = 0;
1548 controller.GoForward();
1550 // We should now have a pending navigation to go forward.
1551 EXPECT_EQ(controller.GetEntryCount(), 2);
1552 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1553 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1554 EXPECT_TRUE(controller.GetLastCommittedEntry());
1555 EXPECT_TRUE(controller.GetPendingEntry());
1556 EXPECT_TRUE(controller.CanGoBack());
1557 EXPECT_TRUE(controller.CanGoToOffset(-1));
1558 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1559 EXPECT_FALSE(controller.CanGoForward());
1560 EXPECT_FALSE(controller.CanGoToOffset(1));
1562 // Timestamp for entry 0 should be on or after that of entry 1
1563 // (since we went back to it).
1564 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1565 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1566 controller.GetEntryAtIndex(1)->GetTimestamp());
1568 test_rvh()->SendNavigate(1, url2);
1569 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1570 navigation_entry_committed_counter_ = 0;
1572 // The forward navigation completed successfully.
1573 EXPECT_EQ(controller.GetEntryCount(), 2);
1574 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1575 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1576 EXPECT_TRUE(controller.GetLastCommittedEntry());
1577 EXPECT_FALSE(controller.GetPendingEntry());
1578 EXPECT_TRUE(controller.CanGoBack());
1579 EXPECT_TRUE(controller.CanGoToOffset(-1));
1580 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1581 EXPECT_FALSE(controller.CanGoForward());
1582 EXPECT_FALSE(controller.CanGoToOffset(1));
1584 // Timestamp for entry 1 should be on or after that of entry 0
1585 // (since we went forward to it).
1586 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1587 controller.GetEntryAtIndex(0)->GetTimestamp());
1590 // Tests what happens when a forward navigation produces a new page.
1591 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1592 NavigationControllerImpl& controller = controller_impl();
1593 TestNotificationTracker notifications;
1594 RegisterForAllNavNotifications(&notifications, &controller);
1596 const GURL url1("http://foo1");
1597 const GURL url2("http://foo2");
1598 const GURL url3("http://foo3");
1600 test_rvh()->SendNavigate(0, url1);
1601 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1602 navigation_entry_committed_counter_ = 0;
1603 test_rvh()->SendNavigate(1, url2);
1604 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1605 navigation_entry_committed_counter_ = 0;
1607 controller.GoBack();
1608 test_rvh()->SendNavigate(0, url1);
1609 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1610 navigation_entry_committed_counter_ = 0;
1612 controller.GoForward();
1613 EXPECT_EQ(0U, notifications.size());
1615 // Should now have a pending navigation to go forward.
1616 EXPECT_EQ(controller.GetEntryCount(), 2);
1617 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1618 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1619 EXPECT_TRUE(controller.GetLastCommittedEntry());
1620 EXPECT_TRUE(controller.GetPendingEntry());
1621 EXPECT_TRUE(controller.CanGoBack());
1622 EXPECT_FALSE(controller.CanGoForward());
1624 test_rvh()->SendNavigate(2, url3);
1625 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1626 navigation_entry_committed_counter_ = 0;
1627 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1629 EXPECT_EQ(controller.GetEntryCount(), 2);
1630 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1631 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1632 EXPECT_TRUE(controller.GetLastCommittedEntry());
1633 EXPECT_FALSE(controller.GetPendingEntry());
1634 EXPECT_TRUE(controller.CanGoBack());
1635 EXPECT_FALSE(controller.CanGoForward());
1638 // Two consequent navigation for the same URL entered in should be considered
1639 // as SAME_PAGE navigation even when we are redirected to some other page.
1640 TEST_F(NavigationControllerTest, Redirect) {
1641 NavigationControllerImpl& controller = controller_impl();
1642 TestNotificationTracker notifications;
1643 RegisterForAllNavNotifications(&notifications, &controller);
1645 const GURL url1("http://foo1");
1646 const GURL url2("http://foo2"); // Redirection target
1648 // First request
1649 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1651 EXPECT_EQ(0U, notifications.size());
1652 test_rvh()->SendNavigate(0, url2);
1653 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1654 navigation_entry_committed_counter_ = 0;
1656 // Second request
1657 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1659 EXPECT_TRUE(controller.GetPendingEntry());
1660 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1661 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1663 ViewHostMsg_FrameNavigate_Params params;
1664 params.page_id = 0;
1665 params.url = url2;
1666 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1667 params.redirects.push_back(GURL("http://foo1"));
1668 params.redirects.push_back(GURL("http://foo2"));
1669 params.should_update_history = false;
1670 params.gesture = NavigationGestureAuto;
1671 params.is_post = false;
1672 params.page_state = PageState::CreateFromURL(url2);
1674 LoadCommittedDetails details;
1676 EXPECT_EQ(0U, notifications.size());
1677 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1678 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1679 navigation_entry_committed_counter_ = 0;
1681 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1682 EXPECT_EQ(controller.GetEntryCount(), 1);
1683 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1684 EXPECT_TRUE(controller.GetLastCommittedEntry());
1685 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1686 EXPECT_FALSE(controller.GetPendingEntry());
1687 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1689 EXPECT_FALSE(controller.CanGoBack());
1690 EXPECT_FALSE(controller.CanGoForward());
1693 // Similar to Redirect above, but the first URL is requested by POST,
1694 // the second URL is requested by GET. NavigationEntry::has_post_data_
1695 // must be cleared. http://crbug.com/21245
1696 TEST_F(NavigationControllerTest, PostThenRedirect) {
1697 NavigationControllerImpl& controller = controller_impl();
1698 TestNotificationTracker notifications;
1699 RegisterForAllNavNotifications(&notifications, &controller);
1701 const GURL url1("http://foo1");
1702 const GURL url2("http://foo2"); // Redirection target
1704 // First request as POST
1705 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1706 controller.GetVisibleEntry()->SetHasPostData(true);
1708 EXPECT_EQ(0U, notifications.size());
1709 test_rvh()->SendNavigate(0, url2);
1710 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1711 navigation_entry_committed_counter_ = 0;
1713 // Second request
1714 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1716 EXPECT_TRUE(controller.GetPendingEntry());
1717 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1718 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1720 ViewHostMsg_FrameNavigate_Params params;
1721 params.page_id = 0;
1722 params.url = url2;
1723 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1724 params.redirects.push_back(GURL("http://foo1"));
1725 params.redirects.push_back(GURL("http://foo2"));
1726 params.should_update_history = false;
1727 params.gesture = NavigationGestureAuto;
1728 params.is_post = false;
1729 params.page_state = PageState::CreateFromURL(url2);
1731 LoadCommittedDetails details;
1733 EXPECT_EQ(0U, notifications.size());
1734 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1735 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1736 navigation_entry_committed_counter_ = 0;
1738 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1739 EXPECT_EQ(controller.GetEntryCount(), 1);
1740 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1741 EXPECT_TRUE(controller.GetLastCommittedEntry());
1742 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1743 EXPECT_FALSE(controller.GetPendingEntry());
1744 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1745 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1747 EXPECT_FALSE(controller.CanGoBack());
1748 EXPECT_FALSE(controller.CanGoForward());
1751 // A redirect right off the bat should be a NEW_PAGE.
1752 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1753 NavigationControllerImpl& controller = controller_impl();
1754 TestNotificationTracker notifications;
1755 RegisterForAllNavNotifications(&notifications, &controller);
1757 const GURL url1("http://foo1");
1758 const GURL url2("http://foo2"); // Redirection target
1760 // First request
1761 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1763 EXPECT_TRUE(controller.GetPendingEntry());
1764 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1765 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1767 ViewHostMsg_FrameNavigate_Params params;
1768 params.page_id = 0;
1769 params.url = url2;
1770 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1771 params.redirects.push_back(GURL("http://foo1"));
1772 params.redirects.push_back(GURL("http://foo2"));
1773 params.should_update_history = false;
1774 params.gesture = NavigationGestureAuto;
1775 params.is_post = false;
1776 params.page_state = PageState::CreateFromURL(url2);
1778 LoadCommittedDetails details;
1780 EXPECT_EQ(0U, notifications.size());
1781 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1782 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1783 navigation_entry_committed_counter_ = 0;
1785 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1786 EXPECT_EQ(controller.GetEntryCount(), 1);
1787 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1788 EXPECT_TRUE(controller.GetLastCommittedEntry());
1789 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1790 EXPECT_FALSE(controller.GetPendingEntry());
1791 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1793 EXPECT_FALSE(controller.CanGoBack());
1794 EXPECT_FALSE(controller.CanGoForward());
1797 // Tests navigation via link click within a subframe. A new navigation entry
1798 // should be created.
1799 TEST_F(NavigationControllerTest, NewSubframe) {
1800 NavigationControllerImpl& controller = controller_impl();
1801 TestNotificationTracker notifications;
1802 RegisterForAllNavNotifications(&notifications, &controller);
1804 const GURL url1("http://foo1");
1805 test_rvh()->SendNavigate(0, url1);
1806 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1807 navigation_entry_committed_counter_ = 0;
1809 const GURL url2("http://foo2");
1810 ViewHostMsg_FrameNavigate_Params params;
1811 params.page_id = 1;
1812 params.url = url2;
1813 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1814 params.should_update_history = false;
1815 params.gesture = NavigationGestureUser;
1816 params.is_post = false;
1817 params.page_state = PageState::CreateFromURL(url2);
1819 LoadCommittedDetails details;
1820 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1821 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1822 navigation_entry_committed_counter_ = 0;
1823 EXPECT_EQ(url1, details.previous_url);
1824 EXPECT_FALSE(details.is_in_page);
1825 EXPECT_FALSE(details.is_main_frame);
1827 // The new entry should be appended.
1828 EXPECT_EQ(2, controller.GetEntryCount());
1830 // New entry should refer to the new page, but the old URL (entries only
1831 // reflect the toplevel URL).
1832 EXPECT_EQ(url1, details.entry->GetURL());
1833 EXPECT_EQ(params.page_id, details.entry->GetPageID());
1836 // Some pages create a popup, then write an iframe into it. This causes a
1837 // subframe navigation without having any committed entry. Such navigations
1838 // just get thrown on the ground, but we shouldn't crash.
1839 TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
1840 NavigationControllerImpl& controller = controller_impl();
1841 TestNotificationTracker notifications;
1842 RegisterForAllNavNotifications(&notifications, &controller);
1844 // Navigation controller currently has no entries.
1845 const GURL url("http://foo2");
1846 ViewHostMsg_FrameNavigate_Params params;
1847 params.page_id = 1;
1848 params.url = url;
1849 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1850 params.should_update_history = false;
1851 params.gesture = NavigationGestureAuto;
1852 params.is_post = false;
1853 params.page_state = PageState::CreateFromURL(url);
1855 LoadCommittedDetails details;
1856 EXPECT_FALSE(controller.RendererDidNavigate(test_rvh(), params, &details));
1857 EXPECT_EQ(0U, notifications.size());
1860 // Auto subframes are ones the page loads automatically like ads. They should
1861 // not create new navigation entries.
1862 TEST_F(NavigationControllerTest, AutoSubframe) {
1863 NavigationControllerImpl& controller = controller_impl();
1864 TestNotificationTracker notifications;
1865 RegisterForAllNavNotifications(&notifications, &controller);
1867 const GURL url1("http://foo1");
1868 test_rvh()->SendNavigate(0, url1);
1869 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1870 navigation_entry_committed_counter_ = 0;
1872 const GURL url2("http://foo2");
1873 ViewHostMsg_FrameNavigate_Params params;
1874 params.page_id = 0;
1875 params.url = url2;
1876 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1877 params.should_update_history = false;
1878 params.gesture = NavigationGestureUser;
1879 params.is_post = false;
1880 params.page_state = PageState::CreateFromURL(url2);
1882 // Navigating should do nothing.
1883 LoadCommittedDetails details;
1884 EXPECT_FALSE(controller.RendererDidNavigate(test_rvh(), params, &details));
1885 EXPECT_EQ(0U, notifications.size());
1887 // There should still be only one entry.
1888 EXPECT_EQ(1, controller.GetEntryCount());
1891 // Tests navigation and then going back to a subframe navigation.
1892 TEST_F(NavigationControllerTest, BackSubframe) {
1893 NavigationControllerImpl& controller = controller_impl();
1894 TestNotificationTracker notifications;
1895 RegisterForAllNavNotifications(&notifications, &controller);
1897 // Main page.
1898 const GURL url1("http://foo1");
1899 test_rvh()->SendNavigate(0, url1);
1900 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1901 navigation_entry_committed_counter_ = 0;
1903 // First manual subframe navigation.
1904 const GURL url2("http://foo2");
1905 ViewHostMsg_FrameNavigate_Params params;
1906 params.page_id = 1;
1907 params.url = url2;
1908 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1909 params.should_update_history = false;
1910 params.gesture = NavigationGestureUser;
1911 params.is_post = false;
1912 params.page_state = PageState::CreateFromURL(url2);
1914 // This should generate a new entry.
1915 LoadCommittedDetails details;
1916 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1917 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1918 navigation_entry_committed_counter_ = 0;
1919 EXPECT_EQ(2, controller.GetEntryCount());
1921 // Second manual subframe navigation should also make a new entry.
1922 const GURL url3("http://foo3");
1923 params.page_id = 2;
1924 params.url = url3;
1925 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1926 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1927 navigation_entry_committed_counter_ = 0;
1928 EXPECT_EQ(3, controller.GetEntryCount());
1929 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
1931 // Go back one.
1932 controller.GoBack();
1933 params.url = url2;
1934 params.page_id = 1;
1935 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1936 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1937 navigation_entry_committed_counter_ = 0;
1938 EXPECT_EQ(3, controller.GetEntryCount());
1939 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
1940 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1941 EXPECT_FALSE(controller.GetPendingEntry());
1943 // Go back one more.
1944 controller.GoBack();
1945 params.url = url1;
1946 params.page_id = 0;
1947 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
1948 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1949 navigation_entry_committed_counter_ = 0;
1950 EXPECT_EQ(3, controller.GetEntryCount());
1951 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
1952 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1953 EXPECT_FALSE(controller.GetPendingEntry());
1956 TEST_F(NavigationControllerTest, LinkClick) {
1957 NavigationControllerImpl& controller = controller_impl();
1958 TestNotificationTracker notifications;
1959 RegisterForAllNavNotifications(&notifications, &controller);
1961 const GURL url1("http://foo1");
1962 const GURL url2("http://foo2");
1964 test_rvh()->SendNavigate(0, url1);
1965 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1966 navigation_entry_committed_counter_ = 0;
1968 test_rvh()->SendNavigate(1, url2);
1969 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1970 navigation_entry_committed_counter_ = 0;
1972 // Should not have produced a new session history entry.
1973 EXPECT_EQ(controller.GetEntryCount(), 2);
1974 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1975 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1976 EXPECT_TRUE(controller.GetLastCommittedEntry());
1977 EXPECT_FALSE(controller.GetPendingEntry());
1978 EXPECT_TRUE(controller.CanGoBack());
1979 EXPECT_FALSE(controller.CanGoForward());
1982 TEST_F(NavigationControllerTest, InPage) {
1983 NavigationControllerImpl& controller = controller_impl();
1984 TestNotificationTracker notifications;
1985 RegisterForAllNavNotifications(&notifications, &controller);
1987 // Main page.
1988 const GURL url1("http://foo");
1989 test_rvh()->SendNavigate(0, url1);
1990 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1991 navigation_entry_committed_counter_ = 0;
1993 // Ensure main page navigation to same url respects the was_within_same_page
1994 // hint provided in the params.
1995 ViewHostMsg_FrameNavigate_Params self_params;
1996 self_params.page_id = 0;
1997 self_params.url = url1;
1998 self_params.transition = PAGE_TRANSITION_LINK;
1999 self_params.should_update_history = false;
2000 self_params.gesture = NavigationGestureUser;
2001 self_params.is_post = false;
2002 self_params.page_state = PageState::CreateFromURL(url1);
2003 self_params.was_within_same_page = true;
2005 LoadCommittedDetails details;
2006 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), self_params,
2007 &details));
2008 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2009 navigation_entry_committed_counter_ = 0;
2010 EXPECT_TRUE(details.is_in_page);
2011 EXPECT_TRUE(details.did_replace_entry);
2012 EXPECT_EQ(1, controller.GetEntryCount());
2014 // Fragment navigation to a new page_id.
2015 const GURL url2("http://foo#a");
2016 ViewHostMsg_FrameNavigate_Params params;
2017 params.page_id = 1;
2018 params.url = url2;
2019 params.transition = PAGE_TRANSITION_LINK;
2020 params.should_update_history = false;
2021 params.gesture = NavigationGestureUser;
2022 params.is_post = false;
2023 params.page_state = PageState::CreateFromURL(url2);
2024 params.was_within_same_page = true;
2026 // This should generate a new entry.
2027 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
2028 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2029 navigation_entry_committed_counter_ = 0;
2030 EXPECT_TRUE(details.is_in_page);
2031 EXPECT_FALSE(details.did_replace_entry);
2032 EXPECT_EQ(2, controller.GetEntryCount());
2034 // Go back one.
2035 ViewHostMsg_FrameNavigate_Params back_params(params);
2036 controller.GoBack();
2037 back_params.url = url1;
2038 back_params.page_id = 0;
2039 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), back_params,
2040 &details));
2041 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2042 navigation_entry_committed_counter_ = 0;
2043 EXPECT_TRUE(details.is_in_page);
2044 EXPECT_EQ(2, controller.GetEntryCount());
2045 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2046 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2048 // Go forward
2049 ViewHostMsg_FrameNavigate_Params forward_params(params);
2050 controller.GoForward();
2051 forward_params.url = url2;
2052 forward_params.page_id = 1;
2053 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), forward_params,
2054 &details));
2055 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2056 navigation_entry_committed_counter_ = 0;
2057 EXPECT_TRUE(details.is_in_page);
2058 EXPECT_EQ(2, controller.GetEntryCount());
2059 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2060 EXPECT_EQ(forward_params.url,
2061 controller.GetVisibleEntry()->GetURL());
2063 // Now go back and forward again. This is to work around a bug where we would
2064 // compare the incoming URL with the last committed entry rather than the
2065 // one identified by an existing page ID. This would result in the second URL
2066 // losing the reference fragment when you navigate away from it and then back.
2067 controller.GoBack();
2068 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), back_params,
2069 &details));
2070 controller.GoForward();
2071 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), forward_params,
2072 &details));
2073 EXPECT_EQ(forward_params.url,
2074 controller.GetVisibleEntry()->GetURL());
2076 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2077 const GURL url3("http://bar");
2078 params.page_id = 2;
2079 params.url = url3;
2080 navigation_entry_committed_counter_ = 0;
2081 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
2082 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2083 navigation_entry_committed_counter_ = 0;
2084 EXPECT_FALSE(details.is_in_page);
2085 EXPECT_EQ(3, controller.GetEntryCount());
2086 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2089 TEST_F(NavigationControllerTest, InPage_Replace) {
2090 NavigationControllerImpl& controller = controller_impl();
2091 TestNotificationTracker notifications;
2092 RegisterForAllNavNotifications(&notifications, &controller);
2094 // Main page.
2095 const GURL url1("http://foo");
2096 test_rvh()->SendNavigate(0, url1);
2097 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2098 navigation_entry_committed_counter_ = 0;
2100 // First navigation.
2101 const GURL url2("http://foo#a");
2102 ViewHostMsg_FrameNavigate_Params params;
2103 params.page_id = 0; // Same page_id
2104 params.url = url2;
2105 params.transition = PAGE_TRANSITION_LINK;
2106 params.should_update_history = false;
2107 params.gesture = NavigationGestureUser;
2108 params.is_post = false;
2109 params.page_state = PageState::CreateFromURL(url2);
2111 // This should NOT generate a new entry, nor prune the list.
2112 LoadCommittedDetails details;
2113 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
2114 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2115 navigation_entry_committed_counter_ = 0;
2116 EXPECT_TRUE(details.is_in_page);
2117 EXPECT_TRUE(details.did_replace_entry);
2118 EXPECT_EQ(1, controller.GetEntryCount());
2121 // Tests for http://crbug.com/40395
2122 // Simulates this:
2123 // <script>
2124 // window.location.replace("#a");
2125 // window.location='http://foo3/';
2126 // </script>
2127 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2128 NavigationControllerImpl& controller = controller_impl();
2129 TestNotificationTracker notifications;
2130 RegisterForAllNavNotifications(&notifications, &controller);
2132 // Load an initial page.
2134 const GURL url("http://foo/");
2135 test_rvh()->SendNavigate(0, url);
2136 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2137 navigation_entry_committed_counter_ = 0;
2140 // Navigate to a new page.
2142 const GURL url("http://foo2/");
2143 test_rvh()->SendNavigate(1, url);
2144 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2145 navigation_entry_committed_counter_ = 0;
2148 // Navigate within the page.
2150 const GURL url("http://foo2/#a");
2151 ViewHostMsg_FrameNavigate_Params params;
2152 params.page_id = 1; // Same page_id
2153 params.url = url;
2154 params.transition = PAGE_TRANSITION_LINK;
2155 params.redirects.push_back(url);
2156 params.should_update_history = true;
2157 params.gesture = NavigationGestureUnknown;
2158 params.is_post = false;
2159 params.page_state = PageState::CreateFromURL(url);
2161 // This should NOT generate a new entry, nor prune the list.
2162 LoadCommittedDetails details;
2163 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
2164 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2165 navigation_entry_committed_counter_ = 0;
2166 EXPECT_TRUE(details.is_in_page);
2167 EXPECT_TRUE(details.did_replace_entry);
2168 EXPECT_EQ(2, controller.GetEntryCount());
2171 // Perform a client redirect to a new page.
2173 const GURL url("http://foo3/");
2174 ViewHostMsg_FrameNavigate_Params params;
2175 params.page_id = 2; // New page_id
2176 params.url = url;
2177 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
2178 params.redirects.push_back(GURL("http://foo2/#a"));
2179 params.redirects.push_back(url);
2180 params.should_update_history = true;
2181 params.gesture = NavigationGestureUnknown;
2182 params.is_post = false;
2183 params.page_state = PageState::CreateFromURL(url);
2185 // This SHOULD generate a new entry.
2186 LoadCommittedDetails details;
2187 EXPECT_TRUE(controller.RendererDidNavigate(test_rvh(), params, &details));
2188 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2189 navigation_entry_committed_counter_ = 0;
2190 EXPECT_FALSE(details.is_in_page);
2191 EXPECT_EQ(3, controller.GetEntryCount());
2194 // Verify that BACK brings us back to http://foo2/.
2196 const GURL url("http://foo2/");
2197 controller.GoBack();
2198 test_rvh()->SendNavigate(1, url);
2199 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2200 navigation_entry_committed_counter_ = 0;
2201 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2205 // NotificationObserver implementation used in verifying we've received the
2206 // NOTIFICATION_NAV_LIST_PRUNED method.
2207 class PrunedListener : public NotificationObserver {
2208 public:
2209 explicit PrunedListener(NavigationControllerImpl* controller)
2210 : notification_count_(0) {
2211 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2212 Source<NavigationController>(controller));
2215 virtual void Observe(int type,
2216 const NotificationSource& source,
2217 const NotificationDetails& details) OVERRIDE {
2218 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2219 notification_count_++;
2220 details_ = *(Details<PrunedDetails>(details).ptr());
2224 // Number of times NAV_LIST_PRUNED has been observed.
2225 int notification_count_;
2227 // Details from the last NAV_LIST_PRUNED.
2228 PrunedDetails details_;
2230 private:
2231 NotificationRegistrar registrar_;
2233 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2236 // Tests that we limit the number of navigation entries created correctly.
2237 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2238 NavigationControllerImpl& controller = controller_impl();
2239 size_t original_count = NavigationControllerImpl::max_entry_count();
2240 const int kMaxEntryCount = 5;
2242 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2244 int url_index;
2245 // Load up to the max count, all entries should be there.
2246 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2247 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2248 controller.LoadURL(
2249 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2250 test_rvh()->SendNavigate(url_index, url);
2253 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2255 // Created a PrunedListener to observe prune notifications.
2256 PrunedListener listener(&controller);
2258 // Navigate some more.
2259 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2260 controller.LoadURL(
2261 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2262 test_rvh()->SendNavigate(url_index, url);
2263 url_index++;
2265 // We should have got a pruned navigation.
2266 EXPECT_EQ(1, listener.notification_count_);
2267 EXPECT_TRUE(listener.details_.from_front);
2268 EXPECT_EQ(1, listener.details_.count);
2270 // We expect http://www.a.com/0 to be gone.
2271 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2272 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2273 GURL("http:////www.a.com/1"));
2275 // More navigations.
2276 for (int i = 0; i < 3; i++) {
2277 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2278 controller.LoadURL(
2279 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2280 test_rvh()->SendNavigate(url_index, url);
2281 url_index++;
2283 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2284 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2285 GURL("http:////www.a.com/4"));
2287 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2290 // Tests that we can do a restore and navigate to the restored entries and
2291 // everything is updated properly. This can be tricky since there is no
2292 // SiteInstance for the entries created initially.
2293 TEST_F(NavigationControllerTest, RestoreNavigate) {
2294 // Create a NavigationController with a restored set of tabs.
2295 GURL url("http://foo");
2296 std::vector<NavigationEntry*> entries;
2297 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2298 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2299 browser_context());
2300 entry->SetPageID(0);
2301 entry->SetTitle(base::ASCIIToUTF16("Title"));
2302 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2303 const base::Time timestamp = base::Time::Now();
2304 entry->SetTimestamp(timestamp);
2305 entries.push_back(entry);
2306 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2307 WebContents::Create(WebContents::CreateParams(browser_context()))));
2308 NavigationControllerImpl& our_controller = our_contents->GetController();
2309 our_controller.Restore(
2311 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2312 &entries);
2313 ASSERT_EQ(0u, entries.size());
2315 // Before navigating to the restored entry, it should have a restore_type
2316 // and no SiteInstance.
2317 ASSERT_EQ(1, our_controller.GetEntryCount());
2318 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2319 NavigationEntryImpl::FromNavigationEntry(
2320 our_controller.GetEntryAtIndex(0))->restore_type());
2321 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2322 our_controller.GetEntryAtIndex(0))->site_instance());
2324 // After navigating, we should have one entry, and it should be "pending".
2325 // It should now have a SiteInstance and no restore_type.
2326 our_controller.GoToIndex(0);
2327 EXPECT_EQ(1, our_controller.GetEntryCount());
2328 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2329 our_controller.GetPendingEntry());
2330 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2331 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2332 NavigationEntryImpl::FromNavigationEntry
2333 (our_controller.GetEntryAtIndex(0))->restore_type());
2334 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2335 our_controller.GetEntryAtIndex(0))->site_instance());
2337 // Timestamp should remain the same before the navigation finishes.
2338 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2340 // Say we navigated to that entry.
2341 ViewHostMsg_FrameNavigate_Params params;
2342 params.page_id = 0;
2343 params.url = url;
2344 params.transition = PAGE_TRANSITION_LINK;
2345 params.should_update_history = false;
2346 params.gesture = NavigationGestureUser;
2347 params.is_post = false;
2348 params.page_state = PageState::CreateFromURL(url);
2349 LoadCommittedDetails details;
2350 our_controller.RendererDidNavigate(our_contents->GetRenderViewHost(), params,
2351 &details);
2353 // There should be no longer any pending entry and one committed one. This
2354 // means that we were able to locate the entry, assign its site instance, and
2355 // commit it properly.
2356 EXPECT_EQ(1, our_controller.GetEntryCount());
2357 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2358 EXPECT_FALSE(our_controller.GetPendingEntry());
2359 EXPECT_EQ(url,
2360 NavigationEntryImpl::FromNavigationEntry(
2361 our_controller.GetLastCommittedEntry())->site_instance()->
2362 GetSiteURL());
2363 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2364 NavigationEntryImpl::FromNavigationEntry(
2365 our_controller.GetEntryAtIndex(0))->restore_type());
2367 // Timestamp should have been updated.
2368 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2371 // Tests that we can still navigate to a restored entry after a different
2372 // navigation fails and clears the pending entry. http://crbug.com/90085
2373 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2374 // Create a NavigationController with a restored set of tabs.
2375 GURL url("http://foo");
2376 std::vector<NavigationEntry*> entries;
2377 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2378 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2379 browser_context());
2380 entry->SetPageID(0);
2381 entry->SetTitle(base::ASCIIToUTF16("Title"));
2382 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2383 entries.push_back(entry);
2384 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2385 WebContents::Create(WebContents::CreateParams(browser_context()))));
2386 NavigationControllerImpl& our_controller = our_contents->GetController();
2387 our_controller.Restore(
2388 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2389 ASSERT_EQ(0u, entries.size());
2391 // Before navigating to the restored entry, it should have a restore_type
2392 // and no SiteInstance.
2393 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2394 NavigationEntryImpl::FromNavigationEntry(
2395 our_controller.GetEntryAtIndex(0))->restore_type());
2396 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2397 our_controller.GetEntryAtIndex(0))->site_instance());
2399 // After navigating, we should have one entry, and it should be "pending".
2400 // It should now have a SiteInstance and no restore_type.
2401 our_controller.GoToIndex(0);
2402 EXPECT_EQ(1, our_controller.GetEntryCount());
2403 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2404 our_controller.GetPendingEntry());
2405 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2406 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2407 NavigationEntryImpl::FromNavigationEntry(
2408 our_controller.GetEntryAtIndex(0))->restore_type());
2409 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2410 our_controller.GetEntryAtIndex(0))->site_instance());
2412 // This pending navigation may have caused a different navigation to fail,
2413 // which causes the pending entry to be cleared.
2414 TestRenderViewHost* rvh =
2415 static_cast<TestRenderViewHost*>(our_contents->GetRenderViewHost());
2416 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2417 fail_load_params.frame_id = 1;
2418 fail_load_params.is_main_frame = true;
2419 fail_load_params.error_code = net::ERR_ABORTED;
2420 fail_load_params.error_description = base::string16();
2421 fail_load_params.url = url;
2422 fail_load_params.showing_repost_interstitial = false;
2423 main_test_rfh()->OnMessageReceived(
2424 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2425 fail_load_params));
2427 // Now the pending restored entry commits.
2428 ViewHostMsg_FrameNavigate_Params params;
2429 params.page_id = 0;
2430 params.url = url;
2431 params.transition = PAGE_TRANSITION_LINK;
2432 params.should_update_history = false;
2433 params.gesture = NavigationGestureUser;
2434 params.is_post = false;
2435 params.page_state = PageState::CreateFromURL(url);
2436 LoadCommittedDetails details;
2437 our_controller.RendererDidNavigate(rvh, params, &details);
2439 // There should be no pending entry and one committed one.
2440 EXPECT_EQ(1, our_controller.GetEntryCount());
2441 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2442 EXPECT_FALSE(our_controller.GetPendingEntry());
2443 EXPECT_EQ(url,
2444 NavigationEntryImpl::FromNavigationEntry(
2445 our_controller.GetLastCommittedEntry())->site_instance()->
2446 GetSiteURL());
2447 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2448 NavigationEntryImpl::FromNavigationEntry(
2449 our_controller.GetEntryAtIndex(0))->restore_type());
2452 // Make sure that the page type and stuff is correct after an interstitial.
2453 TEST_F(NavigationControllerTest, Interstitial) {
2454 NavigationControllerImpl& controller = controller_impl();
2455 // First navigate somewhere normal.
2456 const GURL url1("http://foo");
2457 controller.LoadURL(
2458 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2459 test_rvh()->SendNavigate(0, url1);
2461 // Now navigate somewhere with an interstitial.
2462 const GURL url2("http://bar");
2463 controller.LoadURL(
2464 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2465 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2466 set_page_type(PAGE_TYPE_INTERSTITIAL);
2468 // At this point the interstitial will be displayed and the load will still
2469 // be pending. If the user continues, the load will commit.
2470 test_rvh()->SendNavigate(1, url2);
2472 // The page should be a normal page again.
2473 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2474 EXPECT_EQ(PAGE_TYPE_NORMAL,
2475 controller.GetLastCommittedEntry()->GetPageType());
2478 TEST_F(NavigationControllerTest, RemoveEntry) {
2479 NavigationControllerImpl& controller = controller_impl();
2480 const GURL url1("http://foo/1");
2481 const GURL url2("http://foo/2");
2482 const GURL url3("http://foo/3");
2483 const GURL url4("http://foo/4");
2484 const GURL url5("http://foo/5");
2485 const GURL pending_url("http://foo/pending");
2486 const GURL default_url("http://foo/default");
2488 controller.LoadURL(
2489 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2490 test_rvh()->SendNavigate(0, url1);
2491 controller.LoadURL(
2492 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2493 test_rvh()->SendNavigate(1, url2);
2494 controller.LoadURL(
2495 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2496 test_rvh()->SendNavigate(2, url3);
2497 controller.LoadURL(
2498 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2499 test_rvh()->SendNavigate(3, url4);
2500 controller.LoadURL(
2501 url5, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2502 test_rvh()->SendNavigate(4, url5);
2504 // Try to remove the last entry. Will fail because it is the current entry.
2505 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2506 EXPECT_EQ(5, controller.GetEntryCount());
2507 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2509 // Go back, but don't commit yet. Check that we can't delete the current
2510 // and pending entries.
2511 controller.GoBack();
2512 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2513 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2515 // Now commit and delete the last entry.
2516 test_rvh()->SendNavigate(3, url4);
2517 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2518 EXPECT_EQ(4, controller.GetEntryCount());
2519 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2520 EXPECT_FALSE(controller.GetPendingEntry());
2522 // Remove an entry which is not the last committed one.
2523 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2524 EXPECT_EQ(3, controller.GetEntryCount());
2525 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2526 EXPECT_FALSE(controller.GetPendingEntry());
2528 // Remove the 2 remaining entries.
2529 controller.RemoveEntryAtIndex(1);
2530 controller.RemoveEntryAtIndex(0);
2532 // This should leave us with only the last committed entry.
2533 EXPECT_EQ(1, controller.GetEntryCount());
2534 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2537 // Tests the transient entry, making sure it goes away with all navigations.
2538 TEST_F(NavigationControllerTest, TransientEntry) {
2539 NavigationControllerImpl& controller = controller_impl();
2540 TestNotificationTracker notifications;
2541 RegisterForAllNavNotifications(&notifications, &controller);
2543 const GURL url0("http://foo/0");
2544 const GURL url1("http://foo/1");
2545 const GURL url2("http://foo/2");
2546 const GURL url3("http://foo/3");
2547 const GURL url3_ref("http://foo/3#bar");
2548 const GURL url4("http://foo/4");
2549 const GURL transient_url("http://foo/transient");
2551 controller.LoadURL(
2552 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2553 test_rvh()->SendNavigate(0, url0);
2554 controller.LoadURL(
2555 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2556 test_rvh()->SendNavigate(1, url1);
2558 notifications.Reset();
2560 // Adding a transient with no pending entry.
2561 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2562 transient_entry->SetURL(transient_url);
2563 controller.SetTransientEntry(transient_entry);
2565 // We should not have received any notifications.
2566 EXPECT_EQ(0U, notifications.size());
2568 // Check our state.
2569 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2570 EXPECT_EQ(controller.GetEntryCount(), 3);
2571 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2572 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2573 EXPECT_TRUE(controller.GetLastCommittedEntry());
2574 EXPECT_FALSE(controller.GetPendingEntry());
2575 EXPECT_TRUE(controller.CanGoBack());
2576 EXPECT_FALSE(controller.CanGoForward());
2577 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2579 // Navigate.
2580 controller.LoadURL(
2581 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2582 test_rvh()->SendNavigate(2, url2);
2584 // We should have navigated, transient entry should be gone.
2585 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2586 EXPECT_EQ(controller.GetEntryCount(), 3);
2588 // Add a transient again, then navigate with no pending entry this time.
2589 transient_entry = new NavigationEntryImpl;
2590 transient_entry->SetURL(transient_url);
2591 controller.SetTransientEntry(transient_entry);
2592 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2593 test_rvh()->SendNavigate(3, url3);
2594 // Transient entry should be gone.
2595 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2596 EXPECT_EQ(controller.GetEntryCount(), 4);
2598 // Initiate a navigation, add a transient then commit navigation.
2599 controller.LoadURL(
2600 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2601 transient_entry = new NavigationEntryImpl;
2602 transient_entry->SetURL(transient_url);
2603 controller.SetTransientEntry(transient_entry);
2604 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2605 test_rvh()->SendNavigate(4, url4);
2606 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2607 EXPECT_EQ(controller.GetEntryCount(), 5);
2609 // Add a transient and go back. This should simply remove the transient.
2610 transient_entry = new NavigationEntryImpl;
2611 transient_entry->SetURL(transient_url);
2612 controller.SetTransientEntry(transient_entry);
2613 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2614 EXPECT_TRUE(controller.CanGoBack());
2615 EXPECT_FALSE(controller.CanGoForward());
2616 controller.GoBack();
2617 // Transient entry should be gone.
2618 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2619 EXPECT_EQ(controller.GetEntryCount(), 5);
2620 test_rvh()->SendNavigate(3, url3);
2622 // Add a transient and go to an entry before the current one.
2623 transient_entry = new NavigationEntryImpl;
2624 transient_entry->SetURL(transient_url);
2625 controller.SetTransientEntry(transient_entry);
2626 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2627 controller.GoToIndex(1);
2628 // The navigation should have been initiated, transient entry should be gone.
2629 EXPECT_FALSE(controller.GetTransientEntry());
2630 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2631 // Visible entry does not update for history navigations until commit.
2632 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2633 test_rvh()->SendNavigate(1, url1);
2634 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2636 // Add a transient and go to an entry after the current one.
2637 transient_entry = new NavigationEntryImpl;
2638 transient_entry->SetURL(transient_url);
2639 controller.SetTransientEntry(transient_entry);
2640 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2641 controller.GoToIndex(3);
2642 // The navigation should have been initiated, transient entry should be gone.
2643 // Because of the transient entry that is removed, going to index 3 makes us
2644 // land on url2 (which is visible after the commit).
2645 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2646 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2647 test_rvh()->SendNavigate(2, url2);
2648 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2650 // Add a transient and go forward.
2651 transient_entry = new NavigationEntryImpl;
2652 transient_entry->SetURL(transient_url);
2653 controller.SetTransientEntry(transient_entry);
2654 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2655 EXPECT_TRUE(controller.CanGoForward());
2656 controller.GoForward();
2657 // We should have navigated, transient entry should be gone.
2658 EXPECT_FALSE(controller.GetTransientEntry());
2659 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2660 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2661 test_rvh()->SendNavigate(3, url3);
2662 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2664 // Add a transient and do an in-page navigation, replacing the current entry.
2665 transient_entry = new NavigationEntryImpl;
2666 transient_entry->SetURL(transient_url);
2667 controller.SetTransientEntry(transient_entry);
2668 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2669 test_rvh()->SendNavigate(3, url3_ref);
2670 // Transient entry should be gone.
2671 EXPECT_FALSE(controller.GetTransientEntry());
2672 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2674 // Ensure the URLs are correct.
2675 EXPECT_EQ(controller.GetEntryCount(), 5);
2676 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2677 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2678 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2679 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2680 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2683 // Test that Reload initiates a new navigation to a transient entry's URL.
2684 TEST_F(NavigationControllerTest, ReloadTransient) {
2685 NavigationControllerImpl& controller = controller_impl();
2686 const GURL url0("http://foo/0");
2687 const GURL url1("http://foo/1");
2688 const GURL transient_url("http://foo/transient");
2690 // Load |url0|, and start a pending navigation to |url1|.
2691 controller.LoadURL(
2692 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2693 test_rvh()->SendNavigate(0, url0);
2694 controller.LoadURL(
2695 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2697 // A transient entry is added, interrupting the navigation.
2698 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2699 transient_entry->SetURL(transient_url);
2700 controller.SetTransientEntry(transient_entry);
2701 EXPECT_TRUE(controller.GetTransientEntry());
2702 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2704 // The page is reloaded, which should remove the pending entry for |url1| and
2705 // the transient entry for |transient_url|, and start a navigation to
2706 // |transient_url|.
2707 controller.Reload(true);
2708 EXPECT_FALSE(controller.GetTransientEntry());
2709 EXPECT_TRUE(controller.GetPendingEntry());
2710 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2711 ASSERT_EQ(controller.GetEntryCount(), 1);
2712 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2714 // Load of |transient_url| completes.
2715 test_rvh()->SendNavigate(1, transient_url);
2716 ASSERT_EQ(controller.GetEntryCount(), 2);
2717 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2718 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2721 // Ensure that renderer initiated pending entries get replaced, so that we
2722 // don't show a stale virtual URL when a navigation commits.
2723 // See http://crbug.com/266922.
2724 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2725 NavigationControllerImpl& controller = controller_impl();
2726 Navigator* navigator =
2727 contents()->GetFrameTree()->root()->navigator();
2729 const GURL url1("nonexistent:12121");
2730 const GURL url1_fixed("http://nonexistent:12121/");
2731 const GURL url2("http://foo");
2733 // We create pending entries for renderer-initiated navigations so that we
2734 // can show them in new tabs when it is safe.
2735 navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url1);
2737 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2738 // the virtual URL to differ from the URL.
2739 controller.GetPendingEntry()->SetURL(url1_fixed);
2740 controller.GetPendingEntry()->SetVirtualURL(url1);
2742 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
2743 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
2744 EXPECT_TRUE(
2745 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2746 is_renderer_initiated());
2748 // If the user clicks another link, we should replace the pending entry.
2749 navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url2);
2750 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2751 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
2753 // Once it commits, the URL and virtual URL should reflect the actual page.
2754 test_rvh()->SendNavigate(0, url2);
2755 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2756 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
2758 // We should not replace the pending entry for an error URL.
2759 navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url1);
2760 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2761 navigator->DidStartProvisionalLoad(
2762 main_test_rfh(), 1, -1, true, GURL(kUnreachableWebDataURL));
2763 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2765 // We should remember if the pending entry will replace the current one.
2766 // http://crbug.com/308444.
2767 navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url1);
2768 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2769 set_should_replace_entry(true);
2770 navigator->DidStartProvisionalLoad(main_test_rfh(), 1, -1, true, url2);
2771 EXPECT_TRUE(
2772 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2773 should_replace_entry());
2774 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2775 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2776 // to the main frame.
2777 test_rvh()->SendNavigate(0, url2);
2778 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2781 // Tests that the URLs for renderer-initiated navigations are not displayed to
2782 // the user until the navigation commits, to prevent URL spoof attacks.
2783 // See http://crbug.com/99016.
2784 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
2785 NavigationControllerImpl& controller = controller_impl();
2786 TestNotificationTracker notifications;
2787 RegisterForAllNavNotifications(&notifications, &controller);
2789 const GURL url0("http://foo/0");
2790 const GURL url1("http://foo/1");
2792 // For typed navigations (browser-initiated), both pending and visible entries
2793 // should update before commit.
2794 controller.LoadURL(url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2795 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
2796 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2797 test_rvh()->SendNavigate(0, url0);
2799 // For link clicks (renderer-initiated navigations), the pending entry should
2800 // update before commit but the visible should not.
2801 NavigationController::LoadURLParams load_url_params(url1);
2802 load_url_params.is_renderer_initiated = true;
2803 controller.LoadURLWithParams(load_url_params);
2804 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2805 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2806 EXPECT_TRUE(
2807 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2808 is_renderer_initiated());
2810 // After commit, both visible should be updated, there should be no pending
2811 // entry, and we should no longer treat the entry as renderer-initiated.
2812 test_rvh()->SendNavigate(1, url1);
2813 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2814 EXPECT_FALSE(controller.GetPendingEntry());
2815 EXPECT_FALSE(
2816 NavigationEntryImpl::FromNavigationEntry(
2817 controller.GetLastCommittedEntry())->is_renderer_initiated());
2819 notifications.Reset();
2822 // Tests that the URLs for renderer-initiated navigations in new tabs are
2823 // displayed to the user before commit, as long as the initial about:blank
2824 // page has not been modified. If so, we must revert to showing about:blank.
2825 // See http://crbug.com/9682.
2826 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
2827 NavigationControllerImpl& controller = controller_impl();
2828 TestNotificationTracker notifications;
2829 RegisterForAllNavNotifications(&notifications, &controller);
2831 const GURL url("http://foo");
2833 // For renderer-initiated navigations in new tabs (with no committed entries),
2834 // we show the pending entry's URL as long as the about:blank page is not
2835 // modified.
2836 NavigationController::LoadURLParams load_url_params(url);
2837 load_url_params.transition_type = PAGE_TRANSITION_LINK;
2838 load_url_params.is_renderer_initiated = true;
2839 controller.LoadURLWithParams(load_url_params);
2840 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2841 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2842 EXPECT_TRUE(
2843 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2844 is_renderer_initiated());
2845 EXPECT_TRUE(controller.IsInitialNavigation());
2846 EXPECT_FALSE(test_rvh()->has_accessed_initial_document());
2848 // There should be no title yet.
2849 EXPECT_TRUE(contents()->GetTitle().empty());
2851 // If something else modifies the contents of the about:blank page, then
2852 // we must revert to showing about:blank to avoid a URL spoof.
2853 test_rvh()->OnMessageReceived(
2854 ViewHostMsg_DidAccessInitialDocument(0));
2855 EXPECT_TRUE(test_rvh()->has_accessed_initial_document());
2856 EXPECT_FALSE(controller.GetVisibleEntry());
2857 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2859 notifications.Reset();
2862 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
2863 NavigationControllerImpl& controller = controller_impl();
2864 TestNotificationTracker notifications;
2865 RegisterForAllNavNotifications(&notifications, &controller);
2867 const GURL url1("http://foo/eh");
2868 const GURL url2("http://foo/bee");
2870 // For renderer-initiated navigations in new tabs (with no committed entries),
2871 // we show the pending entry's URL as long as the about:blank page is not
2872 // modified.
2873 NavigationController::LoadURLParams load_url_params(url1);
2874 load_url_params.transition_type = PAGE_TRANSITION_LINK;
2875 load_url_params.is_renderer_initiated = true;
2876 controller.LoadURLWithParams(load_url_params);
2877 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2878 EXPECT_TRUE(
2879 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2880 is_renderer_initiated());
2881 EXPECT_TRUE(controller.IsInitialNavigation());
2882 EXPECT_FALSE(test_rvh()->has_accessed_initial_document());
2884 // Simulate a commit and then starting a new pending navigation.
2885 test_rvh()->SendNavigate(0, url1);
2886 NavigationController::LoadURLParams load_url2_params(url2);
2887 load_url2_params.transition_type = PAGE_TRANSITION_LINK;
2888 load_url2_params.is_renderer_initiated = true;
2889 controller.LoadURLWithParams(load_url2_params);
2891 // We should not consider this an initial navigation, and thus should
2892 // not show the pending URL.
2893 EXPECT_FALSE(test_rvh()->has_accessed_initial_document());
2894 EXPECT_FALSE(controller.IsInitialNavigation());
2895 EXPECT_TRUE(controller.GetVisibleEntry());
2896 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2898 notifications.Reset();
2901 // Tests that IsInPageNavigation returns appropriate results. Prevents
2902 // regression for bug 1126349.
2903 TEST_F(NavigationControllerTest, IsInPageNavigation) {
2904 NavigationControllerImpl& controller = controller_impl();
2905 // Navigate to URL with no refs.
2906 const GURL url("http://www.google.com/home.html");
2907 test_rvh()->SendNavigate(0, url);
2909 // Reloading the page is not an in-page navigation.
2910 EXPECT_FALSE(controller.IsURLInPageNavigation(url));
2911 const GURL other_url("http://www.google.com/add.html");
2912 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url));
2913 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
2914 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref));
2916 // Navigate to URL with refs.
2917 test_rvh()->SendNavigate(1, url_with_ref);
2919 // Reloading the page is not an in-page navigation.
2920 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref));
2921 EXPECT_FALSE(controller.IsURLInPageNavigation(url));
2922 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url));
2923 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
2924 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref));
2926 // Going to the same url again will be considered in-page
2927 // if the renderer says it is even if the navigation type isn't IN_PAGE.
2928 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
2929 NAVIGATION_TYPE_UNKNOWN));
2931 // Going back to the non ref url will be considered in-page if the navigation
2932 // type is IN_PAGE.
2933 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
2934 NAVIGATION_TYPE_IN_PAGE));
2937 // Some pages can have subframes with the same base URL (minus the reference) as
2938 // the main page. Even though this is hard, it can happen, and we don't want
2939 // these subframe navigations to affect the toplevel document. They should
2940 // instead be ignored. http://crbug.com/5585
2941 TEST_F(NavigationControllerTest, SameSubframe) {
2942 NavigationControllerImpl& controller = controller_impl();
2943 // Navigate the main frame.
2944 const GURL url("http://www.google.com/");
2945 test_rvh()->SendNavigate(0, url);
2947 // We should be at the first navigation entry.
2948 EXPECT_EQ(controller.GetEntryCount(), 1);
2949 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
2951 // Navigate a subframe that would normally count as in-page.
2952 const GURL subframe("http://www.google.com/#");
2953 ViewHostMsg_FrameNavigate_Params params;
2954 params.page_id = 0;
2955 params.url = subframe;
2956 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
2957 params.should_update_history = false;
2958 params.gesture = NavigationGestureAuto;
2959 params.is_post = false;
2960 params.page_state = PageState::CreateFromURL(subframe);
2961 LoadCommittedDetails details;
2962 EXPECT_FALSE(controller.RendererDidNavigate(test_rvh(), params, &details));
2964 // Nothing should have changed.
2965 EXPECT_EQ(controller.GetEntryCount(), 1);
2966 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
2969 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
2970 // false.
2971 TEST_F(NavigationControllerTest, CloneAndGoBack) {
2972 NavigationControllerImpl& controller = controller_impl();
2973 const GURL url1("http://foo1");
2974 const GURL url2("http://foo2");
2975 const base::string16 title(base::ASCIIToUTF16("Title"));
2977 NavigateAndCommit(url1);
2978 controller.GetVisibleEntry()->SetTitle(title);
2979 NavigateAndCommit(url2);
2981 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
2983 ASSERT_EQ(2, clone->GetController().GetEntryCount());
2984 EXPECT_TRUE(clone->GetController().NeedsReload());
2985 clone->GetController().GoBack();
2986 // Navigating back should have triggered needs_reload_ to go false.
2987 EXPECT_FALSE(clone->GetController().NeedsReload());
2989 // Ensure that the pending URL and its title are visible.
2990 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
2991 EXPECT_EQ(title, clone->GetTitle());
2994 // Make sure that reloading a cloned tab doesn't change its pending entry index.
2995 // See http://crbug.com/234491.
2996 TEST_F(NavigationControllerTest, CloneAndReload) {
2997 NavigationControllerImpl& controller = controller_impl();
2998 const GURL url1("http://foo1");
2999 const GURL url2("http://foo2");
3000 const base::string16 title(base::ASCIIToUTF16("Title"));
3002 NavigateAndCommit(url1);
3003 controller.GetVisibleEntry()->SetTitle(title);
3004 NavigateAndCommit(url2);
3006 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3007 clone->GetController().LoadIfNecessary();
3009 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3010 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3012 clone->GetController().Reload(true);
3013 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3016 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3017 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3018 NavigationControllerImpl& controller = controller_impl();
3019 const GURL url1("http://foo1");
3020 const GURL url2("http://foo2");
3022 NavigateAndCommit(url1);
3023 NavigateAndCommit(url2);
3025 // Add an interstitial entry. Should be deleted with controller.
3026 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3027 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3028 controller.SetTransientEntry(interstitial_entry);
3030 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3032 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3035 // Test requesting and triggering a lazy reload.
3036 TEST_F(NavigationControllerTest, LazyReload) {
3037 NavigationControllerImpl& controller = controller_impl();
3038 const GURL url("http://foo");
3039 NavigateAndCommit(url);
3040 ASSERT_FALSE(controller.NeedsReload());
3042 // Request a reload to happen when the controller becomes active (e.g. after
3043 // the renderer gets killed in background on Android).
3044 controller.SetNeedsReload();
3045 ASSERT_TRUE(controller.NeedsReload());
3047 // Set the controller as active, triggering the requested reload.
3048 controller.SetActive(true);
3049 ASSERT_FALSE(controller.NeedsReload());
3052 // Tests a subframe navigation while a toplevel navigation is pending.
3053 // http://crbug.com/43967
3054 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3055 NavigationControllerImpl& controller = controller_impl();
3056 // Load the first page.
3057 const GURL url1("http://foo/");
3058 NavigateAndCommit(url1);
3060 // Now start a pending load to a totally different page, but don't commit it.
3061 const GURL url2("http://bar/");
3062 controller.LoadURL(
3063 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3065 // Send a subframe update from the first page, as if one had just
3066 // automatically loaded. Auto subframes don't increment the page ID.
3067 const GURL url1_sub("http://foo/subframe");
3068 ViewHostMsg_FrameNavigate_Params params;
3069 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3070 params.url = url1_sub;
3071 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3072 params.should_update_history = false;
3073 params.gesture = NavigationGestureAuto;
3074 params.is_post = false;
3075 params.page_state = PageState::CreateFromURL(url1_sub);
3076 LoadCommittedDetails details;
3078 // This should return false meaning that nothing was actually updated.
3079 EXPECT_FALSE(controller.RendererDidNavigate(test_rvh(), params, &details));
3081 // The notification should have updated the last committed one, and not
3082 // the pending load.
3083 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3085 // The active entry should be unchanged by the subframe load.
3086 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3089 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3090 TEST_F(NavigationControllerTest, CopyStateFrom) {
3091 NavigationControllerImpl& controller = controller_impl();
3092 const GURL url1("http://foo1");
3093 const GURL url2("http://foo2");
3095 NavigateAndCommit(url1);
3096 NavigateAndCommit(url2);
3097 controller.GoBack();
3098 contents()->CommitPendingNavigation();
3100 scoped_ptr<TestWebContents> other_contents(
3101 static_cast<TestWebContents*>(CreateTestWebContents()));
3102 NavigationControllerImpl& other_controller = other_contents->GetController();
3103 other_controller.CopyStateFrom(controller);
3105 // other_controller should now contain 2 urls.
3106 ASSERT_EQ(2, other_controller.GetEntryCount());
3107 // We should be looking at the first one.
3108 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3110 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3111 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3112 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3113 // This is a different site than url1, so the IDs start again at 0.
3114 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3116 // The max page ID map should be copied over and updated with the max page ID
3117 // from the current tab.
3118 SiteInstance* instance1 =
3119 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3120 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3122 // Ensure the SessionStorageNamespaceMaps are the same size and have
3123 // the same partitons loaded.
3125 // TODO(ajwong): We should load a url from a different partition earlier
3126 // to make sure this map has more than one entry.
3127 const SessionStorageNamespaceMap& session_storage_namespace_map =
3128 controller.GetSessionStorageNamespaceMap();
3129 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3130 other_controller.GetSessionStorageNamespaceMap();
3131 EXPECT_EQ(session_storage_namespace_map.size(),
3132 other_session_storage_namespace_map.size());
3133 for (SessionStorageNamespaceMap::const_iterator it =
3134 session_storage_namespace_map.begin();
3135 it != session_storage_namespace_map.end();
3136 ++it) {
3137 SessionStorageNamespaceMap::const_iterator other =
3138 other_session_storage_namespace_map.find(it->first);
3139 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3143 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3144 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3145 NavigationControllerImpl& controller = controller_impl();
3146 const GURL url1("http://foo/1");
3147 const GURL url2("http://foo/2");
3148 const GURL url3("http://foo/3");
3150 NavigateAndCommit(url1);
3151 NavigateAndCommit(url2);
3153 // First two entries should have the same SiteInstance.
3154 SiteInstance* instance1 =
3155 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3156 SiteInstance* instance2 =
3157 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3158 EXPECT_EQ(instance1, instance2);
3159 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3160 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3161 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3163 scoped_ptr<TestWebContents> other_contents(
3164 static_cast<TestWebContents*>(CreateTestWebContents()));
3165 NavigationControllerImpl& other_controller = other_contents->GetController();
3166 other_contents->NavigateAndCommit(url3);
3167 other_contents->ExpectSetHistoryLengthAndPrune(
3168 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3169 other_controller.GetEntryAtIndex(0)->GetPageID());
3170 other_controller.CopyStateFromAndPrune(&controller, false);
3172 // other_controller should now contain the 3 urls: url1, url2 and url3.
3174 ASSERT_EQ(3, other_controller.GetEntryCount());
3176 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3178 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3179 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3180 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3181 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3182 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3183 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3185 // A new SiteInstance in a different BrowsingInstance should be used for the
3186 // new tab.
3187 SiteInstance* instance3 =
3188 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3189 EXPECT_NE(instance3, instance1);
3190 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3192 // The max page ID map should be copied over and updated with the max page ID
3193 // from the current tab.
3194 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3195 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3198 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3199 // the target.
3200 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3201 NavigationControllerImpl& controller = controller_impl();
3202 const GURL url1("http://foo1");
3203 const GURL url2("http://foo2");
3204 const GURL url3("http://foo3");
3206 NavigateAndCommit(url1);
3207 NavigateAndCommit(url2);
3208 controller.GoBack();
3209 contents()->CommitPendingNavigation();
3211 scoped_ptr<TestWebContents> other_contents(
3212 static_cast<TestWebContents*>(CreateTestWebContents()));
3213 NavigationControllerImpl& other_controller = other_contents->GetController();
3214 other_contents->NavigateAndCommit(url3);
3215 other_contents->ExpectSetHistoryLengthAndPrune(
3216 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3217 other_controller.GetEntryAtIndex(0)->GetPageID());
3218 other_controller.CopyStateFromAndPrune(&controller, false);
3220 // other_controller should now contain: url1, url3
3222 ASSERT_EQ(2, other_controller.GetEntryCount());
3223 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3225 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3226 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3227 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3229 // The max page ID map should be copied over and updated with the max page ID
3230 // from the current tab.
3231 SiteInstance* instance1 =
3232 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3233 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3236 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3237 // the target.
3238 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3239 NavigationControllerImpl& controller = controller_impl();
3240 const GURL url1("http://foo1");
3241 const GURL url2("http://foo2");
3242 const GURL url3("http://foo3");
3243 const GURL url4("http://foo4");
3245 NavigateAndCommit(url1);
3246 NavigateAndCommit(url2);
3248 scoped_ptr<TestWebContents> other_contents(
3249 static_cast<TestWebContents*>(CreateTestWebContents()));
3250 NavigationControllerImpl& other_controller = other_contents->GetController();
3251 other_contents->NavigateAndCommit(url3);
3252 other_contents->NavigateAndCommit(url4);
3253 other_contents->ExpectSetHistoryLengthAndPrune(
3254 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)), 2,
3255 other_controller.GetEntryAtIndex(0)->GetPageID());
3256 other_controller.CopyStateFromAndPrune(&controller, false);
3258 // other_controller should now contain: url1, url2, url4
3260 ASSERT_EQ(3, other_controller.GetEntryCount());
3261 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3263 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3264 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3265 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3267 // The max page ID map should be copied over and updated with the max page ID
3268 // from the current tab.
3269 SiteInstance* instance1 =
3270 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3271 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3274 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3275 // not the last entry selected in the target.
3276 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3277 NavigationControllerImpl& controller = controller_impl();
3278 const GURL url1("http://foo1");
3279 const GURL url2("http://foo2");
3280 const GURL url3("http://foo3");
3281 const GURL url4("http://foo4");
3283 NavigateAndCommit(url1);
3284 NavigateAndCommit(url2);
3286 scoped_ptr<TestWebContents> other_contents(
3287 static_cast<TestWebContents*>(CreateTestWebContents()));
3288 NavigationControllerImpl& other_controller = other_contents->GetController();
3289 other_contents->NavigateAndCommit(url3);
3290 other_contents->NavigateAndCommit(url4);
3291 other_controller.GoBack();
3292 other_contents->CommitPendingNavigation();
3293 other_contents->ExpectSetHistoryLengthAndPrune(
3294 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3295 other_controller.GetEntryAtIndex(0)->GetPageID());
3296 other_controller.CopyStateFromAndPrune(&controller, false);
3298 // other_controller should now contain: url1, url2, url3
3300 ASSERT_EQ(3, other_controller.GetEntryCount());
3301 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3303 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3304 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3305 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3307 // The max page ID map should be copied over and updated with the max page ID
3308 // from the current tab.
3309 SiteInstance* instance1 =
3310 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3311 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3314 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3315 // a pending entry in the target.
3316 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3317 NavigationControllerImpl& controller = controller_impl();
3318 const GURL url1("http://foo1");
3319 const GURL url2("http://foo2");
3320 const GURL url3("http://foo3");
3321 const GURL url4("http://foo4");
3323 NavigateAndCommit(url1);
3324 NavigateAndCommit(url2);
3325 controller.GoBack();
3326 contents()->CommitPendingNavigation();
3328 scoped_ptr<TestWebContents> other_contents(
3329 static_cast<TestWebContents*>(CreateTestWebContents()));
3330 NavigationControllerImpl& other_controller = other_contents->GetController();
3331 other_contents->NavigateAndCommit(url3);
3332 other_controller.LoadURL(
3333 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3334 other_contents->ExpectSetHistoryLengthAndPrune(
3335 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3336 other_controller.GetEntryAtIndex(0)->GetPageID());
3337 other_controller.CopyStateFromAndPrune(&controller, false);
3339 // other_controller should now contain url1, url3, and a pending entry
3340 // for url4.
3342 ASSERT_EQ(2, other_controller.GetEntryCount());
3343 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3345 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3346 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3348 // And there should be a pending entry for url4.
3349 ASSERT_TRUE(other_controller.GetPendingEntry());
3350 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3352 // The max page ID map should be copied over and updated with the max page ID
3353 // from the current tab.
3354 SiteInstance* instance1 =
3355 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3356 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3359 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3360 // client redirect entry (with the same page ID) in the target. This used to
3361 // crash because the last committed entry would be pruned but max_page_id
3362 // remembered the page ID (http://crbug.com/234809).
3363 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3364 NavigationControllerImpl& controller = controller_impl();
3365 const GURL url1("http://foo1");
3366 const GURL url2a("http://foo2/a");
3367 const GURL url2b("http://foo2/b");
3369 NavigateAndCommit(url1);
3371 scoped_ptr<TestWebContents> other_contents(
3372 static_cast<TestWebContents*>(CreateTestWebContents()));
3373 NavigationControllerImpl& other_controller = other_contents->GetController();
3374 other_contents->NavigateAndCommit(url2a);
3375 // Simulate a client redirect, which has the same page ID as entry 2a.
3376 other_controller.LoadURL(
3377 url2b, Referrer(), PAGE_TRANSITION_LINK, std::string());
3378 other_controller.GetPendingEntry()->SetPageID(
3379 other_controller.GetLastCommittedEntry()->GetPageID());
3381 other_contents->ExpectSetHistoryLengthAndPrune(
3382 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3383 other_controller.GetEntryAtIndex(0)->GetPageID());
3384 other_controller.CopyStateFromAndPrune(&controller, false);
3386 // other_controller should now contain url1, url2a, and a pending entry
3387 // for url2b.
3389 ASSERT_EQ(2, other_controller.GetEntryCount());
3390 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3392 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3393 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3395 // And there should be a pending entry for url4.
3396 ASSERT_TRUE(other_controller.GetPendingEntry());
3397 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3399 // Let the pending entry commit.
3400 other_contents->CommitPendingNavigation();
3402 // The max page ID map should be copied over and updated with the max page ID
3403 // from the current tab.
3404 SiteInstance* instance1 =
3405 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3406 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3409 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3410 // source, and 1 entry in the target. The back pending entry should be ignored.
3411 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3412 NavigationControllerImpl& controller = controller_impl();
3413 const GURL url1("http://foo1");
3414 const GURL url2("http://foo2");
3415 const GURL url3("http://foo3");
3417 NavigateAndCommit(url1);
3418 NavigateAndCommit(url2);
3419 controller.GoBack();
3421 scoped_ptr<TestWebContents> other_contents(
3422 static_cast<TestWebContents*>(CreateTestWebContents()));
3423 NavigationControllerImpl& other_controller = other_contents->GetController();
3424 other_contents->NavigateAndCommit(url3);
3425 other_contents->ExpectSetHistoryLengthAndPrune(
3426 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3427 other_controller.GetEntryAtIndex(0)->GetPageID());
3428 other_controller.CopyStateFromAndPrune(&controller, false);
3430 // other_controller should now contain: url1, url2, url3
3432 ASSERT_EQ(3, other_controller.GetEntryCount());
3433 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3435 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3436 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3437 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3438 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3440 // The max page ID map should be copied over and updated with the max page ID
3441 // from the current tab.
3442 SiteInstance* instance1 =
3443 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3444 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3447 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3448 // when the max entry count is 3. We should prune one entry.
3449 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3450 NavigationControllerImpl& controller = controller_impl();
3451 size_t original_count = NavigationControllerImpl::max_entry_count();
3452 const int kMaxEntryCount = 3;
3454 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3456 const GURL url1("http://foo/1");
3457 const GURL url2("http://foo/2");
3458 const GURL url3("http://foo/3");
3459 const GURL url4("http://foo/4");
3461 // Create a PrunedListener to observe prune notifications.
3462 PrunedListener listener(&controller);
3464 NavigateAndCommit(url1);
3465 NavigateAndCommit(url2);
3466 NavigateAndCommit(url3);
3468 scoped_ptr<TestWebContents> other_contents(
3469 static_cast<TestWebContents*>(CreateTestWebContents()));
3470 NavigationControllerImpl& other_controller = other_contents->GetController();
3471 other_contents->NavigateAndCommit(url4);
3472 other_contents->ExpectSetHistoryLengthAndPrune(
3473 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3474 other_controller.GetEntryAtIndex(0)->GetPageID());
3475 other_controller.CopyStateFromAndPrune(&controller, false);
3477 // We should have received a pruned notification.
3478 EXPECT_EQ(1, listener.notification_count_);
3479 EXPECT_TRUE(listener.details_.from_front);
3480 EXPECT_EQ(1, listener.details_.count);
3482 // other_controller should now contain only 3 urls: url2, url3 and url4.
3484 ASSERT_EQ(3, other_controller.GetEntryCount());
3486 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3488 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3489 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3490 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3491 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3492 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3493 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3495 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3498 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3499 // replace_entry set to true.
3500 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3501 NavigationControllerImpl& controller = controller_impl();
3502 const GURL url1("http://foo/1");
3503 const GURL url2("http://foo/2");
3504 const GURL url3("http://foo/3");
3506 NavigateAndCommit(url1);
3507 NavigateAndCommit(url2);
3509 // First two entries should have the same SiteInstance.
3510 SiteInstance* instance1 =
3511 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3512 SiteInstance* instance2 =
3513 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3514 EXPECT_EQ(instance1, instance2);
3515 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3516 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3517 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3519 scoped_ptr<TestWebContents> other_contents(
3520 static_cast<TestWebContents*>(CreateTestWebContents()));
3521 NavigationControllerImpl& other_controller = other_contents->GetController();
3522 other_contents->NavigateAndCommit(url3);
3523 other_contents->ExpectSetHistoryLengthAndPrune(
3524 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3525 other_controller.GetEntryAtIndex(0)->GetPageID());
3526 other_controller.CopyStateFromAndPrune(&controller, true);
3528 // other_controller should now contain the 2 urls: url1 and url3.
3530 ASSERT_EQ(2, other_controller.GetEntryCount());
3532 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3534 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3535 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3536 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3537 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3539 // A new SiteInstance in a different BrowsingInstance should be used for the
3540 // new tab.
3541 SiteInstance* instance3 =
3542 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3543 EXPECT_NE(instance3, instance1);
3544 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3546 // The max page ID map should be copied over and updated with the max page ID
3547 // from the current tab.
3548 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3549 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3552 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3553 // entry count is 3 and replace_entry is true. We should not prune entries.
3554 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3555 NavigationControllerImpl& controller = controller_impl();
3556 size_t original_count = NavigationControllerImpl::max_entry_count();
3557 const int kMaxEntryCount = 3;
3559 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3561 const GURL url1("http://foo/1");
3562 const GURL url2("http://foo/2");
3563 const GURL url3("http://foo/3");
3564 const GURL url4("http://foo/4");
3566 // Create a PrunedListener to observe prune notifications.
3567 PrunedListener listener(&controller);
3569 NavigateAndCommit(url1);
3570 NavigateAndCommit(url2);
3571 NavigateAndCommit(url3);
3573 scoped_ptr<TestWebContents> other_contents(
3574 static_cast<TestWebContents*>(CreateTestWebContents()));
3575 NavigationControllerImpl& other_controller = other_contents->GetController();
3576 other_contents->NavigateAndCommit(url4);
3577 other_contents->ExpectSetHistoryLengthAndPrune(
3578 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3579 other_controller.GetEntryAtIndex(0)->GetPageID());
3580 other_controller.CopyStateFromAndPrune(&controller, true);
3582 // We should have received no pruned notification.
3583 EXPECT_EQ(0, listener.notification_count_);
3585 // other_controller should now contain only 3 urls: url1, url2 and url4.
3587 ASSERT_EQ(3, other_controller.GetEntryCount());
3589 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3591 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3592 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3593 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3594 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3595 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3596 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3598 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3601 // Tests that navigations initiated from the page (with the history object)
3602 // work as expected without navigation entries.
3603 TEST_F(NavigationControllerTest, HistoryNavigate) {
3604 NavigationControllerImpl& controller = controller_impl();
3605 const GURL url1("http://foo/1");
3606 const GURL url2("http://foo/2");
3607 const GURL url3("http://foo/3");
3609 NavigateAndCommit(url1);
3610 NavigateAndCommit(url2);
3611 NavigateAndCommit(url3);
3612 controller.GoBack();
3613 contents()->CommitPendingNavigation();
3615 // Simulate the page calling history.back(), it should not create a pending
3616 // entry.
3617 contents()->OnGoToEntryAtOffset(-1);
3618 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3619 // The actual cross-navigation is suspended until the current RVH tells us
3620 // it unloaded, simulate that.
3621 contents()->ProceedWithCrossSiteNavigation();
3622 // Also make sure we told the page to navigate.
3623 const IPC::Message* message =
3624 process()->sink().GetFirstMessageMatching(ViewMsg_Navigate::ID);
3625 ASSERT_TRUE(message != NULL);
3626 Tuple1<ViewMsg_Navigate_Params> nav_params;
3627 ViewMsg_Navigate::Read(message, &nav_params);
3628 EXPECT_EQ(url1, nav_params.a.url);
3629 process()->sink().ClearMessages();
3631 // Now test history.forward()
3632 contents()->OnGoToEntryAtOffset(1);
3633 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3634 // The actual cross-navigation is suspended until the current RVH tells us
3635 // it unloaded, simulate that.
3636 contents()->ProceedWithCrossSiteNavigation();
3637 message = process()->sink().GetFirstMessageMatching(ViewMsg_Navigate::ID);
3638 ASSERT_TRUE(message != NULL);
3639 ViewMsg_Navigate::Read(message, &nav_params);
3640 EXPECT_EQ(url3, nav_params.a.url);
3641 process()->sink().ClearMessages();
3643 // Make sure an extravagant history.go() doesn't break.
3644 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
3645 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3646 message = process()->sink().GetFirstMessageMatching(ViewMsg_Navigate::ID);
3647 EXPECT_TRUE(message == NULL);
3650 // Test call to PruneAllButLastCommitted for the only entry.
3651 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
3652 NavigationControllerImpl& controller = controller_impl();
3653 const GURL url1("http://foo1");
3654 NavigateAndCommit(url1);
3656 contents()->ExpectSetHistoryLengthAndPrune(
3657 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3658 controller.GetEntryAtIndex(0)->GetPageID());
3660 controller.PruneAllButLastCommitted();
3662 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3663 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3666 // Test call to PruneAllButLastCommitted for first entry.
3667 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
3668 NavigationControllerImpl& controller = controller_impl();
3669 const GURL url1("http://foo/1");
3670 const GURL url2("http://foo/2");
3671 const GURL url3("http://foo/3");
3673 NavigateAndCommit(url1);
3674 NavigateAndCommit(url2);
3675 NavigateAndCommit(url3);
3676 controller.GoBack();
3677 controller.GoBack();
3678 contents()->CommitPendingNavigation();
3680 contents()->ExpectSetHistoryLengthAndPrune(
3681 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3682 controller.GetEntryAtIndex(0)->GetPageID());
3684 controller.PruneAllButLastCommitted();
3686 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3687 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3690 // Test call to PruneAllButLastCommitted for intermediate entry.
3691 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
3692 NavigationControllerImpl& controller = controller_impl();
3693 const GURL url1("http://foo/1");
3694 const GURL url2("http://foo/2");
3695 const GURL url3("http://foo/3");
3697 NavigateAndCommit(url1);
3698 NavigateAndCommit(url2);
3699 NavigateAndCommit(url3);
3700 controller.GoBack();
3701 contents()->CommitPendingNavigation();
3703 contents()->ExpectSetHistoryLengthAndPrune(
3704 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)), 0,
3705 controller.GetEntryAtIndex(1)->GetPageID());
3707 controller.PruneAllButLastCommitted();
3709 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3710 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
3713 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
3714 // the list of entries.
3715 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
3716 NavigationControllerImpl& controller = controller_impl();
3717 const GURL url1("http://foo/1");
3718 const GURL url2("http://foo/2");
3719 const GURL url3("http://foo/3");
3721 NavigateAndCommit(url1);
3722 NavigateAndCommit(url2);
3724 // Create a pending entry that is not in the entry list.
3725 controller.LoadURL(
3726 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3727 EXPECT_TRUE(controller.GetPendingEntry());
3728 EXPECT_EQ(2, controller.GetEntryCount());
3730 contents()->ExpectSetHistoryLengthAndPrune(
3731 NULL, 0, controller.GetPendingEntry()->GetPageID());
3732 controller.PruneAllButLastCommitted();
3734 // We should only have the last committed and pending entries at this point,
3735 // and the pending entry should still not be in the entry list.
3736 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
3737 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
3738 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3739 EXPECT_TRUE(controller.GetPendingEntry());
3740 EXPECT_EQ(1, controller.GetEntryCount());
3742 // Try to commit the pending entry.
3743 test_rvh()->SendNavigate(2, url3);
3744 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3745 EXPECT_FALSE(controller.GetPendingEntry());
3746 EXPECT_EQ(2, controller.GetEntryCount());
3747 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
3750 // Test to ensure that when we do a history navigation back to the current
3751 // committed page (e.g., going forward to a slow-loading page, then pressing
3752 // the back button), we just stop the navigation to prevent the throbber from
3753 // running continuously. Otherwise, the RenderViewHost forces the throbber to
3754 // start, but WebKit essentially ignores the navigation and never sends a
3755 // message to stop the throbber.
3756 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
3757 NavigationControllerImpl& controller = controller_impl();
3758 const GURL url0("http://foo/0");
3759 const GURL url1("http://foo/1");
3761 NavigateAndCommit(url0);
3762 NavigateAndCommit(url1);
3764 // Go back to the original page, then forward to the slow page, then back
3765 controller.GoBack();
3766 contents()->CommitPendingNavigation();
3768 controller.GoForward();
3769 EXPECT_EQ(1, controller.GetPendingEntryIndex());
3771 controller.GoBack();
3772 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3775 TEST_F(NavigationControllerTest, IsInitialNavigation) {
3776 NavigationControllerImpl& controller = controller_impl();
3777 TestNotificationTracker notifications;
3778 RegisterForAllNavNotifications(&notifications, &controller);
3780 // Initial state.
3781 EXPECT_TRUE(controller.IsInitialNavigation());
3783 // After commit, it stays false.
3784 const GURL url1("http://foo1");
3785 test_rvh()->SendNavigate(0, url1);
3786 EXPECT_EQ(1U, navigation_entry_committed_counter_);
3787 navigation_entry_committed_counter_ = 0;
3788 EXPECT_FALSE(controller.IsInitialNavigation());
3790 // After starting a new navigation, it stays false.
3791 const GURL url2("http://foo2");
3792 controller.LoadURL(
3793 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3796 // Check that the favicon is not reused across a client redirect.
3797 // (crbug.com/28515)
3798 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
3799 const GURL kPageWithFavicon("http://withfavicon.html");
3800 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
3801 const GURL kIconURL("http://withfavicon.ico");
3802 const gfx::Image kDefaultFavicon = FaviconStatus().image;
3804 NavigationControllerImpl& controller = controller_impl();
3805 TestNotificationTracker notifications;
3806 RegisterForAllNavNotifications(&notifications, &controller);
3808 test_rvh()->SendNavigate(0, kPageWithFavicon);
3809 EXPECT_EQ(1U, navigation_entry_committed_counter_);
3810 navigation_entry_committed_counter_ = 0;
3812 NavigationEntry* entry = controller.GetLastCommittedEntry();
3813 EXPECT_TRUE(entry);
3814 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
3816 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
3817 content::FaviconStatus& favicon_status = entry->GetFavicon();
3818 favicon_status.image = CreateImage(SK_ColorWHITE);
3819 favicon_status.url = kIconURL;
3820 favicon_status.valid = true;
3821 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
3823 test_rvh()->SendNavigateWithTransition(
3824 0, // same page ID.
3825 kPageWithoutFavicon,
3826 PAGE_TRANSITION_CLIENT_REDIRECT);
3827 EXPECT_EQ(1U, navigation_entry_committed_counter_);
3828 navigation_entry_committed_counter_ = 0;
3830 entry = controller.GetLastCommittedEntry();
3831 EXPECT_TRUE(entry);
3832 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
3834 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
3837 // Check that the favicon is not cleared for NavigationEntries which were
3838 // previously navigated to.
3839 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
3840 const GURL kUrl1("http://www.a.com/1");
3841 const GURL kUrl2("http://www.a.com/2");
3842 const GURL kIconURL("http://www.a.com/1/favicon.ico");
3844 NavigationControllerImpl& controller = controller_impl();
3845 TestNotificationTracker notifications;
3846 RegisterForAllNavNotifications(&notifications, &controller);
3848 test_rvh()->SendNavigate(0, kUrl1);
3849 EXPECT_EQ(1U, navigation_entry_committed_counter_);
3850 navigation_entry_committed_counter_ = 0;
3852 // Simulate Chromium having set the favicon for |kUrl1|.
3853 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
3854 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
3855 EXPECT_TRUE(entry);
3856 content::FaviconStatus& favicon_status = entry->GetFavicon();
3857 favicon_status.image = favicon_image;
3858 favicon_status.url = kIconURL;
3859 favicon_status.valid = true;
3861 // Navigate to another page and go back to the original page.
3862 test_rvh()->SendNavigate(1, kUrl2);
3863 EXPECT_EQ(1U, navigation_entry_committed_counter_);
3864 navigation_entry_committed_counter_ = 0;
3865 test_rvh()->SendNavigateWithTransition(
3867 kUrl1,
3868 PAGE_TRANSITION_FORWARD_BACK);
3869 EXPECT_EQ(1U, navigation_entry_committed_counter_);
3870 navigation_entry_committed_counter_ = 0;
3872 // Verify that the favicon for the page at |kUrl1| was not cleared.
3873 entry = controller.GetEntryAtIndex(0);
3874 EXPECT_TRUE(entry);
3875 EXPECT_EQ(kUrl1, entry->GetURL());
3876 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
3879 // The test crashes on android: http://crbug.com/170449
3880 #if defined(OS_ANDROID)
3881 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
3882 #else
3883 #define MAYBE_PurgeScreenshot PurgeScreenshot
3884 #endif
3885 // Tests that screenshot are purged correctly.
3886 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
3887 NavigationControllerImpl& controller = controller_impl();
3889 NavigationEntryImpl* entry;
3891 // Navigate enough times to make sure that some screenshots are purged.
3892 for (int i = 0; i < 12; ++i) {
3893 const GURL url(base::StringPrintf("http://foo%d/", i));
3894 NavigateAndCommit(url);
3895 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
3898 MockScreenshotManager* screenshot_manager =
3899 new MockScreenshotManager(&controller);
3900 controller.SetScreenshotManager(screenshot_manager);
3901 for (int i = 0; i < controller.GetEntryCount(); ++i) {
3902 entry = NavigationEntryImpl::FromNavigationEntry(
3903 controller.GetEntryAtIndex(i));
3904 screenshot_manager->TakeScreenshotFor(entry);
3905 EXPECT_TRUE(entry->screenshot().get());
3908 NavigateAndCommit(GURL("https://foo/"));
3909 EXPECT_EQ(13, controller.GetEntryCount());
3910 entry = NavigationEntryImpl::FromNavigationEntry(
3911 controller.GetEntryAtIndex(11));
3912 screenshot_manager->TakeScreenshotFor(entry);
3914 for (int i = 0; i < 2; ++i) {
3915 entry = NavigationEntryImpl::FromNavigationEntry(
3916 controller.GetEntryAtIndex(i));
3917 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
3918 << " not purged";
3921 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
3922 entry = NavigationEntryImpl::FromNavigationEntry(
3923 controller.GetEntryAtIndex(i));
3924 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
3927 // Navigate to index 5 and then try to assign screenshot to all entries.
3928 controller.GoToIndex(5);
3929 contents()->CommitPendingNavigation();
3930 EXPECT_EQ(5, controller.GetCurrentEntryIndex());
3931 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
3932 entry = NavigationEntryImpl::FromNavigationEntry(
3933 controller.GetEntryAtIndex(i));
3934 screenshot_manager->TakeScreenshotFor(entry);
3937 for (int i = 10; i <= 12; ++i) {
3938 entry = NavigationEntryImpl::FromNavigationEntry(
3939 controller.GetEntryAtIndex(i));
3940 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
3941 << " not purged";
3942 screenshot_manager->TakeScreenshotFor(entry);
3945 // Navigate to index 7 and assign screenshot to all entries.
3946 controller.GoToIndex(7);
3947 contents()->CommitPendingNavigation();
3948 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
3949 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
3950 entry = NavigationEntryImpl::FromNavigationEntry(
3951 controller.GetEntryAtIndex(i));
3952 screenshot_manager->TakeScreenshotFor(entry);
3955 for (int i = 0; i < 2; ++i) {
3956 entry = NavigationEntryImpl::FromNavigationEntry(
3957 controller.GetEntryAtIndex(i));
3958 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
3959 << " not purged";
3962 // Clear all screenshots.
3963 EXPECT_EQ(13, controller.GetEntryCount());
3964 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
3965 controller.ClearAllScreenshots();
3966 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
3967 for (int i = 0; i < controller.GetEntryCount(); ++i) {
3968 entry = NavigationEntryImpl::FromNavigationEntry(
3969 controller.GetEntryAtIndex(i));
3970 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
3971 << " not cleared";
3975 // Test that the navigation controller clears its session history when a
3976 // navigation commits with the clear history list flag set.
3977 TEST_F(NavigationControllerTest, ClearHistoryList) {
3978 const GURL url1("http://foo1");
3979 const GURL url2("http://foo2");
3980 const GURL url3("http://foo3");
3981 const GURL url4("http://foo4");
3983 NavigationControllerImpl& controller = controller_impl();
3985 // Create a session history with three entries, second entry is active.
3986 NavigateAndCommit(url1);
3987 NavigateAndCommit(url2);
3988 NavigateAndCommit(url3);
3989 controller.GoBack();
3990 contents()->CommitPendingNavigation();
3991 EXPECT_EQ(3, controller.GetEntryCount());
3992 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
3994 // Create a new pending navigation, and indicate that the session history
3995 // should be cleared.
3996 NavigationController::LoadURLParams params(url4);
3997 params.should_clear_history_list = true;
3998 controller.LoadURLWithParams(params);
4000 // Verify that the pending entry correctly indicates that the session history
4001 // should be cleared.
4002 NavigationEntryImpl* entry =
4003 NavigationEntryImpl::FromNavigationEntry(
4004 controller.GetPendingEntry());
4005 ASSERT_TRUE(entry);
4006 EXPECT_TRUE(entry->should_clear_history_list());
4008 // Assume that the RV correctly cleared its history and commit the navigation.
4009 static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost())->
4010 set_simulate_history_list_was_cleared(true);
4011 contents()->CommitPendingNavigation();
4013 // Verify that the NavigationController's session history was correctly
4014 // cleared.
4015 EXPECT_EQ(1, controller.GetEntryCount());
4016 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4017 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4018 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4019 EXPECT_FALSE(controller.CanGoBack());
4020 EXPECT_FALSE(controller.CanGoForward());
4021 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4024 } // namespace content