1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
7 #include "base/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/path_service.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/cross_site_transferring_request.h"
15 #include "content/browser/frame_host/navigation_controller_impl.h"
16 #include "content/browser/frame_host/navigation_entry_impl.h"
17 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
18 #include "content/browser/frame_host/navigator.h"
19 #include "content/browser/site_instance_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/frame_messages.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/navigation_details.h"
24 #include "content/public/browser/notification_registrar.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents_delegate.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/common/page_state.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/test/mock_render_process_host.h"
32 #include "content/public/test/test_notification_tracker.h"
33 #include "content/public/test/test_utils.h"
34 #include "content/test/test_render_frame_host.h"
35 #include "content/test/test_render_view_host.h"
36 #include "content/test/test_web_contents.h"
37 #include "net/base/net_util.h"
38 #include "skia/ext/platform_canvas.h"
39 #include "testing/gtest/include/gtest/gtest.h"
45 // Creates an image with a 1x1 SkBitmap of the specified |color|.
46 gfx::Image
CreateImage(SkColor color
) {
48 bitmap
.allocN32Pixels(1, 1);
49 bitmap
.eraseColor(color
);
50 return gfx::Image::CreateFrom1xBitmap(bitmap
);
53 // Returns true if images |a| and |b| have the same pixel data.
54 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
55 // Assume that if the 1x bitmaps match, the images match.
56 SkBitmap a_bitmap
= a
.AsBitmap();
57 SkBitmap b_bitmap
= b
.AsBitmap();
59 if (a_bitmap
.width() != b_bitmap
.width() ||
60 a_bitmap
.height() != b_bitmap
.height()) {
63 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
64 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
65 return memcmp(a_bitmap
.getPixels(),
67 a_bitmap
.getSize()) == 0;
70 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
72 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
73 : content::NavigationEntryScreenshotManager(owner
),
74 encoding_screenshot_in_progress_(false) {
77 virtual ~MockScreenshotManager() {
80 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
82 bitmap
.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_
)
97 message_loop_runner_
= new content::MessageLoopRunner
;
98 message_loop_runner_
->Run();
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
);
125 // TimeSmoother tests ----------------------------------------------------------
127 // With no duplicates, GetSmoothedTime should be the identity
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
{
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(
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());
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
{
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_
++;
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(¬ifications
, &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.
315 GO_TO_MIDDLE_PAGE
= -2,
318 GO_TO_BEGINNING
= -2,
323 const int test_offsets
[NUM_TESTS
] = {
331 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
332 int offset
= test_offsets
[test
];
333 controller
.GoToOffset(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(¬ifications
, &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());
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
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());
440 base::Time
GetFixedTime(base::Time time
) {
446 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
447 NavigationControllerImpl
& controller
= controller_impl();
448 TestNotificationTracker notifications
;
449 RegisterForAllNavNotifications(¬ifications
, &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;
465 controller
.LoadURL(url2
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
467 // Simulate the beforeunload ack for the cross-site transition, and then the
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
480 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
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.
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(¬ifications
, &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(¬ifications
, &controller
);
631 const GURL
url1("http://foo1");
633 controller
.LoadURL(url1
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
634 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
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(¶ms
);
643 // The post data should be visible.
644 NavigationEntry
* entry
= controller
.GetVisibleEntry();
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(¬ifications
, &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(¬ifications
, &controller
);
704 // First make an existing committed entry.
705 const GURL
kExistingURL1("http://eh");
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
729 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
730 NavigationControllerImpl
& controller
= controller_impl();
731 TestNotificationTracker notifications
;
732 RegisterForAllNavNotifications(¬ifications
, &controller
);
734 // First make an existing committed entry.
735 const GURL
kExistingURL1("http://eh");
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");
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(¬ifications
, &controller
);
770 // First make some history.
771 const GURL
kExistingURL1("http://foo/eh");
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");
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
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(¬ifications
, &controller
);
814 // First make some history, starting with a privileged URL.
815 const GURL
kExistingURL1("http://privileged");
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");
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.
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(¬ifications
, &controller
);
868 // First make some history.
869 const GURL
kExistingURL1("http://foo/eh");
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");
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");
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(¬ifications
, &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");
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(¬ifications
, &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");
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
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(¬ifications
, &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
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
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(¬ifications
, &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());
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
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(¬ifications
, &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(¬ifications
, &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);
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(¬ifications
, &controller
);
1243 const GURL
original_url("http://foo1");
1244 const GURL
final_url("http://foo2");
1246 // Load up the original URL, but get redirected.
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
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
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
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(¬ifications
, &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(¬ifications
, &controller
);
1435 const GURL
url1("http://foo/1");
1436 const GURL
url2("http://foo/2");
1437 const GURL
url3("http://foo/3");
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(¬ifications
, &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
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
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
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(¬ifications
, &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(¬ifications
, &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(¬ifications
, &controller
);
1677 const GURL
url1("http://foo1");
1678 const GURL
url2("http://foo2"); // Redirection target
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;
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
;
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
,
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(¬ifications
, &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;
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
;
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
,
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(¬ifications
, &controller
);
1791 const GURL
url1("http://foo1");
1792 const GURL
url2("http://foo2"); // Redirection target
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
;
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
,
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(¬ifications
, &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
;
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
,
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(¬ifications
, &controller
);
1880 // Navigation controller currently has no entries.
1881 const GURL
url("http://foo2");
1882 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
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
,
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(¬ifications
, &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
;
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
,
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(¬ifications
, &controller
);
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
;
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
,
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");
1964 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
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());
1972 controller
.GoBack();
1975 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
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();
1988 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
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(¬ifications
, &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(¬ifications
, &controller
);
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
,
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
;
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
,
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());
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
,
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());
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
,
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
,
2113 controller
.GoForward();
2114 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
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");
2123 navigation_entry_committed_counter_
= 0;
2124 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
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(¬ifications
, &controller
);
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
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
,
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
2170 // window.location.replace("#a");
2171 // window.location='http://foo3/';
2173 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2174 NavigationControllerImpl
& controller
= controller_impl();
2175 TestNotificationTracker notifications
;
2176 RegisterForAllNavNotifications(¬ifications
, &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
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
,
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
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
,
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");
2261 params
.page_state
= PageState::CreateFromURL(url
);
2262 params
.was_within_same_page
= true;
2263 test_rvh()->SendNavigateWithParams(¶ms
);
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
{
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_
;
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
);
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
));
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
));
2323 url
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2324 main_test_rfh()->SendNavigate(url_index
, url
);
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
));
2341 url
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2342 main_test_rfh()->SendNavigate(url_index
, url
);
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(),
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
,
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
;
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
,
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());
2422 NavigationEntryImpl::FromNavigationEntry(
2423 our_controller
.GetLastCommittedEntry())->site_instance()->
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(),
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
2485 // Now the pending restored entry commits.
2486 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
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
,
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());
2503 NavigationEntryImpl::FromNavigationEntry(
2504 our_controller
.GetLastCommittedEntry())->site_instance()->
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");
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");
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");
2548 url1
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2549 main_test_rfh()->SendNavigate(0, url1
);
2551 url2
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2552 main_test_rfh()->SendNavigate(1, url2
);
2554 url3
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2555 main_test_rfh()->SendNavigate(2, url3
);
2557 url4
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2558 main_test_rfh()->SendNavigate(3, url4
);
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(¬ifications
, &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");
2611 url0
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2612 main_test_rfh()->SendNavigate(0, url0
);
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());
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);
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.
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|.
2751 url0
, Referrer(), PAGE_TRANSITION_TYPED
, std::string());
2752 main_test_rfh()->SendNavigate(0, url0
);
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
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());
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);
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(¬ifications
, &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());
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());
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(¬ifications
, &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
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());
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(¬ifications
, &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());
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();
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(¬ifications
, &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
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());
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();
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(¬ifications
, &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
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());
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,
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,
3085 const GURL
other_url("http://www.google.com/add.html");
3086 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3088 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3089 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
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,
3098 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3100 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
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,
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,
3111 // Going back to the non ref url will be considered in-page if the navigation
3113 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
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,
3121 // Don't believe the renderer if it claims a cross-origin navigation is
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,
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
;
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
,
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
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/");
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
,
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();
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
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
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
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
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
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
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(
3823 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
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.
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(¬ifications
, &controller
);
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");
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(¬ifications
, &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();
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(
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();
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(¬ifications
, &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();
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(
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);
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
4136 #define MAYBE_PurgeScreenshot PurgeScreenshot
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
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
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
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
4228 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
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");
4245 params
.page_state
= PageState::CreateFromURL(url
);
4246 params
.was_within_same_page
= true;
4247 test_rvh()->SendNavigateWithParams(¶ms
);
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());
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
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());
4314 GURL
url("http://foo");
4315 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
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;
4324 test_rvh()->SendNavigateWithParams(¶ms
);
4326 // history.replaceState() is called.
4327 GURL
replace_url("http://foo#foo");
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(¶ms
);
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