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/frame_navigation_entry.h"
16 #include "content/browser/frame_host/navigation_controller_impl.h"
17 #include "content/browser/frame_host/navigation_entry_impl.h"
18 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
19 #include "content/browser/frame_host/navigation_request.h"
20 #include "content/browser/frame_host/navigator.h"
21 #include "content/browser/frame_host/navigator_impl.h"
22 #include "content/browser/site_instance_impl.h"
23 #include "content/browser/web_contents/web_contents_impl.h"
24 #include "content/common/frame_messages.h"
25 #include "content/common/view_messages.h"
26 #include "content/public/browser/navigation_details.h"
27 #include "content/public/browser/notification_registrar.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "content/public/browser/web_contents_observer.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/page_state.h"
34 #include "content/public/common/page_type.h"
35 #include "content/public/common/url_constants.h"
36 #include "content/public/test/mock_render_process_host.h"
37 #include "content/public/test/test_notification_tracker.h"
38 #include "content/public/test/test_utils.h"
39 #include "content/test/test_render_frame_host.h"
40 #include "content/test/test_render_view_host.h"
41 #include "content/test/test_web_contents.h"
42 #include "net/base/net_util.h"
43 #include "skia/ext/platform_canvas.h"
44 #include "testing/gtest/include/gtest/gtest.h"
50 // Creates an image with a 1x1 SkBitmap of the specified |color|.
51 gfx::Image
CreateImage(SkColor color
) {
53 bitmap
.allocN32Pixels(1, 1);
54 bitmap
.eraseColor(color
);
55 return gfx::Image::CreateFrom1xBitmap(bitmap
);
58 // Returns true if images |a| and |b| have the same pixel data.
59 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
60 // Assume that if the 1x bitmaps match, the images match.
61 SkBitmap a_bitmap
= a
.AsBitmap();
62 SkBitmap b_bitmap
= b
.AsBitmap();
64 if (a_bitmap
.width() != b_bitmap
.width() ||
65 a_bitmap
.height() != b_bitmap
.height()) {
68 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
69 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
70 return memcmp(a_bitmap
.getPixels(),
72 a_bitmap
.getSize()) == 0;
75 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
77 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
78 : content::NavigationEntryScreenshotManager(owner
),
79 encoding_screenshot_in_progress_(false) {
82 ~MockScreenshotManager() override
{}
84 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
86 bitmap
.allocPixels(SkImageInfo::Make(
87 1, 1, kAlpha_8_SkColorType
, kPremul_SkAlphaType
));
88 bitmap
.eraseARGB(0, 0, 0, 0);
89 encoding_screenshot_in_progress_
= true;
90 OnScreenshotTaken(entry
->GetUniqueID(), bitmap
, content::READBACK_SUCCESS
);
91 WaitUntilScreenshotIsReady();
94 int GetScreenshotCount() {
95 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
98 void WaitUntilScreenshotIsReady() {
99 if (!encoding_screenshot_in_progress_
)
101 message_loop_runner_
= new content::MessageLoopRunner
;
102 message_loop_runner_
->Run();
106 // Overridden from content::NavigationEntryScreenshotManager:
107 void TakeScreenshotImpl(content::RenderViewHost
* host
,
108 content::NavigationEntryImpl
* entry
) override
{}
110 void OnScreenshotSet(content::NavigationEntryImpl
* entry
) override
{
111 encoding_screenshot_in_progress_
= false;
112 NavigationEntryScreenshotManager::OnScreenshotSet(entry
);
113 if (message_loop_runner_
.get())
114 message_loop_runner_
->Quit();
117 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
118 bool encoding_screenshot_in_progress_
;
120 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager
);
127 // TimeSmoother tests ----------------------------------------------------------
129 // With no duplicates, GetSmoothedTime should be the identity
131 TEST(TimeSmoother
, Basic
) {
132 NavigationControllerImpl::TimeSmoother smoother
;
133 for (int64 i
= 1; i
< 1000; ++i
) {
134 base::Time t
= base::Time::FromInternalValue(i
);
135 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
139 // With a single duplicate and timestamps thereafter increasing by one
140 // microsecond, the smoothed time should always be one behind.
141 TEST(TimeSmoother
, SingleDuplicate
) {
142 NavigationControllerImpl::TimeSmoother smoother
;
143 base::Time t
= base::Time::FromInternalValue(1);
144 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
145 for (int64 i
= 1; i
< 1000; ++i
) {
146 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
147 t
= base::Time::FromInternalValue(i
);
148 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
152 // With k duplicates and timestamps thereafter increasing by one
153 // microsecond, the smoothed time should always be k behind.
154 TEST(TimeSmoother
, ManyDuplicates
) {
155 const int64 kNumDuplicates
= 100;
156 NavigationControllerImpl::TimeSmoother smoother
;
157 base::Time t
= base::Time::FromInternalValue(1);
158 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
159 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
160 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
162 for (int64 i
= 1; i
< 1000; ++i
) {
163 base::Time expected_t
=
164 base::Time::FromInternalValue(i
+ kNumDuplicates
);
165 t
= base::Time::FromInternalValue(i
);
166 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
170 // If the clock jumps far back enough after a run of duplicates, it
171 // should immediately jump to that value.
172 TEST(TimeSmoother
, ClockBackwardsJump
) {
173 const int64 kNumDuplicates
= 100;
174 NavigationControllerImpl::TimeSmoother smoother
;
175 base::Time t
= base::Time::FromInternalValue(1000);
176 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
177 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1000);
178 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
180 t
= base::Time::FromInternalValue(500);
181 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
184 // NavigationControllerTest ----------------------------------------------------
186 class NavigationControllerTest
187 : public RenderViewHostImplTestHarness
,
188 public WebContentsObserver
{
190 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
193 void SetUp() override
{
194 RenderViewHostImplTestHarness::SetUp();
195 WebContents
* web_contents
= RenderViewHostImplTestHarness::web_contents();
196 ASSERT_TRUE(web_contents
); // The WebContents should be created by now.
197 WebContentsObserver::Observe(web_contents
);
200 // WebContentsObserver:
201 void DidStartNavigationToPendingEntry(
203 NavigationController::ReloadType reload_type
) override
{
204 navigated_url_
= url
;
207 void NavigationEntryCommitted(
208 const LoadCommittedDetails
& load_details
) override
{
209 navigation_entry_committed_counter_
++;
212 const GURL
& navigated_url() const {
213 return navigated_url_
;
216 NavigationControllerImpl
& controller_impl() {
217 return static_cast<NavigationControllerImpl
&>(controller());
220 bool HasNavigationRequest() {
221 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
222 switches::kEnableBrowserSideNavigation
)) {
223 return contents()->GetFrameTree()->root()->navigation_request() !=
226 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
)
230 const GURL
GetLastNavigationURL() {
231 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
232 switches::kEnableBrowserSideNavigation
)) {
233 NavigationRequest
* navigation_request
=
234 contents()->GetFrameTree()->root()->navigation_request();
235 CHECK(navigation_request
);
236 return navigation_request
->common_params().url
;
238 const IPC::Message
* message
=
239 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
);
241 Tuple
<CommonNavigationParams
, StartNavigationParams
,
242 RequestNavigationParams
> nav_params
;
243 FrameMsg_Navigate::Read(message
, &nav_params
);
244 return get
<0>(nav_params
).url
;
249 size_t navigation_entry_committed_counter_
;
252 void RegisterForAllNavNotifications(TestNotificationTracker
* tracker
,
253 NavigationController
* controller
) {
254 tracker
->ListenFor(NOTIFICATION_NAV_LIST_PRUNED
,
255 Source
<NavigationController
>(controller
));
256 tracker
->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED
,
257 Source
<NavigationController
>(controller
));
260 class TestWebContentsDelegate
: public WebContentsDelegate
{
262 explicit TestWebContentsDelegate() :
263 navigation_state_change_count_(0),
264 repost_form_warning_count_(0) {}
266 int navigation_state_change_count() {
267 return navigation_state_change_count_
;
270 int repost_form_warning_count() {
271 return repost_form_warning_count_
;
274 // Keep track of whether the tab has notified us of a navigation state change.
275 void NavigationStateChanged(WebContents
* source
,
276 InvalidateTypes changed_flags
) override
{
277 navigation_state_change_count_
++;
280 void ShowRepostFormWarningDialog(WebContents
* source
) override
{
281 repost_form_warning_count_
++;
285 // The number of times NavigationStateChanged has been called.
286 int navigation_state_change_count_
;
288 // The number of times ShowRepostFormWarningDialog() was called.
289 int repost_form_warning_count_
;
292 // -----------------------------------------------------------------------------
294 TEST_F(NavigationControllerTest
, Defaults
) {
295 NavigationControllerImpl
& controller
= controller_impl();
297 EXPECT_FALSE(controller
.GetPendingEntry());
298 EXPECT_FALSE(controller
.GetVisibleEntry());
299 EXPECT_FALSE(controller
.GetLastCommittedEntry());
300 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
301 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
302 EXPECT_EQ(controller
.GetEntryCount(), 0);
303 EXPECT_FALSE(controller
.CanGoBack());
304 EXPECT_FALSE(controller
.CanGoForward());
307 TEST_F(NavigationControllerTest
, GoToOffset
) {
308 NavigationControllerImpl
& controller
= controller_impl();
309 TestNotificationTracker notifications
;
310 RegisterForAllNavNotifications(¬ifications
, &controller
);
312 const int kNumUrls
= 5;
313 std::vector
<GURL
> urls(kNumUrls
);
314 for (int i
= 0; i
< kNumUrls
; ++i
) {
315 urls
[i
] = GURL(base::StringPrintf("http://www.a.com/%d", i
));
318 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[0], true);
319 main_test_rfh()->PrepareForCommit();
320 main_test_rfh()->SendNavigate(0, 0, true, 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()->SendRendererInitiatedNavigationRequest(urls
[i
], true);
330 main_test_rfh()->PrepareForCommit();
331 main_test_rfh()->SendNavigate(i
, 0, true, urls
[i
]);
332 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
333 navigation_entry_committed_counter_
= 0;
334 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
335 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
336 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
337 EXPECT_FALSE(controller
.CanGoToOffset(1));
340 // We have loaded 5 pages, and are currently at the last-loaded page.
344 GO_TO_MIDDLE_PAGE
= -2,
347 GO_TO_BEGINNING
= -2,
352 const int test_offsets
[NUM_TESTS
] = {
360 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
361 int offset
= test_offsets
[test
];
362 controller
.GoToOffset(offset
);
363 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
365 // Check that the GoToOffset will land on the expected page.
366 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
367 main_test_rfh()->PrepareForCommit();
368 main_test_rfh()->SendNavigate(url_index
, entry_id
, false, urls
[url_index
]);
369 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
370 navigation_entry_committed_counter_
= 0;
371 // Check that we can go to any valid offset into the history.
372 for (size_t j
= 0; j
< urls
.size(); ++j
)
373 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
374 // Check that we can't go beyond the beginning or end of the history.
375 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
376 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
380 TEST_F(NavigationControllerTest
, LoadURL
) {
381 NavigationControllerImpl
& controller
= controller_impl();
382 TestNotificationTracker notifications
;
383 RegisterForAllNavNotifications(¬ifications
, &controller
);
385 const GURL
url1("http://foo1");
386 const GURL
url2("http://foo2");
389 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
390 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
391 // Creating a pending notification should not have issued any of the
392 // notifications we're listening for.
393 EXPECT_EQ(0U, notifications
.size());
395 // The load should now be pending.
396 EXPECT_EQ(controller
.GetEntryCount(), 0);
397 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
398 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
399 EXPECT_FALSE(controller
.GetLastCommittedEntry());
400 ASSERT_TRUE(controller
.GetPendingEntry());
401 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
402 EXPECT_FALSE(controller
.CanGoBack());
403 EXPECT_FALSE(controller
.CanGoForward());
404 EXPECT_EQ(contents()->GetMaxPageID(), -1);
406 // Neither the timestamp nor the status code should have been set yet.
407 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
408 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
410 // We should have gotten no notifications from the preceeding checks.
411 EXPECT_EQ(0U, notifications
.size());
413 main_test_rfh()->PrepareForCommit();
414 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
415 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
416 navigation_entry_committed_counter_
= 0;
418 // The load should now be committed.
419 EXPECT_EQ(controller
.GetEntryCount(), 1);
420 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
421 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
422 EXPECT_TRUE(controller
.GetLastCommittedEntry());
423 EXPECT_FALSE(controller
.GetPendingEntry());
424 ASSERT_TRUE(controller
.GetVisibleEntry());
425 EXPECT_FALSE(controller
.CanGoBack());
426 EXPECT_FALSE(controller
.CanGoForward());
427 EXPECT_EQ(contents()->GetMaxPageID(), 0);
428 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
430 // The timestamp should have been set.
431 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
435 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
436 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
438 // The load should now be pending.
439 EXPECT_EQ(controller
.GetEntryCount(), 1);
440 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
441 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
442 EXPECT_TRUE(controller
.GetLastCommittedEntry());
443 ASSERT_TRUE(controller
.GetPendingEntry());
444 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
445 // TODO(darin): maybe this should really be true?
446 EXPECT_FALSE(controller
.CanGoBack());
447 EXPECT_FALSE(controller
.CanGoForward());
448 EXPECT_EQ(contents()->GetMaxPageID(), 0);
450 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
452 // Simulate the beforeunload ack for the cross-site transition, and then the
454 main_test_rfh()->PrepareForCommit();
455 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
456 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
457 navigation_entry_committed_counter_
= 0;
459 // The load should now be committed.
460 EXPECT_EQ(controller
.GetEntryCount(), 2);
461 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
462 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
463 EXPECT_TRUE(controller
.GetLastCommittedEntry());
464 EXPECT_FALSE(controller
.GetPendingEntry());
465 ASSERT_TRUE(controller
.GetVisibleEntry());
466 EXPECT_TRUE(controller
.CanGoBack());
467 EXPECT_FALSE(controller
.CanGoForward());
468 EXPECT_EQ(contents()->GetMaxPageID(), 1);
470 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
475 base::Time
GetFixedTime(base::Time time
) {
481 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
482 NavigationControllerImpl
& controller
= controller_impl();
483 TestNotificationTracker notifications
;
484 RegisterForAllNavNotifications(¬ifications
, &controller
);
486 // Set the clock to always return a timestamp of 1.
487 controller
.SetGetTimestampCallbackForTest(
488 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
490 const GURL
url1("http://foo1");
491 const GURL
url2("http://foo2");
494 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
495 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
497 main_test_rfh()->PrepareForCommit();
498 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
499 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
500 navigation_entry_committed_counter_
= 0;
504 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
505 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
507 // Simulate the beforeunload ack for the cross-site transition, and then the
509 main_test_rfh()->PrepareForCommit();
510 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
511 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
512 navigation_entry_committed_counter_
= 0;
514 // The two loads should now be committed.
515 ASSERT_EQ(controller
.GetEntryCount(), 2);
517 // Timestamps should be distinct despite the clock returning the
520 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
522 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
525 void CheckNavigationEntryMatchLoadParams(
526 NavigationController::LoadURLParams
& load_params
,
527 NavigationEntryImpl
* entry
) {
528 EXPECT_EQ(load_params
.url
, entry
->GetURL());
529 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
530 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
531 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
532 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
534 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
535 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
536 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
537 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
539 if (NavigationController::UA_OVERRIDE_INHERIT
!=
540 load_params
.override_user_agent
) {
541 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
542 load_params
.override_user_agent
);
543 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
545 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
546 entry
->GetBrowserInitiatedPostData());
547 EXPECT_EQ(load_params
.transferred_global_request_id
,
548 entry
->transferred_global_request_id());
551 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
552 NavigationControllerImpl
& controller
= controller_impl();
554 NavigationController::LoadURLParams
load_params(GURL("http://foo"));
555 load_params
.referrer
=
556 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
557 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
558 load_params
.extra_headers
= "content-type: text/plain";
559 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
560 load_params
.is_renderer_initiated
= true;
561 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
562 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
564 controller
.LoadURLWithParams(load_params
);
565 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
567 // The timestamp should not have been set yet.
569 EXPECT_TRUE(entry
->GetTimestamp().is_null());
571 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
574 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
575 NavigationControllerImpl
& controller
= controller_impl();
577 NavigationController::LoadURLParams
load_params(
578 GURL("data:text/html,dataurl"));
579 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
580 load_params
.base_url_for_data_url
= GURL("http://foo");
581 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
582 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
584 controller
.LoadURLWithParams(load_params
);
585 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
587 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
590 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
591 NavigationControllerImpl
& controller
= controller_impl();
593 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
594 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
595 load_params
.load_type
=
596 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
597 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
600 const unsigned char* raw_data
=
601 reinterpret_cast<const unsigned char*>("d\n\0a2");
602 const int length
= 5;
603 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
604 scoped_refptr
<base::RefCountedBytes
> data
=
605 base::RefCountedBytes::TakeVector(&post_data_vector
);
606 load_params
.browser_initiated_post_data
= data
.get();
608 controller
.LoadURLWithParams(load_params
);
609 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
611 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
614 // Tests what happens when the same page is loaded again. Should not create a
615 // new session history entry. This is what happens when you press enter in the
616 // URL bar to reload: a pending entry is created and then it is discarded when
617 // the load commits (because WebCore didn't actually make a new entry).
618 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
619 NavigationControllerImpl
& controller
= controller_impl();
620 TestNotificationTracker notifications
;
621 RegisterForAllNavNotifications(¬ifications
, &controller
);
623 const GURL
url1("http://foo1");
626 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
627 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
628 EXPECT_EQ(0U, notifications
.size());
629 main_test_rfh()->PrepareForCommit();
630 main_test_rfh()->SendNavigateWithTransition(
631 0, entry_id
, true, url1
, ui::PAGE_TRANSITION_TYPED
);
632 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
633 navigation_entry_committed_counter_
= 0;
635 ASSERT_TRUE(controller
.GetVisibleEntry());
636 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
637 EXPECT_FALSE(timestamp
.is_null());
640 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
641 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
642 EXPECT_EQ(0U, notifications
.size());
643 main_test_rfh()->PrepareForCommit();
644 main_test_rfh()->SendNavigateWithTransition(
645 0, entry_id
, false, url1
, ui::PAGE_TRANSITION_TYPED
);
646 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
647 navigation_entry_committed_counter_
= 0;
649 // We should not have produced a new session history entry.
650 EXPECT_EQ(controller
.GetEntryCount(), 1);
651 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
652 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
653 EXPECT_TRUE(controller
.GetLastCommittedEntry());
654 EXPECT_FALSE(controller
.GetPendingEntry());
655 ASSERT_TRUE(controller
.GetVisibleEntry());
656 EXPECT_FALSE(controller
.CanGoBack());
657 EXPECT_FALSE(controller
.CanGoForward());
659 // The timestamp should have been updated.
661 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
662 // EXPECT_GT once we guarantee that timestamps are unique.
663 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
666 // Load the same page twice, once as a GET and once as a POST.
667 // We should update the post state on the NavigationEntry.
668 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
669 NavigationControllerImpl
& controller
= controller_impl();
670 TestNotificationTracker notifications
;
671 RegisterForAllNavNotifications(¬ifications
, &controller
);
673 const GURL
url1("http://foo1");
676 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
677 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
679 params
.nav_entry_id
= controller
.GetPendingEntry()->GetUniqueID();
680 params
.did_create_new_entry
= true;
682 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
683 params
.is_post
= true;
684 params
.post_id
= 123;
685 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
686 main_test_rfh()->PrepareForCommit();
687 main_test_rfh()->SendNavigateWithParams(¶ms
);
689 // The post data should be visible.
690 NavigationEntry
* entry
= controller
.GetVisibleEntry();
692 EXPECT_TRUE(entry
->GetHasPostData());
693 EXPECT_EQ(entry
->GetPostID(), 123);
696 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
697 main_test_rfh()->PrepareForCommit();
698 main_test_rfh()->SendNavigateWithTransition(
699 0, controller
.GetPendingEntry()->GetUniqueID(),
700 false, url1
, ui::PAGE_TRANSITION_TYPED
);
702 // We should not have produced a new session history entry.
703 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
705 // The post data should have been cleared due to the GET.
706 EXPECT_FALSE(entry
->GetHasPostData());
707 EXPECT_EQ(entry
->GetPostID(), 0);
710 // Tests loading a URL but discarding it before the load commits.
711 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
712 NavigationControllerImpl
& controller
= controller_impl();
713 TestNotificationTracker notifications
;
714 RegisterForAllNavNotifications(¬ifications
, &controller
);
716 const GURL
url1("http://foo1");
717 const GURL
url2("http://foo2");
720 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
721 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
722 EXPECT_EQ(0U, notifications
.size());
723 main_test_rfh()->PrepareForCommit();
724 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
725 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
726 navigation_entry_committed_counter_
= 0;
728 ASSERT_TRUE(controller
.GetVisibleEntry());
729 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
730 EXPECT_FALSE(timestamp
.is_null());
733 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
734 controller
.DiscardNonCommittedEntries();
735 EXPECT_EQ(0U, notifications
.size());
737 // Should not have produced a new session history entry.
738 EXPECT_EQ(controller
.GetEntryCount(), 1);
739 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
740 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
741 EXPECT_TRUE(controller
.GetLastCommittedEntry());
742 EXPECT_FALSE(controller
.GetPendingEntry());
743 ASSERT_TRUE(controller
.GetVisibleEntry());
744 EXPECT_FALSE(controller
.CanGoBack());
745 EXPECT_FALSE(controller
.CanGoForward());
747 // Timestamp should not have changed.
748 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
751 // Tests navigations that come in unrequested. This happens when the user
752 // navigates from the web page, and here we test that there is no pending entry.
753 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
754 NavigationControllerImpl
& controller
= controller_impl();
755 TestNotificationTracker notifications
;
756 RegisterForAllNavNotifications(¬ifications
, &controller
);
758 // First make an existing committed entry.
759 const GURL
kExistingURL1("http://eh");
761 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
762 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
763 main_test_rfh()->PrepareForCommit();
764 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
765 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
766 navigation_entry_committed_counter_
= 0;
768 // Do a new navigation without making a pending one.
769 const GURL
kNewURL("http://see");
770 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL
);
772 // There should no longer be any pending entry, and the second navigation we
773 // just made should be committed.
774 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
775 navigation_entry_committed_counter_
= 0;
776 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
777 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
778 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
781 // Tests navigating to a new URL when there is a new pending navigation that is
782 // not the one that just loaded. This will happen if the user types in a URL to
783 // somewhere slow, and then navigates the current page before the typed URL
785 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
786 NavigationControllerImpl
& controller
= controller_impl();
787 TestNotificationTracker notifications
;
788 RegisterForAllNavNotifications(¬ifications
, &controller
);
790 // First make an existing committed entry.
791 const GURL
kExistingURL1("http://eh");
793 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
794 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
795 main_test_rfh()->PrepareForCommit();
796 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
797 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
798 navigation_entry_committed_counter_
= 0;
800 // Make a pending entry to somewhere new.
801 const GURL
kExistingURL2("http://bee");
803 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
804 EXPECT_EQ(0U, notifications
.size());
806 // After the beforeunload but before it commits...
807 main_test_rfh()->PrepareForCommit();
809 // ... Do a new navigation.
810 const GURL
kNewURL("http://see");
811 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
812 main_test_rfh()->PrepareForCommit();
813 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL
);
815 // There should no longer be any pending entry, and the third navigation we
816 // just made should be committed.
817 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
818 navigation_entry_committed_counter_
= 0;
819 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
820 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
821 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
824 // Tests navigating to a new URL when there is a pending back/forward
825 // navigation. This will happen if the user hits back, but before that commits,
826 // they navigate somewhere new.
827 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
828 NavigationControllerImpl
& controller
= controller_impl();
829 TestNotificationTracker notifications
;
830 RegisterForAllNavNotifications(¬ifications
, &controller
);
832 // First make some history.
833 const GURL
kExistingURL1("http://foo/eh");
835 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
836 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
837 main_test_rfh()->PrepareForCommit();
838 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
839 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
840 navigation_entry_committed_counter_
= 0;
842 const GURL
kExistingURL2("http://foo/bee");
844 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
845 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
846 main_test_rfh()->PrepareForCommit();
847 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
848 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
849 navigation_entry_committed_counter_
= 0;
851 // Now make a pending back/forward navigation. The zeroth entry should be
854 EXPECT_EQ(0U, notifications
.size());
855 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
856 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
858 // Before that commits, do a new navigation.
859 const GURL
kNewURL("http://foo/see");
860 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
861 main_test_rfh()->PrepareForCommit();
862 main_test_rfh()->SendNavigate(3, 0, true, kNewURL
);
864 // There should no longer be any pending entry, and the new navigation we
865 // just made should be committed.
866 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
867 navigation_entry_committed_counter_
= 0;
868 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
869 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
870 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
873 // Tests navigating to a new URL when there is a pending back/forward
874 // navigation to a cross-process, privileged URL. This will happen if the user
875 // hits back, but before that commits, they navigate somewhere new.
876 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
877 NavigationControllerImpl
& controller
= controller_impl();
878 TestNotificationTracker notifications
;
879 RegisterForAllNavNotifications(¬ifications
, &controller
);
881 // First make some history, starting with a privileged URL.
882 const GURL
kExistingURL1("http://privileged");
884 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
885 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
886 // Pretend it has bindings so we can tell if we incorrectly copy it.
887 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
888 main_test_rfh()->PrepareForCommit();
889 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
890 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
891 navigation_entry_committed_counter_
= 0;
893 // Navigate cross-process to a second URL.
894 const GURL
kExistingURL2("http://foo/eh");
896 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
897 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
898 main_test_rfh()->PrepareForCommit();
899 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
900 foo_rfh
->SendNavigate(1, entry_id
, true, kExistingURL2
);
901 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
902 navigation_entry_committed_counter_
= 0;
904 // Now make a pending back/forward navigation to a privileged entry.
905 // The zeroth entry should be pending.
907 foo_rfh
->SendBeforeUnloadACK(true);
908 EXPECT_EQ(0U, notifications
.size());
909 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
910 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
911 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
913 // Before that commits, do a new navigation.
914 const GURL
kNewURL("http://foo/bee");
915 foo_rfh
->SendRendererInitiatedNavigationRequest(kNewURL
, true);
916 foo_rfh
->PrepareForCommit();
917 foo_rfh
->SendNavigate(3, 0, true, kNewURL
);
919 // There should no longer be any pending entry, and the new navigation we
920 // just made should be committed.
921 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
922 navigation_entry_committed_counter_
= 0;
923 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
924 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
925 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
926 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
929 // Tests navigating to an existing URL when there is a pending new navigation.
930 // This will happen if the user enters a URL, but before that commits, the
931 // current page fires history.back().
932 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
933 NavigationControllerImpl
& controller
= controller_impl();
934 TestNotificationTracker notifications
;
935 RegisterForAllNavNotifications(¬ifications
, &controller
);
937 // First make some history.
938 const GURL
kExistingURL1("http://foo/eh");
940 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
941 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
942 main_test_rfh()->PrepareForCommit();
943 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
944 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
945 navigation_entry_committed_counter_
= 0;
947 const GURL
kExistingURL2("http://foo/bee");
949 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
950 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
951 main_test_rfh()->PrepareForCommit();
952 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
953 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
954 navigation_entry_committed_counter_
= 0;
956 // A back navigation comes in from the renderer...
957 controller
.GoToOffset(-1);
958 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
960 // ...while the user tries to navigate to a new page...
961 const GURL
kNewURL("http://foo/see");
963 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
964 EXPECT_EQ(0U, notifications
.size());
965 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
966 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
968 // ...and the back navigation commits.
969 main_test_rfh()->PrepareForCommit();
970 main_test_rfh()->SendNavigate(0, entry_id
, false, kExistingURL1
);
972 // There should no longer be any pending entry, and the back navigation should
974 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
975 navigation_entry_committed_counter_
= 0;
976 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
977 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
978 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
981 // Tests an ignored navigation when there is a pending new navigation.
982 // This will happen if the user enters a URL, but before that commits, the
983 // current blank page reloads. See http://crbug.com/77507.
984 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
985 NavigationControllerImpl
& controller
= controller_impl();
986 TestNotificationTracker notifications
;
987 RegisterForAllNavNotifications(¬ifications
, &controller
);
989 // Set a WebContentsDelegate to listen for state changes.
990 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
991 EXPECT_FALSE(contents()->GetDelegate());
992 contents()->SetDelegate(delegate
.get());
994 // Without any navigations, the renderer starts at about:blank.
995 const GURL
kExistingURL(url::kAboutBlankURL
);
997 // Now make a pending new navigation.
998 const GURL
kNewURL("http://eh");
1000 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1001 EXPECT_EQ(0U, notifications
.size());
1002 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1003 EXPECT_TRUE(controller
.GetPendingEntry());
1004 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1005 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1007 // Before that commits, a document.write and location.reload can cause the
1008 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1009 // PlzNavigate: this will stop the old navigation and start a new one.
1010 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL
, true);
1011 main_test_rfh()->PrepareForCommit();
1012 main_test_rfh()->SendNavigate(-1, 0, false, kExistingURL
);
1014 // This should clear the pending entry and notify of a navigation state
1015 // change, so that we do not keep displaying kNewURL.
1016 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1017 EXPECT_FALSE(controller
.GetPendingEntry());
1018 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1019 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1020 switches::kEnableBrowserSideNavigation
))
1021 EXPECT_EQ(4, delegate
->navigation_state_change_count());
1023 EXPECT_EQ(2, delegate
->navigation_state_change_count());
1025 contents()->SetDelegate(NULL
);
1028 // Tests that the pending entry state is correct after an abort.
1029 // We do not want to clear the pending entry, so that the user doesn't
1030 // lose a typed URL. (See http://crbug.com/9682.)
1031 TEST_F(NavigationControllerTest
, LoadURL_AbortDoesntCancelPending
) {
1032 NavigationControllerImpl
& controller
= controller_impl();
1033 TestNotificationTracker notifications
;
1034 RegisterForAllNavNotifications(¬ifications
, &controller
);
1036 // Set a WebContentsDelegate to listen for state changes.
1037 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1038 EXPECT_FALSE(contents()->GetDelegate());
1039 contents()->SetDelegate(delegate
.get());
1041 // Start with a pending new navigation.
1042 const GURL
kNewURL("http://eh");
1044 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1045 main_test_rfh()->PrepareForCommit();
1046 EXPECT_EQ(0U, notifications
.size());
1047 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1048 EXPECT_TRUE(controller
.GetPendingEntry());
1049 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1050 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1052 // It may abort before committing, if it's a download or due to a stop or
1053 // a new navigation from the user.
1054 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1055 params
.error_code
= net::ERR_ABORTED
;
1056 params
.error_description
= base::string16();
1057 params
.url
= kNewURL
;
1058 params
.showing_repost_interstitial
= false;
1059 main_test_rfh()->OnMessageReceived(
1060 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1063 // This should not clear the pending entry or notify of a navigation state
1064 // change, so that we keep displaying kNewURL (until the user clears it).
1065 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1066 EXPECT_TRUE(controller
.GetPendingEntry());
1067 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1068 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1069 NavigationEntry
* pending_entry
= controller
.GetPendingEntry();
1071 // Ensure that a reload keeps the same pending entry.
1072 controller
.Reload(true);
1073 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1074 EXPECT_TRUE(controller
.GetPendingEntry());
1075 EXPECT_EQ(pending_entry
, controller
.GetPendingEntry());
1076 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1078 contents()->SetDelegate(NULL
);
1081 // Tests that the pending URL is not visible during a renderer-initiated
1082 // redirect and abort. See http://crbug.com/83031.
1083 TEST_F(NavigationControllerTest
, LoadURL_RedirectAbortDoesntShowPendingURL
) {
1084 NavigationControllerImpl
& controller
= controller_impl();
1085 TestNotificationTracker notifications
;
1086 RegisterForAllNavNotifications(¬ifications
, &controller
);
1088 // First make an existing committed entry.
1089 const GURL
kExistingURL("http://foo/eh");
1090 controller
.LoadURL(kExistingURL
, content::Referrer(),
1091 ui::PAGE_TRANSITION_TYPED
, std::string());
1092 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1093 main_test_rfh()->PrepareForCommit();
1094 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL
);
1095 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1096 navigation_entry_committed_counter_
= 0;
1098 // Set a WebContentsDelegate to listen for state changes.
1099 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1100 EXPECT_FALSE(contents()->GetDelegate());
1101 contents()->SetDelegate(delegate
.get());
1103 // Now make a pending new navigation, initiated by the renderer.
1104 const GURL
kNewURL("http://foo/bee");
1105 NavigationController::LoadURLParams
load_url_params(kNewURL
);
1106 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
1107 load_url_params
.is_renderer_initiated
= true;
1108 controller
.LoadURLWithParams(load_url_params
);
1109 EXPECT_EQ(0U, notifications
.size());
1110 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1111 EXPECT_TRUE(controller
.GetPendingEntry());
1112 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1113 EXPECT_EQ(0, delegate
->navigation_state_change_count());
1115 // The visible entry should be the last committed URL, not the pending one.
1116 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1118 // Now the navigation redirects. (There is no corresponding message here.)
1119 const GURL
kRedirectURL("http://foo/see");
1121 // We don't want to change the NavigationEntry's url, in case it cancels.
1122 // Prevents regression of http://crbug.com/77786.
1123 EXPECT_EQ(kNewURL
, controller
.GetPendingEntry()->GetURL());
1125 // It may abort before committing, if it's a download or due to a stop or
1126 // a new navigation from the user.
1127 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1128 params
.error_code
= net::ERR_ABORTED
;
1129 params
.error_description
= base::string16();
1130 params
.url
= kRedirectURL
;
1131 params
.showing_repost_interstitial
= false;
1132 main_test_rfh()->OnMessageReceived(
1133 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1136 // Because the pending entry is renderer initiated and not visible, we
1137 // clear it when it fails.
1138 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1139 EXPECT_FALSE(controller
.GetPendingEntry());
1140 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1141 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1143 // The visible entry should be the last committed URL, not the pending one,
1144 // so that no spoof is possible.
1145 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1147 contents()->SetDelegate(NULL
);
1150 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1151 // at the time they committed. http://crbug.com/173672.
1152 TEST_F(NavigationControllerTest
, LoadURL_WithBindings
) {
1153 NavigationControllerImpl
& controller
= controller_impl();
1154 TestNotificationTracker notifications
;
1155 RegisterForAllNavNotifications(¬ifications
, &controller
);
1156 std::vector
<GURL
> url_chain
;
1158 const GURL
url1("http://foo1");
1159 const GURL
url2("http://foo2");
1161 // Navigate to a first, unprivileged URL.
1163 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1164 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings
,
1165 controller
.GetPendingEntry()->bindings());
1166 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1169 TestRenderFrameHost
* orig_rfh
= contents()->GetMainFrame();
1170 orig_rfh
->PrepareForCommit();
1171 orig_rfh
->SendNavigate(0, entry1_id
, true, url1
);
1172 EXPECT_EQ(controller
.GetEntryCount(), 1);
1173 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1174 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1175 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1177 // Manually increase the number of active frames in the SiteInstance
1178 // that orig_rfh belongs to, to prevent it from being destroyed when
1179 // it gets swapped out, so that we can reuse orig_rfh when the
1180 // controller goes back.
1181 orig_rfh
->GetSiteInstance()->increment_active_frame_count();
1183 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1184 // transition, and set bindings on the pending RenderViewHost to simulate a
1187 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1188 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1189 orig_rfh
->PrepareForCommit();
1190 TestRenderFrameHost
* new_rfh
= contents()->GetPendingMainFrame();
1191 new_rfh
->GetRenderViewHost()->AllowBindings(1);
1192 new_rfh
->SendNavigate(1, entry_id
, true, url2
);
1194 // The second load should be committed, and bindings should be remembered.
1195 EXPECT_EQ(controller
.GetEntryCount(), 2);
1196 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1197 EXPECT_TRUE(controller
.CanGoBack());
1198 EXPECT_EQ(1, controller
.GetLastCommittedEntry()->bindings());
1200 // Going back, the first entry should still appear unprivileged.
1201 controller
.GoBack();
1202 new_rfh
->PrepareForCommit();
1203 orig_rfh
->SendNavigate(0, entry1_id
, false, url1
);
1204 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1205 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1208 TEST_F(NavigationControllerTest
, Reload
) {
1209 NavigationControllerImpl
& controller
= controller_impl();
1210 TestNotificationTracker notifications
;
1211 RegisterForAllNavNotifications(¬ifications
, &controller
);
1213 const GURL
url1("http://foo1");
1216 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1217 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1218 EXPECT_EQ(0U, notifications
.size());
1219 main_test_rfh()->PrepareForCommit();
1220 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1221 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1222 navigation_entry_committed_counter_
= 0;
1223 ASSERT_TRUE(controller
.GetVisibleEntry());
1224 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1225 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1227 controller
.Reload(true);
1228 EXPECT_EQ(0U, notifications
.size());
1230 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
1231 EXPECT_FALSE(timestamp
.is_null());
1233 // The reload is pending.
1234 EXPECT_EQ(controller
.GetEntryCount(), 1);
1235 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1236 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1237 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1238 EXPECT_TRUE(controller
.GetPendingEntry());
1239 EXPECT_FALSE(controller
.CanGoBack());
1240 EXPECT_FALSE(controller
.CanGoForward());
1241 // Make sure the title has been cleared (will be redrawn just after reload).
1242 // Avoids a stale cached title when the new page being reloaded has no title.
1243 // See http://crbug.com/96041.
1244 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1246 main_test_rfh()->PrepareForCommit();
1247 main_test_rfh()->SendNavigate(0, entry_id
, false, url1
);
1248 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1249 navigation_entry_committed_counter_
= 0;
1251 // Now the reload is committed.
1252 EXPECT_EQ(controller
.GetEntryCount(), 1);
1253 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1254 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1255 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1256 EXPECT_FALSE(controller
.GetPendingEntry());
1257 EXPECT_FALSE(controller
.CanGoBack());
1258 EXPECT_FALSE(controller
.CanGoForward());
1260 // The timestamp should have been updated.
1261 ASSERT_TRUE(controller
.GetVisibleEntry());
1262 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
1265 // Tests what happens when a reload navigation produces a new page.
1266 TEST_F(NavigationControllerTest
, Reload_GeneratesNewPage
) {
1267 NavigationControllerImpl
& controller
= controller_impl();
1268 TestNotificationTracker notifications
;
1269 RegisterForAllNavNotifications(¬ifications
, &controller
);
1271 const GURL
url1("http://foo1");
1272 const GURL
url2("http://foo2");
1275 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1276 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1277 main_test_rfh()->PrepareForCommit();
1278 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1279 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1280 navigation_entry_committed_counter_
= 0;
1281 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1283 controller
.Reload(true);
1284 EXPECT_EQ(0U, notifications
.size());
1286 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1287 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1288 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1289 navigation_entry_committed_counter_
= 0;
1291 // Now the reload is committed.
1292 EXPECT_EQ(controller
.GetEntryCount(), 2);
1293 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1294 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1295 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1296 EXPECT_FALSE(controller
.GetPendingEntry());
1297 EXPECT_TRUE(controller
.CanGoBack());
1298 EXPECT_FALSE(controller
.CanGoForward());
1301 // This test ensures that when a guest renderer reloads, the reload goes through
1302 // without ending up in the "we have a wrong process for the URL" branch in
1303 // NavigationControllerImpl::ReloadInternal.
1304 TEST_F(NavigationControllerTest
, ReloadWithGuest
) {
1305 NavigationControllerImpl
& controller
= controller_impl();
1307 const GURL
url1("http://foo1");
1309 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1310 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1311 main_test_rfh()->PrepareForCommit();
1312 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1313 ASSERT_TRUE(controller
.GetVisibleEntry());
1315 // Make the entry believe its RenderProcessHost is a guest.
1316 NavigationEntryImpl
* entry1
= controller
.GetVisibleEntry();
1317 reinterpret_cast<MockRenderProcessHost
*>(
1318 entry1
->site_instance()->GetProcess())->set_is_isolated_guest(true);
1321 controller
.Reload(true);
1323 // The reload is pending. Check that the NavigationEntry didn't get replaced
1324 // because of having the wrong process.
1325 EXPECT_EQ(controller
.GetEntryCount(), 1);
1326 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1327 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1329 NavigationEntryImpl
* entry2
= controller
.GetPendingEntry();
1330 EXPECT_EQ(entry1
, entry2
);
1333 #if !defined(OS_ANDROID) // http://crbug.com/157428
1334 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1335 NavigationControllerImpl
& controller
= controller_impl();
1336 TestNotificationTracker notifications
;
1337 RegisterForAllNavNotifications(¬ifications
, &controller
);
1339 const GURL
original_url("http://foo1");
1340 const GURL
final_url("http://foo2");
1342 // Load up the original URL, but get redirected.
1344 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1345 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1346 EXPECT_EQ(0U, notifications
.size());
1347 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1348 main_test_rfh()->SendNavigateWithOriginalRequestURL(0, entry_id
, true,
1349 final_url
, original_url
);
1350 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1351 navigation_entry_committed_counter_
= 0;
1352 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1354 // The NavigationEntry should save both the original URL and the final
1357 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1358 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1360 // Reload using the original URL.
1361 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1362 controller
.ReloadOriginalRequestURL(false);
1363 EXPECT_EQ(0U, notifications
.size());
1365 // The reload is pending. The request should point to the original URL.
1366 EXPECT_EQ(original_url
, navigated_url());
1367 EXPECT_EQ(controller
.GetEntryCount(), 1);
1368 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1369 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1370 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1371 EXPECT_TRUE(controller
.GetPendingEntry());
1372 EXPECT_FALSE(controller
.CanGoBack());
1373 EXPECT_FALSE(controller
.CanGoForward());
1375 // Make sure the title has been cleared (will be redrawn just after reload).
1376 // Avoids a stale cached title when the new page being reloaded has no title.
1377 // See http://crbug.com/96041.
1378 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1380 // Send that the navigation has proceeded; say it got redirected again.
1381 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1382 main_test_rfh()->SendNavigate(0, entry_id
, false, final_url
);
1383 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1384 navigation_entry_committed_counter_
= 0;
1386 // Now the reload is committed.
1387 EXPECT_EQ(controller
.GetEntryCount(), 1);
1388 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1389 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1390 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1391 EXPECT_FALSE(controller
.GetPendingEntry());
1392 EXPECT_FALSE(controller
.CanGoBack());
1393 EXPECT_FALSE(controller
.CanGoForward());
1396 #endif // !defined(OS_ANDROID)
1398 // Test that certain non-persisted NavigationEntryImpl values get reset after
1400 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1401 NavigationControllerImpl
& controller
= controller_impl();
1403 // The value of "should replace entry" will be tested, but it's an error to
1404 // specify it when there are no entries. Create a simple entry to be replaced.
1405 const GURL
url0("http://foo/0");
1407 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1408 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1409 main_test_rfh()->PrepareForCommit();
1410 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
1412 // Set up the pending entry.
1413 const GURL
url1("http://foo/1");
1415 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1416 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1418 // Set up some sample values.
1419 const unsigned char* raw_data
=
1420 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1421 const int length
= 11;
1422 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1423 scoped_refptr
<base::RefCountedBytes
> post_data
=
1424 base::RefCountedBytes::TakeVector(&post_data_vector
);
1425 GlobalRequestID
transfer_id(3, 4);
1427 // Set non-persisted values on the pending entry.
1428 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1429 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1430 pending_entry
->set_is_renderer_initiated(true);
1431 pending_entry
->set_transferred_global_request_id(transfer_id
);
1432 pending_entry
->set_should_replace_entry(true);
1433 pending_entry
->set_should_clear_history_list(true);
1434 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1435 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1436 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1437 EXPECT_TRUE(pending_entry
->should_replace_entry());
1438 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1440 // Fake a commit response.
1441 main_test_rfh()->PrepareForCommit();
1442 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
1444 // Certain values that are only used for pending entries get reset after
1446 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1447 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1448 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1449 EXPECT_EQ(GlobalRequestID(-1, -1),
1450 committed_entry
->transferred_global_request_id());
1451 EXPECT_FALSE(committed_entry
->should_replace_entry());
1452 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1455 // Test that Redirects are preserved after a commit.
1456 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1457 NavigationControllerImpl
& controller
= controller_impl();
1458 const GURL
url1("http://foo1");
1459 const GURL
url2("http://foo2");
1461 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1462 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1464 // Set up some redirect values.
1465 std::vector
<GURL
> redirects
;
1466 redirects
.push_back(url2
);
1468 // Set redirects on the pending entry.
1469 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1470 pending_entry
->SetRedirectChain(redirects
);
1471 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1472 EXPECT_EQ(url2
, pending_entry
->GetRedirectChain()[0]);
1474 // Normal navigation will preserve redirects in the committed entry.
1475 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1476 main_test_rfh()->SendNavigateWithRedirects(0, entry_id
, true, url1
,
1478 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1479 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1480 EXPECT_EQ(url2
, committed_entry
->GetRedirectChain()[0]);
1483 // Tests what happens when we navigate back successfully
1484 TEST_F(NavigationControllerTest
, Back
) {
1485 NavigationControllerImpl
& controller
= controller_impl();
1486 TestNotificationTracker notifications
;
1487 RegisterForAllNavNotifications(¬ifications
, &controller
);
1489 const GURL
url1("http://foo1");
1490 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1491 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1492 navigation_entry_committed_counter_
= 0;
1494 const GURL
url2("http://foo2");
1495 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
1496 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1497 navigation_entry_committed_counter_
= 0;
1499 controller
.GoBack();
1500 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1501 EXPECT_EQ(0U, notifications
.size());
1503 // We should now have a pending navigation to go back.
1504 EXPECT_EQ(controller
.GetEntryCount(), 2);
1505 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1506 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1507 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1508 EXPECT_TRUE(controller
.GetPendingEntry());
1509 EXPECT_FALSE(controller
.CanGoBack());
1510 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1511 EXPECT_TRUE(controller
.CanGoForward());
1512 EXPECT_TRUE(controller
.CanGoToOffset(1));
1513 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go forward 2 steps.
1515 // Timestamp for entry 1 should be on or after that of entry 0.
1516 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1517 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1518 controller
.GetEntryAtIndex(0)->GetTimestamp());
1520 main_test_rfh()->SendNavigate(0, entry_id
, false, url2
);
1521 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1522 navigation_entry_committed_counter_
= 0;
1524 // The back navigation completed successfully.
1525 EXPECT_EQ(controller
.GetEntryCount(), 2);
1526 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1527 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1528 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1529 EXPECT_FALSE(controller
.GetPendingEntry());
1530 EXPECT_FALSE(controller
.CanGoBack());
1531 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1532 EXPECT_TRUE(controller
.CanGoForward());
1533 EXPECT_TRUE(controller
.CanGoToOffset(1));
1534 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1536 // Timestamp for entry 0 should be on or after that of entry 1
1537 // (since we went back to it).
1538 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1539 controller
.GetEntryAtIndex(1)->GetTimestamp());
1542 // Tests what happens when a back navigation produces a new page.
1543 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1544 NavigationControllerImpl
& controller
= controller_impl();
1545 TestNotificationTracker notifications
;
1546 RegisterForAllNavNotifications(¬ifications
, &controller
);
1548 const GURL
url1("http://foo/1");
1549 const GURL
url2("http://foo/2");
1550 const GURL
url3("http://foo/3");
1553 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1554 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1555 main_test_rfh()->PrepareForCommit();
1556 main_test_rfh()->SendNavigate(0, entry1_id
, true, url1
);
1557 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1558 navigation_entry_committed_counter_
= 0;
1559 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1562 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1563 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1564 main_test_rfh()->PrepareForCommit();
1565 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1566 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1567 navigation_entry_committed_counter_
= 0;
1569 controller
.GoBack();
1570 EXPECT_EQ(0U, notifications
.size());
1572 // We should now have a pending navigation to go back.
1573 EXPECT_EQ(controller
.GetEntryCount(), 2);
1574 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1575 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1576 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1577 EXPECT_TRUE(controller
.GetPendingEntry());
1578 EXPECT_FALSE(controller
.CanGoBack());
1579 EXPECT_TRUE(controller
.CanGoForward());
1581 main_test_rfh()->PrepareForCommitWithServerRedirect(url3
);
1582 main_test_rfh()->SendNavigate(2, entry1_id
, true, url3
);
1583 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1584 navigation_entry_committed_counter_
= 0;
1586 // The back navigation resulted in a completely new navigation.
1587 // TODO(darin): perhaps this behavior will be confusing to users?
1588 EXPECT_EQ(controller
.GetEntryCount(), 3);
1589 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1590 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1591 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1592 EXPECT_FALSE(controller
.GetPendingEntry());
1593 EXPECT_TRUE(controller
.CanGoBack());
1594 EXPECT_FALSE(controller
.CanGoForward());
1597 // Receives a back message when there is a new pending navigation entry.
1598 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1599 NavigationControllerImpl
& controller
= controller_impl();
1600 TestNotificationTracker notifications
;
1601 RegisterForAllNavNotifications(¬ifications
, &controller
);
1603 const GURL
kUrl1("http://foo1");
1604 const GURL
kUrl2("http://foo2");
1605 const GURL
kUrl3("http://foo3");
1607 // First navigate two places so we have some back history.
1608 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
1609 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1610 navigation_entry_committed_counter_
= 0;
1612 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1613 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
1614 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1615 navigation_entry_committed_counter_
= 0;
1617 // Now start a new pending navigation and go back before it commits.
1619 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1620 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1621 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1622 controller
.GoBack();
1624 // The pending navigation should now be the "back" item and the new one
1626 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1627 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1630 // Receives a back message when there is a different renavigation already
1632 TEST_F(NavigationControllerTest
, Back_OtherBackPending
) {
1633 NavigationControllerImpl
& controller
= controller_impl();
1634 const GURL
kUrl1("http://foo/1");
1635 const GURL
kUrl2("http://foo/2");
1636 const GURL
kUrl3("http://foo/3");
1638 // First navigate three places so we have some back history.
1639 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, true);
1640 main_test_rfh()->PrepareForCommit();
1641 main_test_rfh()->SendNavigate(0, 0, true, kUrl1
);
1642 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, true);
1643 main_test_rfh()->PrepareForCommit();
1644 main_test_rfh()->SendNavigate(1, 0, true, kUrl2
);
1645 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl3
, true);
1646 main_test_rfh()->PrepareForCommit();
1647 main_test_rfh()->SendNavigate(2, 0, true, kUrl3
);
1649 // With nothing pending, say we get a renderer back navigation request to the
1651 controller
.GoToOffset(-1);
1652 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1653 main_test_rfh()->PrepareForCommit();
1654 main_test_rfh()->SendNavigate(1, entry_id
, false, kUrl2
);
1656 // We know all the entries have the same site instance, so we can just grab
1657 // a random one for looking up other entries.
1658 SiteInstance
* site_instance
=
1659 controller
.GetLastCommittedEntry()->site_instance();
1661 // That second URL should be the last committed and it should have gotten the
1663 EXPECT_EQ(kUrl2
, controller
.GetEntryWithPageID(site_instance
, 1)->GetURL());
1664 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1665 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1667 // Now go forward to the last item again and say it was committed.
1668 controller
.GoForward();
1669 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1670 main_test_rfh()->PrepareForCommit();
1671 main_test_rfh()->SendNavigate(2, entry_id
, false, kUrl3
);
1673 // Now start going back one to the second page. It will be pending.
1674 controller
.GoBack();
1675 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
1676 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
1678 // Now have the renderer request a navigation back to the first page. This
1679 // will not match the pending one.
1680 controller
.GoToOffset(-2);
1681 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1682 main_test_rfh()->PrepareForCommit();
1683 main_test_rfh()->SendNavigate(0, entry_id
, false, kUrl1
);
1685 // The committed navigation should clear the pending entry.
1686 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1688 // But the navigated entry should be the last committed.
1689 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1690 EXPECT_EQ(kUrl1
, controller
.GetLastCommittedEntry()->GetURL());
1693 // Tests what happens when we navigate forward successfully.
1694 TEST_F(NavigationControllerTest
, Forward
) {
1695 NavigationControllerImpl
& controller
= controller_impl();
1696 TestNotificationTracker notifications
;
1697 RegisterForAllNavNotifications(¬ifications
, &controller
);
1699 const GURL
url1("http://foo1");
1700 const GURL
url2("http://foo2");
1702 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1703 main_test_rfh()->PrepareForCommit();
1704 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1705 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1706 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1707 navigation_entry_committed_counter_
= 0;
1709 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1710 main_test_rfh()->PrepareForCommit();
1711 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1712 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1713 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1714 navigation_entry_committed_counter_
= 0;
1716 controller
.GoBack();
1717 main_test_rfh()->PrepareForCommit();
1718 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1719 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1720 navigation_entry_committed_counter_
= 0;
1722 controller
.GoForward();
1724 // We should now have a pending navigation to go forward.
1725 EXPECT_EQ(controller
.GetEntryCount(), 2);
1726 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1727 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1728 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1729 EXPECT_TRUE(controller
.GetPendingEntry());
1730 EXPECT_TRUE(controller
.CanGoBack());
1731 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1732 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1733 EXPECT_FALSE(controller
.CanGoForward());
1734 EXPECT_FALSE(controller
.CanGoToOffset(1));
1736 // Timestamp for entry 0 should be on or after that of entry 1
1737 // (since we went back to it).
1738 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1739 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1740 controller
.GetEntryAtIndex(1)->GetTimestamp());
1742 main_test_rfh()->PrepareForCommit();
1743 main_test_rfh()->SendNavigate(1, entry2
->GetUniqueID(), false, url2
);
1744 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1745 navigation_entry_committed_counter_
= 0;
1747 // The forward navigation completed successfully.
1748 EXPECT_EQ(controller
.GetEntryCount(), 2);
1749 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1750 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1751 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1752 EXPECT_FALSE(controller
.GetPendingEntry());
1753 EXPECT_TRUE(controller
.CanGoBack());
1754 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1755 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1756 EXPECT_FALSE(controller
.CanGoForward());
1757 EXPECT_FALSE(controller
.CanGoToOffset(1));
1759 // Timestamp for entry 1 should be on or after that of entry 0
1760 // (since we went forward to it).
1761 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1762 controller
.GetEntryAtIndex(0)->GetTimestamp());
1765 // Tests what happens when a forward navigation produces a new page.
1766 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1767 NavigationControllerImpl
& controller
= controller_impl();
1768 TestNotificationTracker notifications
;
1769 RegisterForAllNavNotifications(¬ifications
, &controller
);
1771 const GURL
url1("http://foo1");
1772 const GURL
url2("http://foo2");
1773 const GURL
url3("http://foo3");
1775 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1776 main_test_rfh()->PrepareForCommit();
1777 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1778 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1779 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1780 navigation_entry_committed_counter_
= 0;
1781 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1782 main_test_rfh()->PrepareForCommit();
1783 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1784 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1785 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1786 navigation_entry_committed_counter_
= 0;
1788 controller
.GoBack();
1789 main_test_rfh()->PrepareForCommit();
1790 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1791 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1792 navigation_entry_committed_counter_
= 0;
1794 controller
.GoForward();
1795 EXPECT_EQ(0U, notifications
.size());
1797 // Should now have a pending navigation to go forward.
1798 EXPECT_EQ(controller
.GetEntryCount(), 2);
1799 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1800 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1801 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1802 EXPECT_TRUE(controller
.GetPendingEntry());
1803 EXPECT_TRUE(controller
.CanGoBack());
1804 EXPECT_FALSE(controller
.CanGoForward());
1806 main_test_rfh()->PrepareForCommit();
1807 main_test_rfh()->SendNavigate(2, entry2
->GetUniqueID(), true, url3
);
1808 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1809 navigation_entry_committed_counter_
= 0;
1810 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1812 EXPECT_EQ(controller
.GetEntryCount(), 2);
1813 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1814 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1815 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1816 EXPECT_FALSE(controller
.GetPendingEntry());
1817 EXPECT_TRUE(controller
.CanGoBack());
1818 EXPECT_FALSE(controller
.CanGoForward());
1821 // Two consecutive navigations for the same URL entered in should be considered
1822 // as SAME_PAGE navigation even when we are redirected to some other page.
1823 TEST_F(NavigationControllerTest
, Redirect
) {
1824 NavigationControllerImpl
& controller
= controller_impl();
1825 TestNotificationTracker notifications
;
1826 RegisterForAllNavNotifications(¬ifications
, &controller
);
1828 const GURL
url1("http://foo1");
1829 const GURL
url2("http://foo2"); // Redirection target
1833 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1834 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1836 EXPECT_EQ(0U, notifications
.size());
1838 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1840 params
.nav_entry_id
= entry_id
;
1841 params
.did_create_new_entry
= true;
1843 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1844 params
.redirects
.push_back(GURL("http://foo1"));
1845 params
.redirects
.push_back(GURL("http://foo2"));
1846 params
.should_update_history
= false;
1847 params
.gesture
= NavigationGestureAuto
;
1848 params
.is_post
= false;
1849 params
.page_state
= PageState::CreateFromURL(url2
);
1851 LoadCommittedDetails details
;
1853 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1855 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1856 navigation_entry_committed_counter_
= 0;
1860 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1861 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1863 EXPECT_TRUE(controller
.GetPendingEntry());
1864 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1865 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1867 params
.nav_entry_id
= entry_id
;
1868 params
.did_create_new_entry
= false;
1870 EXPECT_EQ(0U, notifications
.size());
1871 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1873 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1874 navigation_entry_committed_counter_
= 0;
1876 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1877 EXPECT_EQ(controller
.GetEntryCount(), 1);
1878 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1879 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1880 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1881 EXPECT_FALSE(controller
.GetPendingEntry());
1882 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1884 EXPECT_FALSE(controller
.CanGoBack());
1885 EXPECT_FALSE(controller
.CanGoForward());
1888 // Similar to Redirect above, but the first URL is requested by POST,
1889 // the second URL is requested by GET. NavigationEntry::has_post_data_
1890 // must be cleared. http://crbug.com/21245
1891 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1892 NavigationControllerImpl
& controller
= controller_impl();
1893 TestNotificationTracker notifications
;
1894 RegisterForAllNavNotifications(¬ifications
, &controller
);
1896 const GURL
url1("http://foo1");
1897 const GURL
url2("http://foo2"); // Redirection target
1899 // First request as POST.
1901 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1902 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1903 controller
.GetVisibleEntry()->SetHasPostData(true);
1905 EXPECT_EQ(0U, notifications
.size());
1907 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1909 params
.nav_entry_id
= entry_id
;
1910 params
.did_create_new_entry
= true;
1912 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1913 params
.redirects
.push_back(GURL("http://foo1"));
1914 params
.redirects
.push_back(GURL("http://foo2"));
1915 params
.should_update_history
= false;
1916 params
.gesture
= NavigationGestureAuto
;
1917 params
.is_post
= true;
1918 params
.page_state
= PageState::CreateFromURL(url2
);
1920 LoadCommittedDetails details
;
1922 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1924 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1925 navigation_entry_committed_counter_
= 0;
1929 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1930 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1932 EXPECT_TRUE(controller
.GetPendingEntry());
1933 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1934 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1936 params
.nav_entry_id
= entry_id
;
1937 params
.did_create_new_entry
= false;
1938 params
.is_post
= false;
1940 EXPECT_EQ(0U, notifications
.size());
1941 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1943 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1944 navigation_entry_committed_counter_
= 0;
1946 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1947 EXPECT_EQ(controller
.GetEntryCount(), 1);
1948 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1949 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1950 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1951 EXPECT_FALSE(controller
.GetPendingEntry());
1952 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1953 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1955 EXPECT_FALSE(controller
.CanGoBack());
1956 EXPECT_FALSE(controller
.CanGoForward());
1959 // A redirect right off the bat should be a NEW_PAGE.
1960 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1961 NavigationControllerImpl
& controller
= controller_impl();
1962 TestNotificationTracker notifications
;
1963 RegisterForAllNavNotifications(¬ifications
, &controller
);
1965 const GURL
url1("http://foo1");
1966 const GURL
url2("http://foo2"); // Redirection target
1970 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1971 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1973 EXPECT_TRUE(controller
.GetPendingEntry());
1974 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1975 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1977 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1979 params
.nav_entry_id
= entry_id
;
1980 params
.did_create_new_entry
= true;
1982 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1983 params
.redirects
.push_back(GURL("http://foo1"));
1984 params
.redirects
.push_back(GURL("http://foo2"));
1985 params
.should_update_history
= false;
1986 params
.gesture
= NavigationGestureAuto
;
1987 params
.is_post
= false;
1988 params
.page_state
= PageState::CreateFromURL(url2
);
1990 LoadCommittedDetails details
;
1992 EXPECT_EQ(0U, notifications
.size());
1993 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1995 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1996 navigation_entry_committed_counter_
= 0;
1998 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
1999 EXPECT_EQ(controller
.GetEntryCount(), 1);
2000 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
2001 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2002 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2003 EXPECT_FALSE(controller
.GetPendingEntry());
2004 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
2006 EXPECT_FALSE(controller
.CanGoBack());
2007 EXPECT_FALSE(controller
.CanGoForward());
2010 // If something is pumping the event loop in the browser process and is loading
2011 // pages rapidly one after the other, there can be a race with two closely-
2012 // spaced load requests. Once the first load request is sent, will the renderer
2013 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
2014 // IPC, and have the browser process handle that IPC before the caller makes
2015 // another load request, replacing the pending entry of the first request?
2017 // This test is about what happens in such a race when that pending entry
2018 // replacement happens. If it happens, and the first load had the same URL as
2019 // the page before it, we must make sure that the replacement of the pending
2020 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
2022 // (This is a unit test rather than a browser test because it's not currently
2023 // possible to force this sequence of events with a browser test.)
2024 TEST_F(NavigationControllerTest
,
2025 NavigationTypeClassification_ExistingPageRace
) {
2026 NavigationControllerImpl
& controller
= controller_impl();
2027 const GURL
url1("http://foo1");
2028 const GURL
url2("http://foo2");
2030 // Start with a loaded page.
2031 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2032 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
2034 // Start a load of the same page again.
2036 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2037 int entry_id1
= controller
.GetPendingEntry()->GetUniqueID();
2039 // Immediately start loading a different page...
2041 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2042 int entry_id2
= controller
.GetPendingEntry()->GetUniqueID();
2043 EXPECT_NE(entry_id1
, entry_id2
);
2045 // ... and now the renderer sends a commit for the first navigation.
2046 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2048 params
.nav_entry_id
= entry_id1
;
2049 params
.intended_as_new_entry
= true;
2050 params
.did_create_new_entry
= false;
2052 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
2053 params
.page_state
= PageState::CreateFromURL(url1
);
2055 LoadCommittedDetails details
;
2057 main_test_rfh()->PrepareForCommit();
2058 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2060 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
2063 // Tests navigation via link click within a subframe. A new navigation entry
2064 // should be created.
2065 TEST_F(NavigationControllerTest
, NewSubframe
) {
2066 NavigationControllerImpl
& controller
= controller_impl();
2067 TestNotificationTracker notifications
;
2068 RegisterForAllNavNotifications(¬ifications
, &controller
);
2070 const GURL
url1("http://foo1");
2071 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2072 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2073 navigation_entry_committed_counter_
= 0;
2075 const GURL
url2("http://foo2");
2076 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2078 params
.nav_entry_id
= 0;
2079 params
.did_create_new_entry
= true;
2081 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2082 params
.should_update_history
= false;
2083 params
.gesture
= NavigationGestureUser
;
2084 params
.is_post
= false;
2085 params
.page_state
= PageState::CreateFromURL(url2
);
2087 LoadCommittedDetails details
;
2088 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2090 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2091 navigation_entry_committed_counter_
= 0;
2092 EXPECT_EQ(url1
, details
.previous_url
);
2093 EXPECT_FALSE(details
.is_in_page
);
2094 EXPECT_FALSE(details
.is_main_frame
);
2096 // The new entry should be appended.
2097 EXPECT_EQ(2, controller
.GetEntryCount());
2099 // New entry should refer to the new page, but the old URL (entries only
2100 // reflect the toplevel URL).
2101 EXPECT_EQ(url1
, details
.entry
->GetURL());
2102 EXPECT_EQ(params
.page_id
, details
.entry
->GetPageID());
2105 // Auto subframes are ones the page loads automatically like ads. They should
2106 // not create new navigation entries.
2107 // TODO(creis): Test cross-site and nested iframes.
2108 // TODO(creis): Test updating entries for history auto subframe navigations.
2109 TEST_F(NavigationControllerTest
, AutoSubframe
) {
2110 NavigationControllerImpl
& controller
= controller_impl();
2111 TestNotificationTracker notifications
;
2112 RegisterForAllNavNotifications(¬ifications
, &controller
);
2114 const GURL
url1("http://foo/1");
2115 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2116 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2117 navigation_entry_committed_counter_
= 0;
2119 // Add a subframe and navigate it.
2120 main_test_rfh()->OnCreateChildFrame(MSG_ROUTING_NONE
, std::string(),
2121 SandboxFlags::NONE
);
2122 RenderFrameHostImpl
* subframe
=
2123 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2124 const GURL
url2("http://foo/2");
2126 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2128 params
.nav_entry_id
= 0;
2129 params
.did_create_new_entry
= false;
2131 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2132 params
.should_update_history
= false;
2133 params
.gesture
= NavigationGestureUser
;
2134 params
.is_post
= false;
2135 params
.page_state
= PageState::CreateFromURL(url2
);
2137 // Navigating should do nothing.
2138 LoadCommittedDetails details
;
2139 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2140 EXPECT_EQ(0U, notifications
.size());
2143 // There should still be only one entry.
2144 EXPECT_EQ(1, controller
.GetEntryCount());
2145 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2146 EXPECT_EQ(url1
, entry
->GetURL());
2147 EXPECT_EQ(1, entry
->GetPageID());
2148 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
2149 EXPECT_EQ(url1
, root_entry
->url());
2151 // Verify subframe entries if we're in --site-per-process mode.
2152 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2153 switches::kSitePerProcess
)) {
2154 // The entry should now have a subframe FrameNavigationEntry.
2155 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2156 FrameNavigationEntry
* frame_entry
=
2157 entry
->root_node()->children
[0]->frame_entry
.get();
2158 EXPECT_EQ(url2
, frame_entry
->url());
2160 // There are no subframe FrameNavigationEntries by default.
2161 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2164 // Add a second subframe and navigate.
2165 main_test_rfh()->OnCreateChildFrame(MSG_ROUTING_NONE
, std::string(),
2166 SandboxFlags::NONE
);
2167 RenderFrameHostImpl
* subframe2
=
2168 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2169 const GURL
url3("http://foo/3");
2171 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2174 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2175 params
.should_update_history
= false;
2176 params
.gesture
= NavigationGestureUser
;
2177 params
.is_post
= false;
2178 params
.page_state
= PageState::CreateFromURL(url3
);
2180 // Navigating should do nothing.
2181 LoadCommittedDetails details
;
2182 EXPECT_FALSE(controller
.RendererDidNavigate(subframe2
, params
, &details
));
2183 EXPECT_EQ(0U, notifications
.size());
2186 // There should still be only one entry, mostly unchanged.
2187 EXPECT_EQ(1, controller
.GetEntryCount());
2188 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2189 EXPECT_EQ(url1
, entry
->GetURL());
2190 EXPECT_EQ(1, entry
->GetPageID());
2191 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2192 EXPECT_EQ(url1
, root_entry
->url());
2194 // Verify subframe entries if we're in --site-per-process mode.
2195 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2196 switches::kSitePerProcess
)) {
2197 // The entry should now have 2 subframe FrameNavigationEntries.
2198 ASSERT_EQ(2U, entry
->root_node()->children
.size());
2199 FrameNavigationEntry
* new_frame_entry
=
2200 entry
->root_node()->children
[1]->frame_entry
.get();
2201 EXPECT_EQ(url3
, new_frame_entry
->url());
2203 // There are no subframe FrameNavigationEntries by default.
2204 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2208 // Tests navigation and then going back to a subframe navigation.
2209 TEST_F(NavigationControllerTest
, BackSubframe
) {
2210 NavigationControllerImpl
& controller
= controller_impl();
2211 TestNotificationTracker notifications
;
2212 RegisterForAllNavNotifications(¬ifications
, &controller
);
2215 const GURL
url1("http://foo1");
2216 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2217 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2218 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2219 navigation_entry_committed_counter_
= 0;
2221 // First manual subframe navigation.
2222 const GURL
url2("http://foo2");
2223 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2225 params
.nav_entry_id
= 0;
2226 params
.did_create_new_entry
= true;
2228 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2229 params
.should_update_history
= false;
2230 params
.gesture
= NavigationGestureUser
;
2231 params
.is_post
= false;
2232 params
.page_state
= PageState::CreateFromURL(url2
);
2234 // This should generate a new entry.
2235 LoadCommittedDetails details
;
2236 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2238 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2239 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2240 navigation_entry_committed_counter_
= 0;
2241 EXPECT_EQ(2, controller
.GetEntryCount());
2243 // Second manual subframe navigation should also make a new entry.
2244 const GURL
url3("http://foo3");
2246 params
.nav_entry_id
= 0;
2247 params
.did_create_new_entry
= true;
2249 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2250 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2252 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2253 navigation_entry_committed_counter_
= 0;
2254 EXPECT_EQ(3, controller
.GetEntryCount());
2255 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2258 controller
.GoBack();
2260 params
.nav_entry_id
= entry2
->GetUniqueID();
2261 params
.did_create_new_entry
= false;
2263 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2264 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2266 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2267 navigation_entry_committed_counter_
= 0;
2268 EXPECT_EQ(3, controller
.GetEntryCount());
2269 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2270 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2271 EXPECT_FALSE(controller
.GetPendingEntry());
2273 // Go back one more.
2274 controller
.GoBack();
2276 params
.nav_entry_id
= entry1
->GetUniqueID();
2277 params
.did_create_new_entry
= false;
2279 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2280 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2282 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2283 navigation_entry_committed_counter_
= 0;
2284 EXPECT_EQ(3, controller
.GetEntryCount());
2285 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2286 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2287 EXPECT_FALSE(controller
.GetPendingEntry());
2290 TEST_F(NavigationControllerTest
, LinkClick
) {
2291 NavigationControllerImpl
& controller
= controller_impl();
2292 TestNotificationTracker notifications
;
2293 RegisterForAllNavNotifications(¬ifications
, &controller
);
2295 const GURL
url1("http://foo1");
2296 const GURL
url2("http://foo2");
2298 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2299 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2300 navigation_entry_committed_counter_
= 0;
2302 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
2303 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2304 navigation_entry_committed_counter_
= 0;
2306 // Should have produced a new session history entry.
2307 EXPECT_EQ(controller
.GetEntryCount(), 2);
2308 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2309 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2310 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2311 EXPECT_FALSE(controller
.GetPendingEntry());
2312 EXPECT_TRUE(controller
.CanGoBack());
2313 EXPECT_FALSE(controller
.CanGoForward());
2316 TEST_F(NavigationControllerTest
, InPage
) {
2317 NavigationControllerImpl
& controller
= controller_impl();
2318 TestNotificationTracker notifications
;
2319 RegisterForAllNavNotifications(¬ifications
, &controller
);
2322 const GURL
url1("http://foo");
2323 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2324 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2325 navigation_entry_committed_counter_
= 0;
2327 // Ensure main page navigation to same url respects the was_within_same_page
2328 // hint provided in the params.
2329 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2330 self_params
.page_id
= 0;
2331 self_params
.nav_entry_id
= 0;
2332 self_params
.did_create_new_entry
= false;
2333 self_params
.url
= url1
;
2334 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2335 self_params
.should_update_history
= false;
2336 self_params
.gesture
= NavigationGestureUser
;
2337 self_params
.is_post
= false;
2338 self_params
.page_state
= PageState::CreateFromURL(url1
);
2339 self_params
.was_within_same_page
= true;
2341 LoadCommittedDetails details
;
2342 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2344 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2345 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2346 navigation_entry_committed_counter_
= 0;
2347 EXPECT_TRUE(details
.is_in_page
);
2348 EXPECT_TRUE(details
.did_replace_entry
);
2349 EXPECT_EQ(1, controller
.GetEntryCount());
2351 // Fragment navigation to a new page_id.
2352 const GURL
url2("http://foo#a");
2353 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2355 params
.nav_entry_id
= 0;
2356 params
.did_create_new_entry
= true;
2358 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2359 params
.should_update_history
= false;
2360 params
.gesture
= NavigationGestureUser
;
2361 params
.is_post
= false;
2362 params
.page_state
= PageState::CreateFromURL(url2
);
2363 params
.was_within_same_page
= true;
2365 // This should generate a new entry.
2366 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2368 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2369 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2370 navigation_entry_committed_counter_
= 0;
2371 EXPECT_TRUE(details
.is_in_page
);
2372 EXPECT_FALSE(details
.did_replace_entry
);
2373 EXPECT_EQ(2, controller
.GetEntryCount());
2376 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2377 controller
.GoBack();
2378 back_params
.url
= url1
;
2379 back_params
.page_id
= 0;
2380 back_params
.nav_entry_id
= entry1
->GetUniqueID();
2381 back_params
.did_create_new_entry
= false;
2382 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2384 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2385 navigation_entry_committed_counter_
= 0;
2386 EXPECT_TRUE(details
.is_in_page
);
2387 EXPECT_EQ(2, controller
.GetEntryCount());
2388 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2389 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2392 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2393 controller
.GoForward();
2394 forward_params
.url
= url2
;
2395 forward_params
.page_id
= 1;
2396 forward_params
.nav_entry_id
= entry2
->GetUniqueID();
2397 forward_params
.did_create_new_entry
= false;
2398 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2400 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2401 navigation_entry_committed_counter_
= 0;
2402 EXPECT_TRUE(details
.is_in_page
);
2403 EXPECT_EQ(2, controller
.GetEntryCount());
2404 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2405 EXPECT_EQ(forward_params
.url
,
2406 controller
.GetVisibleEntry()->GetURL());
2408 // Now go back and forward again. This is to work around a bug where we would
2409 // compare the incoming URL with the last committed entry rather than the
2410 // one identified by an existing page ID. This would result in the second URL
2411 // losing the reference fragment when you navigate away from it and then back.
2412 controller
.GoBack();
2413 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2415 controller
.GoForward();
2416 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2418 EXPECT_EQ(forward_params
.url
,
2419 controller
.GetVisibleEntry()->GetURL());
2421 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2422 const GURL
url3("http://bar");
2424 params
.nav_entry_id
= 0;
2425 params
.did_create_new_entry
= true;
2427 navigation_entry_committed_counter_
= 0;
2428 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2430 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2431 navigation_entry_committed_counter_
= 0;
2432 EXPECT_FALSE(details
.is_in_page
);
2433 EXPECT_EQ(3, controller
.GetEntryCount());
2434 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2437 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2438 NavigationControllerImpl
& controller
= controller_impl();
2439 TestNotificationTracker notifications
;
2440 RegisterForAllNavNotifications(¬ifications
, &controller
);
2443 const GURL
url1("http://foo");
2444 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2445 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2446 navigation_entry_committed_counter_
= 0;
2448 // First navigation.
2449 const GURL
url2("http://foo#a");
2450 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2451 params
.page_id
= 0; // Same page_id
2452 params
.nav_entry_id
= 0;
2453 params
.did_create_new_entry
= false;
2455 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2456 params
.should_update_history
= false;
2457 params
.gesture
= NavigationGestureUser
;
2458 params
.is_post
= false;
2459 params
.page_state
= PageState::CreateFromURL(url2
);
2460 params
.was_within_same_page
= true;
2462 // This should NOT generate a new entry, nor prune the list.
2463 LoadCommittedDetails details
;
2464 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2466 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2467 navigation_entry_committed_counter_
= 0;
2468 EXPECT_TRUE(details
.is_in_page
);
2469 EXPECT_TRUE(details
.did_replace_entry
);
2470 EXPECT_EQ(1, controller
.GetEntryCount());
2473 // Tests for http://crbug.com/40395
2476 // window.location.replace("#a");
2477 // window.location='http://foo3/';
2479 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2480 NavigationControllerImpl
& controller
= controller_impl();
2481 TestNotificationTracker notifications
;
2482 RegisterForAllNavNotifications(¬ifications
, &controller
);
2484 // Load an initial page.
2486 const GURL
url("http://foo/");
2487 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
2488 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2489 navigation_entry_committed_counter_
= 0;
2492 // Navigate to a new page.
2494 const GURL
url("http://foo2/");
2495 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url
);
2496 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2497 navigation_entry_committed_counter_
= 0;
2500 // Navigate within the page.
2502 const GURL
url("http://foo2/#a");
2503 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2504 params
.page_id
= 1; // Same page_id
2505 params
.nav_entry_id
= 0;
2506 params
.did_create_new_entry
= false;
2508 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2509 params
.redirects
.push_back(url
);
2510 params
.should_update_history
= true;
2511 params
.gesture
= NavigationGestureUnknown
;
2512 params
.is_post
= false;
2513 params
.page_state
= PageState::CreateFromURL(url
);
2514 params
.was_within_same_page
= true;
2516 // This should NOT generate a new entry, nor prune the list.
2517 LoadCommittedDetails details
;
2518 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2520 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2521 navigation_entry_committed_counter_
= 0;
2522 EXPECT_TRUE(details
.is_in_page
);
2523 EXPECT_TRUE(details
.did_replace_entry
);
2524 EXPECT_EQ(2, controller
.GetEntryCount());
2527 // Perform a client redirect to a new page.
2529 const GURL
url("http://foo3/");
2530 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2531 params
.page_id
= 2; // New page_id
2532 params
.nav_entry_id
= 0;
2533 params
.did_create_new_entry
= true;
2535 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2536 params
.redirects
.push_back(GURL("http://foo2/#a"));
2537 params
.redirects
.push_back(url
);
2538 params
.should_update_history
= true;
2539 params
.gesture
= NavigationGestureUnknown
;
2540 params
.is_post
= false;
2541 params
.page_state
= PageState::CreateFromURL(url
);
2543 // This SHOULD generate a new entry.
2544 LoadCommittedDetails details
;
2545 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2547 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2548 navigation_entry_committed_counter_
= 0;
2549 EXPECT_FALSE(details
.is_in_page
);
2550 EXPECT_EQ(3, controller
.GetEntryCount());
2553 // Verify that BACK brings us back to http://foo2/.
2555 const GURL
url("http://foo2/");
2556 controller
.GoBack();
2557 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2558 main_test_rfh()->PrepareForCommit();
2559 main_test_rfh()->SendNavigate(1, entry_id
, false, url
);
2560 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2561 navigation_entry_committed_counter_
= 0;
2562 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2566 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2568 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2569 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2570 GURL
url("http://foo");
2572 params
.nav_entry_id
= 0;
2573 params
.did_create_new_entry
= true;
2575 params
.page_state
= PageState::CreateFromURL(url
);
2576 params
.was_within_same_page
= true;
2577 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
2578 main_test_rfh()->PrepareForCommit();
2579 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2580 // We pass if we don't crash.
2583 // NotificationObserver implementation used in verifying we've received the
2584 // NOTIFICATION_NAV_LIST_PRUNED method.
2585 class PrunedListener
: public NotificationObserver
{
2587 explicit PrunedListener(NavigationControllerImpl
* controller
)
2588 : notification_count_(0) {
2589 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2590 Source
<NavigationController
>(controller
));
2593 void Observe(int type
,
2594 const NotificationSource
& source
,
2595 const NotificationDetails
& details
) override
{
2596 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2597 notification_count_
++;
2598 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2602 // Number of times NAV_LIST_PRUNED has been observed.
2603 int notification_count_
;
2605 // Details from the last NAV_LIST_PRUNED.
2606 PrunedDetails details_
;
2609 NotificationRegistrar registrar_
;
2611 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2614 // Tests that we limit the number of navigation entries created correctly.
2615 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2616 NavigationControllerImpl
& controller
= controller_impl();
2617 size_t original_count
= NavigationControllerImpl::max_entry_count();
2618 const int kMaxEntryCount
= 5;
2620 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2623 // Load up to the max count, all entries should be there.
2624 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2625 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2627 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2628 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2629 main_test_rfh()->PrepareForCommit();
2630 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2633 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2635 // Created a PrunedListener to observe prune notifications.
2636 PrunedListener
listener(&controller
);
2638 // Navigate some more.
2639 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2641 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2642 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2643 main_test_rfh()->PrepareForCommit();
2644 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2647 // We should have got a pruned navigation.
2648 EXPECT_EQ(1, listener
.notification_count_
);
2649 EXPECT_TRUE(listener
.details_
.from_front
);
2650 EXPECT_EQ(1, listener
.details_
.count
);
2652 // We expect http://www.a.com/0 to be gone.
2653 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2654 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2655 GURL("http://www.a.com/1"));
2657 // More navigations.
2658 for (int i
= 0; i
< 3; i
++) {
2659 url
= GURL(base::StringPrintf("http://www.a.com/%d", url_index
));
2661 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2662 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2663 main_test_rfh()->PrepareForCommit();
2664 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2667 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2668 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2669 GURL("http://www.a.com/4"));
2671 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2674 // Tests that we can do a restore and navigate to the restored entries and
2675 // everything is updated properly. This can be tricky since there is no
2676 // SiteInstance for the entries created initially.
2677 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2678 // Create a NavigationController with a restored set of tabs.
2679 GURL
url("http://foo");
2680 std::vector
<NavigationEntry
*> entries
;
2681 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
2682 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2684 entry
->SetPageID(0);
2685 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2686 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2687 const base::Time timestamp
= base::Time::Now();
2688 entry
->SetTimestamp(timestamp
);
2689 entries
.push_back(entry
);
2690 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2691 WebContents::Create(WebContents::CreateParams(browser_context()))));
2692 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2693 our_controller
.Restore(
2695 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2697 ASSERT_EQ(0u, entries
.size());
2699 // Before navigating to the restored entry, it should have a restore_type
2700 // and no SiteInstance.
2701 ASSERT_EQ(1, our_controller
.GetEntryCount());
2702 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2703 our_controller
.GetEntryAtIndex(0)->restore_type());
2704 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2706 // After navigating, we should have one entry, and it should be "pending".
2707 // It should now have a SiteInstance and no restore_type.
2708 our_controller
.GoToIndex(0);
2709 EXPECT_EQ(1, our_controller
.GetEntryCount());
2710 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2711 our_controller
.GetPendingEntry());
2712 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2713 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2714 our_controller
.GetEntryAtIndex(0)->restore_type());
2715 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2717 // Timestamp should remain the same before the navigation finishes.
2718 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2720 // Say we navigated to that entry.
2721 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2723 params
.nav_entry_id
= our_controller
.GetPendingEntry()->GetUniqueID();
2724 params
.did_create_new_entry
= false;
2726 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2727 params
.should_update_history
= false;
2728 params
.gesture
= NavigationGestureUser
;
2729 params
.is_post
= false;
2730 params
.page_state
= PageState::CreateFromURL(url
);
2731 LoadCommittedDetails details
;
2732 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2735 // There should be no longer any pending entry and one committed one. This
2736 // means that we were able to locate the entry, assign its site instance, and
2737 // commit it properly.
2738 EXPECT_EQ(1, our_controller
.GetEntryCount());
2739 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2740 EXPECT_FALSE(our_controller
.GetPendingEntry());
2743 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2744 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2745 our_controller
.GetEntryAtIndex(0)->restore_type());
2747 // Timestamp should have been updated.
2748 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2751 // Tests that we can still navigate to a restored entry after a different
2752 // navigation fails and clears the pending entry. http://crbug.com/90085
2753 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2754 // Create a NavigationController with a restored set of tabs.
2755 GURL
url("http://foo");
2756 std::vector
<NavigationEntry
*> entries
;
2757 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
2758 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2760 entry
->SetPageID(0);
2761 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2762 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2763 entries
.push_back(entry
);
2764 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2765 WebContents::Create(WebContents::CreateParams(browser_context()))));
2766 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2767 our_controller
.Restore(
2768 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2769 ASSERT_EQ(0u, entries
.size());
2771 // Before navigating to the restored entry, it should have a restore_type
2772 // and no SiteInstance.
2773 entry
= our_controller
.GetEntryAtIndex(0);
2774 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2775 our_controller
.GetEntryAtIndex(0)->restore_type());
2776 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2778 // After navigating, we should have one entry, and it should be "pending".
2779 // It should now have a SiteInstance and no restore_type.
2780 our_controller
.GoToIndex(0);
2781 EXPECT_EQ(1, our_controller
.GetEntryCount());
2782 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2783 our_controller
.GetPendingEntry());
2784 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2785 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2786 our_controller
.GetEntryAtIndex(0)->restore_type());
2787 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2789 // This pending navigation may have caused a different navigation to fail,
2790 // which causes the pending entry to be cleared.
2791 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2792 fail_load_params
.error_code
= net::ERR_ABORTED
;
2793 fail_load_params
.error_description
= base::string16();
2794 fail_load_params
.url
= url
;
2795 fail_load_params
.showing_repost_interstitial
= false;
2796 main_test_rfh()->OnMessageReceived(
2797 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2800 // Now the pending restored entry commits.
2801 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2803 params
.nav_entry_id
= entry
->GetUniqueID();
2804 params
.did_create_new_entry
= false;
2806 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2807 params
.should_update_history
= false;
2808 params
.gesture
= NavigationGestureUser
;
2809 params
.is_post
= false;
2810 params
.page_state
= PageState::CreateFromURL(url
);
2811 LoadCommittedDetails details
;
2812 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2815 // There should be no pending entry and one committed one.
2816 EXPECT_EQ(1, our_controller
.GetEntryCount());
2817 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2818 EXPECT_FALSE(our_controller
.GetPendingEntry());
2821 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2822 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2823 our_controller
.GetEntryAtIndex(0)->restore_type());
2826 // Make sure that the page type and stuff is correct after an interstitial.
2827 TEST_F(NavigationControllerTest
, Interstitial
) {
2828 NavigationControllerImpl
& controller
= controller_impl();
2829 // First navigate somewhere normal.
2830 const GURL
url1("http://foo");
2832 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2833 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2834 main_test_rfh()->PrepareForCommit();
2835 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2837 // Now navigate somewhere with an interstitial.
2838 const GURL
url2("http://bar");
2839 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
,
2841 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2842 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2844 // At this point the interstitial will be displayed and the load will still
2845 // be pending. If the user continues, the load will commit.
2846 main_test_rfh()->PrepareForCommit();
2847 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2849 // The page should be a normal page again.
2850 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2851 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2852 controller
.GetLastCommittedEntry()->GetPageType());
2855 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2856 NavigationControllerImpl
& controller
= controller_impl();
2857 const GURL
url1("http://foo/1");
2858 const GURL
url2("http://foo/2");
2859 const GURL
url3("http://foo/3");
2860 const GURL
url4("http://foo/4");
2861 const GURL
url5("http://foo/5");
2862 const GURL
pending_url("http://foo/pending");
2863 const GURL
default_url("http://foo/default");
2866 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2867 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2868 main_test_rfh()->PrepareForCommit();
2869 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2871 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2872 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2873 main_test_rfh()->PrepareForCommit();
2874 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2876 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2877 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2878 main_test_rfh()->PrepareForCommit();
2879 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2881 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2882 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2883 main_test_rfh()->PrepareForCommit();
2884 main_test_rfh()->SendNavigate(3, entry_id
, true, url4
);
2886 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2887 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2888 main_test_rfh()->PrepareForCommit();
2889 main_test_rfh()->SendNavigate(4, entry_id
, true, url5
);
2891 // Try to remove the last entry. Will fail because it is the current entry.
2892 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2893 EXPECT_EQ(5, controller
.GetEntryCount());
2894 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2896 // Go back, but don't commit yet. Check that we can't delete the current
2897 // and pending entries.
2898 controller
.GoBack();
2899 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2900 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2901 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
2903 // Now commit and delete the last entry.
2904 main_test_rfh()->PrepareForCommit();
2905 main_test_rfh()->SendNavigate(3, entry_id
, false, url4
);
2906 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2907 EXPECT_EQ(4, controller
.GetEntryCount());
2908 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
2909 EXPECT_FALSE(controller
.GetPendingEntry());
2911 // Remove an entry which is not the last committed one.
2912 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
2913 EXPECT_EQ(3, controller
.GetEntryCount());
2914 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
2915 EXPECT_FALSE(controller
.GetPendingEntry());
2917 // Remove the 2 remaining entries.
2918 controller
.RemoveEntryAtIndex(1);
2919 controller
.RemoveEntryAtIndex(0);
2921 // This should leave us with only the last committed entry.
2922 EXPECT_EQ(1, controller
.GetEntryCount());
2923 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
2926 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
2927 NavigationControllerImpl
& controller
= controller_impl();
2928 const GURL
url1("http://foo/1");
2929 const GURL
url2("http://foo/2");
2930 const GURL
url3("http://foo/3");
2931 const GURL
default_url("http://foo/default");
2934 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2935 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2936 main_test_rfh()->PrepareForCommit();
2937 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2939 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2940 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2941 main_test_rfh()->PrepareForCommit();
2942 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2944 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2945 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2946 main_test_rfh()->PrepareForCommit();
2947 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2949 // Go back, but don't commit yet. Check that we can't delete the current
2950 // and pending entries.
2951 controller
.GoBack();
2952 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2953 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
2954 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
2956 // Remove the first entry, while there is a pending entry. This is expected
2957 // to discard the pending entry.
2958 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
2959 EXPECT_FALSE(controller
.GetPendingEntry());
2960 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2962 // We should update the last committed entry index.
2963 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
2965 // Now commit and ensure we land on the right entry.
2966 main_test_rfh()->PrepareForCommit();
2967 main_test_rfh()->SendNavigate(1, entry_id
, false, url2
);
2968 EXPECT_EQ(2, controller
.GetEntryCount());
2969 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
2970 EXPECT_FALSE(controller
.GetPendingEntry());
2973 // Tests the transient entry, making sure it goes away with all navigations.
2974 TEST_F(NavigationControllerTest
, TransientEntry
) {
2975 NavigationControllerImpl
& controller
= controller_impl();
2976 TestNotificationTracker notifications
;
2977 RegisterForAllNavNotifications(¬ifications
, &controller
);
2979 const GURL
url0("http://foo/0");
2980 const GURL
url1("http://foo/1");
2981 const GURL
url2("http://foo/2");
2982 const GURL
url3("http://foo/3");
2983 const GURL
url3_ref("http://foo/3#bar");
2984 const GURL
url4("http://foo/4");
2985 const GURL
transient_url("http://foo/transient");
2988 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2989 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2990 main_test_rfh()->PrepareForCommit();
2991 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
2993 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2994 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2995 main_test_rfh()->PrepareForCommit();
2996 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
2998 notifications
.Reset();
3000 // Adding a transient with no pending entry.
3001 NavigationEntryImpl
* transient_entry
= new NavigationEntryImpl
;
3002 transient_entry
->SetURL(transient_url
);
3003 controller
.SetTransientEntry(transient_entry
);
3005 // We should not have received any notifications.
3006 EXPECT_EQ(0U, notifications
.size());
3009 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3010 EXPECT_EQ(controller
.GetEntryCount(), 3);
3011 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
3012 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
3013 EXPECT_TRUE(controller
.GetLastCommittedEntry());
3014 EXPECT_FALSE(controller
.GetPendingEntry());
3015 EXPECT_TRUE(controller
.CanGoBack());
3016 EXPECT_FALSE(controller
.CanGoForward());
3017 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3021 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3022 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3023 main_test_rfh()->PrepareForCommit();
3024 main_test_rfh()->SendNavigate(2, entry_id
, true, url2
);
3026 // We should have navigated, transient entry should be gone.
3027 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3028 EXPECT_EQ(controller
.GetEntryCount(), 3);
3030 // Add a transient again, then navigate with no pending entry this time.
3031 transient_entry
= new NavigationEntryImpl
;
3032 transient_entry
->SetURL(transient_url
);
3033 controller
.SetTransientEntry(transient_entry
);
3034 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3035 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3
, true);
3036 main_test_rfh()->PrepareForCommit();
3037 main_test_rfh()->SendNavigate(3, 0, true, url3
);
3038 // Transient entry should be gone.
3039 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3040 EXPECT_EQ(controller
.GetEntryCount(), 4);
3042 // Initiate a navigation, add a transient then commit navigation.
3044 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3045 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3046 transient_entry
= new NavigationEntryImpl
;
3047 transient_entry
->SetURL(transient_url
);
3048 controller
.SetTransientEntry(transient_entry
);
3049 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3050 main_test_rfh()->PrepareForCommit();
3051 main_test_rfh()->SendNavigate(4, entry_id
, true, url4
);
3052 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3053 EXPECT_EQ(controller
.GetEntryCount(), 5);
3055 // Add a transient and go back. This should simply remove the transient.
3056 transient_entry
= new NavigationEntryImpl
;
3057 transient_entry
->SetURL(transient_url
);
3058 controller
.SetTransientEntry(transient_entry
);
3059 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3060 EXPECT_TRUE(controller
.CanGoBack());
3061 EXPECT_FALSE(controller
.CanGoForward());
3062 controller
.GoBack();
3063 // Transient entry should be gone.
3064 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3065 EXPECT_EQ(controller
.GetEntryCount(), 5);
3067 // Suppose the page requested a history navigation backward.
3068 controller
.GoToOffset(-1);
3069 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3070 main_test_rfh()->PrepareForCommit();
3071 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3073 // Add a transient and go to an entry before the current one.
3074 transient_entry
= new NavigationEntryImpl
;
3075 transient_entry
->SetURL(transient_url
);
3076 controller
.SetTransientEntry(transient_entry
);
3077 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3078 controller
.GoToIndex(1);
3079 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3080 // The navigation should have been initiated, transient entry should be gone.
3081 EXPECT_FALSE(controller
.GetTransientEntry());
3082 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3083 // Visible entry does not update for history navigations until commit.
3084 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3085 main_test_rfh()->PrepareForCommit();
3086 main_test_rfh()->SendNavigate(1, entry_id
, false, url1
);
3087 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3089 // Add a transient and go to an entry after the current one.
3090 transient_entry
= new NavigationEntryImpl
;
3091 transient_entry
->SetURL(transient_url
);
3092 controller
.SetTransientEntry(transient_entry
);
3093 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3094 controller
.GoToIndex(3);
3095 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3096 // The navigation should have been initiated, transient entry should be gone.
3097 // Because of the transient entry that is removed, going to index 3 makes us
3098 // land on url2 (which is visible after the commit).
3099 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3100 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3101 main_test_rfh()->PrepareForCommit();
3102 main_test_rfh()->SendNavigate(2, entry_id
, false, url2
);
3103 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3105 // Add a transient and go forward.
3106 transient_entry
= new NavigationEntryImpl
;
3107 transient_entry
->SetURL(transient_url
);
3108 controller
.SetTransientEntry(transient_entry
);
3109 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3110 EXPECT_TRUE(controller
.CanGoForward());
3111 controller
.GoForward();
3112 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3113 // We should have navigated, transient entry should be gone.
3114 EXPECT_FALSE(controller
.GetTransientEntry());
3115 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
3116 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3117 main_test_rfh()->PrepareForCommit();
3118 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3119 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3121 // Add a transient and do an in-page navigation, replacing the current entry.
3122 transient_entry
= new NavigationEntryImpl
;
3123 transient_entry
->SetURL(transient_url
);
3124 controller
.SetTransientEntry(transient_entry
);
3125 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3127 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref
, false);
3128 main_test_rfh()->PrepareForCommit();
3129 main_test_rfh()->SendNavigate(3, 0, false, url3_ref
);
3130 // Transient entry should be gone.
3131 EXPECT_FALSE(controller
.GetTransientEntry());
3132 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
3134 // Ensure the URLs are correct.
3135 EXPECT_EQ(controller
.GetEntryCount(), 5);
3136 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3137 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
3138 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
3139 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
3140 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
3143 // Test that Reload initiates a new navigation to a transient entry's URL.
3144 TEST_F(NavigationControllerTest
, ReloadTransient
) {
3145 NavigationControllerImpl
& controller
= controller_impl();
3146 const GURL
url0("http://foo/0");
3147 const GURL
url1("http://foo/1");
3148 const GURL
transient_url("http://foo/transient");
3150 // Load |url0|, and start a pending navigation to |url1|.
3152 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3153 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3154 main_test_rfh()->PrepareForCommit();
3155 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3157 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3159 // A transient entry is added, interrupting the navigation.
3160 NavigationEntryImpl
* transient_entry
= new NavigationEntryImpl
;
3161 transient_entry
->SetURL(transient_url
);
3162 controller
.SetTransientEntry(transient_entry
);
3163 EXPECT_TRUE(controller
.GetTransientEntry());
3164 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3166 // The page is reloaded, which should remove the pending entry for |url1| and
3167 // the transient entry for |transient_url|, and start a navigation to
3169 controller
.Reload(true);
3170 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3171 EXPECT_FALSE(controller
.GetTransientEntry());
3172 EXPECT_TRUE(controller
.GetPendingEntry());
3173 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3174 ASSERT_EQ(controller
.GetEntryCount(), 1);
3175 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3177 // Load of |transient_url| completes.
3178 main_test_rfh()->PrepareForCommit();
3179 main_test_rfh()->SendNavigate(1, entry_id
, true, transient_url
);
3180 ASSERT_EQ(controller
.GetEntryCount(), 2);
3181 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3182 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
3185 // Ensure that renderer initiated pending entries get replaced, so that we
3186 // don't show a stale virtual URL when a navigation commits.
3187 // See http://crbug.com/266922.
3188 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
3189 NavigationControllerImpl
& controller
= controller_impl();
3190 Navigator
* navigator
=
3191 contents()->GetFrameTree()->root()->navigator();
3193 const GURL
url1("nonexistent:12121");
3194 const GURL
url1_fixed("http://nonexistent:12121/");
3195 const GURL
url2("http://foo");
3197 // We create pending entries for renderer-initiated navigations so that we
3198 // can show them in new tabs when it is safe.
3199 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
, false);
3201 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3202 // the virtual URL to differ from the URL.
3203 controller
.GetPendingEntry()->SetURL(url1_fixed
);
3204 controller
.GetPendingEntry()->SetVirtualURL(url1
);
3206 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
3207 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
3208 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3210 // If the user clicks another link, we should replace the pending entry.
3211 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3212 main_test_rfh()->PrepareForCommit();
3213 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
, false);
3214 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3215 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
3217 // Once it commits, the URL and virtual URL should reflect the actual page.
3218 main_test_rfh()->SendNavigate(0, 0, true, url2
);
3219 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3220 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
3222 // We should not replace the pending entry for an error URL.
3223 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
, false);
3224 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3225 navigator
->DidStartProvisionalLoad(main_test_rfh(),
3226 GURL(kUnreachableWebDataURL
), false);
3227 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3229 // We should remember if the pending entry will replace the current one.
3230 // http://crbug.com/308444.
3231 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
, false);
3232 controller
.GetPendingEntry()->set_should_replace_entry(true);
3234 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3235 main_test_rfh()->PrepareForCommit();
3236 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
, false);
3237 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
3238 main_test_rfh()->SendNavigate(0, 0, false, url2
);
3239 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3242 // Tests that the URLs for renderer-initiated navigations are not displayed to
3243 // the user until the navigation commits, to prevent URL spoof attacks.
3244 // See http://crbug.com/99016.
3245 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
3246 NavigationControllerImpl
& controller
= controller_impl();
3247 TestNotificationTracker notifications
;
3248 RegisterForAllNavNotifications(¬ifications
, &controller
);
3250 const GURL
url0("http://foo/0");
3251 const GURL
url1("http://foo/1");
3253 // For typed navigations (browser-initiated), both pending and visible entries
3254 // should update before commit.
3256 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3257 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3258 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
3259 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3260 main_test_rfh()->PrepareForCommit();
3261 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3263 // For link clicks (renderer-initiated navigations), the pending entry should
3264 // update before commit but the visible should not.
3265 NavigationController::LoadURLParams
load_url_params(url1
);
3266 load_url_params
.is_renderer_initiated
= true;
3267 controller
.LoadURLWithParams(load_url_params
);
3268 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3269 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3270 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3271 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3273 // After commit, both visible should be updated, there should be no pending
3274 // entry, and we should no longer treat the entry as renderer-initiated.
3275 main_test_rfh()->PrepareForCommit();
3276 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3277 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3278 EXPECT_FALSE(controller
.GetPendingEntry());
3279 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3281 notifications
.Reset();
3284 // Tests that the URLs for renderer-initiated navigations in new tabs are
3285 // displayed to the user before commit, as long as the initial about:blank
3286 // page has not been modified. If so, we must revert to showing about:blank.
3287 // See http://crbug.com/9682.
3288 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3289 NavigationControllerImpl
& controller
= controller_impl();
3290 TestNotificationTracker notifications
;
3291 RegisterForAllNavNotifications(¬ifications
, &controller
);
3293 const GURL
url("http://foo");
3295 // For renderer-initiated navigations in new tabs (with no committed entries),
3296 // we show the pending entry's URL as long as the about:blank page is not
3298 NavigationController::LoadURLParams
load_url_params(url
);
3299 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3300 load_url_params
.is_renderer_initiated
= true;
3301 controller
.LoadURLWithParams(load_url_params
);
3302 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3303 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3304 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3305 EXPECT_TRUE(controller
.IsInitialNavigation());
3306 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3308 // There should be no title yet.
3309 EXPECT_TRUE(contents()->GetTitle().empty());
3311 // If something else modifies the contents of the about:blank page, then
3312 // we must revert to showing about:blank to avoid a URL spoof.
3313 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3314 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3315 EXPECT_FALSE(controller
.GetVisibleEntry());
3316 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3318 notifications
.Reset();
3321 // Tests that the URLs for browser-initiated navigations in new tabs are
3322 // displayed to the user even after they fail, as long as the initial
3323 // about:blank page has not been modified. If so, we must revert to showing
3324 // about:blank. See http://crbug.com/355537.
3325 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3326 NavigationControllerImpl
& controller
= controller_impl();
3327 TestNotificationTracker notifications
;
3328 RegisterForAllNavNotifications(¬ifications
, &controller
);
3330 const GURL
url("http://foo");
3332 // For browser-initiated navigations in new tabs (with no committed entries),
3333 // we show the pending entry's URL as long as the about:blank page is not
3334 // modified. This is possible in cases that the user types a URL into a popup
3335 // tab created with a slow URL.
3336 NavigationController::LoadURLParams
load_url_params(url
);
3337 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3338 load_url_params
.is_renderer_initiated
= false;
3339 controller
.LoadURLWithParams(load_url_params
);
3340 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3341 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3342 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3343 EXPECT_TRUE(controller
.IsInitialNavigation());
3344 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3346 // There should be no title yet.
3347 EXPECT_TRUE(contents()->GetTitle().empty());
3349 // Suppose it aborts before committing, if it's a 204 or download or due to a
3350 // stop or a new navigation from the user. The URL should remain visible.
3351 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3352 params
.error_code
= net::ERR_ABORTED
;
3353 params
.error_description
= base::string16();
3355 params
.showing_repost_interstitial
= false;
3356 main_test_rfh()->OnMessageReceived(
3357 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3358 contents()->SetIsLoading(false, true, NULL
);
3359 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3361 // If something else later modifies the contents of the about:blank page, then
3362 // we must revert to showing about:blank to avoid a URL spoof.
3363 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3364 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3365 EXPECT_FALSE(controller
.GetVisibleEntry());
3366 EXPECT_FALSE(controller
.GetPendingEntry());
3368 notifications
.Reset();
3371 // Tests that the URLs for renderer-initiated navigations in new tabs are
3372 // displayed to the user even after they fail, as long as the initial
3373 // about:blank page has not been modified. If so, we must revert to showing
3374 // about:blank. See http://crbug.com/355537.
3375 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3376 NavigationControllerImpl
& controller
= controller_impl();
3377 TestNotificationTracker notifications
;
3378 RegisterForAllNavNotifications(¬ifications
, &controller
);
3380 const GURL
url("http://foo");
3382 // For renderer-initiated navigations in new tabs (with no committed entries),
3383 // we show the pending entry's URL as long as the about:blank page is not
3385 NavigationController::LoadURLParams
load_url_params(url
);
3386 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3387 load_url_params
.is_renderer_initiated
= true;
3388 controller
.LoadURLWithParams(load_url_params
);
3389 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3390 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3391 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3392 EXPECT_TRUE(controller
.IsInitialNavigation());
3393 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3395 // There should be no title yet.
3396 EXPECT_TRUE(contents()->GetTitle().empty());
3398 // Suppose it aborts before committing, if it's a 204 or download or due to a
3399 // stop or a new navigation from the user. The URL should remain visible.
3400 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3401 params
.error_code
= net::ERR_ABORTED
;
3402 params
.error_description
= base::string16();
3404 params
.showing_repost_interstitial
= false;
3405 main_test_rfh()->OnMessageReceived(
3406 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3407 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3409 // If something else later modifies the contents of the about:blank page, then
3410 // we must revert to showing about:blank to avoid a URL spoof.
3411 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3412 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3413 EXPECT_FALSE(controller
.GetVisibleEntry());
3414 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3416 notifications
.Reset();
3419 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3420 NavigationControllerImpl
& controller
= controller_impl();
3421 TestNotificationTracker notifications
;
3422 RegisterForAllNavNotifications(¬ifications
, &controller
);
3424 const GURL
url1("http://foo/eh");
3425 const GURL
url2("http://foo/bee");
3427 // For renderer-initiated navigations in new tabs (with no committed entries),
3428 // we show the pending entry's URL as long as the about:blank page is not
3430 NavigationController::LoadURLParams
load_url_params(url1
);
3431 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3432 load_url_params
.is_renderer_initiated
= true;
3433 controller
.LoadURLWithParams(load_url_params
);
3434 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3435 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3436 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3437 EXPECT_TRUE(controller
.IsInitialNavigation());
3438 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3440 // Simulate a commit and then starting a new pending navigation.
3441 main_test_rfh()->PrepareForCommit();
3442 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3443 NavigationController::LoadURLParams
load_url2_params(url2
);
3444 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3445 load_url2_params
.is_renderer_initiated
= true;
3446 controller
.LoadURLWithParams(load_url2_params
);
3448 // We should not consider this an initial navigation, and thus should
3449 // not show the pending URL.
3450 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3451 EXPECT_FALSE(controller
.IsInitialNavigation());
3452 EXPECT_TRUE(controller
.GetVisibleEntry());
3453 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3455 notifications
.Reset();
3458 // Tests that IsInPageNavigation returns appropriate results. Prevents
3459 // regression for bug 1126349.
3460 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3461 NavigationControllerImpl
& controller
= controller_impl();
3462 const GURL
url("http://www.google.com/home.html");
3464 // If the renderer claims it performed an in-page navigation from
3465 // about:blank, trust the renderer.
3466 // This can happen when an iframe is created and populated via
3467 // document.write(), then tries to perform a fragment navigation.
3468 // TODO(japhet): We should only trust the renderer if the about:blank
3469 // was the first document in the given frame, but we don't have enough
3470 // information to identify that case currently.
3471 const GURL
blank_url(url::kAboutBlankURL
);
3472 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url
);
3473 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3476 // Navigate to URL with no refs.
3477 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3479 // Reloading the page is not an in-page navigation.
3480 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false, main_test_rfh()));
3481 const GURL
other_url("http://www.google.com/add.html");
3482 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3484 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3485 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3488 // Navigate to URL with refs.
3489 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref
);
3491 // Reloading the page is not an in-page navigation.
3492 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3494 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3496 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3498 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3499 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3502 // Going to the same url again will be considered in-page
3503 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3504 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3507 // Going back to the non ref url will be considered in-page if the navigation
3509 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3512 // If the renderer says this is a same-origin in-page navigation, believe it.
3513 // This is the pushState/replaceState case.
3514 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3517 // Test allow_universal_access_from_file_urls flag.
3518 const GURL
different_origin_url("http://www.example.com");
3519 MockRenderProcessHost
* rph
= main_test_rfh()->GetProcess();
3520 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3521 prefs
.allow_universal_access_from_file_urls
= true;
3522 test_rvh()->UpdateWebkitPreferences(prefs
);
3523 prefs
= test_rvh()->GetWebkitPreferences();
3524 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3525 // Allow in page navigation if existing URL is file scheme.
3526 const GURL
file_url("file:///foo/index.html");
3527 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url
);
3528 EXPECT_EQ(0, rph
->bad_msg_count());
3529 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3531 EXPECT_EQ(0, rph
->bad_msg_count());
3532 // Don't honor allow_universal_access_from_file_urls if existing URL is
3534 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3535 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3537 EXPECT_EQ(1, rph
->bad_msg_count());
3539 // Remove allow_universal_access_from_file_urls flag.
3540 prefs
.allow_universal_access_from_file_urls
= false;
3541 test_rvh()->UpdateWebkitPreferences(prefs
);
3542 prefs
= test_rvh()->GetWebkitPreferences();
3543 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3545 // Don't believe the renderer if it claims a cross-origin navigation is
3547 EXPECT_EQ(1, rph
->bad_msg_count());
3548 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3550 EXPECT_EQ(2, rph
->bad_msg_count());
3553 // Some pages can have subframes with the same base URL (minus the reference) as
3554 // the main page. Even though this is hard, it can happen, and we don't want
3555 // these subframe navigations to affect the toplevel document. They should
3556 // instead be ignored. http://crbug.com/5585
3557 TEST_F(NavigationControllerTest
, SameSubframe
) {
3558 NavigationControllerImpl
& controller
= controller_impl();
3559 // Navigate the main frame.
3560 const GURL
url("http://www.google.com/");
3561 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
3563 // We should be at the first navigation entry.
3564 EXPECT_EQ(controller
.GetEntryCount(), 1);
3565 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3567 // Navigate a subframe that would normally count as in-page.
3568 const GURL
subframe("http://www.google.com/#");
3569 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3571 params
.nav_entry_id
= 0;
3572 params
.did_create_new_entry
= false;
3573 params
.url
= subframe
;
3574 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3575 params
.should_update_history
= false;
3576 params
.gesture
= NavigationGestureAuto
;
3577 params
.is_post
= false;
3578 params
.page_state
= PageState::CreateFromURL(subframe
);
3579 LoadCommittedDetails details
;
3580 EXPECT_FALSE(controller
.RendererDidNavigate(main_test_rfh(), params
,
3583 // Nothing should have changed.
3584 EXPECT_EQ(controller
.GetEntryCount(), 1);
3585 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3588 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3590 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3591 NavigationControllerImpl
& controller
= controller_impl();
3592 const GURL
url1("http://foo1");
3593 const GURL
url2("http://foo2");
3594 const base::string16
title(base::ASCIIToUTF16("Title"));
3596 NavigateAndCommit(url1
);
3597 controller
.GetVisibleEntry()->SetTitle(title
);
3598 NavigateAndCommit(url2
);
3600 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3602 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3603 EXPECT_TRUE(clone
->GetController().NeedsReload());
3604 clone
->GetController().GoBack();
3605 // Navigating back should have triggered needs_reload_ to go false.
3606 EXPECT_FALSE(clone
->GetController().NeedsReload());
3608 // Ensure that the pending URL and its title are visible.
3609 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3610 EXPECT_EQ(title
, clone
->GetTitle());
3613 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3614 // See http://crbug.com/234491.
3615 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3616 NavigationControllerImpl
& controller
= controller_impl();
3617 const GURL
url1("http://foo1");
3618 const GURL
url2("http://foo2");
3619 const base::string16
title(base::ASCIIToUTF16("Title"));
3621 NavigateAndCommit(url1
);
3622 controller
.GetVisibleEntry()->SetTitle(title
);
3623 NavigateAndCommit(url2
);
3625 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3626 clone
->GetController().LoadIfNecessary();
3628 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3629 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3631 clone
->GetController().Reload(true);
3632 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3635 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3636 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3637 NavigationControllerImpl
& controller
= controller_impl();
3638 const GURL
url1("http://foo1");
3639 const GURL
url2("http://foo2");
3641 NavigateAndCommit(url1
);
3642 NavigateAndCommit(url2
);
3644 // Add an interstitial entry. Should be deleted with controller.
3645 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3646 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3647 controller
.SetTransientEntry(interstitial_entry
);
3649 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3651 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3654 // Test requesting and triggering a lazy reload.
3655 TEST_F(NavigationControllerTest
, LazyReload
) {
3656 NavigationControllerImpl
& controller
= controller_impl();
3657 const GURL
url("http://foo");
3658 NavigateAndCommit(url
);
3659 ASSERT_FALSE(controller
.NeedsReload());
3660 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3661 controller
.GetLastCommittedEntry()->GetTransitionType());
3663 // Request a reload to happen when the controller becomes active (e.g. after
3664 // the renderer gets killed in background on Android).
3665 controller
.SetNeedsReload();
3666 ASSERT_TRUE(controller
.NeedsReload());
3667 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3668 controller
.GetLastCommittedEntry()->GetTransitionType());
3670 // Set the controller as active, triggering the requested reload.
3671 controller
.SetActive(true);
3672 ASSERT_FALSE(controller
.NeedsReload());
3673 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3674 controller
.GetPendingEntry()->GetTransitionType());
3677 // Test requesting and triggering a lazy reload without any committed entry.
3678 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3679 NavigationControllerImpl
& controller
= controller_impl();
3680 const GURL
url("http://foo");
3681 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3682 ASSERT_FALSE(controller
.NeedsReload());
3683 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3684 controller
.GetPendingEntry()->GetTransitionType());
3686 // Request a reload to happen when the controller becomes active (e.g. after
3687 // the renderer gets killed in background on Android).
3688 controller
.SetNeedsReload();
3689 ASSERT_TRUE(controller
.NeedsReload());
3690 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3691 controller
.GetPendingEntry()->GetTransitionType());
3693 // Set the controller as active, triggering the requested reload.
3694 controller
.SetActive(true);
3695 ASSERT_FALSE(controller
.NeedsReload());
3696 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3697 controller
.GetPendingEntry()->GetTransitionType());
3700 // Tests a subframe navigation while a toplevel navigation is pending.
3701 // http://crbug.com/43967
3702 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3703 NavigationControllerImpl
& controller
= controller_impl();
3704 // Load the first page.
3705 const GURL
url1("http://foo/");
3706 NavigateAndCommit(url1
);
3708 // Now start a pending load to a totally different page, but don't commit it.
3709 const GURL
url2("http://bar/");
3711 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3713 // Send a subframe update from the first page, as if one had just
3714 // automatically loaded. Auto subframes don't increment the page ID.
3715 const GURL
url1_sub("http://foo/subframe");
3716 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3717 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3718 params
.nav_entry_id
= 0;
3719 params
.did_create_new_entry
= false;
3720 params
.url
= url1_sub
;
3721 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3722 params
.should_update_history
= false;
3723 params
.gesture
= NavigationGestureAuto
;
3724 params
.is_post
= false;
3725 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3726 LoadCommittedDetails details
;
3728 // This should return false meaning that nothing was actually updated.
3729 EXPECT_FALSE(controller
.RendererDidNavigate(main_test_rfh(), params
,
3732 // The notification should have updated the last committed one, and not
3733 // the pending load.
3734 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3736 // The active entry should be unchanged by the subframe load.
3737 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3740 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3741 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3742 NavigationControllerImpl
& controller
= controller_impl();
3743 const GURL
url1("http://foo1");
3744 const GURL
url2("http://foo2");
3746 NavigateAndCommit(url1
);
3747 NavigateAndCommit(url2
);
3748 controller
.GoBack();
3749 contents()->CommitPendingNavigation();
3751 scoped_ptr
<TestWebContents
> other_contents(
3752 static_cast<TestWebContents
*>(CreateTestWebContents()));
3753 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3754 other_controller
.CopyStateFrom(controller
);
3756 // other_controller should now contain 2 urls.
3757 ASSERT_EQ(2, other_controller
.GetEntryCount());
3758 // We should be looking at the first one.
3759 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3761 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3762 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3763 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3764 // This is a different site than url1, so the IDs start again at 0.
3765 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3767 // The max page ID map should be copied over and updated with the max page ID
3768 // from the current tab.
3769 SiteInstance
* instance1
=
3770 other_controller
.GetEntryAtIndex(0)->site_instance();
3771 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3773 // Ensure the SessionStorageNamespaceMaps are the same size and have
3774 // the same partitons loaded.
3776 // TODO(ajwong): We should load a url from a different partition earlier
3777 // to make sure this map has more than one entry.
3778 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3779 controller
.GetSessionStorageNamespaceMap();
3780 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3781 other_controller
.GetSessionStorageNamespaceMap();
3782 EXPECT_EQ(session_storage_namespace_map
.size(),
3783 other_session_storage_namespace_map
.size());
3784 for (SessionStorageNamespaceMap::const_iterator it
=
3785 session_storage_namespace_map
.begin();
3786 it
!= session_storage_namespace_map
.end();
3788 SessionStorageNamespaceMap::const_iterator other
=
3789 other_session_storage_namespace_map
.find(it
->first
);
3790 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3794 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3795 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3796 NavigationControllerImpl
& controller
= controller_impl();
3797 const GURL
url1("http://foo/1");
3798 const GURL
url2("http://foo/2");
3799 const GURL
url3("http://foo/3");
3801 NavigateAndCommit(url1
);
3802 NavigateAndCommit(url2
);
3804 // First two entries should have the same SiteInstance.
3805 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3806 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3807 EXPECT_EQ(instance1
, instance2
);
3808 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3809 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3810 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3812 scoped_ptr
<TestWebContents
> other_contents(
3813 static_cast<TestWebContents
*>(CreateTestWebContents()));
3814 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3815 other_contents
->NavigateAndCommit(url3
);
3816 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3817 other_controller
.CopyStateFromAndPrune(&controller
, false);
3819 // other_controller should now contain the 3 urls: url1, url2 and url3.
3821 ASSERT_EQ(3, other_controller
.GetEntryCount());
3823 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3825 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3826 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3827 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3828 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3829 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3830 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3832 // A new SiteInstance in a different BrowsingInstance should be used for the
3834 SiteInstance
* instance3
=
3835 other_controller
.GetEntryAtIndex(2)->site_instance();
3836 EXPECT_NE(instance3
, instance1
);
3837 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3839 // The max page ID map should be copied over and updated with the max page ID
3840 // from the current tab.
3841 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3842 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3845 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3847 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3848 NavigationControllerImpl
& controller
= controller_impl();
3849 const GURL
url1("http://foo1");
3850 const GURL
url2("http://foo2");
3851 const GURL
url3("http://foo3");
3853 NavigateAndCommit(url1
);
3854 NavigateAndCommit(url2
);
3855 controller
.GoBack();
3856 contents()->CommitPendingNavigation();
3858 scoped_ptr
<TestWebContents
> other_contents(
3859 static_cast<TestWebContents
*>(CreateTestWebContents()));
3860 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3861 other_contents
->NavigateAndCommit(url3
);
3862 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3863 other_controller
.CopyStateFromAndPrune(&controller
, false);
3865 // other_controller should now contain: url1, url3
3867 ASSERT_EQ(2, other_controller
.GetEntryCount());
3868 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3870 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3871 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3872 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3874 // The max page ID map should be copied over and updated with the max page ID
3875 // from the current tab.
3876 SiteInstance
* instance1
=
3877 other_controller
.GetEntryAtIndex(1)->site_instance();
3878 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3881 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3883 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3884 NavigationControllerImpl
& controller
= controller_impl();
3885 const GURL
url1("http://foo1");
3886 const GURL
url2("http://foo2");
3887 const GURL
url3("http://foo3");
3888 const GURL
url4("http://foo4");
3890 NavigateAndCommit(url1
);
3891 NavigateAndCommit(url2
);
3893 scoped_ptr
<TestWebContents
> other_contents(
3894 static_cast<TestWebContents
*>(CreateTestWebContents()));
3895 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3896 other_contents
->NavigateAndCommit(url3
);
3897 other_contents
->NavigateAndCommit(url4
);
3898 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3899 other_controller
.CopyStateFromAndPrune(&controller
, false);
3901 // other_controller should now contain: url1, url2, url4
3903 ASSERT_EQ(3, other_controller
.GetEntryCount());
3904 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3906 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3907 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3908 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
3910 // The max page ID map should be copied over and updated with the max page ID
3911 // from the current tab.
3912 SiteInstance
* instance1
=
3913 other_controller
.GetEntryAtIndex(2)->site_instance();
3914 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3917 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3918 // not the last entry selected in the target.
3919 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
3920 NavigationControllerImpl
& controller
= controller_impl();
3921 const GURL
url1("http://foo1");
3922 const GURL
url2("http://foo2");
3923 const GURL
url3("http://foo3");
3924 const GURL
url4("http://foo4");
3926 NavigateAndCommit(url1
);
3927 NavigateAndCommit(url2
);
3929 scoped_ptr
<TestWebContents
> other_contents(
3930 static_cast<TestWebContents
*>(CreateTestWebContents()));
3931 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3932 other_contents
->NavigateAndCommit(url3
);
3933 other_contents
->NavigateAndCommit(url4
);
3934 other_controller
.GoBack();
3935 other_contents
->CommitPendingNavigation();
3936 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3937 other_controller
.CopyStateFromAndPrune(&controller
, false);
3939 // other_controller should now contain: url1, url2, url3
3941 ASSERT_EQ(3, other_controller
.GetEntryCount());
3942 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3944 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3945 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3946 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3948 // The max page ID map should be copied over and updated with the max page ID
3949 // from the current tab.
3950 SiteInstance
* instance1
=
3951 other_controller
.GetEntryAtIndex(2)->site_instance();
3952 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3955 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3956 // a pending entry in the target.
3957 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
3958 NavigationControllerImpl
& controller
= controller_impl();
3959 const GURL
url1("http://foo1");
3960 const GURL
url2("http://foo2");
3961 const GURL
url3("http://foo3");
3962 const GURL
url4("http://foo4");
3964 NavigateAndCommit(url1
);
3965 NavigateAndCommit(url2
);
3966 controller
.GoBack();
3967 contents()->CommitPendingNavigation();
3969 scoped_ptr
<TestWebContents
> other_contents(
3970 static_cast<TestWebContents
*>(CreateTestWebContents()));
3971 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3972 other_contents
->NavigateAndCommit(url3
);
3973 other_controller
.LoadURL(
3974 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3975 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3976 other_controller
.CopyStateFromAndPrune(&controller
, false);
3978 // other_controller should now contain url1, url3, and a pending entry
3981 ASSERT_EQ(2, other_controller
.GetEntryCount());
3982 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
3984 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3985 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3987 // And there should be a pending entry for url4.
3988 ASSERT_TRUE(other_controller
.GetPendingEntry());
3989 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
3991 // The max page ID map should be copied over and updated with the max page ID
3992 // from the current tab.
3993 SiteInstance
* instance1
=
3994 other_controller
.GetEntryAtIndex(0)->site_instance();
3995 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3998 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3999 // client redirect entry (with the same page ID) in the target. This used to
4000 // crash because the last committed entry would be pruned but max_page_id
4001 // remembered the page ID (http://crbug.com/234809).
4002 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
4003 NavigationControllerImpl
& controller
= controller_impl();
4004 const GURL
url1("http://foo1");
4005 const GURL
url2a("http://foo2/a");
4006 const GURL
url2b("http://foo2/b");
4008 NavigateAndCommit(url1
);
4010 scoped_ptr
<TestWebContents
> other_contents(
4011 static_cast<TestWebContents
*>(CreateTestWebContents()));
4012 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4013 other_contents
->NavigateAndCommit(url2a
);
4014 // Simulate a client redirect, which has the same page ID as entry 2a.
4015 other_controller
.LoadURL(
4016 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
4017 NavigationEntry
* entry
= other_controller
.GetPendingEntry();
4018 entry
->SetPageID(other_controller
.GetLastCommittedEntry()->GetPageID());
4020 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4021 other_controller
.CopyStateFromAndPrune(&controller
, false);
4023 // other_controller should now contain url1, url2a, and a pending entry
4026 ASSERT_EQ(2, other_controller
.GetEntryCount());
4027 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4029 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4030 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
4032 // And there should be a pending entry for url4.
4033 ASSERT_TRUE(other_controller
.GetPendingEntry());
4034 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
4036 // Let the pending entry commit.
4037 other_contents
->TestDidNavigate(other_contents
->GetMainFrame(),
4038 entry
->GetPageID(), 0, false, url2b
,
4039 ui::PAGE_TRANSITION_LINK
);
4041 // The max page ID map should be copied over and updated with the max page ID
4042 // from the current tab.
4043 SiteInstance
* instance1
=
4044 other_controller
.GetEntryAtIndex(1)->site_instance();
4045 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4048 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4049 // source, and 1 entry in the target. The back pending entry should be ignored.
4050 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
4051 NavigationControllerImpl
& controller
= controller_impl();
4052 const GURL
url1("http://foo1");
4053 const GURL
url2("http://foo2");
4054 const GURL
url3("http://foo3");
4056 NavigateAndCommit(url1
);
4057 NavigateAndCommit(url2
);
4058 controller
.GoBack();
4060 scoped_ptr
<TestWebContents
> other_contents(
4061 static_cast<TestWebContents
*>(CreateTestWebContents()));
4062 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4063 other_contents
->NavigateAndCommit(url3
);
4064 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4065 other_controller
.CopyStateFromAndPrune(&controller
, false);
4067 // other_controller should now contain: url1, url2, url3
4069 ASSERT_EQ(3, other_controller
.GetEntryCount());
4070 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4072 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4073 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4074 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4075 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4077 // The max page ID map should be copied over and updated with the max page ID
4078 // from the current tab.
4079 SiteInstance
* instance1
=
4080 other_controller
.GetEntryAtIndex(2)->site_instance();
4081 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4084 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4085 // when the max entry count is 3. We should prune one entry.
4086 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
4087 NavigationControllerImpl
& controller
= controller_impl();
4088 size_t original_count
= NavigationControllerImpl::max_entry_count();
4089 const int kMaxEntryCount
= 3;
4091 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4093 const GURL
url1("http://foo/1");
4094 const GURL
url2("http://foo/2");
4095 const GURL
url3("http://foo/3");
4096 const GURL
url4("http://foo/4");
4098 // Create a PrunedListener to observe prune notifications.
4099 PrunedListener
listener(&controller
);
4101 NavigateAndCommit(url1
);
4102 NavigateAndCommit(url2
);
4103 NavigateAndCommit(url3
);
4105 scoped_ptr
<TestWebContents
> other_contents(
4106 static_cast<TestWebContents
*>(CreateTestWebContents()));
4107 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4108 other_contents
->NavigateAndCommit(url4
);
4109 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4110 other_controller
.CopyStateFromAndPrune(&controller
, false);
4112 // We should have received a pruned notification.
4113 EXPECT_EQ(1, listener
.notification_count_
);
4114 EXPECT_TRUE(listener
.details_
.from_front
);
4115 EXPECT_EQ(1, listener
.details_
.count
);
4117 // other_controller should now contain only 3 urls: url2, url3 and url4.
4119 ASSERT_EQ(3, other_controller
.GetEntryCount());
4121 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4123 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
4124 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4125 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4126 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
4127 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
4128 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4130 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4133 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4134 // replace_entry set to true.
4135 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
4136 NavigationControllerImpl
& controller
= controller_impl();
4137 const GURL
url1("http://foo/1");
4138 const GURL
url2("http://foo/2");
4139 const GURL
url3("http://foo/3");
4141 NavigateAndCommit(url1
);
4142 NavigateAndCommit(url2
);
4144 // First two entries should have the same SiteInstance.
4145 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
4146 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
4147 EXPECT_EQ(instance1
, instance2
);
4148 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
4149 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
4150 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
4152 scoped_ptr
<TestWebContents
> other_contents(
4153 static_cast<TestWebContents
*>(CreateTestWebContents()));
4154 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4155 other_contents
->NavigateAndCommit(url3
);
4156 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4157 other_controller
.CopyStateFromAndPrune(&controller
, true);
4159 // other_controller should now contain the 2 urls: url1 and url3.
4161 ASSERT_EQ(2, other_controller
.GetEntryCount());
4163 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
4165 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4166 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4167 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4168 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
4170 // A new SiteInstance in a different BrowsingInstance should be used for the
4172 SiteInstance
* instance3
=
4173 other_controller
.GetEntryAtIndex(1)->site_instance();
4174 EXPECT_NE(instance3
, instance1
);
4175 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
4177 // The max page ID map should be copied over and updated with the max page ID
4178 // from the current tab.
4179 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4180 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
4183 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4184 // entry count is 3 and replace_entry is true. We should not prune entries.
4185 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
4186 NavigationControllerImpl
& controller
= controller_impl();
4187 size_t original_count
= NavigationControllerImpl::max_entry_count();
4188 const int kMaxEntryCount
= 3;
4190 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4192 const GURL
url1("http://foo/1");
4193 const GURL
url2("http://foo/2");
4194 const GURL
url3("http://foo/3");
4195 const GURL
url4("http://foo/4");
4197 // Create a PrunedListener to observe prune notifications.
4198 PrunedListener
listener(&controller
);
4200 NavigateAndCommit(url1
);
4201 NavigateAndCommit(url2
);
4202 NavigateAndCommit(url3
);
4204 scoped_ptr
<TestWebContents
> other_contents(
4205 static_cast<TestWebContents
*>(CreateTestWebContents()));
4206 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4207 other_contents
->NavigateAndCommit(url4
);
4208 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4209 other_controller
.CopyStateFromAndPrune(&controller
, true);
4211 // We should have received no pruned notification.
4212 EXPECT_EQ(0, listener
.notification_count_
);
4214 // other_controller should now contain only 3 urls: url1, url2 and url4.
4216 ASSERT_EQ(3, other_controller
.GetEntryCount());
4218 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4220 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4221 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4222 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4223 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4224 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
4225 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4227 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4230 // Tests that we can navigate to the restored entries
4231 // imported by CopyStateFromAndPrune.
4232 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
4233 const GURL kRestoredUrls
[] = {
4234 GURL("http://site1.com"),
4235 GURL("http://site2.com"),
4237 const GURL
kInitialUrl("http://site3.com");
4239 std::vector
<NavigationEntry
*> entries
;
4240 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
4241 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
4242 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
4243 std::string(), browser_context());
4244 entry
->SetPageID(static_cast<int>(i
));
4245 entries
.push_back(entry
);
4248 // Create a WebContents with restored entries.
4249 scoped_ptr
<TestWebContents
> source_contents(
4250 static_cast<TestWebContents
*>(CreateTestWebContents()));
4251 NavigationControllerImpl
& source_controller
=
4252 source_contents
->GetController();
4253 source_controller
.Restore(
4255 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
4257 ASSERT_EQ(0u, entries
.size());
4258 source_controller
.LoadIfNecessary();
4259 source_contents
->CommitPendingNavigation();
4261 // Load a page, then copy state from |source_contents|.
4262 NavigateAndCommit(kInitialUrl
);
4263 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4264 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
4265 ASSERT_EQ(3, controller_impl().GetEntryCount());
4267 // Go back to the first entry one at a time and
4268 // verify that it works as expected.
4269 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4270 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
4272 controller_impl().GoBack();
4273 contents()->CommitPendingNavigation();
4274 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4275 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
4277 controller_impl().GoBack();
4278 contents()->CommitPendingNavigation();
4279 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4280 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
4283 // Tests that navigations initiated from the page (with the history object)
4284 // work as expected, creating pending entries.
4285 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
4286 NavigationControllerImpl
& controller
= controller_impl();
4287 const GURL
url1("http://foo/1");
4288 const GURL
url2("http://foo/2");
4289 const GURL
url3("http://foo/3");
4291 NavigateAndCommit(url1
);
4292 NavigateAndCommit(url2
);
4293 NavigateAndCommit(url3
);
4294 controller
.GoBack();
4295 contents()->CommitPendingNavigation();
4296 process()->sink().ClearMessages();
4298 // Simulate the page calling history.back(). It should create a pending entry.
4299 contents()->OnGoToEntryAtOffset(-1);
4300 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4301 // The actual cross-navigation is suspended until the current RVH tells us
4302 // it unloaded, simulate that.
4303 contents()->ProceedWithCrossSiteNavigation();
4304 // Also make sure we told the page to navigate.
4305 GURL nav_url
= GetLastNavigationURL();
4306 EXPECT_EQ(url1
, nav_url
);
4307 contents()->CommitPendingNavigation();
4308 process()->sink().ClearMessages();
4310 // Now test history.forward()
4311 contents()->OnGoToEntryAtOffset(2);
4312 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4313 // The actual cross-navigation is suspended until the current RVH tells us
4314 // it unloaded, simulate that.
4315 contents()->ProceedWithCrossSiteNavigation();
4316 nav_url
= GetLastNavigationURL();
4317 EXPECT_EQ(url3
, nav_url
);
4318 contents()->CommitPendingNavigation();
4319 process()->sink().ClearMessages();
4321 controller
.DiscardNonCommittedEntries();
4323 // Make sure an extravagant history.go() doesn't break.
4324 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4325 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4326 EXPECT_FALSE(HasNavigationRequest());
4329 // Test call to PruneAllButLastCommitted for the only entry.
4330 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4331 NavigationControllerImpl
& controller
= controller_impl();
4332 const GURL
url1("http://foo1");
4333 NavigateAndCommit(url1
);
4335 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4337 controller
.PruneAllButLastCommitted();
4339 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4340 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4343 // Test call to PruneAllButLastCommitted for first entry.
4344 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4345 NavigationControllerImpl
& controller
= controller_impl();
4346 const GURL
url1("http://foo/1");
4347 const GURL
url2("http://foo/2");
4348 const GURL
url3("http://foo/3");
4350 NavigateAndCommit(url1
);
4351 NavigateAndCommit(url2
);
4352 NavigateAndCommit(url3
);
4353 controller
.GoBack();
4354 controller
.GoBack();
4355 contents()->CommitPendingNavigation();
4357 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4359 controller
.PruneAllButLastCommitted();
4361 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4362 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4365 // Test call to PruneAllButLastCommitted for intermediate entry.
4366 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4367 NavigationControllerImpl
& controller
= controller_impl();
4368 const GURL
url1("http://foo/1");
4369 const GURL
url2("http://foo/2");
4370 const GURL
url3("http://foo/3");
4372 NavigateAndCommit(url1
);
4373 NavigateAndCommit(url2
);
4374 NavigateAndCommit(url3
);
4375 controller
.GoBack();
4376 contents()->CommitPendingNavigation();
4378 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4380 controller
.PruneAllButLastCommitted();
4382 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4383 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4386 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4387 // the list of entries.
4388 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4389 NavigationControllerImpl
& controller
= controller_impl();
4390 const GURL
url1("http://foo/1");
4391 const GURL
url2("http://foo/2");
4392 const GURL
url3("http://foo/3");
4394 NavigateAndCommit(url1
);
4395 NavigateAndCommit(url2
);
4397 // Create a pending entry that is not in the entry list.
4399 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4400 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
4401 EXPECT_TRUE(controller
.GetPendingEntry());
4402 EXPECT_EQ(2, controller
.GetEntryCount());
4404 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4405 controller
.PruneAllButLastCommitted();
4407 // We should only have the last committed and pending entries at this point,
4408 // and the pending entry should still not be in the entry list.
4409 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4410 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4411 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4412 EXPECT_TRUE(controller
.GetPendingEntry());
4413 EXPECT_EQ(1, controller
.GetEntryCount());
4415 // Try to commit the pending entry.
4416 main_test_rfh()->PrepareForCommit();
4417 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
4418 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4419 EXPECT_FALSE(controller
.GetPendingEntry());
4420 EXPECT_EQ(2, controller
.GetEntryCount());
4421 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4424 // Test to ensure that when we do a history navigation back to the current
4425 // committed page (e.g., going forward to a slow-loading page, then pressing
4426 // the back button), we just stop the navigation to prevent the throbber from
4427 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4428 // start, but WebKit essentially ignores the navigation and never sends a
4429 // message to stop the throbber.
4430 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4431 NavigationControllerImpl
& controller
= controller_impl();
4432 const GURL
url0("http://foo/0");
4433 const GURL
url1("http://foo/1");
4435 NavigateAndCommit(url0
);
4436 NavigateAndCommit(url1
);
4438 // Go back to the original page, then forward to the slow page, then back
4439 controller
.GoBack();
4440 contents()->CommitPendingNavigation();
4442 controller
.GoForward();
4443 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4445 controller
.GoBack();
4446 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4449 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4450 NavigationControllerImpl
& controller
= controller_impl();
4451 TestNotificationTracker notifications
;
4452 RegisterForAllNavNotifications(¬ifications
, &controller
);
4455 EXPECT_TRUE(controller
.IsInitialNavigation());
4457 // After commit, it stays false.
4458 const GURL
url1("http://foo1");
4459 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
4460 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4461 navigation_entry_committed_counter_
= 0;
4462 EXPECT_FALSE(controller
.IsInitialNavigation());
4464 // After starting a new navigation, it stays false.
4465 const GURL
url2("http://foo2");
4467 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4470 // Check that the favicon is not reused across a client redirect.
4471 // (crbug.com/28515)
4472 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4473 const GURL
kPageWithFavicon("http://withfavicon.html");
4474 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4475 const GURL
kIconURL("http://withfavicon.ico");
4476 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4478 NavigationControllerImpl
& controller
= controller_impl();
4479 TestNotificationTracker notifications
;
4480 RegisterForAllNavNotifications(¬ifications
, &controller
);
4482 main_test_rfh()->NavigateAndCommitRendererInitiated(
4483 0, true, kPageWithFavicon
);
4484 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4485 navigation_entry_committed_counter_
= 0;
4487 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4489 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4491 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4492 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4493 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4494 favicon_status
.url
= kIconURL
;
4495 favicon_status
.valid
= true;
4496 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4498 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon
,
4500 main_test_rfh()->PrepareForCommit();
4501 main_test_rfh()->SendNavigateWithTransition(
4504 false, // no new entry
4505 kPageWithoutFavicon
, ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4506 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4507 navigation_entry_committed_counter_
= 0;
4509 entry
= controller
.GetLastCommittedEntry();
4511 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4513 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4516 // Check that the favicon is not cleared for NavigationEntries which were
4517 // previously navigated to.
4518 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4519 const GURL
kUrl1("http://www.a.com/1");
4520 const GURL
kUrl2("http://www.a.com/2");
4521 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4523 NavigationControllerImpl
& controller
= controller_impl();
4524 TestNotificationTracker notifications
;
4525 RegisterForAllNavNotifications(¬ifications
, &controller
);
4527 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
4528 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4529 navigation_entry_committed_counter_
= 0;
4531 // Simulate Chromium having set the favicon for |kUrl1|.
4532 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4533 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4535 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4536 favicon_status
.image
= favicon_image
;
4537 favicon_status
.url
= kIconURL
;
4538 favicon_status
.valid
= true;
4540 // Navigate to another page and go back to the original page.
4541 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
4542 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4543 navigation_entry_committed_counter_
= 0;
4544 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, false);
4545 main_test_rfh()->PrepareForCommit();
4546 main_test_rfh()->SendNavigateWithTransition(
4547 0, controller
.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1
,
4548 ui::PAGE_TRANSITION_FORWARD_BACK
);
4549 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4550 navigation_entry_committed_counter_
= 0;
4552 // Verify that the favicon for the page at |kUrl1| was not cleared.
4553 entry
= controller
.GetEntryAtIndex(0);
4555 EXPECT_EQ(kUrl1
, entry
->GetURL());
4556 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4559 // The test crashes on android: http://crbug.com/170449
4560 #if defined(OS_ANDROID)
4561 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4563 #define MAYBE_PurgeScreenshot PurgeScreenshot
4565 // Tests that screenshot are purged correctly.
4566 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4567 NavigationControllerImpl
& controller
= controller_impl();
4569 NavigationEntryImpl
* entry
;
4571 // Navigate enough times to make sure that some screenshots are purged.
4572 for (int i
= 0; i
< 12; ++i
) {
4573 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4574 NavigateAndCommit(url
);
4575 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4578 MockScreenshotManager
* screenshot_manager
=
4579 new MockScreenshotManager(&controller
);
4580 controller
.SetScreenshotManager(screenshot_manager
);
4581 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4582 entry
= controller
.GetEntryAtIndex(i
);
4583 screenshot_manager
->TakeScreenshotFor(entry
);
4584 EXPECT_TRUE(entry
->screenshot().get());
4587 NavigateAndCommit(GURL("https://foo/"));
4588 EXPECT_EQ(13, controller
.GetEntryCount());
4589 entry
= controller
.GetEntryAtIndex(11);
4590 screenshot_manager
->TakeScreenshotFor(entry
);
4592 for (int i
= 0; i
< 2; ++i
) {
4593 entry
= controller
.GetEntryAtIndex(i
);
4594 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4598 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4599 entry
= controller
.GetEntryAtIndex(i
);
4600 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4603 // Navigate to index 5 and then try to assign screenshot to all entries.
4604 controller
.GoToIndex(5);
4605 contents()->CommitPendingNavigation();
4606 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4607 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4608 entry
= controller
.GetEntryAtIndex(i
);
4609 screenshot_manager
->TakeScreenshotFor(entry
);
4612 for (int i
= 10; i
<= 12; ++i
) {
4613 entry
= controller
.GetEntryAtIndex(i
);
4614 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4616 screenshot_manager
->TakeScreenshotFor(entry
);
4619 // Navigate to index 7 and assign screenshot to all entries.
4620 controller
.GoToIndex(7);
4621 contents()->CommitPendingNavigation();
4622 EXPECT_EQ(7, controller
.GetCurrentEntryIndex());
4623 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4624 entry
= controller
.GetEntryAtIndex(i
);
4625 screenshot_manager
->TakeScreenshotFor(entry
);
4628 for (int i
= 0; i
< 2; ++i
) {
4629 entry
= controller
.GetEntryAtIndex(i
);
4630 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4634 // Clear all screenshots.
4635 EXPECT_EQ(13, controller
.GetEntryCount());
4636 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4637 controller
.ClearAllScreenshots();
4638 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4639 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4640 entry
= controller
.GetEntryAtIndex(i
);
4641 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4646 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4648 main_test_rfh()->NavigateAndCommitRendererInitiated(
4649 1, true, GURL("http://foo"));
4651 // Set title and favicon.
4652 base::string16
title(base::ASCIIToUTF16("Title"));
4653 FaviconStatus favicon
;
4654 favicon
.valid
= true;
4655 favicon
.url
= GURL("http://foo/favicon.ico");
4656 controller().GetLastCommittedEntry()->SetTitle(title
);
4657 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4659 // history.pushState() is called.
4660 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4661 GURL
kUrl2("http://foo#foo");
4663 params
.nav_entry_id
= 0;
4664 params
.did_create_new_entry
= true;
4666 params
.page_state
= PageState::CreateFromURL(kUrl2
);
4667 params
.was_within_same_page
= true;
4668 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, false);
4669 main_test_rfh()->PrepareForCommit();
4670 main_test_rfh()->SendNavigateWithParams(¶ms
);
4672 // The title should immediately be visible on the new NavigationEntry.
4673 base::string16 new_title
=
4674 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4675 EXPECT_EQ(title
, new_title
);
4676 FaviconStatus new_favicon
=
4677 controller().GetLastCommittedEntry()->GetFavicon();
4678 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4679 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4682 // Test that the navigation controller clears its session history when a
4683 // navigation commits with the clear history list flag set.
4684 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4685 const GURL
url1("http://foo1");
4686 const GURL
url2("http://foo2");
4687 const GURL
url3("http://foo3");
4688 const GURL
url4("http://foo4");
4690 NavigationControllerImpl
& controller
= controller_impl();
4692 // Create a session history with three entries, second entry is active.
4693 NavigateAndCommit(url1
);
4694 NavigateAndCommit(url2
);
4695 NavigateAndCommit(url3
);
4696 controller
.GoBack();
4697 contents()->CommitPendingNavigation();
4698 EXPECT_EQ(3, controller
.GetEntryCount());
4699 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4701 // Create a new pending navigation, and indicate that the session history
4702 // should be cleared.
4703 NavigationController::LoadURLParams
params(url4
);
4704 params
.should_clear_history_list
= true;
4705 controller
.LoadURLWithParams(params
);
4707 // Verify that the pending entry correctly indicates that the session history
4708 // should be cleared.
4709 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4711 EXPECT_TRUE(entry
->should_clear_history_list());
4713 // Assume that the RenderFrame correctly cleared its history and commit the
4715 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4716 switches::kEnableBrowserSideNavigation
)) {
4717 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4719 contents()->GetPendingMainFrame()->
4720 set_simulate_history_list_was_cleared(true);
4721 contents()->CommitPendingNavigation();
4723 // Verify that the NavigationController's session history was correctly
4725 EXPECT_EQ(1, controller
.GetEntryCount());
4726 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4727 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4728 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4729 EXPECT_FALSE(controller
.CanGoBack());
4730 EXPECT_FALSE(controller
.CanGoForward());
4731 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4734 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4735 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4736 EXPECT_FALSE(contents()->GetDelegate());
4737 contents()->SetDelegate(delegate
.get());
4740 GURL
url("http://foo");
4741 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4743 params
.nav_entry_id
= 0;
4744 params
.did_create_new_entry
= true;
4746 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4747 params
.gesture
= NavigationGestureUser
;
4748 params
.page_state
= PageState::CreateFromURL(url
);
4749 params
.was_within_same_page
= false;
4750 params
.is_post
= true;
4752 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
4753 main_test_rfh()->PrepareForCommit();
4754 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4756 // history.replaceState() is called.
4757 GURL
replace_url("http://foo#foo");
4759 params
.nav_entry_id
= 0;
4760 params
.did_create_new_entry
= false;
4761 params
.url
= replace_url
;
4762 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4763 params
.gesture
= NavigationGestureUser
;
4764 params
.page_state
= PageState::CreateFromURL(replace_url
);
4765 params
.was_within_same_page
= true;
4766 params
.is_post
= false;
4767 params
.post_id
= -1;
4768 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url
, false);
4769 main_test_rfh()->PrepareForCommit();
4770 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4772 // Now reload. replaceState overrides the POST, so we should not show a
4773 // repost warning dialog.
4774 controller_impl().Reload(true);
4775 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4778 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4779 GURL
url("http://foo");
4780 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4782 params
.nav_entry_id
= 0;
4783 params
.did_create_new_entry
= true;
4785 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4786 params
.gesture
= NavigationGestureUser
;
4787 params
.page_state
= PageState::CreateFromURL(url
);
4788 params
.was_within_same_page
= false;
4789 params
.is_post
= true;
4791 params
.url_is_unreachable
= true;
4793 // Navigate to new page.
4795 LoadCommittedDetails details
;
4796 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4797 EXPECT_EQ(PAGE_TYPE_ERROR
,
4798 controller_impl().GetLastCommittedEntry()->GetPageType());
4799 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4802 // Navigate to existing page.
4804 params
.did_create_new_entry
= false;
4805 LoadCommittedDetails details
;
4806 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4807 EXPECT_EQ(PAGE_TYPE_ERROR
,
4808 controller_impl().GetLastCommittedEntry()->GetPageType());
4809 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4812 // Navigate to same page.
4813 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4814 // same-page transition.
4815 controller_impl().LoadURL(
4816 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4817 params
.nav_entry_id
= controller_impl().GetPendingEntry()->GetUniqueID();
4818 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4820 LoadCommittedDetails details
;
4821 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4822 EXPECT_EQ(PAGE_TYPE_ERROR
,
4823 controller_impl().GetLastCommittedEntry()->GetPageType());
4824 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4827 // Navigate in page.
4828 params
.url
= GURL("http://foo#foo");
4829 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4830 params
.was_within_same_page
= true;
4832 LoadCommittedDetails details
;
4833 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4834 EXPECT_EQ(PAGE_TYPE_ERROR
,
4835 controller_impl().GetLastCommittedEntry()->GetPageType());
4836 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, details
.type
);
4840 } // namespace content