Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_unittest.cc
blob523b3f98611037965ae13d8daa448ef6f2824d79
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/cross_site_transferring_request.h"
15 #include "content/browser/frame_host/navigation_controller_impl.h"
16 #include "content/browser/frame_host/navigation_entry_impl.h"
17 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
18 #include "content/browser/frame_host/navigator.h"
19 #include "content/browser/site_instance_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/frame_messages.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/navigation_details.h"
24 #include "content/public/browser/notification_registrar.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/common/page_state.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/test/mock_render_process_host.h"
32 #include "content/public/test/test_notification_tracker.h"
33 #include "content/public/test/test_utils.h"
34 #include "content/test/test_render_frame_host.h"
35 #include "content/test/test_render_view_host.h"
36 #include "content/test/test_web_contents.h"
37 #include "net/base/net_util.h"
38 #include "skia/ext/platform_canvas.h"
39 #include "testing/gtest/include/gtest/gtest.h"
41 using base::Time;
43 namespace {
45 // Creates an image with a 1x1 SkBitmap of the specified |color|.
46 gfx::Image CreateImage(SkColor color) {
47 SkBitmap bitmap;
48 bitmap.allocN32Pixels(1, 1);
49 bitmap.eraseColor(color);
50 return gfx::Image::CreateFrom1xBitmap(bitmap);
53 // Returns true if images |a| and |b| have the same pixel data.
54 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
55 // Assume that if the 1x bitmaps match, the images match.
56 SkBitmap a_bitmap = a.AsBitmap();
57 SkBitmap b_bitmap = b.AsBitmap();
59 if (a_bitmap.width() != b_bitmap.width() ||
60 a_bitmap.height() != b_bitmap.height()) {
61 return false;
63 SkAutoLockPixels a_bitmap_lock(a_bitmap);
64 SkAutoLockPixels b_bitmap_lock(b_bitmap);
65 return memcmp(a_bitmap.getPixels(),
66 b_bitmap.getPixels(),
67 a_bitmap.getSize()) == 0;
70 class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
71 public:
72 explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
73 : content::NavigationEntryScreenshotManager(owner),
74 encoding_screenshot_in_progress_(false) {
77 virtual ~MockScreenshotManager() {
80 void TakeScreenshotFor(content::NavigationEntryImpl* entry) {
81 SkBitmap bitmap;
82 bitmap.allocPixels(SkImageInfo::Make(
83 1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
84 bitmap.eraseARGB(0, 0, 0, 0);
85 encoding_screenshot_in_progress_ = true;
86 OnScreenshotTaken(entry->GetUniqueID(), true, bitmap);
87 WaitUntilScreenshotIsReady();
90 int GetScreenshotCount() {
91 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
94 void WaitUntilScreenshotIsReady() {
95 if (!encoding_screenshot_in_progress_)
96 return;
97 message_loop_runner_ = new content::MessageLoopRunner;
98 message_loop_runner_->Run();
101 private:
102 // Overridden from content::NavigationEntryScreenshotManager:
103 virtual void TakeScreenshotImpl(
104 content::RenderViewHost* host,
105 content::NavigationEntryImpl* entry) OVERRIDE {
108 virtual void OnScreenshotSet(content::NavigationEntryImpl* entry) OVERRIDE {
109 encoding_screenshot_in_progress_ = false;
110 NavigationEntryScreenshotManager::OnScreenshotSet(entry);
111 if (message_loop_runner_.get())
112 message_loop_runner_->Quit();
115 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
116 bool encoding_screenshot_in_progress_;
118 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
121 } // namespace
123 namespace content {
125 // TimeSmoother tests ----------------------------------------------------------
127 // With no duplicates, GetSmoothedTime should be the identity
128 // function.
129 TEST(TimeSmoother, Basic) {
130 NavigationControllerImpl::TimeSmoother smoother;
131 for (int64 i = 1; i < 1000; ++i) {
132 base::Time t = base::Time::FromInternalValue(i);
133 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
137 // With a single duplicate and timestamps thereafter increasing by one
138 // microsecond, the smoothed time should always be one behind.
139 TEST(TimeSmoother, SingleDuplicate) {
140 NavigationControllerImpl::TimeSmoother smoother;
141 base::Time t = base::Time::FromInternalValue(1);
142 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
143 for (int64 i = 1; i < 1000; ++i) {
144 base::Time expected_t = base::Time::FromInternalValue(i + 1);
145 t = base::Time::FromInternalValue(i);
146 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
150 // With k duplicates and timestamps thereafter increasing by one
151 // microsecond, the smoothed time should always be k behind.
152 TEST(TimeSmoother, ManyDuplicates) {
153 const int64 kNumDuplicates = 100;
154 NavigationControllerImpl::TimeSmoother smoother;
155 base::Time t = base::Time::FromInternalValue(1);
156 for (int64 i = 0; i < kNumDuplicates; ++i) {
157 base::Time expected_t = base::Time::FromInternalValue(i + 1);
158 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
160 for (int64 i = 1; i < 1000; ++i) {
161 base::Time expected_t =
162 base::Time::FromInternalValue(i + kNumDuplicates);
163 t = base::Time::FromInternalValue(i);
164 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
168 // If the clock jumps far back enough after a run of duplicates, it
169 // should immediately jump to that value.
170 TEST(TimeSmoother, ClockBackwardsJump) {
171 const int64 kNumDuplicates = 100;
172 NavigationControllerImpl::TimeSmoother smoother;
173 base::Time t = base::Time::FromInternalValue(1000);
174 for (int64 i = 0; i < kNumDuplicates; ++i) {
175 base::Time expected_t = base::Time::FromInternalValue(i + 1000);
176 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
178 t = base::Time::FromInternalValue(500);
179 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
182 // NavigationControllerTest ----------------------------------------------------
184 class NavigationControllerTest
185 : public RenderViewHostImplTestHarness,
186 public WebContentsObserver {
187 public:
188 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
191 virtual void SetUp() OVERRIDE {
192 RenderViewHostImplTestHarness::SetUp();
193 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents();
194 ASSERT_TRUE(web_contents); // The WebContents should be created by now.
195 WebContentsObserver::Observe(web_contents);
198 // WebContentsObserver:
199 virtual void DidStartNavigationToPendingEntry(
200 const GURL& url,
201 NavigationController::ReloadType reload_type) OVERRIDE {
202 navigated_url_ = url;
205 virtual void NavigationEntryCommitted(
206 const LoadCommittedDetails& load_details) OVERRIDE {
207 navigation_entry_committed_counter_++;
210 const GURL& navigated_url() const {
211 return navigated_url_;
214 NavigationControllerImpl& controller_impl() {
215 return static_cast<NavigationControllerImpl&>(controller());
218 protected:
219 GURL navigated_url_;
220 size_t navigation_entry_committed_counter_;
223 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
224 NavigationController* controller) {
225 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED,
226 Source<NavigationController>(controller));
227 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED,
228 Source<NavigationController>(controller));
231 SiteInstance* GetSiteInstanceFromEntry(NavigationEntry* entry) {
232 return NavigationEntryImpl::FromNavigationEntry(entry)->site_instance();
235 class TestWebContentsDelegate : public WebContentsDelegate {
236 public:
237 explicit TestWebContentsDelegate() :
238 navigation_state_change_count_(0),
239 repost_form_warning_count_(0) {}
241 int navigation_state_change_count() {
242 return navigation_state_change_count_;
245 int repost_form_warning_count() {
246 return repost_form_warning_count_;
249 // Keep track of whether the tab has notified us of a navigation state change.
250 virtual void NavigationStateChanged(const WebContents* source,
251 InvalidateTypes changed_flags) OVERRIDE {
252 navigation_state_change_count_++;
255 virtual void ShowRepostFormWarningDialog(WebContents* source) OVERRIDE {
256 repost_form_warning_count_++;
259 private:
260 // The number of times NavigationStateChanged has been called.
261 int navigation_state_change_count_;
263 // The number of times ShowRepostFormWarningDialog() was called.
264 int repost_form_warning_count_;
267 // -----------------------------------------------------------------------------
269 TEST_F(NavigationControllerTest, Defaults) {
270 NavigationControllerImpl& controller = controller_impl();
272 EXPECT_FALSE(controller.GetPendingEntry());
273 EXPECT_FALSE(controller.GetVisibleEntry());
274 EXPECT_FALSE(controller.GetLastCommittedEntry());
275 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
276 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
277 EXPECT_EQ(controller.GetEntryCount(), 0);
278 EXPECT_FALSE(controller.CanGoBack());
279 EXPECT_FALSE(controller.CanGoForward());
282 TEST_F(NavigationControllerTest, GoToOffset) {
283 NavigationControllerImpl& controller = controller_impl();
284 TestNotificationTracker notifications;
285 RegisterForAllNavNotifications(&notifications, &controller);
287 const int kNumUrls = 5;
288 std::vector<GURL> urls(kNumUrls);
289 for (int i = 0; i < kNumUrls; ++i) {
290 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
293 main_test_rfh()->SendNavigate(0, urls[0]);
294 EXPECT_EQ(1U, navigation_entry_committed_counter_);
295 navigation_entry_committed_counter_ = 0;
296 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
297 EXPECT_FALSE(controller.CanGoBack());
298 EXPECT_FALSE(controller.CanGoForward());
299 EXPECT_FALSE(controller.CanGoToOffset(1));
301 for (int i = 1; i <= 4; ++i) {
302 main_test_rfh()->SendNavigate(i, urls[i]);
303 EXPECT_EQ(1U, navigation_entry_committed_counter_);
304 navigation_entry_committed_counter_ = 0;
305 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
306 EXPECT_TRUE(controller.CanGoToOffset(-i));
307 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
308 EXPECT_FALSE(controller.CanGoToOffset(1));
311 // We have loaded 5 pages, and are currently at the last-loaded page.
312 int url_index = 4;
314 enum Tests {
315 GO_TO_MIDDLE_PAGE = -2,
316 GO_FORWARDS = 1,
317 GO_BACKWARDS = -1,
318 GO_TO_BEGINNING = -2,
319 GO_TO_END = 4,
320 NUM_TESTS = 5,
323 const int test_offsets[NUM_TESTS] = {
324 GO_TO_MIDDLE_PAGE,
325 GO_FORWARDS,
326 GO_BACKWARDS,
327 GO_TO_BEGINNING,
328 GO_TO_END
331 for (int test = 0; test < NUM_TESTS; ++test) {
332 int offset = test_offsets[test];
333 controller.GoToOffset(offset);
334 url_index += offset;
335 // Check that the GoToOffset will land on the expected page.
336 EXPECT_EQ(urls[url_index], controller.GetPendingEntry()->GetVirtualURL());
337 main_test_rfh()->SendNavigate(url_index, urls[url_index]);
338 EXPECT_EQ(1U, navigation_entry_committed_counter_);
339 navigation_entry_committed_counter_ = 0;
340 // Check that we can go to any valid offset into the history.
341 for (size_t j = 0; j < urls.size(); ++j)
342 EXPECT_TRUE(controller.CanGoToOffset(j - url_index));
343 // Check that we can't go beyond the beginning or end of the history.
344 EXPECT_FALSE(controller.CanGoToOffset(-(url_index + 1)));
345 EXPECT_FALSE(controller.CanGoToOffset(urls.size() - url_index));
349 TEST_F(NavigationControllerTest, LoadURL) {
350 NavigationControllerImpl& controller = controller_impl();
351 TestNotificationTracker notifications;
352 RegisterForAllNavNotifications(&notifications, &controller);
354 const GURL url1("http://foo1");
355 const GURL url2("http://foo2");
357 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
358 // Creating a pending notification should not have issued any of the
359 // notifications we're listening for.
360 EXPECT_EQ(0U, notifications.size());
362 // The load should now be pending.
363 EXPECT_EQ(controller.GetEntryCount(), 0);
364 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
365 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
366 EXPECT_FALSE(controller.GetLastCommittedEntry());
367 ASSERT_TRUE(controller.GetPendingEntry());
368 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
369 EXPECT_FALSE(controller.CanGoBack());
370 EXPECT_FALSE(controller.CanGoForward());
371 EXPECT_EQ(contents()->GetMaxPageID(), -1);
373 // Neither the timestamp nor the status code should have been set yet.
374 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
375 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
377 // We should have gotten no notifications from the preceeding checks.
378 EXPECT_EQ(0U, notifications.size());
380 main_test_rfh()->SendNavigate(0, url1);
381 EXPECT_EQ(1U, navigation_entry_committed_counter_);
382 navigation_entry_committed_counter_ = 0;
384 // The load should now be committed.
385 EXPECT_EQ(controller.GetEntryCount(), 1);
386 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
387 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
388 EXPECT_TRUE(controller.GetLastCommittedEntry());
389 EXPECT_FALSE(controller.GetPendingEntry());
390 ASSERT_TRUE(controller.GetVisibleEntry());
391 EXPECT_FALSE(controller.CanGoBack());
392 EXPECT_FALSE(controller.CanGoForward());
393 EXPECT_EQ(contents()->GetMaxPageID(), 0);
394 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
395 controller.GetLastCommittedEntry())->bindings());
397 // The timestamp should have been set.
398 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
400 // Load another...
401 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
403 // The load should now be pending.
404 EXPECT_EQ(controller.GetEntryCount(), 1);
405 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
406 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
407 EXPECT_TRUE(controller.GetLastCommittedEntry());
408 ASSERT_TRUE(controller.GetPendingEntry());
409 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
410 // TODO(darin): maybe this should really be true?
411 EXPECT_FALSE(controller.CanGoBack());
412 EXPECT_FALSE(controller.CanGoForward());
413 EXPECT_EQ(contents()->GetMaxPageID(), 0);
415 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
417 // Simulate the beforeunload ack for the cross-site transition, and then the
418 // commit.
419 test_rvh()->SendBeforeUnloadACK(true);
420 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
421 EXPECT_EQ(1U, navigation_entry_committed_counter_);
422 navigation_entry_committed_counter_ = 0;
424 // The load should now be committed.
425 EXPECT_EQ(controller.GetEntryCount(), 2);
426 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
427 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
428 EXPECT_TRUE(controller.GetLastCommittedEntry());
429 EXPECT_FALSE(controller.GetPendingEntry());
430 ASSERT_TRUE(controller.GetVisibleEntry());
431 EXPECT_TRUE(controller.CanGoBack());
432 EXPECT_FALSE(controller.CanGoForward());
433 EXPECT_EQ(contents()->GetMaxPageID(), 1);
435 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
438 namespace {
440 base::Time GetFixedTime(base::Time time) {
441 return time;
444 } // namespace
446 TEST_F(NavigationControllerTest, LoadURLSameTime) {
447 NavigationControllerImpl& controller = controller_impl();
448 TestNotificationTracker notifications;
449 RegisterForAllNavNotifications(&notifications, &controller);
451 // Set the clock to always return a timestamp of 1.
452 controller.SetGetTimestampCallbackForTest(
453 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
455 const GURL url1("http://foo1");
456 const GURL url2("http://foo2");
458 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
460 main_test_rfh()->SendNavigate(0, url1);
461 EXPECT_EQ(1U, navigation_entry_committed_counter_);
462 navigation_entry_committed_counter_ = 0;
464 // Load another...
465 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
467 // Simulate the beforeunload ack for the cross-site transition, and then the
468 // commit.
469 test_rvh()->SendBeforeUnloadACK(true);
470 main_test_rfh()->SendNavigate(1, url2);
471 EXPECT_EQ(1U, navigation_entry_committed_counter_);
472 navigation_entry_committed_counter_ = 0;
474 // The two loads should now be committed.
475 ASSERT_EQ(controller.GetEntryCount(), 2);
477 // Timestamps should be distinct despite the clock returning the
478 // same value.
479 EXPECT_EQ(1u,
480 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
481 EXPECT_EQ(2u,
482 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
485 void CheckNavigationEntryMatchLoadParams(
486 NavigationController::LoadURLParams& load_params,
487 NavigationEntryImpl* entry) {
488 EXPECT_EQ(load_params.url, entry->GetURL());
489 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
490 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
491 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
492 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
494 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
495 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
496 if (!load_params.virtual_url_for_data_url.is_empty()) {
497 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
499 if (NavigationController::UA_OVERRIDE_INHERIT !=
500 load_params.override_user_agent) {
501 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
502 load_params.override_user_agent);
503 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
505 EXPECT_EQ(load_params.browser_initiated_post_data.get(),
506 entry->GetBrowserInitiatedPostData());
507 EXPECT_EQ(load_params.transferred_global_request_id,
508 entry->transferred_global_request_id());
511 TEST_F(NavigationControllerTest, LoadURLWithParams) {
512 NavigationControllerImpl& controller = controller_impl();
514 NavigationController::LoadURLParams load_params(GURL("http://foo"));
515 load_params.referrer =
516 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
517 load_params.transition_type = PAGE_TRANSITION_GENERATED;
518 load_params.extra_headers = "content-type: text/plain";
519 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
520 load_params.is_renderer_initiated = true;
521 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
522 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
524 controller.LoadURLWithParams(load_params);
525 NavigationEntryImpl* entry =
526 NavigationEntryImpl::FromNavigationEntry(
527 controller.GetPendingEntry());
529 // The timestamp should not have been set yet.
530 ASSERT_TRUE(entry);
531 EXPECT_TRUE(entry->GetTimestamp().is_null());
533 CheckNavigationEntryMatchLoadParams(load_params, entry);
536 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
537 NavigationControllerImpl& controller = controller_impl();
539 NavigationController::LoadURLParams load_params(
540 GURL("data:text/html,dataurl"));
541 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
542 load_params.base_url_for_data_url = GURL("http://foo");
543 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
544 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
546 controller.LoadURLWithParams(load_params);
547 NavigationEntryImpl* entry =
548 NavigationEntryImpl::FromNavigationEntry(
549 controller.GetPendingEntry());
551 CheckNavigationEntryMatchLoadParams(load_params, entry);
554 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
555 NavigationControllerImpl& controller = controller_impl();
557 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
558 load_params.transition_type = PAGE_TRANSITION_TYPED;
559 load_params.load_type =
560 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
561 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
564 const unsigned char* raw_data =
565 reinterpret_cast<const unsigned char*>("d\n\0a2");
566 const int length = 5;
567 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
568 scoped_refptr<base::RefCountedBytes> data =
569 base::RefCountedBytes::TakeVector(&post_data_vector);
570 load_params.browser_initiated_post_data = data.get();
572 controller.LoadURLWithParams(load_params);
573 NavigationEntryImpl* entry =
574 NavigationEntryImpl::FromNavigationEntry(
575 controller.GetPendingEntry());
577 CheckNavigationEntryMatchLoadParams(load_params, entry);
580 // Tests what happens when the same page is loaded again. Should not create a
581 // new session history entry. This is what happens when you press enter in the
582 // URL bar to reload: a pending entry is created and then it is discarded when
583 // the load commits (because WebCore didn't actually make a new entry).
584 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
585 NavigationControllerImpl& controller = controller_impl();
586 TestNotificationTracker notifications;
587 RegisterForAllNavNotifications(&notifications, &controller);
589 const GURL url1("http://foo1");
591 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
592 EXPECT_EQ(0U, notifications.size());
593 main_test_rfh()->SendNavigate(0, url1);
594 EXPECT_EQ(1U, navigation_entry_committed_counter_);
595 navigation_entry_committed_counter_ = 0;
597 ASSERT_TRUE(controller.GetVisibleEntry());
598 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
599 EXPECT_FALSE(timestamp.is_null());
601 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
602 EXPECT_EQ(0U, notifications.size());
603 main_test_rfh()->SendNavigate(0, url1);
604 EXPECT_EQ(1U, navigation_entry_committed_counter_);
605 navigation_entry_committed_counter_ = 0;
607 // We should not have produced a new session history entry.
608 EXPECT_EQ(controller.GetEntryCount(), 1);
609 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
610 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
611 EXPECT_TRUE(controller.GetLastCommittedEntry());
612 EXPECT_FALSE(controller.GetPendingEntry());
613 ASSERT_TRUE(controller.GetVisibleEntry());
614 EXPECT_FALSE(controller.CanGoBack());
615 EXPECT_FALSE(controller.CanGoForward());
617 // The timestamp should have been updated.
619 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
620 // EXPECT_GT once we guarantee that timestamps are unique.
621 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
624 // Load the same page twice, once as a GET and once as a POST.
625 // We should update the post state on the NavigationEntry.
626 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
627 NavigationControllerImpl& controller = controller_impl();
628 TestNotificationTracker notifications;
629 RegisterForAllNavNotifications(&notifications, &controller);
631 const GURL url1("http://foo1");
633 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
634 FrameHostMsg_DidCommitProvisionalLoad_Params params;
635 params.page_id = 0;
636 params.url = url1;
637 params.transition = PAGE_TRANSITION_TYPED;
638 params.is_post = true;
639 params.post_id = 123;
640 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
641 main_test_rfh()->SendNavigateWithParams(&params);
643 // The post data should be visible.
644 NavigationEntry* entry = controller.GetVisibleEntry();
645 ASSERT_TRUE(entry);
646 EXPECT_TRUE(entry->GetHasPostData());
647 EXPECT_EQ(entry->GetPostID(), 123);
649 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
650 main_test_rfh()->SendNavigate(0, url1);
652 // We should not have produced a new session history entry.
653 ASSERT_EQ(controller.GetVisibleEntry(), entry);
655 // The post data should have been cleared due to the GET.
656 EXPECT_FALSE(entry->GetHasPostData());
657 EXPECT_EQ(entry->GetPostID(), 0);
660 // Tests loading a URL but discarding it before the load commits.
661 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
662 NavigationControllerImpl& controller = controller_impl();
663 TestNotificationTracker notifications;
664 RegisterForAllNavNotifications(&notifications, &controller);
666 const GURL url1("http://foo1");
667 const GURL url2("http://foo2");
669 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
670 EXPECT_EQ(0U, notifications.size());
671 main_test_rfh()->SendNavigate(0, url1);
672 EXPECT_EQ(1U, navigation_entry_committed_counter_);
673 navigation_entry_committed_counter_ = 0;
675 ASSERT_TRUE(controller.GetVisibleEntry());
676 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
677 EXPECT_FALSE(timestamp.is_null());
679 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
680 controller.DiscardNonCommittedEntries();
681 EXPECT_EQ(0U, notifications.size());
683 // Should not have produced a new session history entry.
684 EXPECT_EQ(controller.GetEntryCount(), 1);
685 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
686 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
687 EXPECT_TRUE(controller.GetLastCommittedEntry());
688 EXPECT_FALSE(controller.GetPendingEntry());
689 ASSERT_TRUE(controller.GetVisibleEntry());
690 EXPECT_FALSE(controller.CanGoBack());
691 EXPECT_FALSE(controller.CanGoForward());
693 // Timestamp should not have changed.
694 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
697 // Tests navigations that come in unrequested. This happens when the user
698 // navigates from the web page, and here we test that there is no pending entry.
699 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
700 NavigationControllerImpl& controller = controller_impl();
701 TestNotificationTracker notifications;
702 RegisterForAllNavNotifications(&notifications, &controller);
704 // First make an existing committed entry.
705 const GURL kExistingURL1("http://eh");
706 controller.LoadURL(
707 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
708 main_test_rfh()->SendNavigate(0, kExistingURL1);
709 EXPECT_EQ(1U, navigation_entry_committed_counter_);
710 navigation_entry_committed_counter_ = 0;
712 // Do a new navigation without making a pending one.
713 const GURL kNewURL("http://see");
714 main_test_rfh()->SendNavigate(99, kNewURL);
716 // There should no longer be any pending entry, and the third navigation we
717 // just made should be committed.
718 EXPECT_EQ(1U, navigation_entry_committed_counter_);
719 navigation_entry_committed_counter_ = 0;
720 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
721 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
722 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
725 // Tests navigating to a new URL when there is a new pending navigation that is
726 // not the one that just loaded. This will happen if the user types in a URL to
727 // somewhere slow, and then navigates the current page before the typed URL
728 // commits.
729 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
730 NavigationControllerImpl& controller = controller_impl();
731 TestNotificationTracker notifications;
732 RegisterForAllNavNotifications(&notifications, &controller);
734 // First make an existing committed entry.
735 const GURL kExistingURL1("http://eh");
736 controller.LoadURL(
737 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
738 main_test_rfh()->SendNavigate(0, kExistingURL1);
739 EXPECT_EQ(1U, navigation_entry_committed_counter_);
740 navigation_entry_committed_counter_ = 0;
742 // Make a pending entry to somewhere new.
743 const GURL kExistingURL2("http://bee");
744 controller.LoadURL(
745 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
746 EXPECT_EQ(0U, notifications.size());
748 // After the beforeunload but before it commits, do a new navigation.
749 test_rvh()->SendBeforeUnloadACK(true);
750 const GURL kNewURL("http://see");
751 contents()->GetPendingMainFrame()->SendNavigate(3, kNewURL);
753 // There should no longer be any pending entry, and the third navigation we
754 // just made should be committed.
755 EXPECT_EQ(1U, navigation_entry_committed_counter_);
756 navigation_entry_committed_counter_ = 0;
757 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
758 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
759 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
762 // Tests navigating to a new URL when there is a pending back/forward
763 // navigation. This will happen if the user hits back, but before that commits,
764 // they navigate somewhere new.
765 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
766 NavigationControllerImpl& controller = controller_impl();
767 TestNotificationTracker notifications;
768 RegisterForAllNavNotifications(&notifications, &controller);
770 // First make some history.
771 const GURL kExistingURL1("http://foo/eh");
772 controller.LoadURL(
773 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
774 main_test_rfh()->SendNavigate(0, kExistingURL1);
775 EXPECT_EQ(1U, navigation_entry_committed_counter_);
776 navigation_entry_committed_counter_ = 0;
778 const GURL kExistingURL2("http://foo/bee");
779 controller.LoadURL(
780 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
781 main_test_rfh()->SendNavigate(1, kExistingURL2);
782 EXPECT_EQ(1U, navigation_entry_committed_counter_);
783 navigation_entry_committed_counter_ = 0;
785 // Now make a pending back/forward navigation. The zeroth entry should be
786 // pending.
787 controller.GoBack();
788 EXPECT_EQ(0U, notifications.size());
789 EXPECT_EQ(0, controller.GetPendingEntryIndex());
790 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
792 // Before that commits, do a new navigation.
793 const GURL kNewURL("http://foo/see");
794 LoadCommittedDetails details;
795 main_test_rfh()->SendNavigate(3, kNewURL);
797 // There should no longer be any pending entry, and the third navigation we
798 // just made should be committed.
799 EXPECT_EQ(1U, navigation_entry_committed_counter_);
800 navigation_entry_committed_counter_ = 0;
801 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
802 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
803 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
806 // Tests navigating to a new URL when there is a pending back/forward
807 // navigation to a cross-process, privileged URL. This will happen if the user
808 // hits back, but before that commits, they navigate somewhere new.
809 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
810 NavigationControllerImpl& controller = controller_impl();
811 TestNotificationTracker notifications;
812 RegisterForAllNavNotifications(&notifications, &controller);
814 // First make some history, starting with a privileged URL.
815 const GURL kExistingURL1("http://privileged");
816 controller.LoadURL(
817 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
818 // Pretend it has bindings so we can tell if we incorrectly copy it.
819 test_rvh()->AllowBindings(2);
820 main_test_rfh()->SendNavigate(0, kExistingURL1);
821 EXPECT_EQ(1U, navigation_entry_committed_counter_);
822 navigation_entry_committed_counter_ = 0;
824 // Navigate cross-process to a second URL.
825 const GURL kExistingURL2("http://foo/eh");
826 controller.LoadURL(
827 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
828 test_rvh()->SendBeforeUnloadACK(true);
829 TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame();
830 foo_rfh->SendNavigate(1, kExistingURL2);
831 EXPECT_EQ(1U, navigation_entry_committed_counter_);
832 navigation_entry_committed_counter_ = 0;
834 // Now make a pending back/forward navigation to a privileged entry.
835 // The zeroth entry should be pending.
836 controller.GoBack();
837 foo_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
838 EXPECT_EQ(0U, notifications.size());
839 EXPECT_EQ(0, controller.GetPendingEntryIndex());
840 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
841 EXPECT_EQ(2, NavigationEntryImpl::FromNavigationEntry(
842 controller.GetPendingEntry())->bindings());
844 // Before that commits, do a new navigation.
845 const GURL kNewURL("http://foo/bee");
846 LoadCommittedDetails details;
847 foo_rfh->SendNavigate(3, kNewURL);
849 // There should no longer be any pending entry, and the third navigation we
850 // just made should be committed.
851 EXPECT_EQ(1U, navigation_entry_committed_counter_);
852 navigation_entry_committed_counter_ = 0;
853 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
854 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
855 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
856 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
857 controller.GetLastCommittedEntry())->bindings());
860 // Tests navigating to an existing URL when there is a pending new navigation.
861 // This will happen if the user enters a URL, but before that commits, the
862 // current page fires history.back().
863 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
864 NavigationControllerImpl& controller = controller_impl();
865 TestNotificationTracker notifications;
866 RegisterForAllNavNotifications(&notifications, &controller);
868 // First make some history.
869 const GURL kExistingURL1("http://foo/eh");
870 controller.LoadURL(
871 kExistingURL1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
872 main_test_rfh()->SendNavigate(0, kExistingURL1);
873 EXPECT_EQ(1U, navigation_entry_committed_counter_);
874 navigation_entry_committed_counter_ = 0;
876 const GURL kExistingURL2("http://foo/bee");
877 controller.LoadURL(
878 kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
879 main_test_rfh()->SendNavigate(1, kExistingURL2);
880 EXPECT_EQ(1U, navigation_entry_committed_counter_);
881 navigation_entry_committed_counter_ = 0;
883 // Now make a pending new navigation.
884 const GURL kNewURL("http://foo/see");
885 controller.LoadURL(
886 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
887 EXPECT_EQ(0U, notifications.size());
888 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
889 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
891 // Before that commits, a back navigation from the renderer commits.
892 main_test_rfh()->SendNavigate(0, kExistingURL1);
894 // There should no longer be any pending entry, and the back navigation we
895 // just made should be committed.
896 EXPECT_EQ(1U, navigation_entry_committed_counter_);
897 navigation_entry_committed_counter_ = 0;
898 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
899 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
900 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
903 // Tests an ignored navigation when there is a pending new navigation.
904 // This will happen if the user enters a URL, but before that commits, the
905 // current blank page reloads. See http://crbug.com/77507.
906 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
907 NavigationControllerImpl& controller = controller_impl();
908 TestNotificationTracker notifications;
909 RegisterForAllNavNotifications(&notifications, &controller);
911 // Set a WebContentsDelegate to listen for state changes.
912 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
913 EXPECT_FALSE(contents()->GetDelegate());
914 contents()->SetDelegate(delegate.get());
916 // Without any navigations, the renderer starts at about:blank.
917 const GURL kExistingURL(url::kAboutBlankURL);
919 // Now make a pending new navigation.
920 const GURL kNewURL("http://eh");
921 controller.LoadURL(
922 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
923 EXPECT_EQ(0U, notifications.size());
924 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
925 EXPECT_TRUE(controller.GetPendingEntry());
926 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
927 EXPECT_EQ(1, delegate->navigation_state_change_count());
929 // Before that commits, a document.write and location.reload can cause the
930 // renderer to send a FrameNavigate with page_id -1.
931 main_test_rfh()->SendNavigate(-1, kExistingURL);
933 // This should clear the pending entry and notify of a navigation state
934 // change, so that we do not keep displaying kNewURL.
935 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
936 EXPECT_FALSE(controller.GetPendingEntry());
937 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
938 EXPECT_EQ(2, delegate->navigation_state_change_count());
940 contents()->SetDelegate(NULL);
943 // Tests that the pending entry state is correct after an abort.
944 // We do not want to clear the pending entry, so that the user doesn't
945 // lose a typed URL. (See http://crbug.com/9682.)
946 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
947 NavigationControllerImpl& controller = controller_impl();
948 TestNotificationTracker notifications;
949 RegisterForAllNavNotifications(&notifications, &controller);
951 // Set a WebContentsDelegate to listen for state changes.
952 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
953 EXPECT_FALSE(contents()->GetDelegate());
954 contents()->SetDelegate(delegate.get());
956 // Start with a pending new navigation.
957 const GURL kNewURL("http://eh");
958 controller.LoadURL(
959 kNewURL, Referrer(), PAGE_TRANSITION_TYPED, std::string());
960 EXPECT_EQ(0U, notifications.size());
961 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
962 EXPECT_TRUE(controller.GetPendingEntry());
963 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
964 EXPECT_EQ(1, delegate->navigation_state_change_count());
966 // It may abort before committing, if it's a download or due to a stop or
967 // a new navigation from the user.
968 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
969 params.error_code = net::ERR_ABORTED;
970 params.error_description = base::string16();
971 params.url = kNewURL;
972 params.showing_repost_interstitial = false;
973 main_test_rfh()->OnMessageReceived(
974 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
975 params));
977 // This should not clear the pending entry or notify of a navigation state
978 // change, so that we keep displaying kNewURL (until the user clears it).
979 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
980 EXPECT_TRUE(controller.GetPendingEntry());
981 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
982 EXPECT_EQ(1, delegate->navigation_state_change_count());
983 NavigationEntry* pending_entry = controller.GetPendingEntry();
985 // Ensure that a reload keeps the same pending entry.
986 controller.Reload(true);
987 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
988 EXPECT_TRUE(controller.GetPendingEntry());
989 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
990 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
992 contents()->SetDelegate(NULL);
995 // Tests that the pending URL is not visible during a renderer-initiated
996 // redirect and abort. See http://crbug.com/83031.
997 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
998 NavigationControllerImpl& controller = controller_impl();
999 TestNotificationTracker notifications;
1000 RegisterForAllNavNotifications(&notifications, &controller);
1002 // First make an existing committed entry.
1003 const GURL kExistingURL("http://foo/eh");
1004 controller.LoadURL(kExistingURL, content::Referrer(),
1005 content::PAGE_TRANSITION_TYPED, std::string());
1006 main_test_rfh()->SendNavigate(0, kExistingURL);
1007 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1008 navigation_entry_committed_counter_ = 0;
1010 // Set a WebContentsDelegate to listen for state changes.
1011 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1012 EXPECT_FALSE(contents()->GetDelegate());
1013 contents()->SetDelegate(delegate.get());
1015 // Now make a pending new navigation, initiated by the renderer.
1016 const GURL kNewURL("http://foo/bee");
1017 NavigationController::LoadURLParams load_url_params(kNewURL);
1018 load_url_params.transition_type = PAGE_TRANSITION_TYPED;
1019 load_url_params.is_renderer_initiated = true;
1020 controller.LoadURLWithParams(load_url_params);
1021 EXPECT_EQ(0U, notifications.size());
1022 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1023 EXPECT_TRUE(controller.GetPendingEntry());
1024 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1025 EXPECT_EQ(0, delegate->navigation_state_change_count());
1027 // The visible entry should be the last committed URL, not the pending one.
1028 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1030 // Now the navigation redirects.
1031 const GURL kRedirectURL("http://foo/see");
1032 main_test_rfh()->OnMessageReceived(
1033 FrameHostMsg_DidRedirectProvisionalLoad(0, // routing_id
1034 -1, // pending page_id
1035 kNewURL, // old url
1036 kRedirectURL)); // new url
1038 // We don't want to change the NavigationEntry's url, in case it cancels.
1039 // Prevents regression of http://crbug.com/77786.
1040 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1042 // It may abort before committing, if it's a download or due to a stop or
1043 // a new navigation from the user.
1044 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1045 params.error_code = net::ERR_ABORTED;
1046 params.error_description = base::string16();
1047 params.url = kRedirectURL;
1048 params.showing_repost_interstitial = false;
1049 main_test_rfh()->OnMessageReceived(
1050 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1051 params));
1053 // Because the pending entry is renderer initiated and not visible, we
1054 // clear it when it fails.
1055 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1056 EXPECT_FALSE(controller.GetPendingEntry());
1057 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1058 EXPECT_EQ(1, delegate->navigation_state_change_count());
1060 // The visible entry should be the last committed URL, not the pending one,
1061 // so that no spoof is possible.
1062 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1064 contents()->SetDelegate(NULL);
1067 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1068 // at the time they committed. http://crbug.com/173672.
1069 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1070 NavigationControllerImpl& controller = controller_impl();
1071 TestNotificationTracker notifications;
1072 RegisterForAllNavNotifications(&notifications, &controller);
1073 std::vector<GURL> url_chain;
1075 const GURL url1("http://foo1");
1076 const GURL url2("http://foo2");
1078 // Navigate to a first, unprivileged URL.
1079 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1080 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1081 NavigationEntryImpl::FromNavigationEntry(
1082 controller.GetPendingEntry())->bindings());
1084 // Commit.
1085 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1086 orig_rfh->SendNavigate(0, url1);
1087 EXPECT_EQ(controller.GetEntryCount(), 1);
1088 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1089 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
1090 controller.GetLastCommittedEntry())->bindings());
1092 // Manually increase the number of active views in the SiteInstance
1093 // that orig_rfh belongs to, to prevent it from being destroyed when
1094 // it gets swapped out, so that we can reuse orig_rfh when the
1095 // controller goes back.
1096 static_cast<SiteInstanceImpl*>(orig_rfh->GetSiteInstance())->
1097 increment_active_view_count();
1099 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1100 // transition, and set bindings on the pending RenderViewHost to simulate a
1101 // privileged url.
1102 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1103 orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
1104 TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
1105 new_rfh->GetRenderViewHost()->AllowBindings(1);
1106 new_rfh->SendNavigate(1, url2);
1108 // The second load should be committed, and bindings should be remembered.
1109 EXPECT_EQ(controller.GetEntryCount(), 2);
1110 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1111 EXPECT_TRUE(controller.CanGoBack());
1112 EXPECT_EQ(1, NavigationEntryImpl::FromNavigationEntry(
1113 controller.GetLastCommittedEntry())->bindings());
1115 // Going back, the first entry should still appear unprivileged.
1116 controller.GoBack();
1117 new_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true);
1118 orig_rfh->SendNavigate(0, url1);
1119 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1120 EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry(
1121 controller.GetLastCommittedEntry())->bindings());
1124 TEST_F(NavigationControllerTest, Reload) {
1125 NavigationControllerImpl& controller = controller_impl();
1126 TestNotificationTracker notifications;
1127 RegisterForAllNavNotifications(&notifications, &controller);
1129 const GURL url1("http://foo1");
1131 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1132 EXPECT_EQ(0U, notifications.size());
1133 main_test_rfh()->SendNavigate(0, url1);
1134 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1135 navigation_entry_committed_counter_ = 0;
1136 ASSERT_TRUE(controller.GetVisibleEntry());
1137 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1138 controller.Reload(true);
1139 EXPECT_EQ(0U, notifications.size());
1141 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1142 EXPECT_FALSE(timestamp.is_null());
1144 // The reload is pending.
1145 EXPECT_EQ(controller.GetEntryCount(), 1);
1146 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1147 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1148 EXPECT_TRUE(controller.GetLastCommittedEntry());
1149 EXPECT_TRUE(controller.GetPendingEntry());
1150 EXPECT_FALSE(controller.CanGoBack());
1151 EXPECT_FALSE(controller.CanGoForward());
1152 // Make sure the title has been cleared (will be redrawn just after reload).
1153 // Avoids a stale cached title when the new page being reloaded has no title.
1154 // See http://crbug.com/96041.
1155 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1157 main_test_rfh()->SendNavigate(0, url1);
1158 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1159 navigation_entry_committed_counter_ = 0;
1161 // Now the reload is committed.
1162 EXPECT_EQ(controller.GetEntryCount(), 1);
1163 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1164 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1165 EXPECT_TRUE(controller.GetLastCommittedEntry());
1166 EXPECT_FALSE(controller.GetPendingEntry());
1167 EXPECT_FALSE(controller.CanGoBack());
1168 EXPECT_FALSE(controller.CanGoForward());
1170 // The timestamp should have been updated.
1171 ASSERT_TRUE(controller.GetVisibleEntry());
1172 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1175 // Tests what happens when a reload navigation produces a new page.
1176 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1177 NavigationControllerImpl& controller = controller_impl();
1178 TestNotificationTracker notifications;
1179 RegisterForAllNavNotifications(&notifications, &controller);
1181 const GURL url1("http://foo1");
1182 const GURL url2("http://foo2");
1184 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1185 main_test_rfh()->SendNavigate(0, url1);
1186 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1187 navigation_entry_committed_counter_ = 0;
1189 controller.Reload(true);
1190 EXPECT_EQ(0U, notifications.size());
1192 main_test_rfh()->SendNavigate(1, url2);
1193 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1194 navigation_entry_committed_counter_ = 0;
1196 // Now the reload is committed.
1197 EXPECT_EQ(controller.GetEntryCount(), 2);
1198 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1199 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1200 EXPECT_TRUE(controller.GetLastCommittedEntry());
1201 EXPECT_FALSE(controller.GetPendingEntry());
1202 EXPECT_TRUE(controller.CanGoBack());
1203 EXPECT_FALSE(controller.CanGoForward());
1206 // This test ensures that when a guest renderer reloads, the reload goes through
1207 // without ending up in the "we have a wrong process for the URL" branch in
1208 // NavigationControllerImpl::ReloadInternal.
1209 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1210 NavigationControllerImpl& controller = controller_impl();
1212 const GURL url1("http://foo1");
1213 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1214 main_test_rfh()->SendNavigate(0, url1);
1215 ASSERT_TRUE(controller.GetVisibleEntry());
1217 // Make the entry believe its RenderProcessHost is a guest.
1218 NavigationEntryImpl* entry1 =
1219 NavigationEntryImpl::FromNavigationEntry(controller.GetVisibleEntry());
1220 reinterpret_cast<MockRenderProcessHost*>(
1221 entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
1223 // And reload.
1224 controller.Reload(true);
1226 // The reload is pending. Check that the NavigationEntry didn't get replaced
1227 // because of having the wrong process.
1228 EXPECT_EQ(controller.GetEntryCount(), 1);
1229 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1230 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1232 NavigationEntryImpl* entry2 =
1233 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1234 EXPECT_EQ(entry1, entry2);
1237 #if !defined(OS_ANDROID) // http://crbug.com/157428
1238 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1239 NavigationControllerImpl& controller = controller_impl();
1240 TestNotificationTracker notifications;
1241 RegisterForAllNavNotifications(&notifications, &controller);
1243 const GURL original_url("http://foo1");
1244 const GURL final_url("http://foo2");
1246 // Load up the original URL, but get redirected.
1247 controller.LoadURL(
1248 original_url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1249 EXPECT_EQ(0U, notifications.size());
1250 main_test_rfh()->SendNavigateWithOriginalRequestURL(
1251 0, final_url, original_url);
1252 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1253 navigation_entry_committed_counter_ = 0;
1255 // The NavigationEntry should save both the original URL and the final
1256 // redirected URL.
1257 EXPECT_EQ(
1258 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1259 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1261 // Reload using the original URL.
1262 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1263 controller.ReloadOriginalRequestURL(false);
1264 EXPECT_EQ(0U, notifications.size());
1266 // The reload is pending. The request should point to the original URL.
1267 EXPECT_EQ(original_url, navigated_url());
1268 EXPECT_EQ(controller.GetEntryCount(), 1);
1269 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1270 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1271 EXPECT_TRUE(controller.GetLastCommittedEntry());
1272 EXPECT_TRUE(controller.GetPendingEntry());
1273 EXPECT_FALSE(controller.CanGoBack());
1274 EXPECT_FALSE(controller.CanGoForward());
1276 // Make sure the title has been cleared (will be redrawn just after reload).
1277 // Avoids a stale cached title when the new page being reloaded has no title.
1278 // See http://crbug.com/96041.
1279 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1281 // Send that the navigation has proceeded; say it got redirected again.
1282 main_test_rfh()->SendNavigate(0, final_url);
1283 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1284 navigation_entry_committed_counter_ = 0;
1286 // Now the reload is committed.
1287 EXPECT_EQ(controller.GetEntryCount(), 1);
1288 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1289 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1290 EXPECT_TRUE(controller.GetLastCommittedEntry());
1291 EXPECT_FALSE(controller.GetPendingEntry());
1292 EXPECT_FALSE(controller.CanGoBack());
1293 EXPECT_FALSE(controller.CanGoForward());
1296 #endif // !defined(OS_ANDROID)
1298 // Test that certain non-persisted NavigationEntryImpl values get reset after
1299 // commit.
1300 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1301 NavigationControllerImpl& controller = controller_impl();
1302 const GURL url1("http://foo1");
1303 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1305 // Set up some sample values.
1306 const unsigned char* raw_data =
1307 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1308 const int length = 11;
1309 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1310 scoped_refptr<base::RefCountedBytes> post_data =
1311 base::RefCountedBytes::TakeVector(&post_data_vector);
1312 GlobalRequestID transfer_id(3, 4);
1313 std::vector<GURL> redirects;
1314 redirects.push_back(GURL("http://foo2"));
1316 // Set non-persisted values on the pending entry.
1317 NavigationEntryImpl* pending_entry =
1318 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1319 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1320 pending_entry->set_is_renderer_initiated(true);
1321 pending_entry->set_transferred_global_request_id(transfer_id);
1322 pending_entry->set_should_replace_entry(true);
1323 pending_entry->set_should_clear_history_list(true);
1324 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1325 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1326 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1327 EXPECT_TRUE(pending_entry->should_replace_entry());
1328 EXPECT_TRUE(pending_entry->should_clear_history_list());
1330 main_test_rfh()->SendNavigate(0, url1);
1332 // Certain values that are only used for pending entries get reset after
1333 // commit.
1334 NavigationEntryImpl* committed_entry =
1335 NavigationEntryImpl::FromNavigationEntry(
1336 controller.GetLastCommittedEntry());
1337 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1338 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1339 EXPECT_EQ(GlobalRequestID(-1, -1),
1340 committed_entry->transferred_global_request_id());
1341 EXPECT_FALSE(committed_entry->should_replace_entry());
1342 EXPECT_FALSE(committed_entry->should_clear_history_list());
1345 // Test that Redirects are preserved after a commit.
1346 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1347 NavigationControllerImpl& controller = controller_impl();
1348 const GURL url1("http://foo1");
1349 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1351 // Set up some redirect values.
1352 std::vector<GURL> redirects;
1353 redirects.push_back(GURL("http://foo2"));
1355 // Set redirects on the pending entry.
1356 NavigationEntryImpl* pending_entry =
1357 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1358 pending_entry->SetRedirectChain(redirects);
1359 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1360 EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
1362 // Normal navigation will preserve redirects in the committed entry.
1363 main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
1364 NavigationEntryImpl* committed_entry =
1365 NavigationEntryImpl::FromNavigationEntry(
1366 controller.GetLastCommittedEntry());
1367 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1368 EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
1371 // Tests what happens when we navigate back successfully
1372 TEST_F(NavigationControllerTest, Back) {
1373 NavigationControllerImpl& controller = controller_impl();
1374 TestNotificationTracker notifications;
1375 RegisterForAllNavNotifications(&notifications, &controller);
1377 const GURL url1("http://foo1");
1378 main_test_rfh()->SendNavigate(0, url1);
1379 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1380 navigation_entry_committed_counter_ = 0;
1382 const GURL url2("http://foo2");
1383 main_test_rfh()->SendNavigate(1, url2);
1384 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1385 navigation_entry_committed_counter_ = 0;
1387 controller.GoBack();
1388 EXPECT_EQ(0U, notifications.size());
1390 // We should now have a pending navigation to go back.
1391 EXPECT_EQ(controller.GetEntryCount(), 2);
1392 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1393 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1394 EXPECT_TRUE(controller.GetLastCommittedEntry());
1395 EXPECT_TRUE(controller.GetPendingEntry());
1396 EXPECT_FALSE(controller.CanGoBack());
1397 EXPECT_FALSE(controller.CanGoToOffset(-1));
1398 EXPECT_TRUE(controller.CanGoForward());
1399 EXPECT_TRUE(controller.CanGoToOffset(1));
1400 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1402 // Timestamp for entry 1 should be on or after that of entry 0.
1403 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1404 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1405 controller.GetEntryAtIndex(0)->GetTimestamp());
1407 main_test_rfh()->SendNavigate(0, url2);
1408 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1409 navigation_entry_committed_counter_ = 0;
1411 // The back navigation completed successfully.
1412 EXPECT_EQ(controller.GetEntryCount(), 2);
1413 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1414 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1415 EXPECT_TRUE(controller.GetLastCommittedEntry());
1416 EXPECT_FALSE(controller.GetPendingEntry());
1417 EXPECT_FALSE(controller.CanGoBack());
1418 EXPECT_FALSE(controller.CanGoToOffset(-1));
1419 EXPECT_TRUE(controller.CanGoForward());
1420 EXPECT_TRUE(controller.CanGoToOffset(1));
1421 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1423 // Timestamp for entry 0 should be on or after that of entry 1
1424 // (since we went back to it).
1425 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1426 controller.GetEntryAtIndex(1)->GetTimestamp());
1429 // Tests what happens when a back navigation produces a new page.
1430 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1431 NavigationControllerImpl& controller = controller_impl();
1432 TestNotificationTracker notifications;
1433 RegisterForAllNavNotifications(&notifications, &controller);
1435 const GURL url1("http://foo/1");
1436 const GURL url2("http://foo/2");
1437 const GURL url3("http://foo/3");
1439 controller.LoadURL(
1440 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1441 main_test_rfh()->SendNavigate(0, url1);
1442 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1443 navigation_entry_committed_counter_ = 0;
1445 controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1446 main_test_rfh()->SendNavigate(1, url2);
1447 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1448 navigation_entry_committed_counter_ = 0;
1450 controller.GoBack();
1451 EXPECT_EQ(0U, notifications.size());
1453 // We should now have a pending navigation to go back.
1454 EXPECT_EQ(controller.GetEntryCount(), 2);
1455 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1456 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1457 EXPECT_TRUE(controller.GetLastCommittedEntry());
1458 EXPECT_TRUE(controller.GetPendingEntry());
1459 EXPECT_FALSE(controller.CanGoBack());
1460 EXPECT_TRUE(controller.CanGoForward());
1462 main_test_rfh()->SendNavigate(2, url3);
1463 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1464 navigation_entry_committed_counter_ = 0;
1466 // The back navigation resulted in a completely new navigation.
1467 // TODO(darin): perhaps this behavior will be confusing to users?
1468 EXPECT_EQ(controller.GetEntryCount(), 3);
1469 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1470 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1471 EXPECT_TRUE(controller.GetLastCommittedEntry());
1472 EXPECT_FALSE(controller.GetPendingEntry());
1473 EXPECT_TRUE(controller.CanGoBack());
1474 EXPECT_FALSE(controller.CanGoForward());
1477 // Receives a back message when there is a new pending navigation entry.
1478 TEST_F(NavigationControllerTest, Back_NewPending) {
1479 NavigationControllerImpl& controller = controller_impl();
1480 TestNotificationTracker notifications;
1481 RegisterForAllNavNotifications(&notifications, &controller);
1483 const GURL kUrl1("http://foo1");
1484 const GURL kUrl2("http://foo2");
1485 const GURL kUrl3("http://foo3");
1487 // First navigate two places so we have some back history.
1488 main_test_rfh()->SendNavigate(0, kUrl1);
1489 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1490 navigation_entry_committed_counter_ = 0;
1492 // controller.LoadURL(kUrl2, PAGE_TRANSITION_TYPED);
1493 main_test_rfh()->SendNavigate(1, kUrl2);
1494 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1495 navigation_entry_committed_counter_ = 0;
1497 // Now start a new pending navigation and go back before it commits.
1498 controller.LoadURL(kUrl3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1499 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1500 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1501 controller.GoBack();
1503 // The pending navigation should now be the "back" item and the new one
1504 // should be gone.
1505 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1506 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1509 // Receives a back message when there is a different renavigation already
1510 // pending.
1511 TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1512 NavigationControllerImpl& controller = controller_impl();
1513 const GURL kUrl1("http://foo/1");
1514 const GURL kUrl2("http://foo/2");
1515 const GURL kUrl3("http://foo/3");
1517 // First navigate three places so we have some back history.
1518 main_test_rfh()->SendNavigate(0, kUrl1);
1519 main_test_rfh()->SendNavigate(1, kUrl2);
1520 main_test_rfh()->SendNavigate(2, kUrl3);
1522 // With nothing pending, say we get a navigation to the second entry.
1523 main_test_rfh()->SendNavigate(1, kUrl2);
1525 // We know all the entries have the same site instance, so we can just grab
1526 // a random one for looking up other entries.
1527 SiteInstance* site_instance =
1528 NavigationEntryImpl::FromNavigationEntry(
1529 controller.GetLastCommittedEntry())->site_instance();
1531 // That second URL should be the last committed and it should have gotten the
1532 // new title.
1533 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1534 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1535 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1537 // Now go forward to the last item again and say it was committed.
1538 controller.GoForward();
1539 main_test_rfh()->SendNavigate(2, kUrl3);
1541 // Now start going back one to the second page. It will be pending.
1542 controller.GoBack();
1543 EXPECT_EQ(1, controller.GetPendingEntryIndex());
1544 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1546 // Not synthesize a totally new back event to the first page. This will not
1547 // match the pending one.
1548 main_test_rfh()->SendNavigate(0, kUrl1);
1550 // The committed navigation should clear the pending entry.
1551 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1553 // But the navigated entry should be the last committed.
1554 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1555 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1558 // Tests what happens when we navigate forward successfully.
1559 TEST_F(NavigationControllerTest, Forward) {
1560 NavigationControllerImpl& controller = controller_impl();
1561 TestNotificationTracker notifications;
1562 RegisterForAllNavNotifications(&notifications, &controller);
1564 const GURL url1("http://foo1");
1565 const GURL url2("http://foo2");
1567 main_test_rfh()->SendNavigate(0, url1);
1568 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1569 navigation_entry_committed_counter_ = 0;
1571 main_test_rfh()->SendNavigate(1, url2);
1572 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1573 navigation_entry_committed_counter_ = 0;
1575 controller.GoBack();
1576 main_test_rfh()->SendNavigate(0, url1);
1577 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1578 navigation_entry_committed_counter_ = 0;
1580 controller.GoForward();
1582 // We should now have a pending navigation to go forward.
1583 EXPECT_EQ(controller.GetEntryCount(), 2);
1584 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1585 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1586 EXPECT_TRUE(controller.GetLastCommittedEntry());
1587 EXPECT_TRUE(controller.GetPendingEntry());
1588 EXPECT_TRUE(controller.CanGoBack());
1589 EXPECT_TRUE(controller.CanGoToOffset(-1));
1590 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1591 EXPECT_FALSE(controller.CanGoForward());
1592 EXPECT_FALSE(controller.CanGoToOffset(1));
1594 // Timestamp for entry 0 should be on or after that of entry 1
1595 // (since we went back to it).
1596 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1597 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1598 controller.GetEntryAtIndex(1)->GetTimestamp());
1600 main_test_rfh()->SendNavigate(1, url2);
1601 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1602 navigation_entry_committed_counter_ = 0;
1604 // The forward navigation completed successfully.
1605 EXPECT_EQ(controller.GetEntryCount(), 2);
1606 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1607 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1608 EXPECT_TRUE(controller.GetLastCommittedEntry());
1609 EXPECT_FALSE(controller.GetPendingEntry());
1610 EXPECT_TRUE(controller.CanGoBack());
1611 EXPECT_TRUE(controller.CanGoToOffset(-1));
1612 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1613 EXPECT_FALSE(controller.CanGoForward());
1614 EXPECT_FALSE(controller.CanGoToOffset(1));
1616 // Timestamp for entry 1 should be on or after that of entry 0
1617 // (since we went forward to it).
1618 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1619 controller.GetEntryAtIndex(0)->GetTimestamp());
1622 // Tests what happens when a forward navigation produces a new page.
1623 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1624 NavigationControllerImpl& controller = controller_impl();
1625 TestNotificationTracker notifications;
1626 RegisterForAllNavNotifications(&notifications, &controller);
1628 const GURL url1("http://foo1");
1629 const GURL url2("http://foo2");
1630 const GURL url3("http://foo3");
1632 main_test_rfh()->SendNavigate(0, url1);
1633 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1634 navigation_entry_committed_counter_ = 0;
1635 main_test_rfh()->SendNavigate(1, url2);
1636 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1637 navigation_entry_committed_counter_ = 0;
1639 controller.GoBack();
1640 main_test_rfh()->SendNavigate(0, url1);
1641 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1642 navigation_entry_committed_counter_ = 0;
1644 controller.GoForward();
1645 EXPECT_EQ(0U, notifications.size());
1647 // Should now have a pending navigation to go forward.
1648 EXPECT_EQ(controller.GetEntryCount(), 2);
1649 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1650 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1651 EXPECT_TRUE(controller.GetLastCommittedEntry());
1652 EXPECT_TRUE(controller.GetPendingEntry());
1653 EXPECT_TRUE(controller.CanGoBack());
1654 EXPECT_FALSE(controller.CanGoForward());
1656 main_test_rfh()->SendNavigate(2, url3);
1657 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1658 navigation_entry_committed_counter_ = 0;
1659 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1661 EXPECT_EQ(controller.GetEntryCount(), 2);
1662 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1663 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1664 EXPECT_TRUE(controller.GetLastCommittedEntry());
1665 EXPECT_FALSE(controller.GetPendingEntry());
1666 EXPECT_TRUE(controller.CanGoBack());
1667 EXPECT_FALSE(controller.CanGoForward());
1670 // Two consequent navigation for the same URL entered in should be considered
1671 // as SAME_PAGE navigation even when we are redirected to some other page.
1672 TEST_F(NavigationControllerTest, Redirect) {
1673 NavigationControllerImpl& controller = controller_impl();
1674 TestNotificationTracker notifications;
1675 RegisterForAllNavNotifications(&notifications, &controller);
1677 const GURL url1("http://foo1");
1678 const GURL url2("http://foo2"); // Redirection target
1680 // First request
1681 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1683 EXPECT_EQ(0U, notifications.size());
1684 main_test_rfh()->SendNavigate(0, url2);
1685 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1686 navigation_entry_committed_counter_ = 0;
1688 // Second request
1689 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1691 EXPECT_TRUE(controller.GetPendingEntry());
1692 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1693 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1695 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1696 params.page_id = 0;
1697 params.url = url2;
1698 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1699 params.redirects.push_back(GURL("http://foo1"));
1700 params.redirects.push_back(GURL("http://foo2"));
1701 params.should_update_history = false;
1702 params.gesture = NavigationGestureAuto;
1703 params.is_post = false;
1704 params.page_state = PageState::CreateFromURL(url2);
1706 LoadCommittedDetails details;
1708 EXPECT_EQ(0U, notifications.size());
1709 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1710 &details));
1711 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1712 navigation_entry_committed_counter_ = 0;
1714 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1715 EXPECT_EQ(controller.GetEntryCount(), 1);
1716 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1717 EXPECT_TRUE(controller.GetLastCommittedEntry());
1718 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1719 EXPECT_FALSE(controller.GetPendingEntry());
1720 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1722 EXPECT_FALSE(controller.CanGoBack());
1723 EXPECT_FALSE(controller.CanGoForward());
1726 // Similar to Redirect above, but the first URL is requested by POST,
1727 // the second URL is requested by GET. NavigationEntry::has_post_data_
1728 // must be cleared. http://crbug.com/21245
1729 TEST_F(NavigationControllerTest, PostThenRedirect) {
1730 NavigationControllerImpl& controller = controller_impl();
1731 TestNotificationTracker notifications;
1732 RegisterForAllNavNotifications(&notifications, &controller);
1734 const GURL url1("http://foo1");
1735 const GURL url2("http://foo2"); // Redirection target
1737 // First request as POST
1738 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1739 controller.GetVisibleEntry()->SetHasPostData(true);
1741 EXPECT_EQ(0U, notifications.size());
1742 main_test_rfh()->SendNavigate(0, url2);
1743 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1744 navigation_entry_committed_counter_ = 0;
1746 // Second request
1747 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1749 EXPECT_TRUE(controller.GetPendingEntry());
1750 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1751 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1753 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1754 params.page_id = 0;
1755 params.url = url2;
1756 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1757 params.redirects.push_back(GURL("http://foo1"));
1758 params.redirects.push_back(GURL("http://foo2"));
1759 params.should_update_history = false;
1760 params.gesture = NavigationGestureAuto;
1761 params.is_post = false;
1762 params.page_state = PageState::CreateFromURL(url2);
1764 LoadCommittedDetails details;
1766 EXPECT_EQ(0U, notifications.size());
1767 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1768 &details));
1769 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1770 navigation_entry_committed_counter_ = 0;
1772 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1773 EXPECT_EQ(controller.GetEntryCount(), 1);
1774 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1775 EXPECT_TRUE(controller.GetLastCommittedEntry());
1776 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1777 EXPECT_FALSE(controller.GetPendingEntry());
1778 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1779 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1781 EXPECT_FALSE(controller.CanGoBack());
1782 EXPECT_FALSE(controller.CanGoForward());
1785 // A redirect right off the bat should be a NEW_PAGE.
1786 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1787 NavigationControllerImpl& controller = controller_impl();
1788 TestNotificationTracker notifications;
1789 RegisterForAllNavNotifications(&notifications, &controller);
1791 const GURL url1("http://foo1");
1792 const GURL url2("http://foo2"); // Redirection target
1794 // First request
1795 controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1797 EXPECT_TRUE(controller.GetPendingEntry());
1798 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1799 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1801 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1802 params.page_id = 0;
1803 params.url = url2;
1804 params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1805 params.redirects.push_back(GURL("http://foo1"));
1806 params.redirects.push_back(GURL("http://foo2"));
1807 params.should_update_history = false;
1808 params.gesture = NavigationGestureAuto;
1809 params.is_post = false;
1810 params.page_state = PageState::CreateFromURL(url2);
1812 LoadCommittedDetails details;
1814 EXPECT_EQ(0U, notifications.size());
1815 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1816 &details));
1817 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1818 navigation_entry_committed_counter_ = 0;
1820 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1821 EXPECT_EQ(controller.GetEntryCount(), 1);
1822 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1823 EXPECT_TRUE(controller.GetLastCommittedEntry());
1824 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1825 EXPECT_FALSE(controller.GetPendingEntry());
1826 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1828 EXPECT_FALSE(controller.CanGoBack());
1829 EXPECT_FALSE(controller.CanGoForward());
1832 // Tests navigation via link click within a subframe. A new navigation entry
1833 // should be created.
1834 TEST_F(NavigationControllerTest, NewSubframe) {
1835 NavigationControllerImpl& controller = controller_impl();
1836 TestNotificationTracker notifications;
1837 RegisterForAllNavNotifications(&notifications, &controller);
1839 const GURL url1("http://foo1");
1840 main_test_rfh()->SendNavigate(0, url1);
1841 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1842 navigation_entry_committed_counter_ = 0;
1844 const GURL url2("http://foo2");
1845 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1846 params.page_id = 1;
1847 params.url = url2;
1848 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1849 params.should_update_history = false;
1850 params.gesture = NavigationGestureUser;
1851 params.is_post = false;
1852 params.page_state = PageState::CreateFromURL(url2);
1854 LoadCommittedDetails details;
1855 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1856 &details));
1857 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1858 navigation_entry_committed_counter_ = 0;
1859 EXPECT_EQ(url1, details.previous_url);
1860 EXPECT_FALSE(details.is_in_page);
1861 EXPECT_FALSE(details.is_main_frame);
1863 // The new entry should be appended.
1864 EXPECT_EQ(2, controller.GetEntryCount());
1866 // New entry should refer to the new page, but the old URL (entries only
1867 // reflect the toplevel URL).
1868 EXPECT_EQ(url1, details.entry->GetURL());
1869 EXPECT_EQ(params.page_id, details.entry->GetPageID());
1872 // Some pages create a popup, then write an iframe into it. This causes a
1873 // subframe navigation without having any committed entry. Such navigations
1874 // just get thrown on the ground, but we shouldn't crash.
1875 TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
1876 NavigationControllerImpl& controller = controller_impl();
1877 TestNotificationTracker notifications;
1878 RegisterForAllNavNotifications(&notifications, &controller);
1880 // Navigation controller currently has no entries.
1881 const GURL url("http://foo2");
1882 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1883 params.page_id = 1;
1884 params.url = url;
1885 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1886 params.should_update_history = false;
1887 params.gesture = NavigationGestureAuto;
1888 params.is_post = false;
1889 params.page_state = PageState::CreateFromURL(url);
1891 LoadCommittedDetails details;
1892 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1893 &details));
1894 EXPECT_EQ(0U, notifications.size());
1897 // Auto subframes are ones the page loads automatically like ads. They should
1898 // not create new navigation entries.
1899 TEST_F(NavigationControllerTest, AutoSubframe) {
1900 NavigationControllerImpl& controller = controller_impl();
1901 TestNotificationTracker notifications;
1902 RegisterForAllNavNotifications(&notifications, &controller);
1904 const GURL url1("http://foo1");
1905 main_test_rfh()->SendNavigate(0, url1);
1906 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1907 navigation_entry_committed_counter_ = 0;
1909 const GURL url2("http://foo2");
1910 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1911 params.page_id = 0;
1912 params.url = url2;
1913 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1914 params.should_update_history = false;
1915 params.gesture = NavigationGestureUser;
1916 params.is_post = false;
1917 params.page_state = PageState::CreateFromURL(url2);
1919 // Navigating should do nothing.
1920 LoadCommittedDetails details;
1921 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1922 &details));
1923 EXPECT_EQ(0U, notifications.size());
1925 // There should still be only one entry.
1926 EXPECT_EQ(1, controller.GetEntryCount());
1929 // Tests navigation and then going back to a subframe navigation.
1930 TEST_F(NavigationControllerTest, BackSubframe) {
1931 NavigationControllerImpl& controller = controller_impl();
1932 TestNotificationTracker notifications;
1933 RegisterForAllNavNotifications(&notifications, &controller);
1935 // Main page.
1936 const GURL url1("http://foo1");
1937 main_test_rfh()->SendNavigate(0, url1);
1938 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1939 navigation_entry_committed_counter_ = 0;
1941 // First manual subframe navigation.
1942 const GURL url2("http://foo2");
1943 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1944 params.page_id = 1;
1945 params.url = url2;
1946 params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1947 params.should_update_history = false;
1948 params.gesture = NavigationGestureUser;
1949 params.is_post = false;
1950 params.page_state = PageState::CreateFromURL(url2);
1952 // This should generate a new entry.
1953 LoadCommittedDetails details;
1954 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1955 &details));
1956 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1957 navigation_entry_committed_counter_ = 0;
1958 EXPECT_EQ(2, controller.GetEntryCount());
1960 // Second manual subframe navigation should also make a new entry.
1961 const GURL url3("http://foo3");
1962 params.page_id = 2;
1963 params.url = url3;
1964 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1965 &details));
1966 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1967 navigation_entry_committed_counter_ = 0;
1968 EXPECT_EQ(3, controller.GetEntryCount());
1969 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
1971 // Go back one.
1972 controller.GoBack();
1973 params.url = url2;
1974 params.page_id = 1;
1975 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1976 &details));
1977 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1978 navigation_entry_committed_counter_ = 0;
1979 EXPECT_EQ(3, controller.GetEntryCount());
1980 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
1981 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1982 EXPECT_FALSE(controller.GetPendingEntry());
1984 // Go back one more.
1985 controller.GoBack();
1986 params.url = url1;
1987 params.page_id = 0;
1988 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1989 &details));
1990 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1991 navigation_entry_committed_counter_ = 0;
1992 EXPECT_EQ(3, controller.GetEntryCount());
1993 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
1994 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1995 EXPECT_FALSE(controller.GetPendingEntry());
1998 TEST_F(NavigationControllerTest, LinkClick) {
1999 NavigationControllerImpl& controller = controller_impl();
2000 TestNotificationTracker notifications;
2001 RegisterForAllNavNotifications(&notifications, &controller);
2003 const GURL url1("http://foo1");
2004 const GURL url2("http://foo2");
2006 main_test_rfh()->SendNavigate(0, url1);
2007 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2008 navigation_entry_committed_counter_ = 0;
2010 main_test_rfh()->SendNavigate(1, url2);
2011 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2012 navigation_entry_committed_counter_ = 0;
2014 // Should not have produced a new session history entry.
2015 EXPECT_EQ(controller.GetEntryCount(), 2);
2016 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2017 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2018 EXPECT_TRUE(controller.GetLastCommittedEntry());
2019 EXPECT_FALSE(controller.GetPendingEntry());
2020 EXPECT_TRUE(controller.CanGoBack());
2021 EXPECT_FALSE(controller.CanGoForward());
2024 TEST_F(NavigationControllerTest, InPage) {
2025 NavigationControllerImpl& controller = controller_impl();
2026 TestNotificationTracker notifications;
2027 RegisterForAllNavNotifications(&notifications, &controller);
2029 // Main page.
2030 const GURL url1("http://foo");
2031 main_test_rfh()->SendNavigate(0, url1);
2032 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2033 navigation_entry_committed_counter_ = 0;
2035 // Ensure main page navigation to same url respects the was_within_same_page
2036 // hint provided in the params.
2037 FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2038 self_params.page_id = 0;
2039 self_params.url = url1;
2040 self_params.transition = PAGE_TRANSITION_LINK;
2041 self_params.should_update_history = false;
2042 self_params.gesture = NavigationGestureUser;
2043 self_params.is_post = false;
2044 self_params.page_state = PageState::CreateFromURL(url1);
2045 self_params.was_within_same_page = true;
2047 LoadCommittedDetails details;
2048 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2049 &details));
2050 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2051 navigation_entry_committed_counter_ = 0;
2052 EXPECT_TRUE(details.is_in_page);
2053 EXPECT_TRUE(details.did_replace_entry);
2054 EXPECT_EQ(1, controller.GetEntryCount());
2056 // Fragment navigation to a new page_id.
2057 const GURL url2("http://foo#a");
2058 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2059 params.page_id = 1;
2060 params.url = url2;
2061 params.transition = PAGE_TRANSITION_LINK;
2062 params.should_update_history = false;
2063 params.gesture = NavigationGestureUser;
2064 params.is_post = false;
2065 params.page_state = PageState::CreateFromURL(url2);
2066 params.was_within_same_page = true;
2068 // This should generate a new entry.
2069 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2070 &details));
2071 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2072 navigation_entry_committed_counter_ = 0;
2073 EXPECT_TRUE(details.is_in_page);
2074 EXPECT_FALSE(details.did_replace_entry);
2075 EXPECT_EQ(2, controller.GetEntryCount());
2077 // Go back one.
2078 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2079 controller.GoBack();
2080 back_params.url = url1;
2081 back_params.page_id = 0;
2082 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2083 &details));
2084 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2085 navigation_entry_committed_counter_ = 0;
2086 EXPECT_TRUE(details.is_in_page);
2087 EXPECT_EQ(2, controller.GetEntryCount());
2088 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2089 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2091 // Go forward
2092 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2093 controller.GoForward();
2094 forward_params.url = url2;
2095 forward_params.page_id = 1;
2096 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2097 &details));
2098 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2099 navigation_entry_committed_counter_ = 0;
2100 EXPECT_TRUE(details.is_in_page);
2101 EXPECT_EQ(2, controller.GetEntryCount());
2102 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2103 EXPECT_EQ(forward_params.url,
2104 controller.GetVisibleEntry()->GetURL());
2106 // Now go back and forward again. This is to work around a bug where we would
2107 // compare the incoming URL with the last committed entry rather than the
2108 // one identified by an existing page ID. This would result in the second URL
2109 // losing the reference fragment when you navigate away from it and then back.
2110 controller.GoBack();
2111 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2112 &details));
2113 controller.GoForward();
2114 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2115 &details));
2116 EXPECT_EQ(forward_params.url,
2117 controller.GetVisibleEntry()->GetURL());
2119 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2120 const GURL url3("http://bar");
2121 params.page_id = 2;
2122 params.url = url3;
2123 navigation_entry_committed_counter_ = 0;
2124 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2125 &details));
2126 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2127 navigation_entry_committed_counter_ = 0;
2128 EXPECT_FALSE(details.is_in_page);
2129 EXPECT_EQ(3, controller.GetEntryCount());
2130 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2133 TEST_F(NavigationControllerTest, InPage_Replace) {
2134 NavigationControllerImpl& controller = controller_impl();
2135 TestNotificationTracker notifications;
2136 RegisterForAllNavNotifications(&notifications, &controller);
2138 // Main page.
2139 const GURL url1("http://foo");
2140 main_test_rfh()->SendNavigate(0, url1);
2141 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2142 navigation_entry_committed_counter_ = 0;
2144 // First navigation.
2145 const GURL url2("http://foo#a");
2146 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2147 params.page_id = 0; // Same page_id
2148 params.url = url2;
2149 params.transition = PAGE_TRANSITION_LINK;
2150 params.should_update_history = false;
2151 params.gesture = NavigationGestureUser;
2152 params.is_post = false;
2153 params.page_state = PageState::CreateFromURL(url2);
2154 params.was_within_same_page = true;
2156 // This should NOT generate a new entry, nor prune the list.
2157 LoadCommittedDetails details;
2158 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2159 &details));
2160 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2161 navigation_entry_committed_counter_ = 0;
2162 EXPECT_TRUE(details.is_in_page);
2163 EXPECT_TRUE(details.did_replace_entry);
2164 EXPECT_EQ(1, controller.GetEntryCount());
2167 // Tests for http://crbug.com/40395
2168 // Simulates this:
2169 // <script>
2170 // window.location.replace("#a");
2171 // window.location='http://foo3/';
2172 // </script>
2173 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2174 NavigationControllerImpl& controller = controller_impl();
2175 TestNotificationTracker notifications;
2176 RegisterForAllNavNotifications(&notifications, &controller);
2178 // Load an initial page.
2180 const GURL url("http://foo/");
2181 main_test_rfh()->SendNavigate(0, url);
2182 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2183 navigation_entry_committed_counter_ = 0;
2186 // Navigate to a new page.
2188 const GURL url("http://foo2/");
2189 main_test_rfh()->SendNavigate(1, url);
2190 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2191 navigation_entry_committed_counter_ = 0;
2194 // Navigate within the page.
2196 const GURL url("http://foo2/#a");
2197 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2198 params.page_id = 1; // Same page_id
2199 params.url = url;
2200 params.transition = PAGE_TRANSITION_LINK;
2201 params.redirects.push_back(url);
2202 params.should_update_history = true;
2203 params.gesture = NavigationGestureUnknown;
2204 params.is_post = false;
2205 params.page_state = PageState::CreateFromURL(url);
2206 params.was_within_same_page = true;
2208 // This should NOT generate a new entry, nor prune the list.
2209 LoadCommittedDetails details;
2210 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2211 &details));
2212 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2213 navigation_entry_committed_counter_ = 0;
2214 EXPECT_TRUE(details.is_in_page);
2215 EXPECT_TRUE(details.did_replace_entry);
2216 EXPECT_EQ(2, controller.GetEntryCount());
2219 // Perform a client redirect to a new page.
2221 const GURL url("http://foo3/");
2222 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2223 params.page_id = 2; // New page_id
2224 params.url = url;
2225 params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
2226 params.redirects.push_back(GURL("http://foo2/#a"));
2227 params.redirects.push_back(url);
2228 params.should_update_history = true;
2229 params.gesture = NavigationGestureUnknown;
2230 params.is_post = false;
2231 params.page_state = PageState::CreateFromURL(url);
2233 // This SHOULD generate a new entry.
2234 LoadCommittedDetails details;
2235 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2236 &details));
2237 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2238 navigation_entry_committed_counter_ = 0;
2239 EXPECT_FALSE(details.is_in_page);
2240 EXPECT_EQ(3, controller.GetEntryCount());
2243 // Verify that BACK brings us back to http://foo2/.
2245 const GURL url("http://foo2/");
2246 controller.GoBack();
2247 main_test_rfh()->SendNavigate(1, url);
2248 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2249 navigation_entry_committed_counter_ = 0;
2250 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2254 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
2256 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2257 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2258 GURL url("http://foo");
2259 params.page_id = 1;
2260 params.url = url;
2261 params.page_state = PageState::CreateFromURL(url);
2262 params.was_within_same_page = true;
2263 test_rvh()->SendNavigateWithParams(&params);
2264 // We pass if we don't crash.
2267 // NotificationObserver implementation used in verifying we've received the
2268 // NOTIFICATION_NAV_LIST_PRUNED method.
2269 class PrunedListener : public NotificationObserver {
2270 public:
2271 explicit PrunedListener(NavigationControllerImpl* controller)
2272 : notification_count_(0) {
2273 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2274 Source<NavigationController>(controller));
2277 virtual void Observe(int type,
2278 const NotificationSource& source,
2279 const NotificationDetails& details) OVERRIDE {
2280 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2281 notification_count_++;
2282 details_ = *(Details<PrunedDetails>(details).ptr());
2286 // Number of times NAV_LIST_PRUNED has been observed.
2287 int notification_count_;
2289 // Details from the last NAV_LIST_PRUNED.
2290 PrunedDetails details_;
2292 private:
2293 NotificationRegistrar registrar_;
2295 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2298 // Tests that we limit the number of navigation entries created correctly.
2299 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2300 NavigationControllerImpl& controller = controller_impl();
2301 size_t original_count = NavigationControllerImpl::max_entry_count();
2302 const int kMaxEntryCount = 5;
2304 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2306 int url_index;
2307 // Load up to the max count, all entries should be there.
2308 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2309 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2310 controller.LoadURL(
2311 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2312 main_test_rfh()->SendNavigate(url_index, url);
2315 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2317 // Created a PrunedListener to observe prune notifications.
2318 PrunedListener listener(&controller);
2320 // Navigate some more.
2321 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2322 controller.LoadURL(
2323 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2324 main_test_rfh()->SendNavigate(url_index, url);
2325 url_index++;
2327 // We should have got a pruned navigation.
2328 EXPECT_EQ(1, listener.notification_count_);
2329 EXPECT_TRUE(listener.details_.from_front);
2330 EXPECT_EQ(1, listener.details_.count);
2332 // We expect http://www.a.com/0 to be gone.
2333 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2334 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2335 GURL("http:////www.a.com/1"));
2337 // More navigations.
2338 for (int i = 0; i < 3; i++) {
2339 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2340 controller.LoadURL(
2341 url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2342 main_test_rfh()->SendNavigate(url_index, url);
2343 url_index++;
2345 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2346 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2347 GURL("http:////www.a.com/4"));
2349 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2352 // Tests that we can do a restore and navigate to the restored entries and
2353 // everything is updated properly. This can be tricky since there is no
2354 // SiteInstance for the entries created initially.
2355 TEST_F(NavigationControllerTest, RestoreNavigate) {
2356 // Create a NavigationController with a restored set of tabs.
2357 GURL url("http://foo");
2358 std::vector<NavigationEntry*> entries;
2359 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2360 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2361 browser_context());
2362 entry->SetPageID(0);
2363 entry->SetTitle(base::ASCIIToUTF16("Title"));
2364 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2365 const base::Time timestamp = base::Time::Now();
2366 entry->SetTimestamp(timestamp);
2367 entries.push_back(entry);
2368 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2369 WebContents::Create(WebContents::CreateParams(browser_context()))));
2370 NavigationControllerImpl& our_controller = our_contents->GetController();
2371 our_controller.Restore(
2373 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2374 &entries);
2375 ASSERT_EQ(0u, entries.size());
2377 // Before navigating to the restored entry, it should have a restore_type
2378 // and no SiteInstance.
2379 ASSERT_EQ(1, our_controller.GetEntryCount());
2380 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2381 NavigationEntryImpl::FromNavigationEntry(
2382 our_controller.GetEntryAtIndex(0))->restore_type());
2383 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2384 our_controller.GetEntryAtIndex(0))->site_instance());
2386 // After navigating, we should have one entry, and it should be "pending".
2387 // It should now have a SiteInstance and no restore_type.
2388 our_controller.GoToIndex(0);
2389 EXPECT_EQ(1, our_controller.GetEntryCount());
2390 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2391 our_controller.GetPendingEntry());
2392 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2393 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2394 NavigationEntryImpl::FromNavigationEntry
2395 (our_controller.GetEntryAtIndex(0))->restore_type());
2396 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2397 our_controller.GetEntryAtIndex(0))->site_instance());
2399 // Timestamp should remain the same before the navigation finishes.
2400 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2402 // Say we navigated to that entry.
2403 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2404 params.page_id = 0;
2405 params.url = url;
2406 params.transition = PAGE_TRANSITION_LINK;
2407 params.should_update_history = false;
2408 params.gesture = NavigationGestureUser;
2409 params.is_post = false;
2410 params.page_state = PageState::CreateFromURL(url);
2411 LoadCommittedDetails details;
2412 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2413 &details);
2415 // There should be no longer any pending entry and one committed one. This
2416 // means that we were able to locate the entry, assign its site instance, and
2417 // commit it properly.
2418 EXPECT_EQ(1, our_controller.GetEntryCount());
2419 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2420 EXPECT_FALSE(our_controller.GetPendingEntry());
2421 EXPECT_EQ(url,
2422 NavigationEntryImpl::FromNavigationEntry(
2423 our_controller.GetLastCommittedEntry())->site_instance()->
2424 GetSiteURL());
2425 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2426 NavigationEntryImpl::FromNavigationEntry(
2427 our_controller.GetEntryAtIndex(0))->restore_type());
2429 // Timestamp should have been updated.
2430 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2433 // Tests that we can still navigate to a restored entry after a different
2434 // navigation fails and clears the pending entry. http://crbug.com/90085
2435 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2436 // Create a NavigationController with a restored set of tabs.
2437 GURL url("http://foo");
2438 std::vector<NavigationEntry*> entries;
2439 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2440 url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2441 browser_context());
2442 entry->SetPageID(0);
2443 entry->SetTitle(base::ASCIIToUTF16("Title"));
2444 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2445 entries.push_back(entry);
2446 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2447 WebContents::Create(WebContents::CreateParams(browser_context()))));
2448 NavigationControllerImpl& our_controller = our_contents->GetController();
2449 our_controller.Restore(
2450 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2451 ASSERT_EQ(0u, entries.size());
2453 // Before navigating to the restored entry, it should have a restore_type
2454 // and no SiteInstance.
2455 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2456 NavigationEntryImpl::FromNavigationEntry(
2457 our_controller.GetEntryAtIndex(0))->restore_type());
2458 EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2459 our_controller.GetEntryAtIndex(0))->site_instance());
2461 // After navigating, we should have one entry, and it should be "pending".
2462 // It should now have a SiteInstance and no restore_type.
2463 our_controller.GoToIndex(0);
2464 EXPECT_EQ(1, our_controller.GetEntryCount());
2465 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2466 our_controller.GetPendingEntry());
2467 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2468 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2469 NavigationEntryImpl::FromNavigationEntry(
2470 our_controller.GetEntryAtIndex(0))->restore_type());
2471 EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2472 our_controller.GetEntryAtIndex(0))->site_instance());
2474 // This pending navigation may have caused a different navigation to fail,
2475 // which causes the pending entry to be cleared.
2476 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2477 fail_load_params.error_code = net::ERR_ABORTED;
2478 fail_load_params.error_description = base::string16();
2479 fail_load_params.url = url;
2480 fail_load_params.showing_repost_interstitial = false;
2481 main_test_rfh()->OnMessageReceived(
2482 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2483 fail_load_params));
2485 // Now the pending restored entry commits.
2486 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2487 params.page_id = 0;
2488 params.url = url;
2489 params.transition = PAGE_TRANSITION_LINK;
2490 params.should_update_history = false;
2491 params.gesture = NavigationGestureUser;
2492 params.is_post = false;
2493 params.page_state = PageState::CreateFromURL(url);
2494 LoadCommittedDetails details;
2495 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2496 &details);
2498 // There should be no pending entry and one committed one.
2499 EXPECT_EQ(1, our_controller.GetEntryCount());
2500 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2501 EXPECT_FALSE(our_controller.GetPendingEntry());
2502 EXPECT_EQ(url,
2503 NavigationEntryImpl::FromNavigationEntry(
2504 our_controller.GetLastCommittedEntry())->site_instance()->
2505 GetSiteURL());
2506 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2507 NavigationEntryImpl::FromNavigationEntry(
2508 our_controller.GetEntryAtIndex(0))->restore_type());
2511 // Make sure that the page type and stuff is correct after an interstitial.
2512 TEST_F(NavigationControllerTest, Interstitial) {
2513 NavigationControllerImpl& controller = controller_impl();
2514 // First navigate somewhere normal.
2515 const GURL url1("http://foo");
2516 controller.LoadURL(
2517 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2518 main_test_rfh()->SendNavigate(0, url1);
2520 // Now navigate somewhere with an interstitial.
2521 const GURL url2("http://bar");
2522 controller.LoadURL(
2523 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2524 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2525 set_page_type(PAGE_TYPE_INTERSTITIAL);
2527 // At this point the interstitial will be displayed and the load will still
2528 // be pending. If the user continues, the load will commit.
2529 main_test_rfh()->SendNavigate(1, url2);
2531 // The page should be a normal page again.
2532 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2533 EXPECT_EQ(PAGE_TYPE_NORMAL,
2534 controller.GetLastCommittedEntry()->GetPageType());
2537 TEST_F(NavigationControllerTest, RemoveEntry) {
2538 NavigationControllerImpl& controller = controller_impl();
2539 const GURL url1("http://foo/1");
2540 const GURL url2("http://foo/2");
2541 const GURL url3("http://foo/3");
2542 const GURL url4("http://foo/4");
2543 const GURL url5("http://foo/5");
2544 const GURL pending_url("http://foo/pending");
2545 const GURL default_url("http://foo/default");
2547 controller.LoadURL(
2548 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2549 main_test_rfh()->SendNavigate(0, url1);
2550 controller.LoadURL(
2551 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2552 main_test_rfh()->SendNavigate(1, url2);
2553 controller.LoadURL(
2554 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2555 main_test_rfh()->SendNavigate(2, url3);
2556 controller.LoadURL(
2557 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2558 main_test_rfh()->SendNavigate(3, url4);
2559 controller.LoadURL(
2560 url5, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2561 main_test_rfh()->SendNavigate(4, url5);
2563 // Try to remove the last entry. Will fail because it is the current entry.
2564 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2565 EXPECT_EQ(5, controller.GetEntryCount());
2566 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2568 // Go back, but don't commit yet. Check that we can't delete the current
2569 // and pending entries.
2570 controller.GoBack();
2571 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2572 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2574 // Now commit and delete the last entry.
2575 main_test_rfh()->SendNavigate(3, url4);
2576 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2577 EXPECT_EQ(4, controller.GetEntryCount());
2578 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2579 EXPECT_FALSE(controller.GetPendingEntry());
2581 // Remove an entry which is not the last committed one.
2582 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2583 EXPECT_EQ(3, controller.GetEntryCount());
2584 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2585 EXPECT_FALSE(controller.GetPendingEntry());
2587 // Remove the 2 remaining entries.
2588 controller.RemoveEntryAtIndex(1);
2589 controller.RemoveEntryAtIndex(0);
2591 // This should leave us with only the last committed entry.
2592 EXPECT_EQ(1, controller.GetEntryCount());
2593 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2596 // Tests the transient entry, making sure it goes away with all navigations.
2597 TEST_F(NavigationControllerTest, TransientEntry) {
2598 NavigationControllerImpl& controller = controller_impl();
2599 TestNotificationTracker notifications;
2600 RegisterForAllNavNotifications(&notifications, &controller);
2602 const GURL url0("http://foo/0");
2603 const GURL url1("http://foo/1");
2604 const GURL url2("http://foo/2");
2605 const GURL url3("http://foo/3");
2606 const GURL url3_ref("http://foo/3#bar");
2607 const GURL url4("http://foo/4");
2608 const GURL transient_url("http://foo/transient");
2610 controller.LoadURL(
2611 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2612 main_test_rfh()->SendNavigate(0, url0);
2613 controller.LoadURL(
2614 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2615 main_test_rfh()->SendNavigate(1, url1);
2617 notifications.Reset();
2619 // Adding a transient with no pending entry.
2620 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2621 transient_entry->SetURL(transient_url);
2622 controller.SetTransientEntry(transient_entry);
2624 // We should not have received any notifications.
2625 EXPECT_EQ(0U, notifications.size());
2627 // Check our state.
2628 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2629 EXPECT_EQ(controller.GetEntryCount(), 3);
2630 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2631 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2632 EXPECT_TRUE(controller.GetLastCommittedEntry());
2633 EXPECT_FALSE(controller.GetPendingEntry());
2634 EXPECT_TRUE(controller.CanGoBack());
2635 EXPECT_FALSE(controller.CanGoForward());
2636 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2638 // Navigate.
2639 controller.LoadURL(
2640 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2641 main_test_rfh()->SendNavigate(2, url2);
2643 // We should have navigated, transient entry should be gone.
2644 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2645 EXPECT_EQ(controller.GetEntryCount(), 3);
2647 // Add a transient again, then navigate with no pending entry this time.
2648 transient_entry = new NavigationEntryImpl;
2649 transient_entry->SetURL(transient_url);
2650 controller.SetTransientEntry(transient_entry);
2651 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2652 main_test_rfh()->SendNavigate(3, url3);
2653 // Transient entry should be gone.
2654 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2655 EXPECT_EQ(controller.GetEntryCount(), 4);
2657 // Initiate a navigation, add a transient then commit navigation.
2658 controller.LoadURL(
2659 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2660 transient_entry = new NavigationEntryImpl;
2661 transient_entry->SetURL(transient_url);
2662 controller.SetTransientEntry(transient_entry);
2663 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2664 main_test_rfh()->SendNavigate(4, url4);
2665 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2666 EXPECT_EQ(controller.GetEntryCount(), 5);
2668 // Add a transient and go back. This should simply remove the transient.
2669 transient_entry = new NavigationEntryImpl;
2670 transient_entry->SetURL(transient_url);
2671 controller.SetTransientEntry(transient_entry);
2672 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2673 EXPECT_TRUE(controller.CanGoBack());
2674 EXPECT_FALSE(controller.CanGoForward());
2675 controller.GoBack();
2676 // Transient entry should be gone.
2677 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2678 EXPECT_EQ(controller.GetEntryCount(), 5);
2679 main_test_rfh()->SendNavigate(3, url3);
2681 // Add a transient and go to an entry before the current one.
2682 transient_entry = new NavigationEntryImpl;
2683 transient_entry->SetURL(transient_url);
2684 controller.SetTransientEntry(transient_entry);
2685 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2686 controller.GoToIndex(1);
2687 // The navigation should have been initiated, transient entry should be gone.
2688 EXPECT_FALSE(controller.GetTransientEntry());
2689 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2690 // Visible entry does not update for history navigations until commit.
2691 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2692 main_test_rfh()->SendNavigate(1, url1);
2693 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2695 // Add a transient and go to an entry after the current one.
2696 transient_entry = new NavigationEntryImpl;
2697 transient_entry->SetURL(transient_url);
2698 controller.SetTransientEntry(transient_entry);
2699 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2700 controller.GoToIndex(3);
2701 // The navigation should have been initiated, transient entry should be gone.
2702 // Because of the transient entry that is removed, going to index 3 makes us
2703 // land on url2 (which is visible after the commit).
2704 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2705 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2706 main_test_rfh()->SendNavigate(2, url2);
2707 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2709 // Add a transient and go forward.
2710 transient_entry = new NavigationEntryImpl;
2711 transient_entry->SetURL(transient_url);
2712 controller.SetTransientEntry(transient_entry);
2713 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2714 EXPECT_TRUE(controller.CanGoForward());
2715 controller.GoForward();
2716 // We should have navigated, transient entry should be gone.
2717 EXPECT_FALSE(controller.GetTransientEntry());
2718 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2719 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2720 main_test_rfh()->SendNavigate(3, url3);
2721 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2723 // Add a transient and do an in-page navigation, replacing the current entry.
2724 transient_entry = new NavigationEntryImpl;
2725 transient_entry->SetURL(transient_url);
2726 controller.SetTransientEntry(transient_entry);
2727 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2728 main_test_rfh()->SendNavigate(3, url3_ref);
2729 // Transient entry should be gone.
2730 EXPECT_FALSE(controller.GetTransientEntry());
2731 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2733 // Ensure the URLs are correct.
2734 EXPECT_EQ(controller.GetEntryCount(), 5);
2735 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2736 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2737 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2738 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2739 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2742 // Test that Reload initiates a new navigation to a transient entry's URL.
2743 TEST_F(NavigationControllerTest, ReloadTransient) {
2744 NavigationControllerImpl& controller = controller_impl();
2745 const GURL url0("http://foo/0");
2746 const GURL url1("http://foo/1");
2747 const GURL transient_url("http://foo/transient");
2749 // Load |url0|, and start a pending navigation to |url1|.
2750 controller.LoadURL(
2751 url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2752 main_test_rfh()->SendNavigate(0, url0);
2753 controller.LoadURL(
2754 url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2756 // A transient entry is added, interrupting the navigation.
2757 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2758 transient_entry->SetURL(transient_url);
2759 controller.SetTransientEntry(transient_entry);
2760 EXPECT_TRUE(controller.GetTransientEntry());
2761 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2763 // The page is reloaded, which should remove the pending entry for |url1| and
2764 // the transient entry for |transient_url|, and start a navigation to
2765 // |transient_url|.
2766 controller.Reload(true);
2767 EXPECT_FALSE(controller.GetTransientEntry());
2768 EXPECT_TRUE(controller.GetPendingEntry());
2769 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2770 ASSERT_EQ(controller.GetEntryCount(), 1);
2771 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2773 // Load of |transient_url| completes.
2774 main_test_rfh()->SendNavigate(1, transient_url);
2775 ASSERT_EQ(controller.GetEntryCount(), 2);
2776 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2777 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2780 // Ensure that renderer initiated pending entries get replaced, so that we
2781 // don't show a stale virtual URL when a navigation commits.
2782 // See http://crbug.com/266922.
2783 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2784 NavigationControllerImpl& controller = controller_impl();
2785 Navigator* navigator =
2786 contents()->GetFrameTree()->root()->navigator();
2788 const GURL url1("nonexistent:12121");
2789 const GURL url1_fixed("http://nonexistent:12121/");
2790 const GURL url2("http://foo");
2792 // We create pending entries for renderer-initiated navigations so that we
2793 // can show them in new tabs when it is safe.
2794 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2796 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2797 // the virtual URL to differ from the URL.
2798 controller.GetPendingEntry()->SetURL(url1_fixed);
2799 controller.GetPendingEntry()->SetVirtualURL(url1);
2801 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
2802 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
2803 EXPECT_TRUE(
2804 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2805 is_renderer_initiated());
2807 // If the user clicks another link, we should replace the pending entry.
2808 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2809 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2810 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
2812 // Once it commits, the URL and virtual URL should reflect the actual page.
2813 main_test_rfh()->SendNavigate(0, url2);
2814 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2815 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
2817 // We should not replace the pending entry for an error URL.
2818 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2819 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2820 navigator->DidStartProvisionalLoad(main_test_rfh(),
2821 GURL(kUnreachableWebDataURL), false);
2822 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2824 // We should remember if the pending entry will replace the current one.
2825 // http://crbug.com/308444.
2826 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2827 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2828 set_should_replace_entry(true);
2829 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
2830 EXPECT_TRUE(
2831 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2832 should_replace_entry());
2833 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2834 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2835 // to the main frame.
2836 main_test_rfh()->SendNavigate(0, url2);
2837 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2840 // Tests that the URLs for renderer-initiated navigations are not displayed to
2841 // the user until the navigation commits, to prevent URL spoof attacks.
2842 // See http://crbug.com/99016.
2843 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
2844 NavigationControllerImpl& controller = controller_impl();
2845 TestNotificationTracker notifications;
2846 RegisterForAllNavNotifications(&notifications, &controller);
2848 const GURL url0("http://foo/0");
2849 const GURL url1("http://foo/1");
2851 // For typed navigations (browser-initiated), both pending and visible entries
2852 // should update before commit.
2853 controller.LoadURL(url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2854 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
2855 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2856 main_test_rfh()->SendNavigate(0, url0);
2858 // For link clicks (renderer-initiated navigations), the pending entry should
2859 // update before commit but the visible should not.
2860 NavigationController::LoadURLParams load_url_params(url1);
2861 load_url_params.is_renderer_initiated = true;
2862 controller.LoadURLWithParams(load_url_params);
2863 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2864 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2865 EXPECT_TRUE(
2866 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2867 is_renderer_initiated());
2869 // After commit, both visible should be updated, there should be no pending
2870 // entry, and we should no longer treat the entry as renderer-initiated.
2871 main_test_rfh()->SendNavigate(1, url1);
2872 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2873 EXPECT_FALSE(controller.GetPendingEntry());
2874 EXPECT_FALSE(
2875 NavigationEntryImpl::FromNavigationEntry(
2876 controller.GetLastCommittedEntry())->is_renderer_initiated());
2878 notifications.Reset();
2881 // Tests that the URLs for renderer-initiated navigations in new tabs are
2882 // displayed to the user before commit, as long as the initial about:blank
2883 // page has not been modified. If so, we must revert to showing about:blank.
2884 // See http://crbug.com/9682.
2885 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
2886 NavigationControllerImpl& controller = controller_impl();
2887 TestNotificationTracker notifications;
2888 RegisterForAllNavNotifications(&notifications, &controller);
2890 const GURL url("http://foo");
2892 // For renderer-initiated navigations in new tabs (with no committed entries),
2893 // we show the pending entry's URL as long as the about:blank page is not
2894 // modified.
2895 NavigationController::LoadURLParams load_url_params(url);
2896 load_url_params.transition_type = PAGE_TRANSITION_LINK;
2897 load_url_params.is_renderer_initiated = true;
2898 controller.LoadURLWithParams(load_url_params);
2899 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2900 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2901 EXPECT_TRUE(
2902 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2903 is_renderer_initiated());
2904 EXPECT_TRUE(controller.IsInitialNavigation());
2905 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2907 // There should be no title yet.
2908 EXPECT_TRUE(contents()->GetTitle().empty());
2910 // If something else modifies the contents of the about:blank page, then
2911 // we must revert to showing about:blank to avoid a URL spoof.
2912 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
2913 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
2914 EXPECT_FALSE(controller.GetVisibleEntry());
2915 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2917 notifications.Reset();
2920 // Tests that the URLs for browser-initiated navigations in new tabs are
2921 // displayed to the user even after they fail, as long as the initial
2922 // about:blank page has not been modified. If so, we must revert to showing
2923 // about:blank. See http://crbug.com/355537.
2924 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
2925 NavigationControllerImpl& controller = controller_impl();
2926 TestNotificationTracker notifications;
2927 RegisterForAllNavNotifications(&notifications, &controller);
2929 const GURL url("http://foo");
2931 // For browser-initiated navigations in new tabs (with no committed entries),
2932 // we show the pending entry's URL as long as the about:blank page is not
2933 // modified. This is possible in cases that the user types a URL into a popup
2934 // tab created with a slow URL.
2935 NavigationController::LoadURLParams load_url_params(url);
2936 load_url_params.transition_type = PAGE_TRANSITION_TYPED;
2937 load_url_params.is_renderer_initiated = false;
2938 controller.LoadURLWithParams(load_url_params);
2939 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2940 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2941 EXPECT_FALSE(
2942 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2943 is_renderer_initiated());
2944 EXPECT_TRUE(controller.IsInitialNavigation());
2945 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2947 // There should be no title yet.
2948 EXPECT_TRUE(contents()->GetTitle().empty());
2950 // Suppose it aborts before committing, if it's a 204 or download or due to a
2951 // stop or a new navigation from the user. The URL should remain visible.
2952 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
2953 params.error_code = net::ERR_ABORTED;
2954 params.error_description = base::string16();
2955 params.url = url;
2956 params.showing_repost_interstitial = false;
2957 main_test_rfh()->OnMessageReceived(
2958 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
2959 contents()->SetIsLoading(test_rvh(), false, true, NULL);
2960 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2962 // If something else later modifies the contents of the about:blank page, then
2963 // we must revert to showing about:blank to avoid a URL spoof.
2964 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
2965 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
2966 EXPECT_FALSE(controller.GetVisibleEntry());
2967 EXPECT_FALSE(controller.GetPendingEntry());
2969 notifications.Reset();
2972 // Tests that the URLs for renderer-initiated navigations in new tabs are
2973 // displayed to the user even after they fail, as long as the initial
2974 // about:blank page has not been modified. If so, we must revert to showing
2975 // about:blank. See http://crbug.com/355537.
2976 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
2977 NavigationControllerImpl& controller = controller_impl();
2978 TestNotificationTracker notifications;
2979 RegisterForAllNavNotifications(&notifications, &controller);
2981 const GURL url("http://foo");
2983 // For renderer-initiated navigations in new tabs (with no committed entries),
2984 // we show the pending entry's URL as long as the about:blank page is not
2985 // modified.
2986 NavigationController::LoadURLParams load_url_params(url);
2987 load_url_params.transition_type = PAGE_TRANSITION_LINK;
2988 load_url_params.is_renderer_initiated = true;
2989 controller.LoadURLWithParams(load_url_params);
2990 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2991 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2992 EXPECT_TRUE(
2993 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2994 is_renderer_initiated());
2995 EXPECT_TRUE(controller.IsInitialNavigation());
2996 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2998 // There should be no title yet.
2999 EXPECT_TRUE(contents()->GetTitle().empty());
3001 // Suppose it aborts before committing, if it's a 204 or download or due to a
3002 // stop or a new navigation from the user. The URL should remain visible.
3003 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3004 params.error_code = net::ERR_ABORTED;
3005 params.error_description = base::string16();
3006 params.url = url;
3007 params.showing_repost_interstitial = false;
3008 main_test_rfh()->OnMessageReceived(
3009 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3010 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3012 // If something else later modifies the contents of the about:blank page, then
3013 // we must revert to showing about:blank to avoid a URL spoof.
3014 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3015 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3016 EXPECT_FALSE(controller.GetVisibleEntry());
3017 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3019 notifications.Reset();
3022 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3023 NavigationControllerImpl& controller = controller_impl();
3024 TestNotificationTracker notifications;
3025 RegisterForAllNavNotifications(&notifications, &controller);
3027 const GURL url1("http://foo/eh");
3028 const GURL url2("http://foo/bee");
3030 // For renderer-initiated navigations in new tabs (with no committed entries),
3031 // we show the pending entry's URL as long as the about:blank page is not
3032 // modified.
3033 NavigationController::LoadURLParams load_url_params(url1);
3034 load_url_params.transition_type = PAGE_TRANSITION_LINK;
3035 load_url_params.is_renderer_initiated = true;
3036 controller.LoadURLWithParams(load_url_params);
3037 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3038 EXPECT_TRUE(
3039 NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
3040 is_renderer_initiated());
3041 EXPECT_TRUE(controller.IsInitialNavigation());
3042 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3044 // Simulate a commit and then starting a new pending navigation.
3045 main_test_rfh()->SendNavigate(0, url1);
3046 NavigationController::LoadURLParams load_url2_params(url2);
3047 load_url2_params.transition_type = PAGE_TRANSITION_LINK;
3048 load_url2_params.is_renderer_initiated = true;
3049 controller.LoadURLWithParams(load_url2_params);
3051 // We should not consider this an initial navigation, and thus should
3052 // not show the pending URL.
3053 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3054 EXPECT_FALSE(controller.IsInitialNavigation());
3055 EXPECT_TRUE(controller.GetVisibleEntry());
3056 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3058 notifications.Reset();
3061 // Tests that IsInPageNavigation returns appropriate results. Prevents
3062 // regression for bug 1126349.
3063 TEST_F(NavigationControllerTest, IsInPageNavigation) {
3064 NavigationControllerImpl& controller = controller_impl();
3065 const GURL url("http://www.google.com/home.html");
3067 // If the renderer claims it performed an in-page navigation from
3068 // about:blank, trust the renderer.
3069 // This can happen when an iframe is created and populated via
3070 // document.write(), then tries to perform a fragment navigation.
3071 // TODO(japhet): We should only trust the renderer if the about:blank
3072 // was the first document in the given frame, but we don't have enough
3073 // information to identify that case currently.
3074 const GURL blank_url(url::kAboutBlankURL);
3075 main_test_rfh()->SendNavigate(0, blank_url);
3076 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3077 main_test_rfh()));
3079 // Navigate to URL with no refs.
3080 main_test_rfh()->SendNavigate(0, url);
3082 // Reloading the page is not an in-page navigation.
3083 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3084 main_test_rfh()));
3085 const GURL other_url("http://www.google.com/add.html");
3086 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3087 main_test_rfh()));
3088 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3089 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3090 main_test_rfh()));
3092 // Navigate to URL with refs.
3093 main_test_rfh()->SendNavigate(1, url_with_ref);
3095 // Reloading the page is not an in-page navigation.
3096 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
3097 main_test_rfh()));
3098 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3099 main_test_rfh()));
3100 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3101 main_test_rfh()));
3102 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3103 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
3104 main_test_rfh()));
3106 // Going to the same url again will be considered in-page
3107 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3108 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3109 main_test_rfh()));
3111 // Going back to the non ref url will be considered in-page if the navigation
3112 // type is IN_PAGE.
3113 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3114 main_test_rfh()));
3116 // If the renderer says this is a same-origin in-page navigation, believe it.
3117 // This is the pushState/replaceState case.
3118 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
3119 main_test_rfh()));
3121 // Don't believe the renderer if it claims a cross-origin navigation is
3122 // in-page.
3123 const GURL different_origin_url("http://www.example.com");
3124 MockRenderProcessHost* rph =
3125 static_cast<MockRenderProcessHost*>(main_test_rfh()->GetProcess());
3126 EXPECT_EQ(0, rph->bad_msg_count());
3127 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3128 main_test_rfh()));
3129 EXPECT_EQ(1, rph->bad_msg_count());
3132 // Some pages can have subframes with the same base URL (minus the reference) as
3133 // the main page. Even though this is hard, it can happen, and we don't want
3134 // these subframe navigations to affect the toplevel document. They should
3135 // instead be ignored. http://crbug.com/5585
3136 TEST_F(NavigationControllerTest, SameSubframe) {
3137 NavigationControllerImpl& controller = controller_impl();
3138 // Navigate the main frame.
3139 const GURL url("http://www.google.com/");
3140 main_test_rfh()->SendNavigate(0, url);
3142 // We should be at the first navigation entry.
3143 EXPECT_EQ(controller.GetEntryCount(), 1);
3144 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3146 // Navigate a subframe that would normally count as in-page.
3147 const GURL subframe("http://www.google.com/#");
3148 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3149 params.page_id = 0;
3150 params.url = subframe;
3151 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3152 params.should_update_history = false;
3153 params.gesture = NavigationGestureAuto;
3154 params.is_post = false;
3155 params.page_state = PageState::CreateFromURL(subframe);
3156 LoadCommittedDetails details;
3157 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3158 &details));
3160 // Nothing should have changed.
3161 EXPECT_EQ(controller.GetEntryCount(), 1);
3162 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3165 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3166 // false.
3167 TEST_F(NavigationControllerTest, CloneAndGoBack) {
3168 NavigationControllerImpl& controller = controller_impl();
3169 const GURL url1("http://foo1");
3170 const GURL url2("http://foo2");
3171 const base::string16 title(base::ASCIIToUTF16("Title"));
3173 NavigateAndCommit(url1);
3174 controller.GetVisibleEntry()->SetTitle(title);
3175 NavigateAndCommit(url2);
3177 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3179 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3180 EXPECT_TRUE(clone->GetController().NeedsReload());
3181 clone->GetController().GoBack();
3182 // Navigating back should have triggered needs_reload_ to go false.
3183 EXPECT_FALSE(clone->GetController().NeedsReload());
3185 // Ensure that the pending URL and its title are visible.
3186 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3187 EXPECT_EQ(title, clone->GetTitle());
3190 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3191 // See http://crbug.com/234491.
3192 TEST_F(NavigationControllerTest, CloneAndReload) {
3193 NavigationControllerImpl& controller = controller_impl();
3194 const GURL url1("http://foo1");
3195 const GURL url2("http://foo2");
3196 const base::string16 title(base::ASCIIToUTF16("Title"));
3198 NavigateAndCommit(url1);
3199 controller.GetVisibleEntry()->SetTitle(title);
3200 NavigateAndCommit(url2);
3202 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3203 clone->GetController().LoadIfNecessary();
3205 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3206 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3208 clone->GetController().Reload(true);
3209 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3212 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3213 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3214 NavigationControllerImpl& controller = controller_impl();
3215 const GURL url1("http://foo1");
3216 const GURL url2("http://foo2");
3218 NavigateAndCommit(url1);
3219 NavigateAndCommit(url2);
3221 // Add an interstitial entry. Should be deleted with controller.
3222 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3223 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3224 controller.SetTransientEntry(interstitial_entry);
3226 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3228 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3231 // Test requesting and triggering a lazy reload.
3232 TEST_F(NavigationControllerTest, LazyReload) {
3233 NavigationControllerImpl& controller = controller_impl();
3234 const GURL url("http://foo");
3235 NavigateAndCommit(url);
3236 ASSERT_FALSE(controller.NeedsReload());
3238 // Request a reload to happen when the controller becomes active (e.g. after
3239 // the renderer gets killed in background on Android).
3240 controller.SetNeedsReload();
3241 ASSERT_TRUE(controller.NeedsReload());
3243 // Set the controller as active, triggering the requested reload.
3244 controller.SetActive(true);
3245 ASSERT_FALSE(controller.NeedsReload());
3248 // Tests a subframe navigation while a toplevel navigation is pending.
3249 // http://crbug.com/43967
3250 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3251 NavigationControllerImpl& controller = controller_impl();
3252 // Load the first page.
3253 const GURL url1("http://foo/");
3254 NavigateAndCommit(url1);
3256 // Now start a pending load to a totally different page, but don't commit it.
3257 const GURL url2("http://bar/");
3258 controller.LoadURL(
3259 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3261 // Send a subframe update from the first page, as if one had just
3262 // automatically loaded. Auto subframes don't increment the page ID.
3263 const GURL url1_sub("http://foo/subframe");
3264 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3265 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3266 params.url = url1_sub;
3267 params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3268 params.should_update_history = false;
3269 params.gesture = NavigationGestureAuto;
3270 params.is_post = false;
3271 params.page_state = PageState::CreateFromURL(url1_sub);
3272 LoadCommittedDetails details;
3274 // This should return false meaning that nothing was actually updated.
3275 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3276 &details));
3278 // The notification should have updated the last committed one, and not
3279 // the pending load.
3280 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3282 // The active entry should be unchanged by the subframe load.
3283 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3286 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3287 TEST_F(NavigationControllerTest, CopyStateFrom) {
3288 NavigationControllerImpl& controller = controller_impl();
3289 const GURL url1("http://foo1");
3290 const GURL url2("http://foo2");
3292 NavigateAndCommit(url1);
3293 NavigateAndCommit(url2);
3294 controller.GoBack();
3295 contents()->CommitPendingNavigation();
3297 scoped_ptr<TestWebContents> other_contents(
3298 static_cast<TestWebContents*>(CreateTestWebContents()));
3299 NavigationControllerImpl& other_controller = other_contents->GetController();
3300 other_controller.CopyStateFrom(controller);
3302 // other_controller should now contain 2 urls.
3303 ASSERT_EQ(2, other_controller.GetEntryCount());
3304 // We should be looking at the first one.
3305 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3307 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3308 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3309 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3310 // This is a different site than url1, so the IDs start again at 0.
3311 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3313 // The max page ID map should be copied over and updated with the max page ID
3314 // from the current tab.
3315 SiteInstance* instance1 =
3316 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3317 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3319 // Ensure the SessionStorageNamespaceMaps are the same size and have
3320 // the same partitons loaded.
3322 // TODO(ajwong): We should load a url from a different partition earlier
3323 // to make sure this map has more than one entry.
3324 const SessionStorageNamespaceMap& session_storage_namespace_map =
3325 controller.GetSessionStorageNamespaceMap();
3326 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3327 other_controller.GetSessionStorageNamespaceMap();
3328 EXPECT_EQ(session_storage_namespace_map.size(),
3329 other_session_storage_namespace_map.size());
3330 for (SessionStorageNamespaceMap::const_iterator it =
3331 session_storage_namespace_map.begin();
3332 it != session_storage_namespace_map.end();
3333 ++it) {
3334 SessionStorageNamespaceMap::const_iterator other =
3335 other_session_storage_namespace_map.find(it->first);
3336 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3340 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3341 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3342 NavigationControllerImpl& controller = controller_impl();
3343 const GURL url1("http://foo/1");
3344 const GURL url2("http://foo/2");
3345 const GURL url3("http://foo/3");
3347 NavigateAndCommit(url1);
3348 NavigateAndCommit(url2);
3350 // First two entries should have the same SiteInstance.
3351 SiteInstance* instance1 =
3352 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3353 SiteInstance* instance2 =
3354 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3355 EXPECT_EQ(instance1, instance2);
3356 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3357 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3358 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3360 scoped_ptr<TestWebContents> other_contents(
3361 static_cast<TestWebContents*>(CreateTestWebContents()));
3362 NavigationControllerImpl& other_controller = other_contents->GetController();
3363 other_contents->NavigateAndCommit(url3);
3364 other_contents->ExpectSetHistoryLengthAndPrune(
3365 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3366 other_controller.GetEntryAtIndex(0)->GetPageID());
3367 other_controller.CopyStateFromAndPrune(&controller, false);
3369 // other_controller should now contain the 3 urls: url1, url2 and url3.
3371 ASSERT_EQ(3, other_controller.GetEntryCount());
3373 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3375 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3376 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3377 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3378 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3379 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3380 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3382 // A new SiteInstance in a different BrowsingInstance should be used for the
3383 // new tab.
3384 SiteInstance* instance3 =
3385 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3386 EXPECT_NE(instance3, instance1);
3387 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3389 // The max page ID map should be copied over and updated with the max page ID
3390 // from the current tab.
3391 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3392 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3395 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3396 // the target.
3397 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3398 NavigationControllerImpl& controller = controller_impl();
3399 const GURL url1("http://foo1");
3400 const GURL url2("http://foo2");
3401 const GURL url3("http://foo3");
3403 NavigateAndCommit(url1);
3404 NavigateAndCommit(url2);
3405 controller.GoBack();
3406 contents()->CommitPendingNavigation();
3408 scoped_ptr<TestWebContents> other_contents(
3409 static_cast<TestWebContents*>(CreateTestWebContents()));
3410 NavigationControllerImpl& other_controller = other_contents->GetController();
3411 other_contents->NavigateAndCommit(url3);
3412 other_contents->ExpectSetHistoryLengthAndPrune(
3413 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3414 other_controller.GetEntryAtIndex(0)->GetPageID());
3415 other_controller.CopyStateFromAndPrune(&controller, false);
3417 // other_controller should now contain: url1, url3
3419 ASSERT_EQ(2, other_controller.GetEntryCount());
3420 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3422 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3423 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3424 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3426 // The max page ID map should be copied over and updated with the max page ID
3427 // from the current tab.
3428 SiteInstance* instance1 =
3429 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3430 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3433 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3434 // the target.
3435 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3436 NavigationControllerImpl& controller = controller_impl();
3437 const GURL url1("http://foo1");
3438 const GURL url2("http://foo2");
3439 const GURL url3("http://foo3");
3440 const GURL url4("http://foo4");
3442 NavigateAndCommit(url1);
3443 NavigateAndCommit(url2);
3445 scoped_ptr<TestWebContents> other_contents(
3446 static_cast<TestWebContents*>(CreateTestWebContents()));
3447 NavigationControllerImpl& other_controller = other_contents->GetController();
3448 other_contents->NavigateAndCommit(url3);
3449 other_contents->NavigateAndCommit(url4);
3450 other_contents->ExpectSetHistoryLengthAndPrune(
3451 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)), 2,
3452 other_controller.GetEntryAtIndex(0)->GetPageID());
3453 other_controller.CopyStateFromAndPrune(&controller, false);
3455 // other_controller should now contain: url1, url2, url4
3457 ASSERT_EQ(3, other_controller.GetEntryCount());
3458 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3460 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3461 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3462 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3464 // The max page ID map should be copied over and updated with the max page ID
3465 // from the current tab.
3466 SiteInstance* instance1 =
3467 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3468 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3471 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3472 // not the last entry selected in the target.
3473 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3474 NavigationControllerImpl& controller = controller_impl();
3475 const GURL url1("http://foo1");
3476 const GURL url2("http://foo2");
3477 const GURL url3("http://foo3");
3478 const GURL url4("http://foo4");
3480 NavigateAndCommit(url1);
3481 NavigateAndCommit(url2);
3483 scoped_ptr<TestWebContents> other_contents(
3484 static_cast<TestWebContents*>(CreateTestWebContents()));
3485 NavigationControllerImpl& other_controller = other_contents->GetController();
3486 other_contents->NavigateAndCommit(url3);
3487 other_contents->NavigateAndCommit(url4);
3488 other_controller.GoBack();
3489 other_contents->CommitPendingNavigation();
3490 other_contents->ExpectSetHistoryLengthAndPrune(
3491 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3492 other_controller.GetEntryAtIndex(0)->GetPageID());
3493 other_controller.CopyStateFromAndPrune(&controller, false);
3495 // other_controller should now contain: url1, url2, url3
3497 ASSERT_EQ(3, other_controller.GetEntryCount());
3498 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3500 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3501 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3502 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3504 // The max page ID map should be copied over and updated with the max page ID
3505 // from the current tab.
3506 SiteInstance* instance1 =
3507 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3508 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3511 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3512 // a pending entry in the target.
3513 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3514 NavigationControllerImpl& controller = controller_impl();
3515 const GURL url1("http://foo1");
3516 const GURL url2("http://foo2");
3517 const GURL url3("http://foo3");
3518 const GURL url4("http://foo4");
3520 NavigateAndCommit(url1);
3521 NavigateAndCommit(url2);
3522 controller.GoBack();
3523 contents()->CommitPendingNavigation();
3525 scoped_ptr<TestWebContents> other_contents(
3526 static_cast<TestWebContents*>(CreateTestWebContents()));
3527 NavigationControllerImpl& other_controller = other_contents->GetController();
3528 other_contents->NavigateAndCommit(url3);
3529 other_controller.LoadURL(
3530 url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3531 other_contents->ExpectSetHistoryLengthAndPrune(
3532 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3533 other_controller.GetEntryAtIndex(0)->GetPageID());
3534 other_controller.CopyStateFromAndPrune(&controller, false);
3536 // other_controller should now contain url1, url3, and a pending entry
3537 // for url4.
3539 ASSERT_EQ(2, other_controller.GetEntryCount());
3540 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3542 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3543 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3545 // And there should be a pending entry for url4.
3546 ASSERT_TRUE(other_controller.GetPendingEntry());
3547 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3549 // The max page ID map should be copied over and updated with the max page ID
3550 // from the current tab.
3551 SiteInstance* instance1 =
3552 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3553 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3556 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3557 // client redirect entry (with the same page ID) in the target. This used to
3558 // crash because the last committed entry would be pruned but max_page_id
3559 // remembered the page ID (http://crbug.com/234809).
3560 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3561 NavigationControllerImpl& controller = controller_impl();
3562 const GURL url1("http://foo1");
3563 const GURL url2a("http://foo2/a");
3564 const GURL url2b("http://foo2/b");
3566 NavigateAndCommit(url1);
3568 scoped_ptr<TestWebContents> other_contents(
3569 static_cast<TestWebContents*>(CreateTestWebContents()));
3570 NavigationControllerImpl& other_controller = other_contents->GetController();
3571 other_contents->NavigateAndCommit(url2a);
3572 // Simulate a client redirect, which has the same page ID as entry 2a.
3573 other_controller.LoadURL(
3574 url2b, Referrer(), PAGE_TRANSITION_LINK, std::string());
3575 other_controller.GetPendingEntry()->SetPageID(
3576 other_controller.GetLastCommittedEntry()->GetPageID());
3578 other_contents->ExpectSetHistoryLengthAndPrune(
3579 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3580 other_controller.GetEntryAtIndex(0)->GetPageID());
3581 other_controller.CopyStateFromAndPrune(&controller, false);
3583 // other_controller should now contain url1, url2a, and a pending entry
3584 // for url2b.
3586 ASSERT_EQ(2, other_controller.GetEntryCount());
3587 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3589 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3590 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3592 // And there should be a pending entry for url4.
3593 ASSERT_TRUE(other_controller.GetPendingEntry());
3594 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3596 // Let the pending entry commit.
3597 other_contents->CommitPendingNavigation();
3599 // The max page ID map should be copied over and updated with the max page ID
3600 // from the current tab.
3601 SiteInstance* instance1 =
3602 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3603 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3606 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3607 // source, and 1 entry in the target. The back pending entry should be ignored.
3608 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3609 NavigationControllerImpl& controller = controller_impl();
3610 const GURL url1("http://foo1");
3611 const GURL url2("http://foo2");
3612 const GURL url3("http://foo3");
3614 NavigateAndCommit(url1);
3615 NavigateAndCommit(url2);
3616 controller.GoBack();
3618 scoped_ptr<TestWebContents> other_contents(
3619 static_cast<TestWebContents*>(CreateTestWebContents()));
3620 NavigationControllerImpl& other_controller = other_contents->GetController();
3621 other_contents->NavigateAndCommit(url3);
3622 other_contents->ExpectSetHistoryLengthAndPrune(
3623 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3624 other_controller.GetEntryAtIndex(0)->GetPageID());
3625 other_controller.CopyStateFromAndPrune(&controller, false);
3627 // other_controller should now contain: url1, url2, url3
3629 ASSERT_EQ(3, other_controller.GetEntryCount());
3630 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3632 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3633 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3634 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3635 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3637 // The max page ID map should be copied over and updated with the max page ID
3638 // from the current tab.
3639 SiteInstance* instance1 =
3640 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3641 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3644 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3645 // when the max entry count is 3. We should prune one entry.
3646 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3647 NavigationControllerImpl& controller = controller_impl();
3648 size_t original_count = NavigationControllerImpl::max_entry_count();
3649 const int kMaxEntryCount = 3;
3651 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3653 const GURL url1("http://foo/1");
3654 const GURL url2("http://foo/2");
3655 const GURL url3("http://foo/3");
3656 const GURL url4("http://foo/4");
3658 // Create a PrunedListener to observe prune notifications.
3659 PrunedListener listener(&controller);
3661 NavigateAndCommit(url1);
3662 NavigateAndCommit(url2);
3663 NavigateAndCommit(url3);
3665 scoped_ptr<TestWebContents> other_contents(
3666 static_cast<TestWebContents*>(CreateTestWebContents()));
3667 NavigationControllerImpl& other_controller = other_contents->GetController();
3668 other_contents->NavigateAndCommit(url4);
3669 other_contents->ExpectSetHistoryLengthAndPrune(
3670 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3671 other_controller.GetEntryAtIndex(0)->GetPageID());
3672 other_controller.CopyStateFromAndPrune(&controller, false);
3674 // We should have received a pruned notification.
3675 EXPECT_EQ(1, listener.notification_count_);
3676 EXPECT_TRUE(listener.details_.from_front);
3677 EXPECT_EQ(1, listener.details_.count);
3679 // other_controller should now contain only 3 urls: url2, url3 and url4.
3681 ASSERT_EQ(3, other_controller.GetEntryCount());
3683 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3685 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3686 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3687 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3688 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3689 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3690 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3692 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3695 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3696 // replace_entry set to true.
3697 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3698 NavigationControllerImpl& controller = controller_impl();
3699 const GURL url1("http://foo/1");
3700 const GURL url2("http://foo/2");
3701 const GURL url3("http://foo/3");
3703 NavigateAndCommit(url1);
3704 NavigateAndCommit(url2);
3706 // First two entries should have the same SiteInstance.
3707 SiteInstance* instance1 =
3708 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3709 SiteInstance* instance2 =
3710 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3711 EXPECT_EQ(instance1, instance2);
3712 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3713 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3714 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3716 scoped_ptr<TestWebContents> other_contents(
3717 static_cast<TestWebContents*>(CreateTestWebContents()));
3718 NavigationControllerImpl& other_controller = other_contents->GetController();
3719 other_contents->NavigateAndCommit(url3);
3720 other_contents->ExpectSetHistoryLengthAndPrune(
3721 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3722 other_controller.GetEntryAtIndex(0)->GetPageID());
3723 other_controller.CopyStateFromAndPrune(&controller, true);
3725 // other_controller should now contain the 2 urls: url1 and url3.
3727 ASSERT_EQ(2, other_controller.GetEntryCount());
3729 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3731 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3732 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3733 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3734 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3736 // A new SiteInstance in a different BrowsingInstance should be used for the
3737 // new tab.
3738 SiteInstance* instance3 =
3739 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3740 EXPECT_NE(instance3, instance1);
3741 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3743 // The max page ID map should be copied over and updated with the max page ID
3744 // from the current tab.
3745 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3746 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3749 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3750 // entry count is 3 and replace_entry is true. We should not prune entries.
3751 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3752 NavigationControllerImpl& controller = controller_impl();
3753 size_t original_count = NavigationControllerImpl::max_entry_count();
3754 const int kMaxEntryCount = 3;
3756 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3758 const GURL url1("http://foo/1");
3759 const GURL url2("http://foo/2");
3760 const GURL url3("http://foo/3");
3761 const GURL url4("http://foo/4");
3763 // Create a PrunedListener to observe prune notifications.
3764 PrunedListener listener(&controller);
3766 NavigateAndCommit(url1);
3767 NavigateAndCommit(url2);
3768 NavigateAndCommit(url3);
3770 scoped_ptr<TestWebContents> other_contents(
3771 static_cast<TestWebContents*>(CreateTestWebContents()));
3772 NavigationControllerImpl& other_controller = other_contents->GetController();
3773 other_contents->NavigateAndCommit(url4);
3774 other_contents->ExpectSetHistoryLengthAndPrune(
3775 GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3776 other_controller.GetEntryAtIndex(0)->GetPageID());
3777 other_controller.CopyStateFromAndPrune(&controller, true);
3779 // We should have received no pruned notification.
3780 EXPECT_EQ(0, listener.notification_count_);
3782 // other_controller should now contain only 3 urls: url1, url2 and url4.
3784 ASSERT_EQ(3, other_controller.GetEntryCount());
3786 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3788 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3789 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3790 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3791 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3792 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3793 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3795 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3798 // Tests that we can navigate to the restored entries
3799 // imported by CopyStateFromAndPrune.
3800 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
3801 const GURL kRestoredUrls[] = {
3802 GURL("http://site1.com"),
3803 GURL("http://site2.com"),
3805 const GURL kInitialUrl("http://site3.com");
3807 std::vector<NavigationEntry*> entries;
3808 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
3809 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
3810 kRestoredUrls[i], Referrer(), PAGE_TRANSITION_RELOAD, false,
3811 std::string(), browser_context());
3812 entry->SetPageID(static_cast<int>(i));
3813 entries.push_back(entry);
3816 // Create a WebContents with restored entries.
3817 scoped_ptr<TestWebContents> source_contents(
3818 static_cast<TestWebContents*>(CreateTestWebContents()));
3819 NavigationControllerImpl& source_controller =
3820 source_contents->GetController();
3821 source_controller.Restore(
3822 entries.size() - 1,
3823 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
3824 &entries);
3825 ASSERT_EQ(0u, entries.size());
3826 source_controller.LoadIfNecessary();
3827 source_contents->CommitPendingNavigation();
3829 // Load a page, then copy state from |source_contents|.
3830 NavigateAndCommit(kInitialUrl);
3831 contents()->ExpectSetHistoryLengthAndPrune(
3832 GetSiteInstanceFromEntry(controller_impl().GetEntryAtIndex(0)), 2,
3833 controller_impl().GetEntryAtIndex(0)->GetPageID());
3834 controller_impl().CopyStateFromAndPrune(&source_controller, false);
3835 ASSERT_EQ(3, controller_impl().GetEntryCount());
3837 // Go back to the first entry one at a time and
3838 // verify that it works as expected.
3839 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
3840 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
3842 controller_impl().GoBack();
3843 contents()->CommitPendingNavigation();
3844 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
3845 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
3847 controller_impl().GoBack();
3848 contents()->CommitPendingNavigation();
3849 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
3850 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
3853 // Tests that navigations initiated from the page (with the history object)
3854 // work as expected, creating pending entries.
3855 TEST_F(NavigationControllerTest, HistoryNavigate) {
3856 NavigationControllerImpl& controller = controller_impl();
3857 const GURL url1("http://foo/1");
3858 const GURL url2("http://foo/2");
3859 const GURL url3("http://foo/3");
3861 NavigateAndCommit(url1);
3862 NavigateAndCommit(url2);
3863 NavigateAndCommit(url3);
3864 controller.GoBack();
3865 contents()->CommitPendingNavigation();
3867 // Simulate the page calling history.back(). It should create a pending entry.
3868 contents()->OnGoToEntryAtOffset(-1);
3869 EXPECT_EQ(0, controller.GetPendingEntryIndex());
3870 // The actual cross-navigation is suspended until the current RVH tells us
3871 // it unloaded, simulate that.
3872 contents()->ProceedWithCrossSiteNavigation();
3873 // Also make sure we told the page to navigate.
3874 const IPC::Message* message =
3875 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3876 ASSERT_TRUE(message != NULL);
3877 Tuple1<FrameMsg_Navigate_Params> nav_params;
3878 FrameMsg_Navigate::Read(message, &nav_params);
3879 EXPECT_EQ(url1, nav_params.a.url);
3880 process()->sink().ClearMessages();
3882 // Now test history.forward()
3883 contents()->OnGoToEntryAtOffset(2);
3884 EXPECT_EQ(2, controller.GetPendingEntryIndex());
3885 // The actual cross-navigation is suspended until the current RVH tells us
3886 // it unloaded, simulate that.
3887 contents()->ProceedWithCrossSiteNavigation();
3888 message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3889 ASSERT_TRUE(message != NULL);
3890 FrameMsg_Navigate::Read(message, &nav_params);
3891 EXPECT_EQ(url3, nav_params.a.url);
3892 process()->sink().ClearMessages();
3894 controller.DiscardNonCommittedEntries();
3896 // Make sure an extravagant history.go() doesn't break.
3897 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
3898 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3899 message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3900 EXPECT_TRUE(message == NULL);
3903 // Test call to PruneAllButLastCommitted for the only entry.
3904 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
3905 NavigationControllerImpl& controller = controller_impl();
3906 const GURL url1("http://foo1");
3907 NavigateAndCommit(url1);
3909 contents()->ExpectSetHistoryLengthAndPrune(
3910 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3911 controller.GetEntryAtIndex(0)->GetPageID());
3913 controller.PruneAllButLastCommitted();
3915 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3916 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3919 // Test call to PruneAllButLastCommitted for first entry.
3920 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
3921 NavigationControllerImpl& controller = controller_impl();
3922 const GURL url1("http://foo/1");
3923 const GURL url2("http://foo/2");
3924 const GURL url3("http://foo/3");
3926 NavigateAndCommit(url1);
3927 NavigateAndCommit(url2);
3928 NavigateAndCommit(url3);
3929 controller.GoBack();
3930 controller.GoBack();
3931 contents()->CommitPendingNavigation();
3933 contents()->ExpectSetHistoryLengthAndPrune(
3934 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3935 controller.GetEntryAtIndex(0)->GetPageID());
3937 controller.PruneAllButLastCommitted();
3939 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3940 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3943 // Test call to PruneAllButLastCommitted for intermediate entry.
3944 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
3945 NavigationControllerImpl& controller = controller_impl();
3946 const GURL url1("http://foo/1");
3947 const GURL url2("http://foo/2");
3948 const GURL url3("http://foo/3");
3950 NavigateAndCommit(url1);
3951 NavigateAndCommit(url2);
3952 NavigateAndCommit(url3);
3953 controller.GoBack();
3954 contents()->CommitPendingNavigation();
3956 contents()->ExpectSetHistoryLengthAndPrune(
3957 GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)), 0,
3958 controller.GetEntryAtIndex(1)->GetPageID());
3960 controller.PruneAllButLastCommitted();
3962 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3963 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
3966 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
3967 // the list of entries.
3968 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
3969 NavigationControllerImpl& controller = controller_impl();
3970 const GURL url1("http://foo/1");
3971 const GURL url2("http://foo/2");
3972 const GURL url3("http://foo/3");
3974 NavigateAndCommit(url1);
3975 NavigateAndCommit(url2);
3977 // Create a pending entry that is not in the entry list.
3978 controller.LoadURL(
3979 url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3980 EXPECT_TRUE(controller.GetPendingEntry());
3981 EXPECT_EQ(2, controller.GetEntryCount());
3983 contents()->ExpectSetHistoryLengthAndPrune(
3984 NULL, 0, controller.GetPendingEntry()->GetPageID());
3985 controller.PruneAllButLastCommitted();
3987 // We should only have the last committed and pending entries at this point,
3988 // and the pending entry should still not be in the entry list.
3989 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
3990 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
3991 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3992 EXPECT_TRUE(controller.GetPendingEntry());
3993 EXPECT_EQ(1, controller.GetEntryCount());
3995 // Try to commit the pending entry.
3996 main_test_rfh()->SendNavigate(2, url3);
3997 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3998 EXPECT_FALSE(controller.GetPendingEntry());
3999 EXPECT_EQ(2, controller.GetEntryCount());
4000 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
4003 // Test to ensure that when we do a history navigation back to the current
4004 // committed page (e.g., going forward to a slow-loading page, then pressing
4005 // the back button), we just stop the navigation to prevent the throbber from
4006 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4007 // start, but WebKit essentially ignores the navigation and never sends a
4008 // message to stop the throbber.
4009 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
4010 NavigationControllerImpl& controller = controller_impl();
4011 const GURL url0("http://foo/0");
4012 const GURL url1("http://foo/1");
4014 NavigateAndCommit(url0);
4015 NavigateAndCommit(url1);
4017 // Go back to the original page, then forward to the slow page, then back
4018 controller.GoBack();
4019 contents()->CommitPendingNavigation();
4021 controller.GoForward();
4022 EXPECT_EQ(1, controller.GetPendingEntryIndex());
4024 controller.GoBack();
4025 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4028 TEST_F(NavigationControllerTest, IsInitialNavigation) {
4029 NavigationControllerImpl& controller = controller_impl();
4030 TestNotificationTracker notifications;
4031 RegisterForAllNavNotifications(&notifications, &controller);
4033 // Initial state.
4034 EXPECT_TRUE(controller.IsInitialNavigation());
4036 // After commit, it stays false.
4037 const GURL url1("http://foo1");
4038 main_test_rfh()->SendNavigate(0, url1);
4039 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4040 navigation_entry_committed_counter_ = 0;
4041 EXPECT_FALSE(controller.IsInitialNavigation());
4043 // After starting a new navigation, it stays false.
4044 const GURL url2("http://foo2");
4045 controller.LoadURL(
4046 url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
4049 // Check that the favicon is not reused across a client redirect.
4050 // (crbug.com/28515)
4051 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4052 const GURL kPageWithFavicon("http://withfavicon.html");
4053 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4054 const GURL kIconURL("http://withfavicon.ico");
4055 const gfx::Image kDefaultFavicon = FaviconStatus().image;
4057 NavigationControllerImpl& controller = controller_impl();
4058 TestNotificationTracker notifications;
4059 RegisterForAllNavNotifications(&notifications, &controller);
4061 main_test_rfh()->SendNavigate(0, kPageWithFavicon);
4062 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4063 navigation_entry_committed_counter_ = 0;
4065 NavigationEntry* entry = controller.GetLastCommittedEntry();
4066 EXPECT_TRUE(entry);
4067 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4069 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4070 content::FaviconStatus& favicon_status = entry->GetFavicon();
4071 favicon_status.image = CreateImage(SK_ColorWHITE);
4072 favicon_status.url = kIconURL;
4073 favicon_status.valid = true;
4074 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4076 main_test_rfh()->SendNavigateWithTransition(
4077 0, // same page ID.
4078 kPageWithoutFavicon,
4079 PAGE_TRANSITION_CLIENT_REDIRECT);
4080 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4081 navigation_entry_committed_counter_ = 0;
4083 entry = controller.GetLastCommittedEntry();
4084 EXPECT_TRUE(entry);
4085 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4087 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4090 // Check that the favicon is not cleared for NavigationEntries which were
4091 // previously navigated to.
4092 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4093 const GURL kUrl1("http://www.a.com/1");
4094 const GURL kUrl2("http://www.a.com/2");
4095 const GURL kIconURL("http://www.a.com/1/favicon.ico");
4097 NavigationControllerImpl& controller = controller_impl();
4098 TestNotificationTracker notifications;
4099 RegisterForAllNavNotifications(&notifications, &controller);
4101 main_test_rfh()->SendNavigate(0, kUrl1);
4102 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4103 navigation_entry_committed_counter_ = 0;
4105 // Simulate Chromium having set the favicon for |kUrl1|.
4106 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4107 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4108 EXPECT_TRUE(entry);
4109 content::FaviconStatus& favicon_status = entry->GetFavicon();
4110 favicon_status.image = favicon_image;
4111 favicon_status.url = kIconURL;
4112 favicon_status.valid = true;
4114 // Navigate to another page and go back to the original page.
4115 main_test_rfh()->SendNavigate(1, kUrl2);
4116 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4117 navigation_entry_committed_counter_ = 0;
4118 main_test_rfh()->SendNavigateWithTransition(
4120 kUrl1,
4121 PAGE_TRANSITION_FORWARD_BACK);
4122 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4123 navigation_entry_committed_counter_ = 0;
4125 // Verify that the favicon for the page at |kUrl1| was not cleared.
4126 entry = controller.GetEntryAtIndex(0);
4127 EXPECT_TRUE(entry);
4128 EXPECT_EQ(kUrl1, entry->GetURL());
4129 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4132 // The test crashes on android: http://crbug.com/170449
4133 #if defined(OS_ANDROID)
4134 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4135 #else
4136 #define MAYBE_PurgeScreenshot PurgeScreenshot
4137 #endif
4138 // Tests that screenshot are purged correctly.
4139 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4140 NavigationControllerImpl& controller = controller_impl();
4142 NavigationEntryImpl* entry;
4144 // Navigate enough times to make sure that some screenshots are purged.
4145 for (int i = 0; i < 12; ++i) {
4146 const GURL url(base::StringPrintf("http://foo%d/", i));
4147 NavigateAndCommit(url);
4148 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4151 MockScreenshotManager* screenshot_manager =
4152 new MockScreenshotManager(&controller);
4153 controller.SetScreenshotManager(screenshot_manager);
4154 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4155 entry = NavigationEntryImpl::FromNavigationEntry(
4156 controller.GetEntryAtIndex(i));
4157 screenshot_manager->TakeScreenshotFor(entry);
4158 EXPECT_TRUE(entry->screenshot().get());
4161 NavigateAndCommit(GURL("https://foo/"));
4162 EXPECT_EQ(13, controller.GetEntryCount());
4163 entry = NavigationEntryImpl::FromNavigationEntry(
4164 controller.GetEntryAtIndex(11));
4165 screenshot_manager->TakeScreenshotFor(entry);
4167 for (int i = 0; i < 2; ++i) {
4168 entry = NavigationEntryImpl::FromNavigationEntry(
4169 controller.GetEntryAtIndex(i));
4170 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4171 << " not purged";
4174 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4175 entry = NavigationEntryImpl::FromNavigationEntry(
4176 controller.GetEntryAtIndex(i));
4177 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4180 // Navigate to index 5 and then try to assign screenshot to all entries.
4181 controller.GoToIndex(5);
4182 contents()->CommitPendingNavigation();
4183 EXPECT_EQ(5, controller.GetCurrentEntryIndex());
4184 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4185 entry = NavigationEntryImpl::FromNavigationEntry(
4186 controller.GetEntryAtIndex(i));
4187 screenshot_manager->TakeScreenshotFor(entry);
4190 for (int i = 10; i <= 12; ++i) {
4191 entry = NavigationEntryImpl::FromNavigationEntry(
4192 controller.GetEntryAtIndex(i));
4193 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4194 << " not purged";
4195 screenshot_manager->TakeScreenshotFor(entry);
4198 // Navigate to index 7 and assign screenshot to all entries.
4199 controller.GoToIndex(7);
4200 contents()->CommitPendingNavigation();
4201 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4202 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4203 entry = NavigationEntryImpl::FromNavigationEntry(
4204 controller.GetEntryAtIndex(i));
4205 screenshot_manager->TakeScreenshotFor(entry);
4208 for (int i = 0; i < 2; ++i) {
4209 entry = NavigationEntryImpl::FromNavigationEntry(
4210 controller.GetEntryAtIndex(i));
4211 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4212 << " not purged";
4215 // Clear all screenshots.
4216 EXPECT_EQ(13, controller.GetEntryCount());
4217 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4218 controller.ClearAllScreenshots();
4219 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4220 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4221 entry = NavigationEntryImpl::FromNavigationEntry(
4222 controller.GetEntryAtIndex(i));
4223 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4224 << " not cleared";
4228 TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) {
4229 // Navigate.
4230 test_rvh()->SendNavigate(1, GURL("http://foo"));
4232 // Set title and favicon.
4233 base::string16 title(base::ASCIIToUTF16("Title"));
4234 FaviconStatus favicon;
4235 favicon.valid = true;
4236 favicon.url = GURL("http://foo/favicon.ico");
4237 controller().GetLastCommittedEntry()->SetTitle(title);
4238 controller().GetLastCommittedEntry()->GetFavicon() = favicon;
4240 // history.pushState() is called.
4241 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4242 GURL url("http://foo#foo");
4243 params.page_id = 2;
4244 params.url = url;
4245 params.page_state = PageState::CreateFromURL(url);
4246 params.was_within_same_page = true;
4247 test_rvh()->SendNavigateWithParams(&params);
4249 // The title should immediately be visible on the new NavigationEntry.
4250 base::string16 new_title =
4251 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4252 EXPECT_EQ(title, new_title);
4253 FaviconStatus new_favicon =
4254 controller().GetLastCommittedEntry()->GetFavicon();
4255 EXPECT_EQ(favicon.valid, new_favicon.valid);
4256 EXPECT_EQ(favicon.url, new_favicon.url);
4259 // Test that the navigation controller clears its session history when a
4260 // navigation commits with the clear history list flag set.
4261 TEST_F(NavigationControllerTest, ClearHistoryList) {
4262 const GURL url1("http://foo1");
4263 const GURL url2("http://foo2");
4264 const GURL url3("http://foo3");
4265 const GURL url4("http://foo4");
4267 NavigationControllerImpl& controller = controller_impl();
4269 // Create a session history with three entries, second entry is active.
4270 NavigateAndCommit(url1);
4271 NavigateAndCommit(url2);
4272 NavigateAndCommit(url3);
4273 controller.GoBack();
4274 contents()->CommitPendingNavigation();
4275 EXPECT_EQ(3, controller.GetEntryCount());
4276 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4278 // Create a new pending navigation, and indicate that the session history
4279 // should be cleared.
4280 NavigationController::LoadURLParams params(url4);
4281 params.should_clear_history_list = true;
4282 controller.LoadURLWithParams(params);
4284 // Verify that the pending entry correctly indicates that the session history
4285 // should be cleared.
4286 NavigationEntryImpl* entry =
4287 NavigationEntryImpl::FromNavigationEntry(
4288 controller.GetPendingEntry());
4289 ASSERT_TRUE(entry);
4290 EXPECT_TRUE(entry->should_clear_history_list());
4292 // Assume that the RV correctly cleared its history and commit the navigation.
4293 contents()->GetPendingMainFrame()->GetRenderViewHost()->
4294 set_simulate_history_list_was_cleared(true);
4295 contents()->CommitPendingNavigation();
4297 // Verify that the NavigationController's session history was correctly
4298 // cleared.
4299 EXPECT_EQ(1, controller.GetEntryCount());
4300 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4301 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4302 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4303 EXPECT_FALSE(controller.CanGoBack());
4304 EXPECT_FALSE(controller.CanGoForward());
4305 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4308 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
4309 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
4310 EXPECT_FALSE(contents()->GetDelegate());
4311 contents()->SetDelegate(delegate.get());
4313 // Submit a form.
4314 GURL url("http://foo");
4315 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4316 params.page_id = 1;
4317 params.url = url;
4318 params.transition = PAGE_TRANSITION_FORM_SUBMIT;
4319 params.gesture = NavigationGestureUser;
4320 params.page_state = PageState::CreateFromURL(url);
4321 params.was_within_same_page = false;
4322 params.is_post = true;
4323 params.post_id = 2;
4324 test_rvh()->SendNavigateWithParams(&params);
4326 // history.replaceState() is called.
4327 GURL replace_url("http://foo#foo");
4328 params.page_id = 1;
4329 params.url = replace_url;
4330 params.transition = PAGE_TRANSITION_LINK;
4331 params.gesture = NavigationGestureUser;
4332 params.page_state = PageState::CreateFromURL(replace_url);
4333 params.was_within_same_page = true;
4334 params.is_post = false;
4335 params.post_id = -1;
4336 test_rvh()->SendNavigateWithParams(&params);
4338 // Now reload. replaceState overrides the POST, so we should not show a
4339 // repost warning dialog.
4340 controller_impl().Reload(true);
4341 EXPECT_EQ(0, delegate->repost_form_warning_count());
4344 } // namespace content