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()->PrepareForCommit(urls
[0]);
320 main_test_rfh()->SendNavigate(0, urls
[0]);
321 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
322 navigation_entry_committed_counter_
= 0;
323 EXPECT_EQ(urls
[0], controller
.GetVisibleEntry()->GetVirtualURL());
324 EXPECT_FALSE(controller
.CanGoBack());
325 EXPECT_FALSE(controller
.CanGoForward());
326 EXPECT_FALSE(controller
.CanGoToOffset(1));
328 for (int i
= 1; i
<= 4; ++i
) {
329 main_test_rfh()->PrepareForCommit(urls
[i
]);
330 main_test_rfh()->SendNavigate(i
, urls
[i
]);
331 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
332 navigation_entry_committed_counter_
= 0;
333 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
334 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
335 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
336 EXPECT_FALSE(controller
.CanGoToOffset(1));
339 // We have loaded 5 pages, and are currently at the last-loaded page.
343 GO_TO_MIDDLE_PAGE
= -2,
346 GO_TO_BEGINNING
= -2,
351 const int test_offsets
[NUM_TESTS
] = {
359 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
360 int offset
= test_offsets
[test
];
361 controller
.GoToOffset(offset
);
363 // Check that the GoToOffset will land on the expected page.
364 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
365 main_test_rfh()->PrepareForCommit(urls
[url_index
]);
366 main_test_rfh()->SendNavigate(url_index
, urls
[url_index
]);
367 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
368 navigation_entry_committed_counter_
= 0;
369 // Check that we can go to any valid offset into the history.
370 for (size_t j
= 0; j
< urls
.size(); ++j
)
371 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
372 // Check that we can't go beyond the beginning or end of the history.
373 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
374 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
378 TEST_F(NavigationControllerTest
, LoadURL
) {
379 NavigationControllerImpl
& controller
= controller_impl();
380 TestNotificationTracker notifications
;
381 RegisterForAllNavNotifications(¬ifications
, &controller
);
383 const GURL
url1("http://foo1");
384 const GURL
url2("http://foo2");
387 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
388 // Creating a pending notification should not have issued any of the
389 // notifications we're listening for.
390 EXPECT_EQ(0U, notifications
.size());
392 // The load should now be pending.
393 EXPECT_EQ(controller
.GetEntryCount(), 0);
394 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
395 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
396 EXPECT_FALSE(controller
.GetLastCommittedEntry());
397 ASSERT_TRUE(controller
.GetPendingEntry());
398 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
399 EXPECT_FALSE(controller
.CanGoBack());
400 EXPECT_FALSE(controller
.CanGoForward());
401 EXPECT_EQ(contents()->GetMaxPageID(), -1);
403 // Neither the timestamp nor the status code should have been set yet.
404 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
405 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
407 // We should have gotten no notifications from the preceeding checks.
408 EXPECT_EQ(0U, notifications
.size());
410 main_test_rfh()->PrepareForCommit(url1
);
411 main_test_rfh()->SendNavigate(0, url1
);
412 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
413 navigation_entry_committed_counter_
= 0;
415 // The load should now be committed.
416 EXPECT_EQ(controller
.GetEntryCount(), 1);
417 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
418 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
419 EXPECT_TRUE(controller
.GetLastCommittedEntry());
420 EXPECT_FALSE(controller
.GetPendingEntry());
421 ASSERT_TRUE(controller
.GetVisibleEntry());
422 EXPECT_FALSE(controller
.CanGoBack());
423 EXPECT_FALSE(controller
.CanGoForward());
424 EXPECT_EQ(contents()->GetMaxPageID(), 0);
425 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
427 // The timestamp should have been set.
428 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
432 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
434 // The load should now be pending.
435 EXPECT_EQ(controller
.GetEntryCount(), 1);
436 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
437 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
438 EXPECT_TRUE(controller
.GetLastCommittedEntry());
439 ASSERT_TRUE(controller
.GetPendingEntry());
440 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
441 // TODO(darin): maybe this should really be true?
442 EXPECT_FALSE(controller
.CanGoBack());
443 EXPECT_FALSE(controller
.CanGoForward());
444 EXPECT_EQ(contents()->GetMaxPageID(), 0);
446 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
448 // Simulate the beforeunload ack for the cross-site transition, and then the
450 main_test_rfh()->PrepareForCommit(url2
);
451 contents()->GetPendingMainFrame()->SendNavigate(1, url2
);
452 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
453 navigation_entry_committed_counter_
= 0;
455 // The load should now be committed.
456 EXPECT_EQ(controller
.GetEntryCount(), 2);
457 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
458 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
459 EXPECT_TRUE(controller
.GetLastCommittedEntry());
460 EXPECT_FALSE(controller
.GetPendingEntry());
461 ASSERT_TRUE(controller
.GetVisibleEntry());
462 EXPECT_TRUE(controller
.CanGoBack());
463 EXPECT_FALSE(controller
.CanGoForward());
464 EXPECT_EQ(contents()->GetMaxPageID(), 1);
466 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
471 base::Time
GetFixedTime(base::Time time
) {
477 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
478 NavigationControllerImpl
& controller
= controller_impl();
479 TestNotificationTracker notifications
;
480 RegisterForAllNavNotifications(¬ifications
, &controller
);
482 // Set the clock to always return a timestamp of 1.
483 controller
.SetGetTimestampCallbackForTest(
484 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
486 const GURL
url1("http://foo1");
487 const GURL
url2("http://foo2");
490 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
492 main_test_rfh()->PrepareForCommit(url1
);
493 main_test_rfh()->SendNavigate(0, url1
);
494 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
495 navigation_entry_committed_counter_
= 0;
499 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
501 // Simulate the beforeunload ack for the cross-site transition, and then the
503 main_test_rfh()->PrepareForCommit(url2
);
504 contents()->GetPendingMainFrame()->SendNavigate(1, url2
);
505 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
506 navigation_entry_committed_counter_
= 0;
508 // The two loads should now be committed.
509 ASSERT_EQ(controller
.GetEntryCount(), 2);
511 // Timestamps should be distinct despite the clock returning the
514 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
516 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
519 void CheckNavigationEntryMatchLoadParams(
520 NavigationController::LoadURLParams
& load_params
,
521 NavigationEntryImpl
* entry
) {
522 EXPECT_EQ(load_params
.url
, entry
->GetURL());
523 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
524 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
525 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
526 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
528 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
529 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
530 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
531 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
533 if (NavigationController::UA_OVERRIDE_INHERIT
!=
534 load_params
.override_user_agent
) {
535 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
536 load_params
.override_user_agent
);
537 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
539 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
540 entry
->GetBrowserInitiatedPostData());
541 EXPECT_EQ(load_params
.transferred_global_request_id
,
542 entry
->transferred_global_request_id());
545 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
546 NavigationControllerImpl
& controller
= controller_impl();
548 NavigationController::LoadURLParams
load_params(GURL("http://foo"));
549 load_params
.referrer
=
550 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
551 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
552 load_params
.extra_headers
= "content-type: text/plain";
553 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
554 load_params
.is_renderer_initiated
= true;
555 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
556 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
558 controller
.LoadURLWithParams(load_params
);
559 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
561 // The timestamp should not have been set yet.
563 EXPECT_TRUE(entry
->GetTimestamp().is_null());
565 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
568 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
569 NavigationControllerImpl
& controller
= controller_impl();
571 NavigationController::LoadURLParams
load_params(
572 GURL("data:text/html,dataurl"));
573 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
574 load_params
.base_url_for_data_url
= GURL("http://foo");
575 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
576 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
578 controller
.LoadURLWithParams(load_params
);
579 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
581 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
584 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
585 NavigationControllerImpl
& controller
= controller_impl();
587 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
588 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
589 load_params
.load_type
=
590 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
591 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
594 const unsigned char* raw_data
=
595 reinterpret_cast<const unsigned char*>("d\n\0a2");
596 const int length
= 5;
597 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
598 scoped_refptr
<base::RefCountedBytes
> data
=
599 base::RefCountedBytes::TakeVector(&post_data_vector
);
600 load_params
.browser_initiated_post_data
= data
.get();
602 controller
.LoadURLWithParams(load_params
);
603 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
605 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
608 // Tests what happens when the same page is loaded again. Should not create a
609 // new session history entry. This is what happens when you press enter in the
610 // URL bar to reload: a pending entry is created and then it is discarded when
611 // the load commits (because WebCore didn't actually make a new entry).
612 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
613 NavigationControllerImpl
& controller
= controller_impl();
614 TestNotificationTracker notifications
;
615 RegisterForAllNavNotifications(¬ifications
, &controller
);
617 const GURL
url1("http://foo1");
620 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
621 EXPECT_EQ(0U, notifications
.size());
622 main_test_rfh()->PrepareForCommit(url1
);
623 main_test_rfh()->SendNavigate(0, url1
);
624 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
625 navigation_entry_committed_counter_
= 0;
627 ASSERT_TRUE(controller
.GetVisibleEntry());
628 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
629 EXPECT_FALSE(timestamp
.is_null());
632 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
633 EXPECT_EQ(0U, notifications
.size());
634 main_test_rfh()->PrepareForCommit(url1
);
635 main_test_rfh()->SendNavigate(0, url1
);
636 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
637 navigation_entry_committed_counter_
= 0;
639 // We should not have produced a new session history entry.
640 EXPECT_EQ(controller
.GetEntryCount(), 1);
641 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
642 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
643 EXPECT_TRUE(controller
.GetLastCommittedEntry());
644 EXPECT_FALSE(controller
.GetPendingEntry());
645 ASSERT_TRUE(controller
.GetVisibleEntry());
646 EXPECT_FALSE(controller
.CanGoBack());
647 EXPECT_FALSE(controller
.CanGoForward());
649 // The timestamp should have been updated.
651 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
652 // EXPECT_GT once we guarantee that timestamps are unique.
653 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
656 // Load the same page twice, once as a GET and once as a POST.
657 // We should update the post state on the NavigationEntry.
658 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
659 NavigationControllerImpl
& controller
= controller_impl();
660 TestNotificationTracker notifications
;
661 RegisterForAllNavNotifications(¬ifications
, &controller
);
663 const GURL
url1("http://foo1");
666 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
667 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
670 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
671 params
.is_post
= true;
672 params
.post_id
= 123;
673 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
674 main_test_rfh()->PrepareForCommit(url1
);
675 main_test_rfh()->SendNavigateWithParams(¶ms
);
677 // The post data should be visible.
678 NavigationEntry
* entry
= controller
.GetVisibleEntry();
680 EXPECT_TRUE(entry
->GetHasPostData());
681 EXPECT_EQ(entry
->GetPostID(), 123);
684 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
685 main_test_rfh()->PrepareForCommit(url1
);
686 main_test_rfh()->SendNavigate(0, url1
);
688 // We should not have produced a new session history entry.
689 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
691 // The post data should have been cleared due to the GET.
692 EXPECT_FALSE(entry
->GetHasPostData());
693 EXPECT_EQ(entry
->GetPostID(), 0);
696 // Tests loading a URL but discarding it before the load commits.
697 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
698 NavigationControllerImpl
& controller
= controller_impl();
699 TestNotificationTracker notifications
;
700 RegisterForAllNavNotifications(¬ifications
, &controller
);
702 const GURL
url1("http://foo1");
703 const GURL
url2("http://foo2");
706 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
707 EXPECT_EQ(0U, notifications
.size());
708 main_test_rfh()->SendNavigate(0, url1
);
709 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
710 navigation_entry_committed_counter_
= 0;
712 ASSERT_TRUE(controller
.GetVisibleEntry());
713 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
714 EXPECT_FALSE(timestamp
.is_null());
717 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
718 controller
.DiscardNonCommittedEntries();
719 EXPECT_EQ(0U, notifications
.size());
721 // Should not have produced a new session history entry.
722 EXPECT_EQ(controller
.GetEntryCount(), 1);
723 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
724 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
725 EXPECT_TRUE(controller
.GetLastCommittedEntry());
726 EXPECT_FALSE(controller
.GetPendingEntry());
727 ASSERT_TRUE(controller
.GetVisibleEntry());
728 EXPECT_FALSE(controller
.CanGoBack());
729 EXPECT_FALSE(controller
.CanGoForward());
731 // Timestamp should not have changed.
732 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
735 // Tests navigations that come in unrequested. This happens when the user
736 // navigates from the web page, and here we test that there is no pending entry.
737 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
738 NavigationControllerImpl
& controller
= controller_impl();
739 TestNotificationTracker notifications
;
740 RegisterForAllNavNotifications(¬ifications
, &controller
);
742 // First make an existing committed entry.
743 const GURL
kExistingURL1("http://eh");
745 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
746 main_test_rfh()->SendNavigate(0, kExistingURL1
);
747 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
748 navigation_entry_committed_counter_
= 0;
750 // Do a new navigation without making a pending one.
751 const GURL
kNewURL("http://see");
752 main_test_rfh()->SendNavigate(99, kNewURL
);
754 // There should no longer be any pending entry, and the third navigation we
755 // just made should be committed.
756 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
757 navigation_entry_committed_counter_
= 0;
758 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
759 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
760 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
763 // Tests navigating to a new URL when there is a new pending navigation that is
764 // not the one that just loaded. This will happen if the user types in a URL to
765 // somewhere slow, and then navigates the current page before the typed URL
767 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
768 NavigationControllerImpl
& controller
= controller_impl();
769 TestNotificationTracker notifications
;
770 RegisterForAllNavNotifications(¬ifications
, &controller
);
772 // First make an existing committed entry.
773 const GURL
kExistingURL1("http://eh");
775 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
776 main_test_rfh()->PrepareForCommit(kExistingURL1
);
777 main_test_rfh()->SendNavigate(0, kExistingURL1
);
778 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
779 navigation_entry_committed_counter_
= 0;
781 // Make a pending entry to somewhere new.
782 const GURL
kExistingURL2("http://bee");
784 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
785 EXPECT_EQ(0U, notifications
.size());
787 // After the beforeunload but before it commits, do a new navigation.
788 main_test_rfh()->PrepareForCommit(kExistingURL2
);
789 const GURL
kNewURL("http://see");
790 main_test_rfh()->PrepareForCommit(kNewURL
);
791 contents()->GetMainFrame()->SendNavigate(3, kNewURL
);
793 // There should no longer be any pending entry, and the third navigation we
794 // just made should be committed.
795 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
796 navigation_entry_committed_counter_
= 0;
797 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
798 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
799 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
802 // Tests navigating to a new URL when there is a pending back/forward
803 // navigation. This will happen if the user hits back, but before that commits,
804 // they navigate somewhere new.
805 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
806 NavigationControllerImpl
& controller
= controller_impl();
807 TestNotificationTracker notifications
;
808 RegisterForAllNavNotifications(¬ifications
, &controller
);
810 // First make some history.
811 const GURL
kExistingURL1("http://foo/eh");
813 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
814 main_test_rfh()->PrepareForCommit(kExistingURL1
);
815 main_test_rfh()->SendNavigate(0, kExistingURL1
);
816 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
817 navigation_entry_committed_counter_
= 0;
819 const GURL
kExistingURL2("http://foo/bee");
821 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
822 main_test_rfh()->PrepareForCommit(kExistingURL2
);
823 main_test_rfh()->SendNavigate(1, kExistingURL2
);
824 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
825 navigation_entry_committed_counter_
= 0;
827 // Now make a pending back/forward navigation. The zeroth entry should be
830 EXPECT_EQ(0U, notifications
.size());
831 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
832 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
834 // Before that commits, do a new navigation.
835 const GURL
kNewURL("http://foo/see");
836 main_test_rfh()->PrepareForCommit(kNewURL
);
837 main_test_rfh()->SendNavigate(3, kNewURL
);
839 // There should no longer be any pending entry, and the third navigation we
840 // just made should be committed.
841 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
842 navigation_entry_committed_counter_
= 0;
843 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
844 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
845 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
848 // Tests navigating to a new URL when there is a pending back/forward
849 // navigation to a cross-process, privileged URL. This will happen if the user
850 // hits back, but before that commits, they navigate somewhere new.
851 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
852 NavigationControllerImpl
& controller
= controller_impl();
853 TestNotificationTracker notifications
;
854 RegisterForAllNavNotifications(¬ifications
, &controller
);
856 // First make some history, starting with a privileged URL.
857 const GURL
kExistingURL1("http://privileged");
859 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
860 // Pretend it has bindings so we can tell if we incorrectly copy it.
861 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
862 main_test_rfh()->PrepareForCommit(kExistingURL1
);
863 main_test_rfh()->SendNavigate(0, kExistingURL1
);
864 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
865 navigation_entry_committed_counter_
= 0;
867 // Navigate cross-process to a second URL.
868 const GURL
kExistingURL2("http://foo/eh");
870 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
871 main_test_rfh()->PrepareForCommit(kExistingURL2
);
872 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
873 foo_rfh
->SendNavigate(1, kExistingURL2
);
874 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
875 navigation_entry_committed_counter_
= 0;
877 // Now make a pending back/forward navigation to a privileged entry.
878 // The zeroth entry should be pending.
880 foo_rfh
->SendBeforeUnloadACK(true);
881 EXPECT_EQ(0U, notifications
.size());
882 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
883 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
884 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
886 // Before that commits, do a new navigation.
887 const GURL
kNewURL("http://foo/bee");
888 foo_rfh
->PrepareForCommit(kNewURL
);
889 foo_rfh
->SendNavigate(3, kNewURL
);
891 // There should no longer be any pending entry, and the third navigation we
892 // just made should be committed.
893 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
894 navigation_entry_committed_counter_
= 0;
895 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
896 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
897 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
898 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
901 // Tests navigating to an existing URL when there is a pending new navigation.
902 // This will happen if the user enters a URL, but before that commits, the
903 // current page fires history.back().
904 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
905 NavigationControllerImpl
& controller
= controller_impl();
906 TestNotificationTracker notifications
;
907 RegisterForAllNavNotifications(¬ifications
, &controller
);
909 // First make some history.
910 const GURL
kExistingURL1("http://foo/eh");
912 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
913 main_test_rfh()->PrepareForCommit(kExistingURL1
);
914 main_test_rfh()->SendNavigate(0, kExistingURL1
);
915 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
916 navigation_entry_committed_counter_
= 0;
918 const GURL
kExistingURL2("http://foo/bee");
920 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
921 main_test_rfh()->PrepareForCommit(kExistingURL2
);
922 main_test_rfh()->SendNavigate(1, kExistingURL2
);
923 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
924 navigation_entry_committed_counter_
= 0;
926 // Now make a pending new navigation.
927 const GURL
kNewURL("http://foo/see");
929 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
930 EXPECT_EQ(0U, notifications
.size());
931 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
932 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
934 // Before that commits, a back navigation from the renderer commits.
935 main_test_rfh()->PrepareForCommit(kExistingURL1
);
936 main_test_rfh()->SendNavigate(0, kExistingURL1
);
938 // There should no longer be any pending entry, and the back navigation we
939 // just made should be committed.
940 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
941 navigation_entry_committed_counter_
= 0;
942 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
943 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
944 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
947 // Tests an ignored navigation when there is a pending new navigation.
948 // This will happen if the user enters a URL, but before that commits, the
949 // current blank page reloads. See http://crbug.com/77507.
950 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
951 NavigationControllerImpl
& controller
= controller_impl();
952 TestNotificationTracker notifications
;
953 RegisterForAllNavNotifications(¬ifications
, &controller
);
955 // Set a WebContentsDelegate to listen for state changes.
956 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
957 EXPECT_FALSE(contents()->GetDelegate());
958 contents()->SetDelegate(delegate
.get());
960 // Without any navigations, the renderer starts at about:blank.
961 const GURL
kExistingURL(url::kAboutBlankURL
);
963 // Now make a pending new navigation.
964 const GURL
kNewURL("http://eh");
966 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
967 EXPECT_EQ(0U, notifications
.size());
968 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
969 EXPECT_TRUE(controller
.GetPendingEntry());
970 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
971 EXPECT_EQ(1, delegate
->navigation_state_change_count());
973 // Before that commits, a document.write and location.reload can cause the
974 // renderer to send a FrameNavigate with page_id -1.
975 main_test_rfh()->PrepareForCommit(kExistingURL
);
976 main_test_rfh()->SendNavigate(-1, kExistingURL
);
978 // This should clear the pending entry and notify of a navigation state
979 // change, so that we do not keep displaying kNewURL.
980 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
981 EXPECT_FALSE(controller
.GetPendingEntry());
982 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
983 EXPECT_EQ(2, delegate
->navigation_state_change_count());
985 contents()->SetDelegate(NULL
);
988 // Tests that the pending entry state is correct after an abort.
989 // We do not want to clear the pending entry, so that the user doesn't
990 // lose a typed URL. (See http://crbug.com/9682.)
991 TEST_F(NavigationControllerTest
, LoadURL_AbortDoesntCancelPending
) {
992 NavigationControllerImpl
& controller
= controller_impl();
993 TestNotificationTracker notifications
;
994 RegisterForAllNavNotifications(¬ifications
, &controller
);
996 // Set a WebContentsDelegate to listen for state changes.
997 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
998 EXPECT_FALSE(contents()->GetDelegate());
999 contents()->SetDelegate(delegate
.get());
1001 // Start with a pending new navigation.
1002 const GURL
kNewURL("http://eh");
1004 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1005 EXPECT_EQ(0U, notifications
.size());
1006 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1007 EXPECT_TRUE(controller
.GetPendingEntry());
1008 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1009 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1011 // It may abort before committing, if it's a download or due to a stop or
1012 // a new navigation from the user.
1013 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1014 params
.error_code
= net::ERR_ABORTED
;
1015 params
.error_description
= base::string16();
1016 params
.url
= kNewURL
;
1017 params
.showing_repost_interstitial
= false;
1018 main_test_rfh()->OnMessageReceived(
1019 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1022 // This should not clear the pending entry or notify of a navigation state
1023 // change, so that we keep displaying kNewURL (until the user clears it).
1024 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1025 EXPECT_TRUE(controller
.GetPendingEntry());
1026 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1027 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1028 NavigationEntry
* pending_entry
= controller
.GetPendingEntry();
1030 // Ensure that a reload keeps the same pending entry.
1031 controller
.Reload(true);
1032 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1033 EXPECT_TRUE(controller
.GetPendingEntry());
1034 EXPECT_EQ(pending_entry
, controller
.GetPendingEntry());
1035 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1037 contents()->SetDelegate(NULL
);
1040 // Tests that the pending URL is not visible during a renderer-initiated
1041 // redirect and abort. See http://crbug.com/83031.
1042 TEST_F(NavigationControllerTest
, LoadURL_RedirectAbortDoesntShowPendingURL
) {
1043 NavigationControllerImpl
& controller
= controller_impl();
1044 TestNotificationTracker notifications
;
1045 RegisterForAllNavNotifications(¬ifications
, &controller
);
1047 // First make an existing committed entry.
1048 const GURL
kExistingURL("http://foo/eh");
1049 controller
.LoadURL(kExistingURL
, content::Referrer(),
1050 ui::PAGE_TRANSITION_TYPED
, std::string());
1051 main_test_rfh()->SendNavigate(1, kExistingURL
);
1052 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1053 navigation_entry_committed_counter_
= 0;
1055 // Set a WebContentsDelegate to listen for state changes.
1056 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1057 EXPECT_FALSE(contents()->GetDelegate());
1058 contents()->SetDelegate(delegate
.get());
1060 // Now make a pending new navigation, initiated by the renderer.
1061 const GURL
kNewURL("http://foo/bee");
1062 NavigationController::LoadURLParams
load_url_params(kNewURL
);
1063 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
1064 load_url_params
.is_renderer_initiated
= true;
1065 controller
.LoadURLWithParams(load_url_params
);
1066 EXPECT_EQ(0U, notifications
.size());
1067 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1068 EXPECT_TRUE(controller
.GetPendingEntry());
1069 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1070 EXPECT_EQ(0, delegate
->navigation_state_change_count());
1072 // The visible entry should be the last committed URL, not the pending one.
1073 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1075 // Now the navigation redirects. (There is no corresponding message here.)
1076 const GURL
kRedirectURL("http://foo/see");
1078 // We don't want to change the NavigationEntry's url, in case it cancels.
1079 // Prevents regression of http://crbug.com/77786.
1080 EXPECT_EQ(kNewURL
, controller
.GetPendingEntry()->GetURL());
1082 // It may abort before committing, if it's a download or due to a stop or
1083 // a new navigation from the user.
1084 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1085 params
.error_code
= net::ERR_ABORTED
;
1086 params
.error_description
= base::string16();
1087 params
.url
= kRedirectURL
;
1088 params
.showing_repost_interstitial
= false;
1089 main_test_rfh()->OnMessageReceived(
1090 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1093 // Because the pending entry is renderer initiated and not visible, we
1094 // clear it when it fails.
1095 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1096 EXPECT_FALSE(controller
.GetPendingEntry());
1097 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1098 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1100 // The visible entry should be the last committed URL, not the pending one,
1101 // so that no spoof is possible.
1102 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1104 contents()->SetDelegate(NULL
);
1107 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1108 // at the time they committed. http://crbug.com/173672.
1109 TEST_F(NavigationControllerTest
, LoadURL_WithBindings
) {
1110 NavigationControllerImpl
& controller
= controller_impl();
1111 TestNotificationTracker notifications
;
1112 RegisterForAllNavNotifications(¬ifications
, &controller
);
1113 std::vector
<GURL
> url_chain
;
1115 const GURL
url1("http://foo1");
1116 const GURL
url2("http://foo2");
1118 // Navigate to a first, unprivileged URL.
1120 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1121 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings
,
1122 controller
.GetPendingEntry()->bindings());
1125 TestRenderFrameHost
* orig_rfh
= contents()->GetMainFrame();
1126 orig_rfh
->PrepareForCommit(url1
);
1127 orig_rfh
->SendNavigate(0, url1
);
1128 EXPECT_EQ(controller
.GetEntryCount(), 1);
1129 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1130 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1132 // Manually increase the number of active frames in the SiteInstance
1133 // that orig_rfh belongs to, to prevent it from being destroyed when
1134 // it gets swapped out, so that we can reuse orig_rfh when the
1135 // controller goes back.
1136 orig_rfh
->GetSiteInstance()->increment_active_frame_count();
1138 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1139 // transition, and set bindings on the pending RenderViewHost to simulate a
1142 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1143 orig_rfh
->PrepareForCommit(url2
);
1144 TestRenderFrameHost
* new_rfh
= contents()->GetPendingMainFrame();
1145 new_rfh
->GetRenderViewHost()->AllowBindings(1);
1146 new_rfh
->SendNavigate(1, url2
);
1148 // The second load should be committed, and bindings should be remembered.
1149 EXPECT_EQ(controller
.GetEntryCount(), 2);
1150 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1151 EXPECT_TRUE(controller
.CanGoBack());
1152 EXPECT_EQ(1, controller
.GetLastCommittedEntry()->bindings());
1154 // Going back, the first entry should still appear unprivileged.
1155 controller
.GoBack();
1156 new_rfh
->PrepareForCommit(url1
);
1157 orig_rfh
->SendNavigate(0, url1
);
1158 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1159 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1162 TEST_F(NavigationControllerTest
, Reload
) {
1163 NavigationControllerImpl
& controller
= controller_impl();
1164 TestNotificationTracker notifications
;
1165 RegisterForAllNavNotifications(¬ifications
, &controller
);
1167 const GURL
url1("http://foo1");
1170 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1171 EXPECT_EQ(0U, notifications
.size());
1172 main_test_rfh()->PrepareForCommit(url1
);
1173 main_test_rfh()->SendNavigate(0, url1
);
1174 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1175 navigation_entry_committed_counter_
= 0;
1176 ASSERT_TRUE(controller
.GetVisibleEntry());
1177 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1178 controller
.Reload(true);
1179 EXPECT_EQ(0U, notifications
.size());
1181 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
1182 EXPECT_FALSE(timestamp
.is_null());
1184 // The reload is pending.
1185 EXPECT_EQ(controller
.GetEntryCount(), 1);
1186 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1187 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1188 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1189 EXPECT_TRUE(controller
.GetPendingEntry());
1190 EXPECT_FALSE(controller
.CanGoBack());
1191 EXPECT_FALSE(controller
.CanGoForward());
1192 // Make sure the title has been cleared (will be redrawn just after reload).
1193 // Avoids a stale cached title when the new page being reloaded has no title.
1194 // See http://crbug.com/96041.
1195 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1197 main_test_rfh()->PrepareForCommit(url1
);
1198 main_test_rfh()->SendNavigate(0, url1
);
1199 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1200 navigation_entry_committed_counter_
= 0;
1202 // Now the reload is committed.
1203 EXPECT_EQ(controller
.GetEntryCount(), 1);
1204 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1205 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1206 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1207 EXPECT_FALSE(controller
.GetPendingEntry());
1208 EXPECT_FALSE(controller
.CanGoBack());
1209 EXPECT_FALSE(controller
.CanGoForward());
1211 // The timestamp should have been updated.
1212 ASSERT_TRUE(controller
.GetVisibleEntry());
1213 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
1216 // Tests what happens when a reload navigation produces a new page.
1217 TEST_F(NavigationControllerTest
, Reload_GeneratesNewPage
) {
1218 NavigationControllerImpl
& controller
= controller_impl();
1219 TestNotificationTracker notifications
;
1220 RegisterForAllNavNotifications(¬ifications
, &controller
);
1222 const GURL
url1("http://foo1");
1223 const GURL
url2("http://foo2");
1226 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1227 main_test_rfh()->PrepareForCommit(url1
);
1228 main_test_rfh()->SendNavigate(0, url1
);
1229 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1230 navigation_entry_committed_counter_
= 0;
1232 controller
.Reload(true);
1233 EXPECT_EQ(0U, notifications
.size());
1235 main_test_rfh()->PrepareForCommit(url2
);
1236 main_test_rfh()->SendNavigate(1, url2
);
1237 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1238 navigation_entry_committed_counter_
= 0;
1240 // Now the reload is committed.
1241 EXPECT_EQ(controller
.GetEntryCount(), 2);
1242 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1243 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1244 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1245 EXPECT_FALSE(controller
.GetPendingEntry());
1246 EXPECT_TRUE(controller
.CanGoBack());
1247 EXPECT_FALSE(controller
.CanGoForward());
1250 // This test ensures that when a guest renderer reloads, the reload goes through
1251 // without ending up in the "we have a wrong process for the URL" branch in
1252 // NavigationControllerImpl::ReloadInternal.
1253 TEST_F(NavigationControllerTest
, ReloadWithGuest
) {
1254 NavigationControllerImpl
& controller
= controller_impl();
1256 const GURL
url1("http://foo1");
1258 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1259 main_test_rfh()->SendNavigate(0, url1
);
1260 ASSERT_TRUE(controller
.GetVisibleEntry());
1262 // Make the entry believe its RenderProcessHost is a guest.
1263 NavigationEntryImpl
* entry1
= controller
.GetVisibleEntry();
1264 reinterpret_cast<MockRenderProcessHost
*>(
1265 entry1
->site_instance()->GetProcess())->set_is_isolated_guest(true);
1268 controller
.Reload(true);
1270 // The reload is pending. Check that the NavigationEntry didn't get replaced
1271 // because of having the wrong process.
1272 EXPECT_EQ(controller
.GetEntryCount(), 1);
1273 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1274 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1276 NavigationEntryImpl
* entry2
= controller
.GetPendingEntry();
1277 EXPECT_EQ(entry1
, entry2
);
1280 #if !defined(OS_ANDROID) // http://crbug.com/157428
1281 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1282 NavigationControllerImpl
& controller
= controller_impl();
1283 TestNotificationTracker notifications
;
1284 RegisterForAllNavNotifications(¬ifications
, &controller
);
1286 const GURL
original_url("http://foo1");
1287 const GURL
final_url("http://foo2");
1289 // Load up the original URL, but get redirected.
1291 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1292 EXPECT_EQ(0U, notifications
.size());
1293 main_test_rfh()->PrepareForCommit(final_url
);
1294 main_test_rfh()->SendNavigateWithOriginalRequestURL(
1295 0, final_url
, original_url
);
1296 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1297 navigation_entry_committed_counter_
= 0;
1299 // The NavigationEntry should save both the original URL and the final
1302 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1303 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1305 // Reload using the original URL.
1306 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1307 controller
.ReloadOriginalRequestURL(false);
1308 EXPECT_EQ(0U, notifications
.size());
1310 // The reload is pending. The request should point to the original URL.
1311 EXPECT_EQ(original_url
, navigated_url());
1312 EXPECT_EQ(controller
.GetEntryCount(), 1);
1313 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1314 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1315 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1316 EXPECT_TRUE(controller
.GetPendingEntry());
1317 EXPECT_FALSE(controller
.CanGoBack());
1318 EXPECT_FALSE(controller
.CanGoForward());
1320 // Make sure the title has been cleared (will be redrawn just after reload).
1321 // Avoids a stale cached title when the new page being reloaded has no title.
1322 // See http://crbug.com/96041.
1323 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1325 // Send that the navigation has proceeded; say it got redirected again.
1326 main_test_rfh()->PrepareForCommit(final_url
);
1327 main_test_rfh()->SendNavigate(0, final_url
);
1328 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1329 navigation_entry_committed_counter_
= 0;
1331 // Now the reload is committed.
1332 EXPECT_EQ(controller
.GetEntryCount(), 1);
1333 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1334 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1335 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1336 EXPECT_FALSE(controller
.GetPendingEntry());
1337 EXPECT_FALSE(controller
.CanGoBack());
1338 EXPECT_FALSE(controller
.CanGoForward());
1341 #endif // !defined(OS_ANDROID)
1343 // Test that certain non-persisted NavigationEntryImpl values get reset after
1345 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1346 NavigationControllerImpl
& controller
= controller_impl();
1348 // The value of "should replace entry" will be tested, but it's an error to
1349 // specify it when there are no entries. Create a simple entry to be replaced.
1350 const GURL
url0("http://foo/0");
1352 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1353 main_test_rfh()->SendNavigate(0, url0
);
1355 // Set up the pending entry.
1356 const GURL
url1("http://foo/1");
1358 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1360 // Set up some sample values.
1361 const unsigned char* raw_data
=
1362 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1363 const int length
= 11;
1364 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1365 scoped_refptr
<base::RefCountedBytes
> post_data
=
1366 base::RefCountedBytes::TakeVector(&post_data_vector
);
1367 GlobalRequestID
transfer_id(3, 4);
1369 // Set non-persisted values on the pending entry.
1370 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1371 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1372 pending_entry
->set_is_renderer_initiated(true);
1373 pending_entry
->set_transferred_global_request_id(transfer_id
);
1374 pending_entry
->set_should_replace_entry(true);
1375 pending_entry
->set_should_clear_history_list(true);
1376 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1377 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1378 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1379 EXPECT_TRUE(pending_entry
->should_replace_entry());
1380 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1382 // Fake a commit response.
1383 main_test_rfh()->SendNavigate(1, url1
);
1385 // Certain values that are only used for pending entries get reset after
1387 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1388 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1389 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1390 EXPECT_EQ(GlobalRequestID(-1, -1),
1391 committed_entry
->transferred_global_request_id());
1392 EXPECT_FALSE(committed_entry
->should_replace_entry());
1393 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1396 // Test that Redirects are preserved after a commit.
1397 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1398 NavigationControllerImpl
& controller
= controller_impl();
1399 const GURL
url1("http://foo1");
1401 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1403 // Set up some redirect values.
1404 std::vector
<GURL
> redirects
;
1405 redirects
.push_back(GURL("http://foo2"));
1407 // Set redirects on the pending entry.
1408 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1409 pending_entry
->SetRedirectChain(redirects
);
1410 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1411 EXPECT_EQ(GURL("http://foo2"), pending_entry
->GetRedirectChain()[0]);
1413 // Normal navigation will preserve redirects in the committed entry.
1414 main_test_rfh()->SendNavigateWithRedirects(0, url1
, redirects
);
1415 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1416 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1417 EXPECT_EQ(GURL("http://foo2"), committed_entry
->GetRedirectChain()[0]);
1420 // Tests what happens when we navigate back successfully
1421 TEST_F(NavigationControllerTest
, Back
) {
1422 NavigationControllerImpl
& controller
= controller_impl();
1423 TestNotificationTracker notifications
;
1424 RegisterForAllNavNotifications(¬ifications
, &controller
);
1426 const GURL
url1("http://foo1");
1427 main_test_rfh()->SendNavigate(0, url1
);
1428 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1429 navigation_entry_committed_counter_
= 0;
1431 const GURL
url2("http://foo2");
1432 main_test_rfh()->SendNavigate(1, url2
);
1433 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1434 navigation_entry_committed_counter_
= 0;
1436 controller
.GoBack();
1437 EXPECT_EQ(0U, notifications
.size());
1439 // We should now have a pending navigation to go back.
1440 EXPECT_EQ(controller
.GetEntryCount(), 2);
1441 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1442 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1443 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1444 EXPECT_TRUE(controller
.GetPendingEntry());
1445 EXPECT_FALSE(controller
.CanGoBack());
1446 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1447 EXPECT_TRUE(controller
.CanGoForward());
1448 EXPECT_TRUE(controller
.CanGoToOffset(1));
1449 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1451 // Timestamp for entry 1 should be on or after that of entry 0.
1452 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1453 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1454 controller
.GetEntryAtIndex(0)->GetTimestamp());
1456 main_test_rfh()->SendNavigate(0, url2
);
1457 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1458 navigation_entry_committed_counter_
= 0;
1460 // The back navigation completed successfully.
1461 EXPECT_EQ(controller
.GetEntryCount(), 2);
1462 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1463 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1464 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1465 EXPECT_FALSE(controller
.GetPendingEntry());
1466 EXPECT_FALSE(controller
.CanGoBack());
1467 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1468 EXPECT_TRUE(controller
.CanGoForward());
1469 EXPECT_TRUE(controller
.CanGoToOffset(1));
1470 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1472 // Timestamp for entry 0 should be on or after that of entry 1
1473 // (since we went back to it).
1474 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1475 controller
.GetEntryAtIndex(1)->GetTimestamp());
1478 // Tests what happens when a back navigation produces a new page.
1479 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1480 NavigationControllerImpl
& controller
= controller_impl();
1481 TestNotificationTracker notifications
;
1482 RegisterForAllNavNotifications(¬ifications
, &controller
);
1484 const GURL
url1("http://foo/1");
1485 const GURL
url2("http://foo/2");
1486 const GURL
url3("http://foo/3");
1489 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1490 main_test_rfh()->PrepareForCommit(url1
);
1491 main_test_rfh()->SendNavigate(0, url1
);
1492 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1493 navigation_entry_committed_counter_
= 0;
1496 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1497 main_test_rfh()->PrepareForCommit(url2
);
1498 main_test_rfh()->SendNavigate(1, url2
);
1499 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1500 navigation_entry_committed_counter_
= 0;
1502 controller
.GoBack();
1503 EXPECT_EQ(0U, notifications
.size());
1505 // We should now have a pending navigation to go back.
1506 EXPECT_EQ(controller
.GetEntryCount(), 2);
1507 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1508 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1509 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1510 EXPECT_TRUE(controller
.GetPendingEntry());
1511 EXPECT_FALSE(controller
.CanGoBack());
1512 EXPECT_TRUE(controller
.CanGoForward());
1514 main_test_rfh()->PrepareForCommit(url3
);
1515 main_test_rfh()->SendNavigate(2, url3
);
1516 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1517 navigation_entry_committed_counter_
= 0;
1519 // The back navigation resulted in a completely new navigation.
1520 // TODO(darin): perhaps this behavior will be confusing to users?
1521 EXPECT_EQ(controller
.GetEntryCount(), 3);
1522 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1523 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1524 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1525 EXPECT_FALSE(controller
.GetPendingEntry());
1526 EXPECT_TRUE(controller
.CanGoBack());
1527 EXPECT_FALSE(controller
.CanGoForward());
1530 // Receives a back message when there is a new pending navigation entry.
1531 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1532 NavigationControllerImpl
& controller
= controller_impl();
1533 TestNotificationTracker notifications
;
1534 RegisterForAllNavNotifications(¬ifications
, &controller
);
1536 const GURL
kUrl1("http://foo1");
1537 const GURL
kUrl2("http://foo2");
1538 const GURL
kUrl3("http://foo3");
1540 // First navigate two places so we have some back history.
1541 main_test_rfh()->SendNavigate(0, kUrl1
);
1542 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1543 navigation_entry_committed_counter_
= 0;
1545 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1546 main_test_rfh()->SendNavigate(1, kUrl2
);
1547 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1548 navigation_entry_committed_counter_
= 0;
1550 // Now start a new pending navigation and go back before it commits.
1552 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1553 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1554 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1555 controller
.GoBack();
1557 // The pending navigation should now be the "back" item and the new one
1559 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1560 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1563 // Receives a back message when there is a different renavigation already
1565 TEST_F(NavigationControllerTest
, Back_OtherBackPending
) {
1566 NavigationControllerImpl
& controller
= controller_impl();
1567 const GURL
kUrl1("http://foo/1");
1568 const GURL
kUrl2("http://foo/2");
1569 const GURL
kUrl3("http://foo/3");
1571 // First navigate three places so we have some back history.
1572 main_test_rfh()->PrepareForCommit(kUrl1
);
1573 main_test_rfh()->SendNavigate(0, kUrl1
);
1574 main_test_rfh()->PrepareForCommit(kUrl2
);
1575 main_test_rfh()->SendNavigate(1, kUrl2
);
1576 main_test_rfh()->PrepareForCommit(kUrl3
);
1577 main_test_rfh()->SendNavigate(2, kUrl3
);
1579 // With nothing pending, say we get a navigation to the second entry.
1580 main_test_rfh()->PrepareForCommit(kUrl2
);
1581 main_test_rfh()->SendNavigate(1, kUrl2
);
1583 // We know all the entries have the same site instance, so we can just grab
1584 // a random one for looking up other entries.
1585 SiteInstance
* site_instance
=
1586 controller
.GetLastCommittedEntry()->site_instance();
1588 // That second URL should be the last committed and it should have gotten the
1590 EXPECT_EQ(kUrl2
, controller
.GetEntryWithPageID(site_instance
, 1)->GetURL());
1591 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1592 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1594 // Now go forward to the last item again and say it was committed.
1595 controller
.GoForward();
1596 main_test_rfh()->PrepareForCommit(kUrl3
);
1597 main_test_rfh()->SendNavigate(2, kUrl3
);
1599 // Now start going back one to the second page. It will be pending.
1600 controller
.GoBack();
1601 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
1602 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
1604 // Not synthesize a totally new back event to the first page. This will not
1605 // match the pending one.
1606 main_test_rfh()->PrepareForCommit(kUrl1
);
1607 main_test_rfh()->SendNavigate(0, kUrl1
);
1609 // The committed navigation should clear the pending entry.
1610 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1612 // But the navigated entry should be the last committed.
1613 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1614 EXPECT_EQ(kUrl1
, controller
.GetLastCommittedEntry()->GetURL());
1617 // Tests what happens when we navigate forward successfully.
1618 TEST_F(NavigationControllerTest
, Forward
) {
1619 NavigationControllerImpl
& controller
= controller_impl();
1620 TestNotificationTracker notifications
;
1621 RegisterForAllNavNotifications(¬ifications
, &controller
);
1623 const GURL
url1("http://foo1");
1624 const GURL
url2("http://foo2");
1626 main_test_rfh()->PrepareForCommit(url1
);
1627 main_test_rfh()->SendNavigate(0, url1
);
1628 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1629 navigation_entry_committed_counter_
= 0;
1631 main_test_rfh()->PrepareForCommit(url2
);
1632 main_test_rfh()->SendNavigate(1, url2
);
1633 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1634 navigation_entry_committed_counter_
= 0;
1636 controller
.GoBack();
1637 main_test_rfh()->PrepareForCommit(url1
);
1638 main_test_rfh()->SendNavigate(0, url1
);
1639 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1640 navigation_entry_committed_counter_
= 0;
1642 controller
.GoForward();
1644 // We should now have a pending navigation to go forward.
1645 EXPECT_EQ(controller
.GetEntryCount(), 2);
1646 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1647 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1648 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1649 EXPECT_TRUE(controller
.GetPendingEntry());
1650 EXPECT_TRUE(controller
.CanGoBack());
1651 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1652 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1653 EXPECT_FALSE(controller
.CanGoForward());
1654 EXPECT_FALSE(controller
.CanGoToOffset(1));
1656 // Timestamp for entry 0 should be on or after that of entry 1
1657 // (since we went back to it).
1658 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1659 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1660 controller
.GetEntryAtIndex(1)->GetTimestamp());
1662 main_test_rfh()->PrepareForCommit(url2
);
1663 main_test_rfh()->SendNavigate(1, url2
);
1664 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1665 navigation_entry_committed_counter_
= 0;
1667 // The forward navigation completed successfully.
1668 EXPECT_EQ(controller
.GetEntryCount(), 2);
1669 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1670 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1671 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1672 EXPECT_FALSE(controller
.GetPendingEntry());
1673 EXPECT_TRUE(controller
.CanGoBack());
1674 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1675 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1676 EXPECT_FALSE(controller
.CanGoForward());
1677 EXPECT_FALSE(controller
.CanGoToOffset(1));
1679 // Timestamp for entry 1 should be on or after that of entry 0
1680 // (since we went forward to it).
1681 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1682 controller
.GetEntryAtIndex(0)->GetTimestamp());
1685 // Tests what happens when a forward navigation produces a new page.
1686 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1687 NavigationControllerImpl
& controller
= controller_impl();
1688 TestNotificationTracker notifications
;
1689 RegisterForAllNavNotifications(¬ifications
, &controller
);
1691 const GURL
url1("http://foo1");
1692 const GURL
url2("http://foo2");
1693 const GURL
url3("http://foo3");
1695 main_test_rfh()->PrepareForCommit(url1
);
1696 main_test_rfh()->SendNavigate(0, url1
);
1697 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1698 navigation_entry_committed_counter_
= 0;
1699 main_test_rfh()->PrepareForCommit(url2
);
1700 main_test_rfh()->SendNavigate(1, url2
);
1701 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1702 navigation_entry_committed_counter_
= 0;
1704 controller
.GoBack();
1705 main_test_rfh()->PrepareForCommit(url1
);
1706 main_test_rfh()->SendNavigate(0, url1
);
1707 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1708 navigation_entry_committed_counter_
= 0;
1710 controller
.GoForward();
1711 EXPECT_EQ(0U, notifications
.size());
1713 // Should now have a pending navigation to go forward.
1714 EXPECT_EQ(controller
.GetEntryCount(), 2);
1715 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1716 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1717 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1718 EXPECT_TRUE(controller
.GetPendingEntry());
1719 EXPECT_TRUE(controller
.CanGoBack());
1720 EXPECT_FALSE(controller
.CanGoForward());
1722 main_test_rfh()->PrepareForCommit(url3
);
1723 main_test_rfh()->SendNavigate(2, url3
);
1724 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1725 navigation_entry_committed_counter_
= 0;
1726 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1728 EXPECT_EQ(controller
.GetEntryCount(), 2);
1729 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1730 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1731 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1732 EXPECT_FALSE(controller
.GetPendingEntry());
1733 EXPECT_TRUE(controller
.CanGoBack());
1734 EXPECT_FALSE(controller
.CanGoForward());
1737 // Two consequent navigation for the same URL entered in should be considered
1738 // as SAME_PAGE navigation even when we are redirected to some other page.
1739 TEST_F(NavigationControllerTest
, Redirect
) {
1740 NavigationControllerImpl
& controller
= controller_impl();
1741 TestNotificationTracker notifications
;
1742 RegisterForAllNavNotifications(¬ifications
, &controller
);
1744 const GURL
url1("http://foo1");
1745 const GURL
url2("http://foo2"); // Redirection target
1749 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1751 EXPECT_EQ(0U, notifications
.size());
1752 main_test_rfh()->SendNavigate(0, url2
);
1753 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1754 navigation_entry_committed_counter_
= 0;
1758 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1760 EXPECT_TRUE(controller
.GetPendingEntry());
1761 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1762 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1764 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1767 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1768 params
.redirects
.push_back(GURL("http://foo1"));
1769 params
.redirects
.push_back(GURL("http://foo2"));
1770 params
.should_update_history
= false;
1771 params
.gesture
= NavigationGestureAuto
;
1772 params
.is_post
= false;
1773 params
.page_state
= PageState::CreateFromURL(url2
);
1775 LoadCommittedDetails details
;
1777 EXPECT_EQ(0U, notifications
.size());
1778 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1780 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1781 navigation_entry_committed_counter_
= 0;
1783 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1784 EXPECT_EQ(controller
.GetEntryCount(), 1);
1785 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1786 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1787 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1788 EXPECT_FALSE(controller
.GetPendingEntry());
1789 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1791 EXPECT_FALSE(controller
.CanGoBack());
1792 EXPECT_FALSE(controller
.CanGoForward());
1795 // Similar to Redirect above, but the first URL is requested by POST,
1796 // the second URL is requested by GET. NavigationEntry::has_post_data_
1797 // must be cleared. http://crbug.com/21245
1798 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1799 NavigationControllerImpl
& controller
= controller_impl();
1800 TestNotificationTracker notifications
;
1801 RegisterForAllNavNotifications(¬ifications
, &controller
);
1803 const GURL
url1("http://foo1");
1804 const GURL
url2("http://foo2"); // Redirection target
1806 // First request as POST
1808 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1809 controller
.GetVisibleEntry()->SetHasPostData(true);
1811 EXPECT_EQ(0U, notifications
.size());
1812 main_test_rfh()->SendNavigate(0, url2
);
1813 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1814 navigation_entry_committed_counter_
= 0;
1818 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1820 EXPECT_TRUE(controller
.GetPendingEntry());
1821 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1822 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1824 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1827 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1828 params
.redirects
.push_back(GURL("http://foo1"));
1829 params
.redirects
.push_back(GURL("http://foo2"));
1830 params
.should_update_history
= false;
1831 params
.gesture
= NavigationGestureAuto
;
1832 params
.is_post
= false;
1833 params
.page_state
= PageState::CreateFromURL(url2
);
1835 LoadCommittedDetails details
;
1837 EXPECT_EQ(0U, notifications
.size());
1838 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1840 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1841 navigation_entry_committed_counter_
= 0;
1843 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1844 EXPECT_EQ(controller
.GetEntryCount(), 1);
1845 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1846 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1847 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1848 EXPECT_FALSE(controller
.GetPendingEntry());
1849 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1850 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1852 EXPECT_FALSE(controller
.CanGoBack());
1853 EXPECT_FALSE(controller
.CanGoForward());
1856 // A redirect right off the bat should be a NEW_PAGE.
1857 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1858 NavigationControllerImpl
& controller
= controller_impl();
1859 TestNotificationTracker notifications
;
1860 RegisterForAllNavNotifications(¬ifications
, &controller
);
1862 const GURL
url1("http://foo1");
1863 const GURL
url2("http://foo2"); // Redirection target
1867 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1869 EXPECT_TRUE(controller
.GetPendingEntry());
1870 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1871 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1873 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1876 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1877 params
.redirects
.push_back(GURL("http://foo1"));
1878 params
.redirects
.push_back(GURL("http://foo2"));
1879 params
.should_update_history
= false;
1880 params
.gesture
= NavigationGestureAuto
;
1881 params
.is_post
= false;
1882 params
.page_state
= PageState::CreateFromURL(url2
);
1884 LoadCommittedDetails details
;
1886 EXPECT_EQ(0U, notifications
.size());
1887 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1889 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1890 navigation_entry_committed_counter_
= 0;
1892 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
1893 EXPECT_EQ(controller
.GetEntryCount(), 1);
1894 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1895 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1896 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1897 EXPECT_FALSE(controller
.GetPendingEntry());
1898 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1900 EXPECT_FALSE(controller
.CanGoBack());
1901 EXPECT_FALSE(controller
.CanGoForward());
1904 // Tests navigation via link click within a subframe. A new navigation entry
1905 // should be created.
1906 TEST_F(NavigationControllerTest
, NewSubframe
) {
1907 NavigationControllerImpl
& controller
= controller_impl();
1908 TestNotificationTracker notifications
;
1909 RegisterForAllNavNotifications(¬ifications
, &controller
);
1911 const GURL
url1("http://foo1");
1912 main_test_rfh()->SendNavigate(0, url1
);
1913 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1914 navigation_entry_committed_counter_
= 0;
1916 const GURL
url2("http://foo2");
1917 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1920 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
1921 params
.should_update_history
= false;
1922 params
.gesture
= NavigationGestureUser
;
1923 params
.is_post
= false;
1924 params
.page_state
= PageState::CreateFromURL(url2
);
1926 LoadCommittedDetails details
;
1927 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1929 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1930 navigation_entry_committed_counter_
= 0;
1931 EXPECT_EQ(url1
, details
.previous_url
);
1932 EXPECT_FALSE(details
.is_in_page
);
1933 EXPECT_FALSE(details
.is_main_frame
);
1935 // The new entry should be appended.
1936 EXPECT_EQ(2, controller
.GetEntryCount());
1938 // New entry should refer to the new page, but the old URL (entries only
1939 // reflect the toplevel URL).
1940 EXPECT_EQ(url1
, details
.entry
->GetURL());
1941 EXPECT_EQ(params
.page_id
, details
.entry
->GetPageID());
1944 // Some pages create a popup, then write an iframe into it. This causes a
1945 // subframe navigation without having any committed entry. Such navigations
1946 // just get thrown on the ground, but we shouldn't crash.
1947 TEST_F(NavigationControllerTest
, SubframeOnEmptyPage
) {
1948 NavigationControllerImpl
& controller
= controller_impl();
1949 TestNotificationTracker notifications
;
1950 RegisterForAllNavNotifications(¬ifications
, &controller
);
1952 // Navigation controller currently has no entries.
1953 const GURL
url("http://foo2");
1954 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1957 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
1958 params
.should_update_history
= false;
1959 params
.gesture
= NavigationGestureAuto
;
1960 params
.is_post
= false;
1961 params
.page_state
= PageState::CreateFromURL(url
);
1963 LoadCommittedDetails details
;
1964 EXPECT_FALSE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1966 EXPECT_EQ(0U, notifications
.size());
1969 // Auto subframes are ones the page loads automatically like ads. They should
1970 // not create new navigation entries.
1971 TEST_F(NavigationControllerTest
, AutoSubframe
) {
1972 NavigationControllerImpl
& controller
= controller_impl();
1973 TestNotificationTracker notifications
;
1974 RegisterForAllNavNotifications(¬ifications
, &controller
);
1976 const GURL
url1("http://foo1");
1977 main_test_rfh()->SendNavigate(0, url1
);
1978 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1979 navigation_entry_committed_counter_
= 0;
1981 const GURL
url2("http://foo2");
1982 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1985 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
1986 params
.should_update_history
= false;
1987 params
.gesture
= NavigationGestureUser
;
1988 params
.is_post
= false;
1989 params
.page_state
= PageState::CreateFromURL(url2
);
1991 // Navigating should do nothing.
1992 LoadCommittedDetails details
;
1993 EXPECT_FALSE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1995 EXPECT_EQ(0U, notifications
.size());
1997 // There should still be only one entry.
1998 EXPECT_EQ(1, controller
.GetEntryCount());
2001 // Tests navigation and then going back to a subframe navigation.
2002 TEST_F(NavigationControllerTest
, BackSubframe
) {
2003 NavigationControllerImpl
& controller
= controller_impl();
2004 TestNotificationTracker notifications
;
2005 RegisterForAllNavNotifications(¬ifications
, &controller
);
2008 const GURL
url1("http://foo1");
2009 main_test_rfh()->SendNavigate(0, url1
);
2010 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2011 navigation_entry_committed_counter_
= 0;
2013 // First manual subframe navigation.
2014 const GURL
url2("http://foo2");
2015 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2018 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2019 params
.should_update_history
= false;
2020 params
.gesture
= NavigationGestureUser
;
2021 params
.is_post
= false;
2022 params
.page_state
= PageState::CreateFromURL(url2
);
2024 // This should generate a new entry.
2025 LoadCommittedDetails details
;
2026 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2028 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2029 navigation_entry_committed_counter_
= 0;
2030 EXPECT_EQ(2, controller
.GetEntryCount());
2032 // Second manual subframe navigation should also make a new entry.
2033 const GURL
url3("http://foo3");
2036 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
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(3, controller
.GetEntryCount());
2042 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2045 controller
.GoBack();
2048 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2049 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2051 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2052 navigation_entry_committed_counter_
= 0;
2053 EXPECT_EQ(3, controller
.GetEntryCount());
2054 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2055 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2056 EXPECT_FALSE(controller
.GetPendingEntry());
2058 // Go back one more.
2059 controller
.GoBack();
2062 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2063 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2065 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2066 navigation_entry_committed_counter_
= 0;
2067 EXPECT_EQ(3, controller
.GetEntryCount());
2068 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2069 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2070 EXPECT_FALSE(controller
.GetPendingEntry());
2073 TEST_F(NavigationControllerTest
, LinkClick
) {
2074 NavigationControllerImpl
& controller
= controller_impl();
2075 TestNotificationTracker notifications
;
2076 RegisterForAllNavNotifications(¬ifications
, &controller
);
2078 const GURL
url1("http://foo1");
2079 const GURL
url2("http://foo2");
2081 main_test_rfh()->SendNavigate(0, url1
);
2082 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2083 navigation_entry_committed_counter_
= 0;
2085 main_test_rfh()->SendNavigate(1, url2
);
2086 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2087 navigation_entry_committed_counter_
= 0;
2089 // Should not have produced a new session history entry.
2090 EXPECT_EQ(controller
.GetEntryCount(), 2);
2091 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2092 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2093 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2094 EXPECT_FALSE(controller
.GetPendingEntry());
2095 EXPECT_TRUE(controller
.CanGoBack());
2096 EXPECT_FALSE(controller
.CanGoForward());
2099 TEST_F(NavigationControllerTest
, InPage
) {
2100 NavigationControllerImpl
& controller
= controller_impl();
2101 TestNotificationTracker notifications
;
2102 RegisterForAllNavNotifications(¬ifications
, &controller
);
2105 const GURL
url1("http://foo");
2106 main_test_rfh()->SendNavigate(0, url1
);
2107 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2108 navigation_entry_committed_counter_
= 0;
2110 // Ensure main page navigation to same url respects the was_within_same_page
2111 // hint provided in the params.
2112 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2113 self_params
.page_id
= 0;
2114 self_params
.url
= url1
;
2115 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2116 self_params
.should_update_history
= false;
2117 self_params
.gesture
= NavigationGestureUser
;
2118 self_params
.is_post
= false;
2119 self_params
.page_state
= PageState::CreateFromURL(url1
);
2120 self_params
.was_within_same_page
= true;
2122 LoadCommittedDetails details
;
2123 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2125 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2126 navigation_entry_committed_counter_
= 0;
2127 EXPECT_TRUE(details
.is_in_page
);
2128 EXPECT_TRUE(details
.did_replace_entry
);
2129 EXPECT_EQ(1, controller
.GetEntryCount());
2131 // Fragment navigation to a new page_id.
2132 const GURL
url2("http://foo#a");
2133 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2136 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2137 params
.should_update_history
= false;
2138 params
.gesture
= NavigationGestureUser
;
2139 params
.is_post
= false;
2140 params
.page_state
= PageState::CreateFromURL(url2
);
2141 params
.was_within_same_page
= true;
2143 // This should generate a new entry.
2144 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2146 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2147 navigation_entry_committed_counter_
= 0;
2148 EXPECT_TRUE(details
.is_in_page
);
2149 EXPECT_FALSE(details
.did_replace_entry
);
2150 EXPECT_EQ(2, controller
.GetEntryCount());
2153 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2154 controller
.GoBack();
2155 back_params
.url
= url1
;
2156 back_params
.page_id
= 0;
2157 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2159 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2160 navigation_entry_committed_counter_
= 0;
2161 EXPECT_TRUE(details
.is_in_page
);
2162 EXPECT_EQ(2, controller
.GetEntryCount());
2163 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2164 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2167 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2168 controller
.GoForward();
2169 forward_params
.url
= url2
;
2170 forward_params
.page_id
= 1;
2171 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2173 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2174 navigation_entry_committed_counter_
= 0;
2175 EXPECT_TRUE(details
.is_in_page
);
2176 EXPECT_EQ(2, controller
.GetEntryCount());
2177 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2178 EXPECT_EQ(forward_params
.url
,
2179 controller
.GetVisibleEntry()->GetURL());
2181 // Now go back and forward again. This is to work around a bug where we would
2182 // compare the incoming URL with the last committed entry rather than the
2183 // one identified by an existing page ID. This would result in the second URL
2184 // losing the reference fragment when you navigate away from it and then back.
2185 controller
.GoBack();
2186 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2188 controller
.GoForward();
2189 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2191 EXPECT_EQ(forward_params
.url
,
2192 controller
.GetVisibleEntry()->GetURL());
2194 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2195 const GURL
url3("http://bar");
2198 navigation_entry_committed_counter_
= 0;
2199 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2201 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2202 navigation_entry_committed_counter_
= 0;
2203 EXPECT_FALSE(details
.is_in_page
);
2204 EXPECT_EQ(3, controller
.GetEntryCount());
2205 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2208 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2209 NavigationControllerImpl
& controller
= controller_impl();
2210 TestNotificationTracker notifications
;
2211 RegisterForAllNavNotifications(¬ifications
, &controller
);
2214 const GURL
url1("http://foo");
2215 main_test_rfh()->SendNavigate(0, url1
);
2216 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2217 navigation_entry_committed_counter_
= 0;
2219 // First navigation.
2220 const GURL
url2("http://foo#a");
2221 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2222 params
.page_id
= 0; // Same page_id
2224 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2225 params
.should_update_history
= false;
2226 params
.gesture
= NavigationGestureUser
;
2227 params
.is_post
= false;
2228 params
.page_state
= PageState::CreateFromURL(url2
);
2229 params
.was_within_same_page
= true;
2231 // This should NOT generate a new entry, nor prune the list.
2232 LoadCommittedDetails details
;
2233 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2235 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2236 navigation_entry_committed_counter_
= 0;
2237 EXPECT_TRUE(details
.is_in_page
);
2238 EXPECT_TRUE(details
.did_replace_entry
);
2239 EXPECT_EQ(1, controller
.GetEntryCount());
2242 // Tests for http://crbug.com/40395
2245 // window.location.replace("#a");
2246 // window.location='http://foo3/';
2248 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2249 NavigationControllerImpl
& controller
= controller_impl();
2250 TestNotificationTracker notifications
;
2251 RegisterForAllNavNotifications(¬ifications
, &controller
);
2253 // Load an initial page.
2255 const GURL
url("http://foo/");
2256 main_test_rfh()->SendNavigate(0, url
);
2257 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2258 navigation_entry_committed_counter_
= 0;
2261 // Navigate to a new page.
2263 const GURL
url("http://foo2/");
2264 main_test_rfh()->SendNavigate(1, url
);
2265 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2266 navigation_entry_committed_counter_
= 0;
2269 // Navigate within the page.
2271 const GURL
url("http://foo2/#a");
2272 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2273 params
.page_id
= 1; // Same page_id
2275 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2276 params
.redirects
.push_back(url
);
2277 params
.should_update_history
= true;
2278 params
.gesture
= NavigationGestureUnknown
;
2279 params
.is_post
= false;
2280 params
.page_state
= PageState::CreateFromURL(url
);
2281 params
.was_within_same_page
= true;
2283 // This should NOT generate a new entry, nor prune the list.
2284 LoadCommittedDetails details
;
2285 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2287 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2288 navigation_entry_committed_counter_
= 0;
2289 EXPECT_TRUE(details
.is_in_page
);
2290 EXPECT_TRUE(details
.did_replace_entry
);
2291 EXPECT_EQ(2, controller
.GetEntryCount());
2294 // Perform a client redirect to a new page.
2296 const GURL
url("http://foo3/");
2297 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2298 params
.page_id
= 2; // New page_id
2300 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2301 params
.redirects
.push_back(GURL("http://foo2/#a"));
2302 params
.redirects
.push_back(url
);
2303 params
.should_update_history
= true;
2304 params
.gesture
= NavigationGestureUnknown
;
2305 params
.is_post
= false;
2306 params
.page_state
= PageState::CreateFromURL(url
);
2308 // This SHOULD generate a new entry.
2309 LoadCommittedDetails details
;
2310 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2312 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2313 navigation_entry_committed_counter_
= 0;
2314 EXPECT_FALSE(details
.is_in_page
);
2315 EXPECT_EQ(3, controller
.GetEntryCount());
2318 // Verify that BACK brings us back to http://foo2/.
2320 const GURL
url("http://foo2/");
2321 controller
.GoBack();
2322 main_test_rfh()->SendNavigate(1, url
);
2323 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2324 navigation_entry_committed_counter_
= 0;
2325 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2329 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2331 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2332 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2333 GURL
url("http://foo");
2336 params
.page_state
= PageState::CreateFromURL(url
);
2337 params
.was_within_same_page
= true;
2338 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2339 // We pass if we don't crash.
2342 // NotificationObserver implementation used in verifying we've received the
2343 // NOTIFICATION_NAV_LIST_PRUNED method.
2344 class PrunedListener
: public NotificationObserver
{
2346 explicit PrunedListener(NavigationControllerImpl
* controller
)
2347 : notification_count_(0) {
2348 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2349 Source
<NavigationController
>(controller
));
2352 void Observe(int type
,
2353 const NotificationSource
& source
,
2354 const NotificationDetails
& details
) override
{
2355 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2356 notification_count_
++;
2357 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2361 // Number of times NAV_LIST_PRUNED has been observed.
2362 int notification_count_
;
2364 // Details from the last NAV_LIST_PRUNED.
2365 PrunedDetails details_
;
2368 NotificationRegistrar registrar_
;
2370 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2373 // Tests that we limit the number of navigation entries created correctly.
2374 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2375 NavigationControllerImpl
& controller
= controller_impl();
2376 size_t original_count
= NavigationControllerImpl::max_entry_count();
2377 const int kMaxEntryCount
= 5;
2379 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2382 // Load up to the max count, all entries should be there.
2383 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2384 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2386 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2387 main_test_rfh()->PrepareForCommit(url
);
2388 main_test_rfh()->SendNavigate(url_index
, url
);
2391 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2393 // Created a PrunedListener to observe prune notifications.
2394 PrunedListener
listener(&controller
);
2396 // Navigate some more.
2397 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2399 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2400 main_test_rfh()->PrepareForCommit(url
);
2401 main_test_rfh()->SendNavigate(url_index
, url
);
2404 // We should have got a pruned navigation.
2405 EXPECT_EQ(1, listener
.notification_count_
);
2406 EXPECT_TRUE(listener
.details_
.from_front
);
2407 EXPECT_EQ(1, listener
.details_
.count
);
2409 // We expect http://www.a.com/0 to be gone.
2410 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2411 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2412 GURL("http:////www.a.com/1"));
2414 // More navigations.
2415 for (int i
= 0; i
< 3; i
++) {
2416 url
= GURL(base::StringPrintf("http:////www.a.com/%d", url_index
));
2418 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2419 main_test_rfh()->PrepareForCommit(url
);
2420 main_test_rfh()->SendNavigate(url_index
, url
);
2423 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2424 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2425 GURL("http:////www.a.com/4"));
2427 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2430 // Tests that we can do a restore and navigate to the restored entries and
2431 // everything is updated properly. This can be tricky since there is no
2432 // SiteInstance for the entries created initially.
2433 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2434 // Create a NavigationController with a restored set of tabs.
2435 GURL
url("http://foo");
2436 std::vector
<NavigationEntry
*> entries
;
2437 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
2438 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2440 entry
->SetPageID(0);
2441 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2442 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2443 const base::Time timestamp
= base::Time::Now();
2444 entry
->SetTimestamp(timestamp
);
2445 entries
.push_back(entry
);
2446 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2447 WebContents::Create(WebContents::CreateParams(browser_context()))));
2448 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2449 our_controller
.Restore(
2451 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2453 ASSERT_EQ(0u, entries
.size());
2455 // Before navigating to the restored entry, it should have a restore_type
2456 // and no SiteInstance.
2457 ASSERT_EQ(1, our_controller
.GetEntryCount());
2458 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2459 our_controller
.GetEntryAtIndex(0)->restore_type());
2460 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2462 // After navigating, we should have one entry, and it should be "pending".
2463 // It should now have a SiteInstance and no restore_type.
2464 our_controller
.GoToIndex(0);
2465 EXPECT_EQ(1, our_controller
.GetEntryCount());
2466 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2467 our_controller
.GetPendingEntry());
2468 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2469 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2470 our_controller
.GetEntryAtIndex(0)->restore_type());
2471 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2473 // Timestamp should remain the same before the navigation finishes.
2474 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2476 // Say we navigated to that entry.
2477 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2480 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2481 params
.should_update_history
= false;
2482 params
.gesture
= NavigationGestureUser
;
2483 params
.is_post
= false;
2484 params
.page_state
= PageState::CreateFromURL(url
);
2485 LoadCommittedDetails details
;
2486 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2489 // There should be no longer any pending entry and one committed one. This
2490 // means that we were able to locate the entry, assign its site instance, and
2491 // commit it properly.
2492 EXPECT_EQ(1, our_controller
.GetEntryCount());
2493 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2494 EXPECT_FALSE(our_controller
.GetPendingEntry());
2497 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2498 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2499 our_controller
.GetEntryAtIndex(0)->restore_type());
2501 // Timestamp should have been updated.
2502 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2505 // Tests that we can still navigate to a restored entry after a different
2506 // navigation fails and clears the pending entry. http://crbug.com/90085
2507 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2508 // Create a NavigationController with a restored set of tabs.
2509 GURL
url("http://foo");
2510 std::vector
<NavigationEntry
*> entries
;
2511 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
2512 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2514 entry
->SetPageID(0);
2515 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2516 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2517 entries
.push_back(entry
);
2518 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2519 WebContents::Create(WebContents::CreateParams(browser_context()))));
2520 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2521 our_controller
.Restore(
2522 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2523 ASSERT_EQ(0u, entries
.size());
2525 // Before navigating to the restored entry, it should have a restore_type
2526 // and no SiteInstance.
2527 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2528 our_controller
.GetEntryAtIndex(0)->restore_type());
2529 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2531 // After navigating, we should have one entry, and it should be "pending".
2532 // It should now have a SiteInstance and no restore_type.
2533 our_controller
.GoToIndex(0);
2534 EXPECT_EQ(1, our_controller
.GetEntryCount());
2535 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2536 our_controller
.GetPendingEntry());
2537 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2538 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2539 our_controller
.GetEntryAtIndex(0)->restore_type());
2540 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2542 // This pending navigation may have caused a different navigation to fail,
2543 // which causes the pending entry to be cleared.
2544 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2545 fail_load_params
.error_code
= net::ERR_ABORTED
;
2546 fail_load_params
.error_description
= base::string16();
2547 fail_load_params
.url
= url
;
2548 fail_load_params
.showing_repost_interstitial
= false;
2549 main_test_rfh()->OnMessageReceived(
2550 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2553 // Now the pending restored entry commits.
2554 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2557 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2558 params
.should_update_history
= false;
2559 params
.gesture
= NavigationGestureUser
;
2560 params
.is_post
= false;
2561 params
.page_state
= PageState::CreateFromURL(url
);
2562 LoadCommittedDetails details
;
2563 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2566 // There should be no pending entry and one committed one.
2567 EXPECT_EQ(1, our_controller
.GetEntryCount());
2568 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2569 EXPECT_FALSE(our_controller
.GetPendingEntry());
2572 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2573 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2574 our_controller
.GetEntryAtIndex(0)->restore_type());
2577 // Make sure that the page type and stuff is correct after an interstitial.
2578 TEST_F(NavigationControllerTest
, Interstitial
) {
2579 NavigationControllerImpl
& controller
= controller_impl();
2580 // First navigate somewhere normal.
2581 const GURL
url1("http://foo");
2583 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2584 main_test_rfh()->PrepareForCommit(url1
);
2585 main_test_rfh()->SendNavigate(0, url1
);
2587 // Now navigate somewhere with an interstitial.
2588 const GURL
url2("http://bar");
2590 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2591 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2593 // At this point the interstitial will be displayed and the load will still
2594 // be pending. If the user continues, the load will commit.
2595 main_test_rfh()->PrepareForCommit(url2
);
2596 main_test_rfh()->SendNavigate(1, url2
);
2598 // The page should be a normal page again.
2599 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2600 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2601 controller
.GetLastCommittedEntry()->GetPageType());
2604 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2605 NavigationControllerImpl
& controller
= controller_impl();
2606 const GURL
url1("http://foo/1");
2607 const GURL
url2("http://foo/2");
2608 const GURL
url3("http://foo/3");
2609 const GURL
url4("http://foo/4");
2610 const GURL
url5("http://foo/5");
2611 const GURL
pending_url("http://foo/pending");
2612 const GURL
default_url("http://foo/default");
2615 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2616 main_test_rfh()->PrepareForCommit(url1
);
2617 main_test_rfh()->SendNavigate(0, url1
);
2619 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2620 main_test_rfh()->PrepareForCommit(url2
);
2621 main_test_rfh()->SendNavigate(1, url2
);
2623 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2624 main_test_rfh()->PrepareForCommit(url3
);
2625 main_test_rfh()->SendNavigate(2, url3
);
2627 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2628 main_test_rfh()->PrepareForCommit(url4
);
2629 main_test_rfh()->SendNavigate(3, url4
);
2631 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2632 main_test_rfh()->PrepareForCommit(url5
);
2633 main_test_rfh()->SendNavigate(4, url5
);
2635 // Try to remove the last entry. Will fail because it is the current entry.
2636 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2637 EXPECT_EQ(5, controller
.GetEntryCount());
2638 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2640 // Go back, but don't commit yet. Check that we can't delete the current
2641 // and pending entries.
2642 controller
.GoBack();
2643 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2644 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
2646 // Now commit and delete the last entry.
2647 main_test_rfh()->PrepareForCommit(url4
);
2648 main_test_rfh()->SendNavigate(3, url4
);
2649 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2650 EXPECT_EQ(4, controller
.GetEntryCount());
2651 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
2652 EXPECT_FALSE(controller
.GetPendingEntry());
2654 // Remove an entry which is not the last committed one.
2655 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
2656 EXPECT_EQ(3, controller
.GetEntryCount());
2657 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
2658 EXPECT_FALSE(controller
.GetPendingEntry());
2660 // Remove the 2 remaining entries.
2661 controller
.RemoveEntryAtIndex(1);
2662 controller
.RemoveEntryAtIndex(0);
2664 // This should leave us with only the last committed entry.
2665 EXPECT_EQ(1, controller
.GetEntryCount());
2666 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
2669 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
2670 NavigationControllerImpl
& controller
= controller_impl();
2671 const GURL
url1("http://foo/1");
2672 const GURL
url2("http://foo/2");
2673 const GURL
url3("http://foo/3");
2674 const GURL
default_url("http://foo/default");
2677 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2678 main_test_rfh()->PrepareForCommit(url1
);
2679 main_test_rfh()->SendNavigate(0, url1
);
2681 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2682 main_test_rfh()->PrepareForCommit(url2
);
2683 main_test_rfh()->SendNavigate(1, url2
);
2685 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2686 main_test_rfh()->PrepareForCommit(url3
);
2687 main_test_rfh()->SendNavigate(2, url3
);
2689 // Go back, but don't commit yet. Check that we can't delete the current
2690 // and pending entries.
2691 controller
.GoBack();
2692 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
2693 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
2695 // Remove the first entry, while there is a pending entry. This is expected
2696 // to discard the pending entry.
2697 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
2698 EXPECT_FALSE(controller
.GetPendingEntry());
2699 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2701 // We should update the last committed entry index.
2702 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
2704 // Now commit and ensure we land on the right entry.
2705 main_test_rfh()->PrepareForCommit(url2
);
2706 main_test_rfh()->SendNavigate(1, url2
);
2707 EXPECT_EQ(2, controller
.GetEntryCount());
2708 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
2709 EXPECT_FALSE(controller
.GetPendingEntry());
2712 // Tests the transient entry, making sure it goes away with all navigations.
2713 TEST_F(NavigationControllerTest
, TransientEntry
) {
2714 NavigationControllerImpl
& controller
= controller_impl();
2715 TestNotificationTracker notifications
;
2716 RegisterForAllNavNotifications(¬ifications
, &controller
);
2718 const GURL
url0("http://foo/0");
2719 const GURL
url1("http://foo/1");
2720 const GURL
url2("http://foo/2");
2721 const GURL
url3("http://foo/3");
2722 const GURL
url3_ref("http://foo/3#bar");
2723 const GURL
url4("http://foo/4");
2724 const GURL
transient_url("http://foo/transient");
2727 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2728 main_test_rfh()->PrepareForCommit(url0
);
2729 main_test_rfh()->SendNavigate(0, url0
);
2731 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2732 main_test_rfh()->PrepareForCommit(url1
);
2733 main_test_rfh()->SendNavigate(1, url1
);
2735 notifications
.Reset();
2737 // Adding a transient with no pending entry.
2738 NavigationEntryImpl
* transient_entry
= new NavigationEntryImpl
;
2739 transient_entry
->SetURL(transient_url
);
2740 controller
.SetTransientEntry(transient_entry
);
2742 // We should not have received any notifications.
2743 EXPECT_EQ(0U, notifications
.size());
2746 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2747 EXPECT_EQ(controller
.GetEntryCount(), 3);
2748 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2749 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2750 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2751 EXPECT_FALSE(controller
.GetPendingEntry());
2752 EXPECT_TRUE(controller
.CanGoBack());
2753 EXPECT_FALSE(controller
.CanGoForward());
2754 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2758 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2759 main_test_rfh()->PrepareForCommit(url2
);
2760 main_test_rfh()->SendNavigate(2, url2
);
2762 // We should have navigated, transient entry should be gone.
2763 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
2764 EXPECT_EQ(controller
.GetEntryCount(), 3);
2766 // Add a transient again, then navigate with no pending entry this time.
2767 transient_entry
= new NavigationEntryImpl
;
2768 transient_entry
->SetURL(transient_url
);
2769 controller
.SetTransientEntry(transient_entry
);
2770 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2771 main_test_rfh()->PrepareForCommit(url3
);
2772 main_test_rfh()->SendNavigate(3, url3
);
2773 // Transient entry should be gone.
2774 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
2775 EXPECT_EQ(controller
.GetEntryCount(), 4);
2777 // Initiate a navigation, add a transient then commit navigation.
2779 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2780 transient_entry
= new NavigationEntryImpl
;
2781 transient_entry
->SetURL(transient_url
);
2782 controller
.SetTransientEntry(transient_entry
);
2783 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2784 main_test_rfh()->PrepareForCommit(url4
);
2785 main_test_rfh()->SendNavigate(4, url4
);
2786 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
2787 EXPECT_EQ(controller
.GetEntryCount(), 5);
2789 // Add a transient and go back. This should simply remove the transient.
2790 transient_entry
= new NavigationEntryImpl
;
2791 transient_entry
->SetURL(transient_url
);
2792 controller
.SetTransientEntry(transient_entry
);
2793 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2794 EXPECT_TRUE(controller
.CanGoBack());
2795 EXPECT_FALSE(controller
.CanGoForward());
2796 controller
.GoBack();
2797 // Transient entry should be gone.
2798 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
2799 EXPECT_EQ(controller
.GetEntryCount(), 5);
2800 main_test_rfh()->PrepareForCommit(url3
);
2801 main_test_rfh()->SendNavigate(3, url3
);
2803 // Add a transient and go to an entry before the current one.
2804 transient_entry
= new NavigationEntryImpl
;
2805 transient_entry
->SetURL(transient_url
);
2806 controller
.SetTransientEntry(transient_entry
);
2807 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2808 controller
.GoToIndex(1);
2809 // The navigation should have been initiated, transient entry should be gone.
2810 EXPECT_FALSE(controller
.GetTransientEntry());
2811 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
2812 // Visible entry does not update for history navigations until commit.
2813 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
2814 main_test_rfh()->PrepareForCommit(url1
);
2815 main_test_rfh()->SendNavigate(1, url1
);
2816 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
2818 // Add a transient and go to an entry after the current one.
2819 transient_entry
= new NavigationEntryImpl
;
2820 transient_entry
->SetURL(transient_url
);
2821 controller
.SetTransientEntry(transient_entry
);
2822 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2823 controller
.GoToIndex(3);
2824 // The navigation should have been initiated, transient entry should be gone.
2825 // Because of the transient entry that is removed, going to index 3 makes us
2826 // land on url2 (which is visible after the commit).
2827 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
2828 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
2829 main_test_rfh()->PrepareForCommit(url2
);
2830 main_test_rfh()->SendNavigate(2, url2
);
2831 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
2833 // Add a transient and go forward.
2834 transient_entry
= new NavigationEntryImpl
;
2835 transient_entry
->SetURL(transient_url
);
2836 controller
.SetTransientEntry(transient_entry
);
2837 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2838 EXPECT_TRUE(controller
.CanGoForward());
2839 controller
.GoForward();
2840 // We should have navigated, transient entry should be gone.
2841 EXPECT_FALSE(controller
.GetTransientEntry());
2842 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
2843 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
2844 main_test_rfh()->PrepareForCommit(url3
);
2845 main_test_rfh()->SendNavigate(3, url3
);
2846 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
2848 // Add a transient and do an in-page navigation, replacing the current entry.
2849 transient_entry
= new NavigationEntryImpl
;
2850 transient_entry
->SetURL(transient_url
);
2851 controller
.SetTransientEntry(transient_entry
);
2852 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2853 main_test_rfh()->PrepareForCommit(url3_ref
);
2854 main_test_rfh()->SendNavigate(3, url3_ref
);
2855 // Transient entry should be gone.
2856 EXPECT_FALSE(controller
.GetTransientEntry());
2857 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
2859 // Ensure the URLs are correct.
2860 EXPECT_EQ(controller
.GetEntryCount(), 5);
2861 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
2862 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
2863 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
2864 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
2865 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
2868 // Test that Reload initiates a new navigation to a transient entry's URL.
2869 TEST_F(NavigationControllerTest
, ReloadTransient
) {
2870 NavigationControllerImpl
& controller
= controller_impl();
2871 const GURL
url0("http://foo/0");
2872 const GURL
url1("http://foo/1");
2873 const GURL
transient_url("http://foo/transient");
2875 // Load |url0|, and start a pending navigation to |url1|.
2877 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2878 main_test_rfh()->PrepareForCommit(url0
);
2879 main_test_rfh()->SendNavigate(0, url0
);
2881 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2883 // A transient entry is added, interrupting the navigation.
2884 NavigationEntryImpl
* transient_entry
= new NavigationEntryImpl
;
2885 transient_entry
->SetURL(transient_url
);
2886 controller
.SetTransientEntry(transient_entry
);
2887 EXPECT_TRUE(controller
.GetTransientEntry());
2888 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2890 // The page is reloaded, which should remove the pending entry for |url1| and
2891 // the transient entry for |transient_url|, and start a navigation to
2893 controller
.Reload(true);
2894 EXPECT_FALSE(controller
.GetTransientEntry());
2895 EXPECT_TRUE(controller
.GetPendingEntry());
2896 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
2897 ASSERT_EQ(controller
.GetEntryCount(), 1);
2898 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
2900 // Load of |transient_url| completes.
2901 main_test_rfh()->PrepareForCommit(transient_url
);
2902 main_test_rfh()->SendNavigate(1, transient_url
);
2903 ASSERT_EQ(controller
.GetEntryCount(), 2);
2904 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
2905 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
2908 // Ensure that renderer initiated pending entries get replaced, so that we
2909 // don't show a stale virtual URL when a navigation commits.
2910 // See http://crbug.com/266922.
2911 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
2912 NavigationControllerImpl
& controller
= controller_impl();
2913 Navigator
* navigator
=
2914 contents()->GetFrameTree()->root()->navigator();
2916 const GURL
url1("nonexistent:12121");
2917 const GURL
url1_fixed("http://nonexistent:12121/");
2918 const GURL
url2("http://foo");
2920 // We create pending entries for renderer-initiated navigations so that we
2921 // can show them in new tabs when it is safe.
2922 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
, false);
2924 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2925 // the virtual URL to differ from the URL.
2926 controller
.GetPendingEntry()->SetURL(url1_fixed
);
2927 controller
.GetPendingEntry()->SetVirtualURL(url1
);
2929 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
2930 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
2931 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
2933 // If the user clicks another link, we should replace the pending entry.
2934 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
, false);
2935 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
2936 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
2938 // Once it commits, the URL and virtual URL should reflect the actual page.
2939 main_test_rfh()->SendNavigate(0, url2
);
2940 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2941 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
2943 // We should not replace the pending entry for an error URL.
2944 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
, false);
2945 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
2946 navigator
->DidStartProvisionalLoad(main_test_rfh(),
2947 GURL(kUnreachableWebDataURL
), false);
2948 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
2950 // We should remember if the pending entry will replace the current one.
2951 // http://crbug.com/308444.
2952 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
, false);
2953 controller
.GetPendingEntry()->set_should_replace_entry(true);
2954 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
, false);
2955 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
2956 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2957 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2958 // to the main frame.
2959 main_test_rfh()->SendNavigate(0, url2
);
2960 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2963 // Tests that the URLs for renderer-initiated navigations are not displayed to
2964 // the user until the navigation commits, to prevent URL spoof attacks.
2965 // See http://crbug.com/99016.
2966 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
2967 NavigationControllerImpl
& controller
= controller_impl();
2968 TestNotificationTracker notifications
;
2969 RegisterForAllNavNotifications(¬ifications
, &controller
);
2971 const GURL
url0("http://foo/0");
2972 const GURL
url1("http://foo/1");
2974 // For typed navigations (browser-initiated), both pending and visible entries
2975 // should update before commit.
2977 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2978 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
2979 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
2980 main_test_rfh()->PrepareForCommit(url0
);
2981 main_test_rfh()->SendNavigate(0, url0
);
2983 // For link clicks (renderer-initiated navigations), the pending entry should
2984 // update before commit but the visible should not.
2985 NavigationController::LoadURLParams
load_url_params(url1
);
2986 load_url_params
.is_renderer_initiated
= true;
2987 controller
.LoadURLWithParams(load_url_params
);
2988 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
2989 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
2990 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
2992 // After commit, both visible should be updated, there should be no pending
2993 // entry, and we should no longer treat the entry as renderer-initiated.
2994 main_test_rfh()->PrepareForCommit(url1
);
2995 main_test_rfh()->SendNavigate(1, url1
);
2996 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
2997 EXPECT_FALSE(controller
.GetPendingEntry());
2998 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3000 notifications
.Reset();
3003 // Tests that the URLs for renderer-initiated navigations in new tabs are
3004 // displayed to the user before commit, as long as the initial about:blank
3005 // page has not been modified. If so, we must revert to showing about:blank.
3006 // See http://crbug.com/9682.
3007 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3008 NavigationControllerImpl
& controller
= controller_impl();
3009 TestNotificationTracker notifications
;
3010 RegisterForAllNavNotifications(¬ifications
, &controller
);
3012 const GURL
url("http://foo");
3014 // For renderer-initiated navigations in new tabs (with no committed entries),
3015 // we show the pending entry's URL as long as the about:blank page is not
3017 NavigationController::LoadURLParams
load_url_params(url
);
3018 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3019 load_url_params
.is_renderer_initiated
= true;
3020 controller
.LoadURLWithParams(load_url_params
);
3021 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3022 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3023 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3024 EXPECT_TRUE(controller
.IsInitialNavigation());
3025 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3027 // There should be no title yet.
3028 EXPECT_TRUE(contents()->GetTitle().empty());
3030 // If something else modifies the contents of the about:blank page, then
3031 // we must revert to showing about:blank to avoid a URL spoof.
3032 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3033 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3034 EXPECT_FALSE(controller
.GetVisibleEntry());
3035 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3037 notifications
.Reset();
3040 // Tests that the URLs for browser-initiated navigations in new tabs are
3041 // displayed to the user even after they fail, as long as the initial
3042 // about:blank page has not been modified. If so, we must revert to showing
3043 // about:blank. See http://crbug.com/355537.
3044 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3045 NavigationControllerImpl
& controller
= controller_impl();
3046 TestNotificationTracker notifications
;
3047 RegisterForAllNavNotifications(¬ifications
, &controller
);
3049 const GURL
url("http://foo");
3051 // For browser-initiated navigations in new tabs (with no committed entries),
3052 // we show the pending entry's URL as long as the about:blank page is not
3053 // modified. This is possible in cases that the user types a URL into a popup
3054 // tab created with a slow URL.
3055 NavigationController::LoadURLParams
load_url_params(url
);
3056 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3057 load_url_params
.is_renderer_initiated
= false;
3058 controller
.LoadURLWithParams(load_url_params
);
3059 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3060 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3061 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3062 EXPECT_TRUE(controller
.IsInitialNavigation());
3063 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3065 // There should be no title yet.
3066 EXPECT_TRUE(contents()->GetTitle().empty());
3068 // Suppose it aborts before committing, if it's a 204 or download or due to a
3069 // stop or a new navigation from the user. The URL should remain visible.
3070 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3071 params
.error_code
= net::ERR_ABORTED
;
3072 params
.error_description
= base::string16();
3074 params
.showing_repost_interstitial
= false;
3075 main_test_rfh()->OnMessageReceived(
3076 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3077 contents()->SetIsLoading(test_rvh(), false, true, NULL
);
3078 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3080 // If something else later modifies the contents of the about:blank page, then
3081 // we must revert to showing about:blank to avoid a URL spoof.
3082 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3083 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3084 EXPECT_FALSE(controller
.GetVisibleEntry());
3085 EXPECT_FALSE(controller
.GetPendingEntry());
3087 notifications
.Reset();
3090 // Tests that the URLs for renderer-initiated navigations in new tabs are
3091 // displayed to the user even after they fail, as long as the initial
3092 // about:blank page has not been modified. If so, we must revert to showing
3093 // about:blank. See http://crbug.com/355537.
3094 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3095 NavigationControllerImpl
& controller
= controller_impl();
3096 TestNotificationTracker notifications
;
3097 RegisterForAllNavNotifications(¬ifications
, &controller
);
3099 const GURL
url("http://foo");
3101 // For renderer-initiated navigations in new tabs (with no committed entries),
3102 // we show the pending entry's URL as long as the about:blank page is not
3104 NavigationController::LoadURLParams
load_url_params(url
);
3105 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3106 load_url_params
.is_renderer_initiated
= true;
3107 controller
.LoadURLWithParams(load_url_params
);
3108 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3109 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3110 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3111 EXPECT_TRUE(controller
.IsInitialNavigation());
3112 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3114 // There should be no title yet.
3115 EXPECT_TRUE(contents()->GetTitle().empty());
3117 // Suppose it aborts before committing, if it's a 204 or download or due to a
3118 // stop or a new navigation from the user. The URL should remain visible.
3119 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3120 params
.error_code
= net::ERR_ABORTED
;
3121 params
.error_description
= base::string16();
3123 params
.showing_repost_interstitial
= false;
3124 main_test_rfh()->OnMessageReceived(
3125 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3126 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3128 // If something else later modifies the contents of the about:blank page, then
3129 // we must revert to showing about:blank to avoid a URL spoof.
3130 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3131 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3132 EXPECT_FALSE(controller
.GetVisibleEntry());
3133 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3135 notifications
.Reset();
3138 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3139 NavigationControllerImpl
& controller
= controller_impl();
3140 TestNotificationTracker notifications
;
3141 RegisterForAllNavNotifications(¬ifications
, &controller
);
3143 const GURL
url1("http://foo/eh");
3144 const GURL
url2("http://foo/bee");
3146 // For renderer-initiated navigations in new tabs (with no committed entries),
3147 // we show the pending entry's URL as long as the about:blank page is not
3149 NavigationController::LoadURLParams
load_url_params(url1
);
3150 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3151 load_url_params
.is_renderer_initiated
= true;
3152 controller
.LoadURLWithParams(load_url_params
);
3153 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3154 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3155 EXPECT_TRUE(controller
.IsInitialNavigation());
3156 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3158 // Simulate a commit and then starting a new pending navigation.
3159 main_test_rfh()->SendNavigate(0, url1
);
3160 NavigationController::LoadURLParams
load_url2_params(url2
);
3161 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3162 load_url2_params
.is_renderer_initiated
= true;
3163 controller
.LoadURLWithParams(load_url2_params
);
3165 // We should not consider this an initial navigation, and thus should
3166 // not show the pending URL.
3167 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3168 EXPECT_FALSE(controller
.IsInitialNavigation());
3169 EXPECT_TRUE(controller
.GetVisibleEntry());
3170 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3172 notifications
.Reset();
3175 // Tests that IsInPageNavigation returns appropriate results. Prevents
3176 // regression for bug 1126349.
3177 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3178 NavigationControllerImpl
& controller
= controller_impl();
3179 const GURL
url("http://www.google.com/home.html");
3181 // If the renderer claims it performed an in-page navigation from
3182 // about:blank, trust the renderer.
3183 // This can happen when an iframe is created and populated via
3184 // document.write(), then tries to perform a fragment navigation.
3185 // TODO(japhet): We should only trust the renderer if the about:blank
3186 // was the first document in the given frame, but we don't have enough
3187 // information to identify that case currently.
3188 const GURL
blank_url(url::kAboutBlankURL
);
3189 main_test_rfh()->SendNavigate(0, blank_url
);
3190 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3193 // Navigate to URL with no refs.
3194 main_test_rfh()->SendNavigate(0, url
);
3196 // Reloading the page is not an in-page navigation.
3197 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3199 const GURL
other_url("http://www.google.com/add.html");
3200 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3202 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3203 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3206 // Navigate to URL with refs.
3207 main_test_rfh()->SendNavigate(1, url_with_ref
);
3209 // Reloading the page is not an in-page navigation.
3210 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3212 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3214 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3216 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3217 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3220 // Going to the same url again will be considered in-page
3221 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3222 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3225 // Going back to the non ref url will be considered in-page if the navigation
3227 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3230 // If the renderer says this is a same-origin in-page navigation, believe it.
3231 // This is the pushState/replaceState case.
3232 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3235 // Test allow_universal_access_from_file_urls flag.
3236 const GURL
different_origin_url("http://www.example.com");
3237 MockRenderProcessHost
* rph
=
3238 static_cast<MockRenderProcessHost
*>(main_test_rfh()->GetProcess());
3239 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3240 prefs
.allow_universal_access_from_file_urls
= true;
3241 test_rvh()->UpdateWebkitPreferences(prefs
);
3242 prefs
= test_rvh()->GetWebkitPreferences();
3243 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3244 // Allow in page navigation if existing URL is file scheme.
3245 const GURL
file_url("file:///foo/index.html");
3246 main_test_rfh()->SendNavigate(0, file_url
);
3247 EXPECT_EQ(0, rph
->bad_msg_count());
3248 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3250 EXPECT_EQ(0, rph
->bad_msg_count());
3251 // Don't honor allow_universal_access_from_file_urls if existing URL is
3253 main_test_rfh()->SendNavigate(0, url
);
3254 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3256 EXPECT_EQ(1, rph
->bad_msg_count());
3258 // Remove allow_universal_access_from_file_urls flag.
3259 prefs
.allow_universal_access_from_file_urls
= false;
3260 test_rvh()->UpdateWebkitPreferences(prefs
);
3261 prefs
= test_rvh()->GetWebkitPreferences();
3262 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3264 // Don't believe the renderer if it claims a cross-origin navigation is
3266 EXPECT_EQ(1, rph
->bad_msg_count());
3267 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3269 EXPECT_EQ(2, rph
->bad_msg_count());
3272 // Some pages can have subframes with the same base URL (minus the reference) as
3273 // the main page. Even though this is hard, it can happen, and we don't want
3274 // these subframe navigations to affect the toplevel document. They should
3275 // instead be ignored. http://crbug.com/5585
3276 TEST_F(NavigationControllerTest
, SameSubframe
) {
3277 NavigationControllerImpl
& controller
= controller_impl();
3278 // Navigate the main frame.
3279 const GURL
url("http://www.google.com/");
3280 main_test_rfh()->SendNavigate(0, url
);
3282 // We should be at the first navigation entry.
3283 EXPECT_EQ(controller
.GetEntryCount(), 1);
3284 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3286 // Navigate a subframe that would normally count as in-page.
3287 const GURL
subframe("http://www.google.com/#");
3288 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3290 params
.url
= subframe
;
3291 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3292 params
.should_update_history
= false;
3293 params
.gesture
= NavigationGestureAuto
;
3294 params
.is_post
= false;
3295 params
.page_state
= PageState::CreateFromURL(subframe
);
3296 LoadCommittedDetails details
;
3297 EXPECT_FALSE(controller
.RendererDidNavigate(main_test_rfh(), params
,
3300 // Nothing should have changed.
3301 EXPECT_EQ(controller
.GetEntryCount(), 1);
3302 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3305 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3307 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3308 NavigationControllerImpl
& controller
= controller_impl();
3309 const GURL
url1("http://foo1");
3310 const GURL
url2("http://foo2");
3311 const base::string16
title(base::ASCIIToUTF16("Title"));
3313 NavigateAndCommit(url1
);
3314 controller
.GetVisibleEntry()->SetTitle(title
);
3315 NavigateAndCommit(url2
);
3317 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3319 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3320 EXPECT_TRUE(clone
->GetController().NeedsReload());
3321 clone
->GetController().GoBack();
3322 // Navigating back should have triggered needs_reload_ to go false.
3323 EXPECT_FALSE(clone
->GetController().NeedsReload());
3325 // Ensure that the pending URL and its title are visible.
3326 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3327 EXPECT_EQ(title
, clone
->GetTitle());
3330 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3331 // See http://crbug.com/234491.
3332 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3333 NavigationControllerImpl
& controller
= controller_impl();
3334 const GURL
url1("http://foo1");
3335 const GURL
url2("http://foo2");
3336 const base::string16
title(base::ASCIIToUTF16("Title"));
3338 NavigateAndCommit(url1
);
3339 controller
.GetVisibleEntry()->SetTitle(title
);
3340 NavigateAndCommit(url2
);
3342 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3343 clone
->GetController().LoadIfNecessary();
3345 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3346 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3348 clone
->GetController().Reload(true);
3349 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3352 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3353 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3354 NavigationControllerImpl
& controller
= controller_impl();
3355 const GURL
url1("http://foo1");
3356 const GURL
url2("http://foo2");
3358 NavigateAndCommit(url1
);
3359 NavigateAndCommit(url2
);
3361 // Add an interstitial entry. Should be deleted with controller.
3362 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3363 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3364 controller
.SetTransientEntry(interstitial_entry
);
3366 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3368 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3371 // Test requesting and triggering a lazy reload.
3372 TEST_F(NavigationControllerTest
, LazyReload
) {
3373 NavigationControllerImpl
& controller
= controller_impl();
3374 const GURL
url("http://foo");
3375 NavigateAndCommit(url
);
3376 ASSERT_FALSE(controller
.NeedsReload());
3377 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3378 controller
.GetLastCommittedEntry()->GetTransitionType());
3380 // Request a reload to happen when the controller becomes active (e.g. after
3381 // the renderer gets killed in background on Android).
3382 controller
.SetNeedsReload();
3383 ASSERT_TRUE(controller
.NeedsReload());
3384 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3385 controller
.GetLastCommittedEntry()->GetTransitionType());
3387 // Set the controller as active, triggering the requested reload.
3388 controller
.SetActive(true);
3389 ASSERT_FALSE(controller
.NeedsReload());
3390 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3391 controller
.GetPendingEntry()->GetTransitionType());
3394 // Test requesting and triggering a lazy reload without any committed entry.
3395 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3396 NavigationControllerImpl
& controller
= controller_impl();
3397 const GURL
url("http://foo");
3398 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3399 ASSERT_FALSE(controller
.NeedsReload());
3400 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3401 controller
.GetPendingEntry()->GetTransitionType());
3403 // Request a reload to happen when the controller becomes active (e.g. after
3404 // the renderer gets killed in background on Android).
3405 controller
.SetNeedsReload();
3406 ASSERT_TRUE(controller
.NeedsReload());
3407 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3408 controller
.GetPendingEntry()->GetTransitionType());
3410 // Set the controller as active, triggering the requested reload.
3411 controller
.SetActive(true);
3412 ASSERT_FALSE(controller
.NeedsReload());
3413 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3414 controller
.GetPendingEntry()->GetTransitionType());
3417 // Tests a subframe navigation while a toplevel navigation is pending.
3418 // http://crbug.com/43967
3419 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3420 NavigationControllerImpl
& controller
= controller_impl();
3421 // Load the first page.
3422 const GURL
url1("http://foo/");
3423 NavigateAndCommit(url1
);
3425 // Now start a pending load to a totally different page, but don't commit it.
3426 const GURL
url2("http://bar/");
3428 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3430 // Send a subframe update from the first page, as if one had just
3431 // automatically loaded. Auto subframes don't increment the page ID.
3432 const GURL
url1_sub("http://foo/subframe");
3433 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3434 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3435 params
.url
= url1_sub
;
3436 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3437 params
.should_update_history
= false;
3438 params
.gesture
= NavigationGestureAuto
;
3439 params
.is_post
= false;
3440 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3441 LoadCommittedDetails details
;
3443 // This should return false meaning that nothing was actually updated.
3444 EXPECT_FALSE(controller
.RendererDidNavigate(main_test_rfh(), params
,
3447 // The notification should have updated the last committed one, and not
3448 // the pending load.
3449 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3451 // The active entry should be unchanged by the subframe load.
3452 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3455 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3456 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3457 NavigationControllerImpl
& controller
= controller_impl();
3458 const GURL
url1("http://foo1");
3459 const GURL
url2("http://foo2");
3461 NavigateAndCommit(url1
);
3462 NavigateAndCommit(url2
);
3463 controller
.GoBack();
3464 contents()->CommitPendingNavigation();
3466 scoped_ptr
<TestWebContents
> other_contents(
3467 static_cast<TestWebContents
*>(CreateTestWebContents()));
3468 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3469 other_controller
.CopyStateFrom(controller
);
3471 // other_controller should now contain 2 urls.
3472 ASSERT_EQ(2, other_controller
.GetEntryCount());
3473 // We should be looking at the first one.
3474 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3476 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3477 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3478 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3479 // This is a different site than url1, so the IDs start again at 0.
3480 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3482 // The max page ID map should be copied over and updated with the max page ID
3483 // from the current tab.
3484 SiteInstance
* instance1
=
3485 other_controller
.GetEntryAtIndex(0)->site_instance();
3486 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3488 // Ensure the SessionStorageNamespaceMaps are the same size and have
3489 // the same partitons loaded.
3491 // TODO(ajwong): We should load a url from a different partition earlier
3492 // to make sure this map has more than one entry.
3493 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3494 controller
.GetSessionStorageNamespaceMap();
3495 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3496 other_controller
.GetSessionStorageNamespaceMap();
3497 EXPECT_EQ(session_storage_namespace_map
.size(),
3498 other_session_storage_namespace_map
.size());
3499 for (SessionStorageNamespaceMap::const_iterator it
=
3500 session_storage_namespace_map
.begin();
3501 it
!= session_storage_namespace_map
.end();
3503 SessionStorageNamespaceMap::const_iterator other
=
3504 other_session_storage_namespace_map
.find(it
->first
);
3505 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3509 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3510 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3511 NavigationControllerImpl
& controller
= controller_impl();
3512 const GURL
url1("http://foo/1");
3513 const GURL
url2("http://foo/2");
3514 const GURL
url3("http://foo/3");
3516 NavigateAndCommit(url1
);
3517 NavigateAndCommit(url2
);
3519 // First two entries should have the same SiteInstance.
3520 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3521 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3522 EXPECT_EQ(instance1
, instance2
);
3523 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3524 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3525 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3527 scoped_ptr
<TestWebContents
> other_contents(
3528 static_cast<TestWebContents
*>(CreateTestWebContents()));
3529 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3530 other_contents
->NavigateAndCommit(url3
);
3531 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3532 other_controller
.CopyStateFromAndPrune(&controller
, false);
3534 // other_controller should now contain the 3 urls: url1, url2 and url3.
3536 ASSERT_EQ(3, other_controller
.GetEntryCount());
3538 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3540 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3541 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3542 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3543 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3544 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3545 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3547 // A new SiteInstance in a different BrowsingInstance should be used for the
3549 SiteInstance
* instance3
=
3550 other_controller
.GetEntryAtIndex(2)->site_instance();
3551 EXPECT_NE(instance3
, instance1
);
3552 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3554 // The max page ID map should be copied over and updated with the max page ID
3555 // from the current tab.
3556 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3557 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3560 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3562 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3563 NavigationControllerImpl
& controller
= controller_impl();
3564 const GURL
url1("http://foo1");
3565 const GURL
url2("http://foo2");
3566 const GURL
url3("http://foo3");
3568 NavigateAndCommit(url1
);
3569 NavigateAndCommit(url2
);
3570 controller
.GoBack();
3571 contents()->CommitPendingNavigation();
3573 scoped_ptr
<TestWebContents
> other_contents(
3574 static_cast<TestWebContents
*>(CreateTestWebContents()));
3575 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3576 other_contents
->NavigateAndCommit(url3
);
3577 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3578 other_controller
.CopyStateFromAndPrune(&controller
, false);
3580 // other_controller should now contain: url1, url3
3582 ASSERT_EQ(2, other_controller
.GetEntryCount());
3583 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3585 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3586 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3587 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3589 // The max page ID map should be copied over and updated with the max page ID
3590 // from the current tab.
3591 SiteInstance
* instance1
=
3592 other_controller
.GetEntryAtIndex(1)->site_instance();
3593 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3596 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3598 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3599 NavigationControllerImpl
& controller
= controller_impl();
3600 const GURL
url1("http://foo1");
3601 const GURL
url2("http://foo2");
3602 const GURL
url3("http://foo3");
3603 const GURL
url4("http://foo4");
3605 NavigateAndCommit(url1
);
3606 NavigateAndCommit(url2
);
3608 scoped_ptr
<TestWebContents
> other_contents(
3609 static_cast<TestWebContents
*>(CreateTestWebContents()));
3610 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3611 other_contents
->NavigateAndCommit(url3
);
3612 other_contents
->NavigateAndCommit(url4
);
3613 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3614 other_controller
.CopyStateFromAndPrune(&controller
, false);
3616 // other_controller should now contain: url1, url2, url4
3618 ASSERT_EQ(3, other_controller
.GetEntryCount());
3619 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3621 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3622 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3623 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
3625 // The max page ID map should be copied over and updated with the max page ID
3626 // from the current tab.
3627 SiteInstance
* instance1
=
3628 other_controller
.GetEntryAtIndex(2)->site_instance();
3629 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3632 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3633 // not the last entry selected in the target.
3634 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
3635 NavigationControllerImpl
& controller
= controller_impl();
3636 const GURL
url1("http://foo1");
3637 const GURL
url2("http://foo2");
3638 const GURL
url3("http://foo3");
3639 const GURL
url4("http://foo4");
3641 NavigateAndCommit(url1
);
3642 NavigateAndCommit(url2
);
3644 scoped_ptr
<TestWebContents
> other_contents(
3645 static_cast<TestWebContents
*>(CreateTestWebContents()));
3646 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3647 other_contents
->NavigateAndCommit(url3
);
3648 other_contents
->NavigateAndCommit(url4
);
3649 other_controller
.GoBack();
3650 other_contents
->CommitPendingNavigation();
3651 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3652 other_controller
.CopyStateFromAndPrune(&controller
, false);
3654 // other_controller should now contain: url1, url2, url3
3656 ASSERT_EQ(3, other_controller
.GetEntryCount());
3657 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3659 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3660 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3661 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3663 // The max page ID map should be copied over and updated with the max page ID
3664 // from the current tab.
3665 SiteInstance
* instance1
=
3666 other_controller
.GetEntryAtIndex(2)->site_instance();
3667 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3670 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3671 // a pending entry in the target.
3672 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
3673 NavigationControllerImpl
& controller
= controller_impl();
3674 const GURL
url1("http://foo1");
3675 const GURL
url2("http://foo2");
3676 const GURL
url3("http://foo3");
3677 const GURL
url4("http://foo4");
3679 NavigateAndCommit(url1
);
3680 NavigateAndCommit(url2
);
3681 controller
.GoBack();
3682 contents()->CommitPendingNavigation();
3684 scoped_ptr
<TestWebContents
> other_contents(
3685 static_cast<TestWebContents
*>(CreateTestWebContents()));
3686 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3687 other_contents
->NavigateAndCommit(url3
);
3688 other_controller
.LoadURL(
3689 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3690 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3691 other_controller
.CopyStateFromAndPrune(&controller
, false);
3693 // other_controller should now contain url1, url3, and a pending entry
3696 ASSERT_EQ(2, other_controller
.GetEntryCount());
3697 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
3699 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3700 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3702 // And there should be a pending entry for url4.
3703 ASSERT_TRUE(other_controller
.GetPendingEntry());
3704 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
3706 // The max page ID map should be copied over and updated with the max page ID
3707 // from the current tab.
3708 SiteInstance
* instance1
=
3709 other_controller
.GetEntryAtIndex(0)->site_instance();
3710 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3713 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3714 // client redirect entry (with the same page ID) in the target. This used to
3715 // crash because the last committed entry would be pruned but max_page_id
3716 // remembered the page ID (http://crbug.com/234809).
3717 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
3718 NavigationControllerImpl
& controller
= controller_impl();
3719 const GURL
url1("http://foo1");
3720 const GURL
url2a("http://foo2/a");
3721 const GURL
url2b("http://foo2/b");
3723 NavigateAndCommit(url1
);
3725 scoped_ptr
<TestWebContents
> other_contents(
3726 static_cast<TestWebContents
*>(CreateTestWebContents()));
3727 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3728 other_contents
->NavigateAndCommit(url2a
);
3729 // Simulate a client redirect, which has the same page ID as entry 2a.
3730 other_controller
.LoadURL(
3731 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
3732 other_controller
.GetPendingEntry()->SetPageID(
3733 other_controller
.GetLastCommittedEntry()->GetPageID());
3735 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3736 other_controller
.CopyStateFromAndPrune(&controller
, false);
3738 // other_controller should now contain url1, url2a, and a pending entry
3741 ASSERT_EQ(2, other_controller
.GetEntryCount());
3742 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
3744 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3745 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
3747 // And there should be a pending entry for url4.
3748 ASSERT_TRUE(other_controller
.GetPendingEntry());
3749 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
3751 // Let the pending entry commit.
3752 other_contents
->CommitPendingNavigation();
3754 // The max page ID map should be copied over and updated with the max page ID
3755 // from the current tab.
3756 SiteInstance
* instance1
=
3757 other_controller
.GetEntryAtIndex(1)->site_instance();
3758 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3761 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3762 // source, and 1 entry in the target. The back pending entry should be ignored.
3763 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
3764 NavigationControllerImpl
& controller
= controller_impl();
3765 const GURL
url1("http://foo1");
3766 const GURL
url2("http://foo2");
3767 const GURL
url3("http://foo3");
3769 NavigateAndCommit(url1
);
3770 NavigateAndCommit(url2
);
3771 controller
.GoBack();
3773 scoped_ptr
<TestWebContents
> other_contents(
3774 static_cast<TestWebContents
*>(CreateTestWebContents()));
3775 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3776 other_contents
->NavigateAndCommit(url3
);
3777 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3778 other_controller
.CopyStateFromAndPrune(&controller
, false);
3780 // other_controller should now contain: url1, url2, url3
3782 ASSERT_EQ(3, other_controller
.GetEntryCount());
3783 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3785 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3786 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3787 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3788 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3790 // The max page ID map should be copied over and updated with the max page ID
3791 // from the current tab.
3792 SiteInstance
* instance1
=
3793 other_controller
.GetEntryAtIndex(2)->site_instance();
3794 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3797 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3798 // when the max entry count is 3. We should prune one entry.
3799 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
3800 NavigationControllerImpl
& controller
= controller_impl();
3801 size_t original_count
= NavigationControllerImpl::max_entry_count();
3802 const int kMaxEntryCount
= 3;
3804 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
3806 const GURL
url1("http://foo/1");
3807 const GURL
url2("http://foo/2");
3808 const GURL
url3("http://foo/3");
3809 const GURL
url4("http://foo/4");
3811 // Create a PrunedListener to observe prune notifications.
3812 PrunedListener
listener(&controller
);
3814 NavigateAndCommit(url1
);
3815 NavigateAndCommit(url2
);
3816 NavigateAndCommit(url3
);
3818 scoped_ptr
<TestWebContents
> other_contents(
3819 static_cast<TestWebContents
*>(CreateTestWebContents()));
3820 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3821 other_contents
->NavigateAndCommit(url4
);
3822 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3823 other_controller
.CopyStateFromAndPrune(&controller
, false);
3825 // We should have received a pruned notification.
3826 EXPECT_EQ(1, listener
.notification_count_
);
3827 EXPECT_TRUE(listener
.details_
.from_front
);
3828 EXPECT_EQ(1, listener
.details_
.count
);
3830 // other_controller should now contain only 3 urls: url2, url3 and url4.
3832 ASSERT_EQ(3, other_controller
.GetEntryCount());
3834 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3836 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
3837 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3838 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
3839 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
3840 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
3841 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3843 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
3846 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3847 // replace_entry set to true.
3848 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
3849 NavigationControllerImpl
& controller
= controller_impl();
3850 const GURL
url1("http://foo/1");
3851 const GURL
url2("http://foo/2");
3852 const GURL
url3("http://foo/3");
3854 NavigateAndCommit(url1
);
3855 NavigateAndCommit(url2
);
3857 // First two entries should have the same SiteInstance.
3858 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3859 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3860 EXPECT_EQ(instance1
, instance2
);
3861 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3862 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3863 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3865 scoped_ptr
<TestWebContents
> other_contents(
3866 static_cast<TestWebContents
*>(CreateTestWebContents()));
3867 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3868 other_contents
->NavigateAndCommit(url3
);
3869 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3870 other_controller
.CopyStateFromAndPrune(&controller
, true);
3872 // other_controller should now contain the 2 urls: url1 and url3.
3874 ASSERT_EQ(2, other_controller
.GetEntryCount());
3876 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3878 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3879 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3880 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3881 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3883 // A new SiteInstance in a different BrowsingInstance should be used for the
3885 SiteInstance
* instance3
=
3886 other_controller
.GetEntryAtIndex(1)->site_instance();
3887 EXPECT_NE(instance3
, instance1
);
3888 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3890 // The max page ID map should be copied over and updated with the max page ID
3891 // from the current tab.
3892 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3893 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3896 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3897 // entry count is 3 and replace_entry is true. We should not prune entries.
3898 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
3899 NavigationControllerImpl
& controller
= controller_impl();
3900 size_t original_count
= NavigationControllerImpl::max_entry_count();
3901 const int kMaxEntryCount
= 3;
3903 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
3905 const GURL
url1("http://foo/1");
3906 const GURL
url2("http://foo/2");
3907 const GURL
url3("http://foo/3");
3908 const GURL
url4("http://foo/4");
3910 // Create a PrunedListener to observe prune notifications.
3911 PrunedListener
listener(&controller
);
3913 NavigateAndCommit(url1
);
3914 NavigateAndCommit(url2
);
3915 NavigateAndCommit(url3
);
3917 scoped_ptr
<TestWebContents
> other_contents(
3918 static_cast<TestWebContents
*>(CreateTestWebContents()));
3919 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3920 other_contents
->NavigateAndCommit(url4
);
3921 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3922 other_controller
.CopyStateFromAndPrune(&controller
, true);
3924 // We should have received no pruned notification.
3925 EXPECT_EQ(0, listener
.notification_count_
);
3927 // other_controller should now contain only 3 urls: url1, url2 and url4.
3929 ASSERT_EQ(3, other_controller
.GetEntryCount());
3931 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3933 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3934 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3935 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
3936 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3937 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3938 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3940 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
3943 // Tests that we can navigate to the restored entries
3944 // imported by CopyStateFromAndPrune.
3945 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
3946 const GURL kRestoredUrls
[] = {
3947 GURL("http://site1.com"),
3948 GURL("http://site2.com"),
3950 const GURL
kInitialUrl("http://site3.com");
3952 std::vector
<NavigationEntry
*> entries
;
3953 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
3954 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
3955 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
3956 std::string(), browser_context());
3957 entry
->SetPageID(static_cast<int>(i
));
3958 entries
.push_back(entry
);
3961 // Create a WebContents with restored entries.
3962 scoped_ptr
<TestWebContents
> source_contents(
3963 static_cast<TestWebContents
*>(CreateTestWebContents()));
3964 NavigationControllerImpl
& source_controller
=
3965 source_contents
->GetController();
3966 source_controller
.Restore(
3968 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
3970 ASSERT_EQ(0u, entries
.size());
3971 source_controller
.LoadIfNecessary();
3972 source_contents
->CommitPendingNavigation();
3974 // Load a page, then copy state from |source_contents|.
3975 NavigateAndCommit(kInitialUrl
);
3976 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
3977 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
3978 ASSERT_EQ(3, controller_impl().GetEntryCount());
3980 // Go back to the first entry one at a time and
3981 // verify that it works as expected.
3982 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
3983 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
3985 controller_impl().GoBack();
3986 contents()->CommitPendingNavigation();
3987 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
3988 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
3990 controller_impl().GoBack();
3991 contents()->CommitPendingNavigation();
3992 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
3993 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
3996 // Tests that navigations initiated from the page (with the history object)
3997 // work as expected, creating pending entries.
3998 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
3999 NavigationControllerImpl
& controller
= controller_impl();
4000 const GURL
url1("http://foo/1");
4001 const GURL
url2("http://foo/2");
4002 const GURL
url3("http://foo/3");
4004 NavigateAndCommit(url1
);
4005 NavigateAndCommit(url2
);
4006 NavigateAndCommit(url3
);
4007 controller
.GoBack();
4008 contents()->CommitPendingNavigation();
4009 process()->sink().ClearMessages();
4011 // Simulate the page calling history.back(). It should create a pending entry.
4012 contents()->OnGoToEntryAtOffset(-1);
4013 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4014 // The actual cross-navigation is suspended until the current RVH tells us
4015 // it unloaded, simulate that.
4016 contents()->ProceedWithCrossSiteNavigation();
4017 // Also make sure we told the page to navigate.
4018 GURL nav_url
= GetLastNavigationURL();
4019 EXPECT_EQ(url1
, nav_url
);
4020 contents()->CommitPendingNavigation();
4021 process()->sink().ClearMessages();
4023 // Now test history.forward()
4024 contents()->OnGoToEntryAtOffset(2);
4025 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4026 // The actual cross-navigation is suspended until the current RVH tells us
4027 // it unloaded, simulate that.
4028 contents()->ProceedWithCrossSiteNavigation();
4029 nav_url
= GetLastNavigationURL();
4030 EXPECT_EQ(url3
, nav_url
);
4031 contents()->CommitPendingNavigation();
4032 process()->sink().ClearMessages();
4034 controller
.DiscardNonCommittedEntries();
4036 // Make sure an extravagant history.go() doesn't break.
4037 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4038 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4039 EXPECT_FALSE(HasNavigationRequest());
4042 // Test call to PruneAllButLastCommitted for the only entry.
4043 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4044 NavigationControllerImpl
& controller
= controller_impl();
4045 const GURL
url1("http://foo1");
4046 NavigateAndCommit(url1
);
4048 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4050 controller
.PruneAllButLastCommitted();
4052 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4053 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4056 // Test call to PruneAllButLastCommitted for first entry.
4057 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4058 NavigationControllerImpl
& controller
= controller_impl();
4059 const GURL
url1("http://foo/1");
4060 const GURL
url2("http://foo/2");
4061 const GURL
url3("http://foo/3");
4063 NavigateAndCommit(url1
);
4064 NavigateAndCommit(url2
);
4065 NavigateAndCommit(url3
);
4066 controller
.GoBack();
4067 controller
.GoBack();
4068 contents()->CommitPendingNavigation();
4070 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4072 controller
.PruneAllButLastCommitted();
4074 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4075 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4078 // Test call to PruneAllButLastCommitted for intermediate entry.
4079 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4080 NavigationControllerImpl
& controller
= controller_impl();
4081 const GURL
url1("http://foo/1");
4082 const GURL
url2("http://foo/2");
4083 const GURL
url3("http://foo/3");
4085 NavigateAndCommit(url1
);
4086 NavigateAndCommit(url2
);
4087 NavigateAndCommit(url3
);
4088 controller
.GoBack();
4089 contents()->CommitPendingNavigation();
4091 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4093 controller
.PruneAllButLastCommitted();
4095 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4096 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4099 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4100 // the list of entries.
4101 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4102 NavigationControllerImpl
& controller
= controller_impl();
4103 const GURL
url1("http://foo/1");
4104 const GURL
url2("http://foo/2");
4105 const GURL
url3("http://foo/3");
4107 NavigateAndCommit(url1
);
4108 NavigateAndCommit(url2
);
4110 // Create a pending entry that is not in the entry list.
4112 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4113 EXPECT_TRUE(controller
.GetPendingEntry());
4114 EXPECT_EQ(2, controller
.GetEntryCount());
4116 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4117 controller
.PruneAllButLastCommitted();
4119 // We should only have the last committed and pending entries at this point,
4120 // and the pending entry should still not be in the entry list.
4121 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4122 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4123 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4124 EXPECT_TRUE(controller
.GetPendingEntry());
4125 EXPECT_EQ(1, controller
.GetEntryCount());
4127 // Try to commit the pending entry.
4128 main_test_rfh()->PrepareForCommit(url3
);
4129 main_test_rfh()->SendNavigate(2, url3
);
4130 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4131 EXPECT_FALSE(controller
.GetPendingEntry());
4132 EXPECT_EQ(2, controller
.GetEntryCount());
4133 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4136 // Test to ensure that when we do a history navigation back to the current
4137 // committed page (e.g., going forward to a slow-loading page, then pressing
4138 // the back button), we just stop the navigation to prevent the throbber from
4139 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4140 // start, but WebKit essentially ignores the navigation and never sends a
4141 // message to stop the throbber.
4142 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4143 NavigationControllerImpl
& controller
= controller_impl();
4144 const GURL
url0("http://foo/0");
4145 const GURL
url1("http://foo/1");
4147 NavigateAndCommit(url0
);
4148 NavigateAndCommit(url1
);
4150 // Go back to the original page, then forward to the slow page, then back
4151 controller
.GoBack();
4152 contents()->CommitPendingNavigation();
4154 controller
.GoForward();
4155 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4157 controller
.GoBack();
4158 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4161 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4162 NavigationControllerImpl
& controller
= controller_impl();
4163 TestNotificationTracker notifications
;
4164 RegisterForAllNavNotifications(¬ifications
, &controller
);
4167 EXPECT_TRUE(controller
.IsInitialNavigation());
4169 // After commit, it stays false.
4170 const GURL
url1("http://foo1");
4171 main_test_rfh()->SendNavigate(0, url1
);
4172 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4173 navigation_entry_committed_counter_
= 0;
4174 EXPECT_FALSE(controller
.IsInitialNavigation());
4176 // After starting a new navigation, it stays false.
4177 const GURL
url2("http://foo2");
4179 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4182 // Check that the favicon is not reused across a client redirect.
4183 // (crbug.com/28515)
4184 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4185 const GURL
kPageWithFavicon("http://withfavicon.html");
4186 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4187 const GURL
kIconURL("http://withfavicon.ico");
4188 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4190 NavigationControllerImpl
& controller
= controller_impl();
4191 TestNotificationTracker notifications
;
4192 RegisterForAllNavNotifications(¬ifications
, &controller
);
4194 main_test_rfh()->SendNavigate(0, kPageWithFavicon
);
4195 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4196 navigation_entry_committed_counter_
= 0;
4198 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4200 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4202 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4203 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4204 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4205 favicon_status
.url
= kIconURL
;
4206 favicon_status
.valid
= true;
4207 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4209 main_test_rfh()->SendNavigateWithTransition(
4211 kPageWithoutFavicon
,
4212 ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4213 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4214 navigation_entry_committed_counter_
= 0;
4216 entry
= controller
.GetLastCommittedEntry();
4218 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4220 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4223 // Check that the favicon is not cleared for NavigationEntries which were
4224 // previously navigated to.
4225 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4226 const GURL
kUrl1("http://www.a.com/1");
4227 const GURL
kUrl2("http://www.a.com/2");
4228 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4230 NavigationControllerImpl
& controller
= controller_impl();
4231 TestNotificationTracker notifications
;
4232 RegisterForAllNavNotifications(¬ifications
, &controller
);
4234 main_test_rfh()->SendNavigate(0, kUrl1
);
4235 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4236 navigation_entry_committed_counter_
= 0;
4238 // Simulate Chromium having set the favicon for |kUrl1|.
4239 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4240 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4242 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4243 favicon_status
.image
= favicon_image
;
4244 favicon_status
.url
= kIconURL
;
4245 favicon_status
.valid
= true;
4247 // Navigate to another page and go back to the original page.
4248 main_test_rfh()->SendNavigate(1, kUrl2
);
4249 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4250 navigation_entry_committed_counter_
= 0;
4251 main_test_rfh()->SendNavigateWithTransition(
4254 ui::PAGE_TRANSITION_FORWARD_BACK
);
4255 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4256 navigation_entry_committed_counter_
= 0;
4258 // Verify that the favicon for the page at |kUrl1| was not cleared.
4259 entry
= controller
.GetEntryAtIndex(0);
4261 EXPECT_EQ(kUrl1
, entry
->GetURL());
4262 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4265 // The test crashes on android: http://crbug.com/170449
4266 #if defined(OS_ANDROID)
4267 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4269 #define MAYBE_PurgeScreenshot PurgeScreenshot
4271 // Tests that screenshot are purged correctly.
4272 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4273 NavigationControllerImpl
& controller
= controller_impl();
4275 NavigationEntryImpl
* entry
;
4277 // Navigate enough times to make sure that some screenshots are purged.
4278 for (int i
= 0; i
< 12; ++i
) {
4279 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4280 NavigateAndCommit(url
);
4281 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4284 MockScreenshotManager
* screenshot_manager
=
4285 new MockScreenshotManager(&controller
);
4286 controller
.SetScreenshotManager(screenshot_manager
);
4287 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4288 entry
= controller
.GetEntryAtIndex(i
);
4289 screenshot_manager
->TakeScreenshotFor(entry
);
4290 EXPECT_TRUE(entry
->screenshot().get());
4293 NavigateAndCommit(GURL("https://foo/"));
4294 EXPECT_EQ(13, controller
.GetEntryCount());
4295 entry
= controller
.GetEntryAtIndex(11);
4296 screenshot_manager
->TakeScreenshotFor(entry
);
4298 for (int i
= 0; i
< 2; ++i
) {
4299 entry
= controller
.GetEntryAtIndex(i
);
4300 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4304 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4305 entry
= controller
.GetEntryAtIndex(i
);
4306 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4309 // Navigate to index 5 and then try to assign screenshot to all entries.
4310 controller
.GoToIndex(5);
4311 contents()->CommitPendingNavigation();
4312 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4313 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4314 entry
= controller
.GetEntryAtIndex(i
);
4315 screenshot_manager
->TakeScreenshotFor(entry
);
4318 for (int i
= 10; i
<= 12; ++i
) {
4319 entry
= controller
.GetEntryAtIndex(i
);
4320 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4322 screenshot_manager
->TakeScreenshotFor(entry
);
4325 // Navigate to index 7 and assign screenshot to all entries.
4326 controller
.GoToIndex(7);
4327 contents()->CommitPendingNavigation();
4328 EXPECT_EQ(7, 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
= 0; i
< 2; ++i
) {
4335 entry
= controller
.GetEntryAtIndex(i
);
4336 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4340 // Clear all screenshots.
4341 EXPECT_EQ(13, controller
.GetEntryCount());
4342 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4343 controller
.ClearAllScreenshots();
4344 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4345 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4346 entry
= controller
.GetEntryAtIndex(i
);
4347 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4352 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4354 contents()->GetMainFrame()->SendNavigate(1, GURL("http://foo"));
4356 // Set title and favicon.
4357 base::string16
title(base::ASCIIToUTF16("Title"));
4358 FaviconStatus favicon
;
4359 favicon
.valid
= true;
4360 favicon
.url
= GURL("http://foo/favicon.ico");
4361 controller().GetLastCommittedEntry()->SetTitle(title
);
4362 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4364 // history.pushState() is called.
4365 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4366 GURL
url("http://foo#foo");
4369 params
.page_state
= PageState::CreateFromURL(url
);
4370 params
.was_within_same_page
= true;
4371 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4373 // The title should immediately be visible on the new NavigationEntry.
4374 base::string16 new_title
=
4375 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4376 EXPECT_EQ(title
, new_title
);
4377 FaviconStatus new_favicon
=
4378 controller().GetLastCommittedEntry()->GetFavicon();
4379 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4380 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4383 // Test that the navigation controller clears its session history when a
4384 // navigation commits with the clear history list flag set.
4385 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4386 const GURL
url1("http://foo1");
4387 const GURL
url2("http://foo2");
4388 const GURL
url3("http://foo3");
4389 const GURL
url4("http://foo4");
4391 NavigationControllerImpl
& controller
= controller_impl();
4393 // Create a session history with three entries, second entry is active.
4394 NavigateAndCommit(url1
);
4395 NavigateAndCommit(url2
);
4396 NavigateAndCommit(url3
);
4397 controller
.GoBack();
4398 contents()->CommitPendingNavigation();
4399 EXPECT_EQ(3, controller
.GetEntryCount());
4400 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4402 // Create a new pending navigation, and indicate that the session history
4403 // should be cleared.
4404 NavigationController::LoadURLParams
params(url4
);
4405 params
.should_clear_history_list
= true;
4406 controller
.LoadURLWithParams(params
);
4408 // Verify that the pending entry correctly indicates that the session history
4409 // should be cleared.
4410 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4412 EXPECT_TRUE(entry
->should_clear_history_list());
4414 // Assume that the RF correctly cleared its history and commit the navigation.
4415 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4416 switches::kEnableBrowserSideNavigation
)) {
4417 contents()->GetMainFrame()->PrepareForCommit(entry
->GetURL());
4419 contents()->GetPendingMainFrame()->
4420 set_simulate_history_list_was_cleared(true);
4421 contents()->CommitPendingNavigation();
4423 // Verify that the NavigationController's session history was correctly
4425 EXPECT_EQ(1, controller
.GetEntryCount());
4426 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4427 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4428 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4429 EXPECT_FALSE(controller
.CanGoBack());
4430 EXPECT_FALSE(controller
.CanGoForward());
4431 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4434 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4435 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4436 EXPECT_FALSE(contents()->GetDelegate());
4437 contents()->SetDelegate(delegate
.get());
4440 GURL
url("http://foo");
4441 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4444 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4445 params
.gesture
= NavigationGestureUser
;
4446 params
.page_state
= PageState::CreateFromURL(url
);
4447 params
.was_within_same_page
= false;
4448 params
.is_post
= true;
4450 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4452 // history.replaceState() is called.
4453 GURL
replace_url("http://foo#foo");
4455 params
.url
= replace_url
;
4456 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4457 params
.gesture
= NavigationGestureUser
;
4458 params
.page_state
= PageState::CreateFromURL(replace_url
);
4459 params
.was_within_same_page
= true;
4460 params
.is_post
= false;
4461 params
.post_id
= -1;
4462 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4464 // Now reload. replaceState overrides the POST, so we should not show a
4465 // repost warning dialog.
4466 controller_impl().Reload(true);
4467 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4470 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4471 GURL
url("http://foo");
4472 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4475 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4476 params
.gesture
= NavigationGestureUser
;
4477 params
.page_state
= PageState::CreateFromURL(url
);
4478 params
.was_within_same_page
= false;
4479 params
.is_post
= true;
4481 params
.url_is_unreachable
= true;
4482 // Navigate to new page
4484 LoadCommittedDetails details
;
4485 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4486 EXPECT_EQ(PAGE_TYPE_ERROR
,
4487 controller_impl().GetLastCommittedEntry()->GetPageType());
4488 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4491 // Navigate to existing page.
4493 LoadCommittedDetails details
;
4494 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4495 EXPECT_EQ(PAGE_TYPE_ERROR
,
4496 controller_impl().GetLastCommittedEntry()->GetPageType());
4497 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4500 // Navigate to same page.
4501 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4502 // same-page transition.
4503 controller_impl().LoadURL(
4504 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4505 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4507 LoadCommittedDetails details
;
4508 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4509 EXPECT_EQ(PAGE_TYPE_ERROR
,
4510 controller_impl().GetLastCommittedEntry()->GetPageType());
4511 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4514 // Navigate in page.
4515 params
.url
= GURL("http://foo#foo");
4516 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4517 params
.was_within_same_page
= true;
4519 LoadCommittedDetails details
;
4520 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4521 EXPECT_EQ(PAGE_TYPE_ERROR
,
4522 controller_impl().GetLastCommittedEntry()->GetPageType());
4523 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, details
.type
);
4527 } // namespace content