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