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"
45 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
51 // Creates an image with a 1x1 SkBitmap of the specified |color|.
52 gfx::Image
CreateImage(SkColor color
) {
54 bitmap
.allocN32Pixels(1, 1);
55 bitmap
.eraseColor(color
);
56 return gfx::Image::CreateFrom1xBitmap(bitmap
);
59 // Returns true if images |a| and |b| have the same pixel data.
60 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
61 // Assume that if the 1x bitmaps match, the images match.
62 SkBitmap a_bitmap
= a
.AsBitmap();
63 SkBitmap b_bitmap
= b
.AsBitmap();
65 if (a_bitmap
.width() != b_bitmap
.width() ||
66 a_bitmap
.height() != b_bitmap
.height()) {
69 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
70 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
71 return memcmp(a_bitmap
.getPixels(),
73 a_bitmap
.getSize()) == 0;
76 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
78 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
79 : content::NavigationEntryScreenshotManager(owner
),
80 encoding_screenshot_in_progress_(false) {
83 ~MockScreenshotManager() override
{}
85 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
87 bitmap
.allocPixels(SkImageInfo::Make(
88 1, 1, kAlpha_8_SkColorType
, kPremul_SkAlphaType
));
89 bitmap
.eraseARGB(0, 0, 0, 0);
90 encoding_screenshot_in_progress_
= true;
91 OnScreenshotTaken(entry
->GetUniqueID(), bitmap
, content::READBACK_SUCCESS
);
92 WaitUntilScreenshotIsReady();
95 int GetScreenshotCount() {
96 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
99 void WaitUntilScreenshotIsReady() {
100 if (!encoding_screenshot_in_progress_
)
102 message_loop_runner_
= new content::MessageLoopRunner
;
103 message_loop_runner_
->Run();
107 // Overridden from content::NavigationEntryScreenshotManager:
108 void TakeScreenshotImpl(content::RenderViewHost
* host
,
109 content::NavigationEntryImpl
* entry
) override
{}
111 void OnScreenshotSet(content::NavigationEntryImpl
* entry
) override
{
112 encoding_screenshot_in_progress_
= false;
113 NavigationEntryScreenshotManager::OnScreenshotSet(entry
);
114 if (message_loop_runner_
.get())
115 message_loop_runner_
->Quit();
118 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
119 bool encoding_screenshot_in_progress_
;
121 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager
);
128 // TimeSmoother tests ----------------------------------------------------------
130 // With no duplicates, GetSmoothedTime should be the identity
132 TEST(TimeSmoother
, Basic
) {
133 NavigationControllerImpl::TimeSmoother smoother
;
134 for (int64 i
= 1; i
< 1000; ++i
) {
135 base::Time t
= base::Time::FromInternalValue(i
);
136 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
140 // With a single duplicate and timestamps thereafter increasing by one
141 // microsecond, the smoothed time should always be one behind.
142 TEST(TimeSmoother
, SingleDuplicate
) {
143 NavigationControllerImpl::TimeSmoother smoother
;
144 base::Time t
= base::Time::FromInternalValue(1);
145 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
146 for (int64 i
= 1; i
< 1000; ++i
) {
147 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
148 t
= base::Time::FromInternalValue(i
);
149 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
153 // With k duplicates and timestamps thereafter increasing by one
154 // microsecond, the smoothed time should always be k behind.
155 TEST(TimeSmoother
, ManyDuplicates
) {
156 const int64 kNumDuplicates
= 100;
157 NavigationControllerImpl::TimeSmoother smoother
;
158 base::Time t
= base::Time::FromInternalValue(1);
159 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
160 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
161 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
163 for (int64 i
= 1; i
< 1000; ++i
) {
164 base::Time expected_t
=
165 base::Time::FromInternalValue(i
+ kNumDuplicates
);
166 t
= base::Time::FromInternalValue(i
);
167 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
171 // If the clock jumps far back enough after a run of duplicates, it
172 // should immediately jump to that value.
173 TEST(TimeSmoother
, ClockBackwardsJump
) {
174 const int64 kNumDuplicates
= 100;
175 NavigationControllerImpl::TimeSmoother smoother
;
176 base::Time t
= base::Time::FromInternalValue(1000);
177 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
178 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1000);
179 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
181 t
= base::Time::FromInternalValue(500);
182 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
185 // NavigationControllerTest ----------------------------------------------------
187 class NavigationControllerTest
188 : public RenderViewHostImplTestHarness
,
189 public WebContentsObserver
{
191 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
194 void SetUp() override
{
195 RenderViewHostImplTestHarness::SetUp();
196 WebContents
* web_contents
= RenderViewHostImplTestHarness::web_contents();
197 ASSERT_TRUE(web_contents
); // The WebContents should be created by now.
198 WebContentsObserver::Observe(web_contents
);
201 // WebContentsObserver:
202 void DidStartNavigationToPendingEntry(
204 NavigationController::ReloadType reload_type
) override
{
205 navigated_url_
= url
;
208 void NavigationEntryCommitted(
209 const LoadCommittedDetails
& load_details
) override
{
210 navigation_entry_committed_counter_
++;
213 const GURL
& navigated_url() const {
214 return navigated_url_
;
217 NavigationControllerImpl
& controller_impl() {
218 return static_cast<NavigationControllerImpl
&>(controller());
221 bool HasNavigationRequest() {
222 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
223 switches::kEnableBrowserSideNavigation
)) {
224 return contents()->GetFrameTree()->root()->navigation_request() !=
227 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
)
231 const GURL
GetLastNavigationURL() {
232 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
233 switches::kEnableBrowserSideNavigation
)) {
234 NavigationRequest
* navigation_request
=
235 contents()->GetFrameTree()->root()->navigation_request();
236 CHECK(navigation_request
);
237 return navigation_request
->common_params().url
;
239 const IPC::Message
* message
=
240 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
);
242 base::Tuple
<CommonNavigationParams
, StartNavigationParams
,
243 RequestNavigationParams
> nav_params
;
244 FrameMsg_Navigate::Read(message
, &nav_params
);
245 return base::get
<0>(nav_params
).url
;
250 size_t navigation_entry_committed_counter_
;
253 void RegisterForAllNavNotifications(TestNotificationTracker
* tracker
,
254 NavigationController
* controller
) {
255 tracker
->ListenFor(NOTIFICATION_NAV_LIST_PRUNED
,
256 Source
<NavigationController
>(controller
));
257 tracker
->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED
,
258 Source
<NavigationController
>(controller
));
261 class TestWebContentsDelegate
: public WebContentsDelegate
{
263 explicit TestWebContentsDelegate() :
264 navigation_state_change_count_(0),
265 repost_form_warning_count_(0) {}
267 int navigation_state_change_count() {
268 return navigation_state_change_count_
;
271 int repost_form_warning_count() {
272 return repost_form_warning_count_
;
275 // Keep track of whether the tab has notified us of a navigation state change.
276 void NavigationStateChanged(WebContents
* source
,
277 InvalidateTypes changed_flags
) override
{
278 navigation_state_change_count_
++;
281 void ShowRepostFormWarningDialog(WebContents
* source
) override
{
282 repost_form_warning_count_
++;
286 // The number of times NavigationStateChanged has been called.
287 int navigation_state_change_count_
;
289 // The number of times ShowRepostFormWarningDialog() was called.
290 int repost_form_warning_count_
;
293 // -----------------------------------------------------------------------------
295 TEST_F(NavigationControllerTest
, Defaults
) {
296 NavigationControllerImpl
& controller
= controller_impl();
298 EXPECT_FALSE(controller
.GetPendingEntry());
299 EXPECT_FALSE(controller
.GetVisibleEntry());
300 EXPECT_FALSE(controller
.GetLastCommittedEntry());
301 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
302 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
303 EXPECT_EQ(controller
.GetEntryCount(), 0);
304 EXPECT_FALSE(controller
.CanGoBack());
305 EXPECT_FALSE(controller
.CanGoForward());
308 TEST_F(NavigationControllerTest
, GoToOffset
) {
309 NavigationControllerImpl
& controller
= controller_impl();
310 TestNotificationTracker notifications
;
311 RegisterForAllNavNotifications(¬ifications
, &controller
);
313 const int kNumUrls
= 5;
314 std::vector
<GURL
> urls(kNumUrls
);
315 for (int i
= 0; i
< kNumUrls
; ++i
) {
316 urls
[i
] = GURL(base::StringPrintf("http://www.a.com/%d", i
));
319 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[0], true);
320 main_test_rfh()->PrepareForCommit();
321 main_test_rfh()->SendNavigate(0, 0, true, urls
[0]);
322 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
323 navigation_entry_committed_counter_
= 0;
324 EXPECT_EQ(urls
[0], controller
.GetVisibleEntry()->GetVirtualURL());
325 EXPECT_FALSE(controller
.CanGoBack());
326 EXPECT_FALSE(controller
.CanGoForward());
327 EXPECT_FALSE(controller
.CanGoToOffset(1));
329 for (int i
= 1; i
<= 4; ++i
) {
330 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[i
], true);
331 main_test_rfh()->PrepareForCommit();
332 main_test_rfh()->SendNavigate(i
, 0, true, urls
[i
]);
333 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
334 navigation_entry_committed_counter_
= 0;
335 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
336 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
337 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
338 EXPECT_FALSE(controller
.CanGoToOffset(1));
341 // We have loaded 5 pages, and are currently at the last-loaded page.
345 GO_TO_MIDDLE_PAGE
= -2,
348 GO_TO_BEGINNING
= -2,
353 const int test_offsets
[NUM_TESTS
] = {
361 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
362 int offset
= test_offsets
[test
];
363 controller
.GoToOffset(offset
);
364 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
366 // Check that the GoToOffset will land on the expected page.
367 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
368 main_test_rfh()->PrepareForCommit();
369 main_test_rfh()->SendNavigate(url_index
, entry_id
, false, urls
[url_index
]);
370 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
371 navigation_entry_committed_counter_
= 0;
372 // Check that we can go to any valid offset into the history.
373 for (size_t j
= 0; j
< urls
.size(); ++j
)
374 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
375 // Check that we can't go beyond the beginning or end of the history.
376 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
377 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
381 TEST_F(NavigationControllerTest
, LoadURL
) {
382 NavigationControllerImpl
& controller
= controller_impl();
383 TestNotificationTracker notifications
;
384 RegisterForAllNavNotifications(¬ifications
, &controller
);
386 const GURL
url1("http://foo1");
387 const GURL
url2("http://foo2");
390 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
391 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
392 // Creating a pending notification should not have issued any of the
393 // notifications we're listening for.
394 EXPECT_EQ(0U, notifications
.size());
396 // The load should now be pending.
397 EXPECT_EQ(controller
.GetEntryCount(), 0);
398 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
399 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
400 EXPECT_FALSE(controller
.GetLastCommittedEntry());
401 ASSERT_TRUE(controller
.GetPendingEntry());
402 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
403 EXPECT_FALSE(controller
.CanGoBack());
404 EXPECT_FALSE(controller
.CanGoForward());
405 EXPECT_EQ(contents()->GetMaxPageID(), -1);
407 // Neither the timestamp nor the status code should have been set yet.
408 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
409 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
411 // We should have gotten no notifications from the preceeding checks.
412 EXPECT_EQ(0U, notifications
.size());
414 main_test_rfh()->PrepareForCommit();
415 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
416 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
417 navigation_entry_committed_counter_
= 0;
419 // The load should now be committed.
420 EXPECT_EQ(controller
.GetEntryCount(), 1);
421 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
422 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
423 EXPECT_TRUE(controller
.GetLastCommittedEntry());
424 EXPECT_FALSE(controller
.GetPendingEntry());
425 ASSERT_TRUE(controller
.GetVisibleEntry());
426 EXPECT_FALSE(controller
.CanGoBack());
427 EXPECT_FALSE(controller
.CanGoForward());
428 EXPECT_EQ(contents()->GetMaxPageID(), 0);
429 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
431 // The timestamp should have been set.
432 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
436 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
437 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
439 // The load should now be pending.
440 EXPECT_EQ(controller
.GetEntryCount(), 1);
441 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
442 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
443 EXPECT_TRUE(controller
.GetLastCommittedEntry());
444 ASSERT_TRUE(controller
.GetPendingEntry());
445 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
446 // TODO(darin): maybe this should really be true?
447 EXPECT_FALSE(controller
.CanGoBack());
448 EXPECT_FALSE(controller
.CanGoForward());
449 EXPECT_EQ(contents()->GetMaxPageID(), 0);
451 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
453 // Simulate the beforeunload ack for the cross-site transition, and then the
455 main_test_rfh()->PrepareForCommit();
456 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
457 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
458 navigation_entry_committed_counter_
= 0;
460 // The load should now be committed.
461 EXPECT_EQ(controller
.GetEntryCount(), 2);
462 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
463 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
464 EXPECT_TRUE(controller
.GetLastCommittedEntry());
465 EXPECT_FALSE(controller
.GetPendingEntry());
466 ASSERT_TRUE(controller
.GetVisibleEntry());
467 EXPECT_TRUE(controller
.CanGoBack());
468 EXPECT_FALSE(controller
.CanGoForward());
469 EXPECT_EQ(contents()->GetMaxPageID(), 1);
471 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
476 base::Time
GetFixedTime(base::Time time
) {
482 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
483 NavigationControllerImpl
& controller
= controller_impl();
484 TestNotificationTracker notifications
;
485 RegisterForAllNavNotifications(¬ifications
, &controller
);
487 // Set the clock to always return a timestamp of 1.
488 controller
.SetGetTimestampCallbackForTest(
489 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
491 const GURL
url1("http://foo1");
492 const GURL
url2("http://foo2");
495 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
496 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
498 main_test_rfh()->PrepareForCommit();
499 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
500 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
501 navigation_entry_committed_counter_
= 0;
505 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
506 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
508 // Simulate the beforeunload ack for the cross-site transition, and then the
510 main_test_rfh()->PrepareForCommit();
511 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
512 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
513 navigation_entry_committed_counter_
= 0;
515 // The two loads should now be committed.
516 ASSERT_EQ(controller
.GetEntryCount(), 2);
518 // Timestamps should be distinct despite the clock returning the
521 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
523 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
526 void CheckNavigationEntryMatchLoadParams(
527 NavigationController::LoadURLParams
& load_params
,
528 NavigationEntryImpl
* entry
) {
529 EXPECT_EQ(load_params
.url
, entry
->GetURL());
530 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
531 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
532 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
533 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
535 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
536 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
537 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
538 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
540 if (NavigationController::UA_OVERRIDE_INHERIT
!=
541 load_params
.override_user_agent
) {
542 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
543 load_params
.override_user_agent
);
544 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
546 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
547 entry
->GetBrowserInitiatedPostData());
548 EXPECT_EQ(load_params
.transferred_global_request_id
,
549 entry
->transferred_global_request_id());
552 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
553 NavigationControllerImpl
& controller
= controller_impl();
555 NavigationController::LoadURLParams
load_params(GURL("http://foo"));
556 load_params
.referrer
=
557 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
558 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
559 load_params
.extra_headers
= "content-type: text/plain";
560 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
561 load_params
.is_renderer_initiated
= true;
562 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
563 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
565 controller
.LoadURLWithParams(load_params
);
566 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
568 // The timestamp should not have been set yet.
570 EXPECT_TRUE(entry
->GetTimestamp().is_null());
572 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
575 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
576 NavigationControllerImpl
& controller
= controller_impl();
578 NavigationController::LoadURLParams
load_params(
579 GURL("data:text/html,dataurl"));
580 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
581 load_params
.base_url_for_data_url
= GURL("http://foo");
582 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
583 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
585 controller
.LoadURLWithParams(load_params
);
586 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
588 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
591 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
592 NavigationControllerImpl
& controller
= controller_impl();
594 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
595 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
596 load_params
.load_type
=
597 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
598 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
601 const unsigned char* raw_data
=
602 reinterpret_cast<const unsigned char*>("d\n\0a2");
603 const int length
= 5;
604 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
605 scoped_refptr
<base::RefCountedBytes
> data
=
606 base::RefCountedBytes::TakeVector(&post_data_vector
);
607 load_params
.browser_initiated_post_data
= data
.get();
609 controller
.LoadURLWithParams(load_params
);
610 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
612 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
615 // Tests what happens when the same page is loaded again. Should not create a
616 // new session history entry. This is what happens when you press enter in the
617 // URL bar to reload: a pending entry is created and then it is discarded when
618 // the load commits (because WebCore didn't actually make a new entry).
619 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
620 NavigationControllerImpl
& controller
= controller_impl();
621 TestNotificationTracker notifications
;
622 RegisterForAllNavNotifications(¬ifications
, &controller
);
624 const GURL
url1("http://foo1");
627 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
628 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
629 EXPECT_EQ(0U, notifications
.size());
630 main_test_rfh()->PrepareForCommit();
631 main_test_rfh()->SendNavigateWithTransition(
632 0, entry_id
, true, url1
, ui::PAGE_TRANSITION_TYPED
);
633 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
634 navigation_entry_committed_counter_
= 0;
636 ASSERT_TRUE(controller
.GetVisibleEntry());
637 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
638 EXPECT_FALSE(timestamp
.is_null());
641 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
642 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
643 EXPECT_EQ(0U, notifications
.size());
644 main_test_rfh()->PrepareForCommit();
645 main_test_rfh()->SendNavigateWithTransition(
646 0, entry_id
, false, url1
, ui::PAGE_TRANSITION_TYPED
);
647 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
648 navigation_entry_committed_counter_
= 0;
650 // We should not have produced a new session history entry.
651 EXPECT_EQ(controller
.GetEntryCount(), 1);
652 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
653 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
654 EXPECT_TRUE(controller
.GetLastCommittedEntry());
655 EXPECT_FALSE(controller
.GetPendingEntry());
656 ASSERT_TRUE(controller
.GetVisibleEntry());
657 EXPECT_FALSE(controller
.CanGoBack());
658 EXPECT_FALSE(controller
.CanGoForward());
660 // The timestamp should have been updated.
662 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
663 // EXPECT_GT once we guarantee that timestamps are unique.
664 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
667 // Load the same page twice, once as a GET and once as a POST.
668 // We should update the post state on the NavigationEntry.
669 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
670 NavigationControllerImpl
& controller
= controller_impl();
671 TestNotificationTracker notifications
;
672 RegisterForAllNavNotifications(¬ifications
, &controller
);
674 const GURL
url1("http://foo1");
677 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
678 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
680 params
.nav_entry_id
= controller
.GetPendingEntry()->GetUniqueID();
681 params
.did_create_new_entry
= true;
683 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
684 params
.is_post
= true;
685 params
.post_id
= 123;
686 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
687 main_test_rfh()->PrepareForCommit();
688 main_test_rfh()->SendNavigateWithParams(¶ms
);
690 // The post data should be visible.
691 NavigationEntry
* entry
= controller
.GetVisibleEntry();
693 EXPECT_TRUE(entry
->GetHasPostData());
694 EXPECT_EQ(entry
->GetPostID(), 123);
697 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
698 main_test_rfh()->PrepareForCommit();
699 main_test_rfh()->SendNavigateWithTransition(
700 0, controller
.GetPendingEntry()->GetUniqueID(),
701 false, url1
, ui::PAGE_TRANSITION_TYPED
);
703 // We should not have produced a new session history entry.
704 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
706 // The post data should have been cleared due to the GET.
707 EXPECT_FALSE(entry
->GetHasPostData());
708 EXPECT_EQ(entry
->GetPostID(), 0);
711 // Tests loading a URL but discarding it before the load commits.
712 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
713 NavigationControllerImpl
& controller
= controller_impl();
714 TestNotificationTracker notifications
;
715 RegisterForAllNavNotifications(¬ifications
, &controller
);
717 const GURL
url1("http://foo1");
718 const GURL
url2("http://foo2");
721 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
722 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
723 EXPECT_EQ(0U, notifications
.size());
724 main_test_rfh()->PrepareForCommit();
725 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
726 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
727 navigation_entry_committed_counter_
= 0;
729 ASSERT_TRUE(controller
.GetVisibleEntry());
730 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
731 EXPECT_FALSE(timestamp
.is_null());
734 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
735 controller
.DiscardNonCommittedEntries();
736 EXPECT_EQ(0U, notifications
.size());
738 // Should not have produced a new session history entry.
739 EXPECT_EQ(controller
.GetEntryCount(), 1);
740 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
741 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
742 EXPECT_TRUE(controller
.GetLastCommittedEntry());
743 EXPECT_FALSE(controller
.GetPendingEntry());
744 ASSERT_TRUE(controller
.GetVisibleEntry());
745 EXPECT_FALSE(controller
.CanGoBack());
746 EXPECT_FALSE(controller
.CanGoForward());
748 // Timestamp should not have changed.
749 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
752 // Tests navigations that come in unrequested. This happens when the user
753 // navigates from the web page, and here we test that there is no pending entry.
754 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
755 NavigationControllerImpl
& controller
= controller_impl();
756 TestNotificationTracker notifications
;
757 RegisterForAllNavNotifications(¬ifications
, &controller
);
759 // First make an existing committed entry.
760 const GURL
kExistingURL1("http://eh");
762 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
763 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
764 main_test_rfh()->PrepareForCommit();
765 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
766 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
767 navigation_entry_committed_counter_
= 0;
769 // Do a new navigation without making a pending one.
770 const GURL
kNewURL("http://see");
771 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL
);
773 // There should no longer be any pending entry, and the second navigation we
774 // just made should be committed.
775 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
776 navigation_entry_committed_counter_
= 0;
777 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
778 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
779 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
782 // Tests navigating to a new URL when there is a new pending navigation that is
783 // not the one that just loaded. This will happen if the user types in a URL to
784 // somewhere slow, and then navigates the current page before the typed URL
786 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
787 NavigationControllerImpl
& controller
= controller_impl();
788 TestNotificationTracker notifications
;
789 RegisterForAllNavNotifications(¬ifications
, &controller
);
791 // First make an existing committed entry.
792 const GURL
kExistingURL1("http://eh");
794 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
795 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
796 main_test_rfh()->PrepareForCommit();
797 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
798 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
799 navigation_entry_committed_counter_
= 0;
801 // Make a pending entry to somewhere new.
802 const GURL
kExistingURL2("http://bee");
804 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
805 EXPECT_EQ(0U, notifications
.size());
807 // After the beforeunload but before it commits...
808 main_test_rfh()->PrepareForCommit();
810 // ... Do a new navigation.
811 const GURL
kNewURL("http://see");
812 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
813 main_test_rfh()->PrepareForCommit();
814 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL
);
816 // There should no longer be any pending entry, and the third navigation we
817 // just made should be committed.
818 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
819 navigation_entry_committed_counter_
= 0;
820 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
821 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
822 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
825 // Tests navigating to a new URL when there is a pending back/forward
826 // navigation. This will happen if the user hits back, but before that commits,
827 // they navigate somewhere new.
828 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
829 NavigationControllerImpl
& controller
= controller_impl();
830 TestNotificationTracker notifications
;
831 RegisterForAllNavNotifications(¬ifications
, &controller
);
833 // First make some history.
834 const GURL
kExistingURL1("http://foo/eh");
836 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
837 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
838 main_test_rfh()->PrepareForCommit();
839 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
840 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
841 navigation_entry_committed_counter_
= 0;
843 const GURL
kExistingURL2("http://foo/bee");
845 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
846 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
847 main_test_rfh()->PrepareForCommit();
848 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
849 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
850 navigation_entry_committed_counter_
= 0;
852 // Now make a pending back/forward navigation. The zeroth entry should be
855 EXPECT_EQ(0U, notifications
.size());
856 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
857 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
859 // Before that commits, do a new navigation.
860 const GURL
kNewURL("http://foo/see");
861 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
862 main_test_rfh()->PrepareForCommit();
863 main_test_rfh()->SendNavigate(3, 0, true, kNewURL
);
865 // There should no longer be any pending entry, and the new navigation we
866 // just made should be committed.
867 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
868 navigation_entry_committed_counter_
= 0;
869 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
870 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
871 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
874 // Tests navigating to a new URL when there is a pending back/forward
875 // navigation to a cross-process, privileged URL. This will happen if the user
876 // hits back, but before that commits, they navigate somewhere new.
877 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
878 NavigationControllerImpl
& controller
= controller_impl();
879 TestNotificationTracker notifications
;
880 RegisterForAllNavNotifications(¬ifications
, &controller
);
882 // First make some history, starting with a privileged URL.
883 const GURL
kExistingURL1("http://privileged");
885 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
886 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
887 // Pretend it has bindings so we can tell if we incorrectly copy it.
888 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
889 main_test_rfh()->PrepareForCommit();
890 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
891 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
892 navigation_entry_committed_counter_
= 0;
894 // Navigate cross-process to a second URL.
895 const GURL
kExistingURL2("http://foo/eh");
897 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
898 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
899 main_test_rfh()->PrepareForCommit();
900 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
901 foo_rfh
->SendNavigate(1, entry_id
, true, kExistingURL2
);
902 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
903 navigation_entry_committed_counter_
= 0;
905 // Now make a pending back/forward navigation to a privileged entry.
906 // The zeroth entry should be pending.
908 foo_rfh
->SendBeforeUnloadACK(true);
909 EXPECT_EQ(0U, notifications
.size());
910 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
911 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
912 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
914 // Before that commits, do a new navigation.
915 const GURL
kNewURL("http://foo/bee");
916 foo_rfh
->SendRendererInitiatedNavigationRequest(kNewURL
, true);
917 foo_rfh
->PrepareForCommit();
918 foo_rfh
->SendNavigate(3, 0, true, kNewURL
);
920 // There should no longer be any pending entry, and the new navigation we
921 // just made should be committed.
922 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
923 navigation_entry_committed_counter_
= 0;
924 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
925 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
926 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
927 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
930 // Tests navigating to an existing URL when there is a pending new navigation.
931 // This will happen if the user enters a URL, but before that commits, the
932 // current page fires history.back().
933 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
934 NavigationControllerImpl
& controller
= controller_impl();
935 TestNotificationTracker notifications
;
936 RegisterForAllNavNotifications(¬ifications
, &controller
);
938 // First make some history.
939 const GURL
kExistingURL1("http://foo/eh");
941 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
942 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
943 main_test_rfh()->PrepareForCommit();
944 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
945 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
946 navigation_entry_committed_counter_
= 0;
948 const GURL
kExistingURL2("http://foo/bee");
950 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
951 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
952 main_test_rfh()->PrepareForCommit();
953 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
954 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
955 navigation_entry_committed_counter_
= 0;
957 // A back navigation comes in from the renderer...
958 controller
.GoToOffset(-1);
959 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
961 // ...while the user tries to navigate to a new page...
962 const GURL
kNewURL("http://foo/see");
964 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
965 EXPECT_EQ(0U, notifications
.size());
966 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
967 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
969 // ...and the back navigation commits.
970 main_test_rfh()->PrepareForCommit();
971 main_test_rfh()->SendNavigate(0, entry_id
, false, kExistingURL1
);
973 // There should no longer be any pending entry, and the back navigation should
975 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
976 navigation_entry_committed_counter_
= 0;
977 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
978 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
979 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
982 // Tests an ignored navigation when there is a pending new navigation.
983 // This will happen if the user enters a URL, but before that commits, the
984 // current blank page reloads. See http://crbug.com/77507.
985 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
986 NavigationControllerImpl
& controller
= controller_impl();
987 TestNotificationTracker notifications
;
988 RegisterForAllNavNotifications(¬ifications
, &controller
);
990 // Set a WebContentsDelegate to listen for state changes.
991 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
992 EXPECT_FALSE(contents()->GetDelegate());
993 contents()->SetDelegate(delegate
.get());
995 // Without any navigations, the renderer starts at about:blank.
996 const GURL
kExistingURL(url::kAboutBlankURL
);
998 // Now make a pending new navigation.
999 const GURL
kNewURL("http://eh");
1001 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1002 EXPECT_EQ(0U, notifications
.size());
1003 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1004 EXPECT_TRUE(controller
.GetPendingEntry());
1005 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1006 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1008 // Before that commits, a document.write and location.reload can cause the
1009 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1010 // PlzNavigate: this will stop the old navigation and start a new one.
1011 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL
, true);
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 contents()->GetPendingMainFrame()->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_for_guests_only(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
1335 void SetOriginalURL(const GURL
& url
,
1336 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1337 params
->original_request_url
= url
;
1341 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1342 NavigationControllerImpl
& controller
= controller_impl();
1343 TestNotificationTracker notifications
;
1344 RegisterForAllNavNotifications(¬ifications
, &controller
);
1346 const GURL
original_url("http://foo1");
1347 const GURL
final_url("http://foo2");
1348 auto set_original_url_callback
= base::Bind(SetOriginalURL
, original_url
);
1350 // Load up the original URL, but get redirected.
1352 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1353 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1354 EXPECT_EQ(0U, notifications
.size());
1355 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1356 main_test_rfh()->SendNavigateWithModificationCallback(
1357 0, entry_id
, true, final_url
, set_original_url_callback
);
1358 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1359 navigation_entry_committed_counter_
= 0;
1360 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1362 // The NavigationEntry should save both the original URL and the final
1365 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1366 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1368 // Reload using the original URL.
1369 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1370 controller
.ReloadOriginalRequestURL(false);
1371 EXPECT_EQ(0U, notifications
.size());
1373 // The reload is pending. The request should point to the original URL.
1374 EXPECT_EQ(original_url
, navigated_url());
1375 EXPECT_EQ(controller
.GetEntryCount(), 1);
1376 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1377 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1378 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1379 EXPECT_TRUE(controller
.GetPendingEntry());
1380 EXPECT_FALSE(controller
.CanGoBack());
1381 EXPECT_FALSE(controller
.CanGoForward());
1383 // Make sure the title has been cleared (will be redrawn just after reload).
1384 // Avoids a stale cached title when the new page being reloaded has no title.
1385 // See http://crbug.com/96041.
1386 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1388 // Send that the navigation has proceeded; say it got redirected again.
1389 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1390 main_test_rfh()->SendNavigate(0, entry_id
, false, final_url
);
1391 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1392 navigation_entry_committed_counter_
= 0;
1394 // Now the reload is committed.
1395 EXPECT_EQ(controller
.GetEntryCount(), 1);
1396 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1397 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1398 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1399 EXPECT_FALSE(controller
.GetPendingEntry());
1400 EXPECT_FALSE(controller
.CanGoBack());
1401 EXPECT_FALSE(controller
.CanGoForward());
1404 #endif // !defined(OS_ANDROID)
1406 // Test that certain non-persisted NavigationEntryImpl values get reset after
1408 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1409 NavigationControllerImpl
& controller
= controller_impl();
1411 // The value of "should replace entry" will be tested, but it's an error to
1412 // specify it when there are no entries. Create a simple entry to be replaced.
1413 const GURL
url0("http://foo/0");
1415 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1416 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1417 main_test_rfh()->PrepareForCommit();
1418 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
1420 // Set up the pending entry.
1421 const GURL
url1("http://foo/1");
1423 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1424 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1426 // Set up some sample values.
1427 const unsigned char* raw_data
=
1428 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1429 const int length
= 11;
1430 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1431 scoped_refptr
<base::RefCountedBytes
> post_data
=
1432 base::RefCountedBytes::TakeVector(&post_data_vector
);
1433 GlobalRequestID
transfer_id(3, 4);
1435 // Set non-persisted values on the pending entry.
1436 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1437 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1438 pending_entry
->set_is_renderer_initiated(true);
1439 pending_entry
->set_transferred_global_request_id(transfer_id
);
1440 pending_entry
->set_should_replace_entry(true);
1441 pending_entry
->set_should_clear_history_list(true);
1442 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1443 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1444 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1445 EXPECT_TRUE(pending_entry
->should_replace_entry());
1446 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1448 // Fake a commit response.
1449 main_test_rfh()->PrepareForCommit();
1450 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
1452 // Certain values that are only used for pending entries get reset after
1454 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1455 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1456 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1457 EXPECT_EQ(GlobalRequestID(-1, -1),
1458 committed_entry
->transferred_global_request_id());
1459 EXPECT_FALSE(committed_entry
->should_replace_entry());
1460 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1464 void SetRedirects(const std::vector
<GURL
>& redirects
,
1465 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1466 params
->redirects
= redirects
;
1470 // Test that Redirects are preserved after a commit.
1471 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1472 NavigationControllerImpl
& controller
= controller_impl();
1473 const GURL
url1("http://foo1");
1474 const GURL
url2("http://foo2");
1476 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1477 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1479 // Set up some redirect values.
1480 std::vector
<GURL
> redirects
;
1481 redirects
.push_back(url2
);
1482 auto set_redirects_callback
= base::Bind(SetRedirects
, redirects
);
1484 // Set redirects on the pending entry.
1485 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1486 pending_entry
->SetRedirectChain(redirects
);
1487 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1488 EXPECT_EQ(url2
, pending_entry
->GetRedirectChain()[0]);
1490 // Normal navigation will preserve redirects in the committed entry.
1491 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1492 main_test_rfh()->SendNavigateWithModificationCallback(0, entry_id
, true, url1
,
1493 set_redirects_callback
);
1494 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1495 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1496 EXPECT_EQ(url2
, committed_entry
->GetRedirectChain()[0]);
1499 // Tests what happens when we navigate back successfully
1500 TEST_F(NavigationControllerTest
, Back
) {
1501 NavigationControllerImpl
& controller
= controller_impl();
1502 TestNotificationTracker notifications
;
1503 RegisterForAllNavNotifications(¬ifications
, &controller
);
1505 const GURL
url1("http://foo1");
1506 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1507 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1508 navigation_entry_committed_counter_
= 0;
1510 const GURL
url2("http://foo2");
1511 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
1512 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1513 navigation_entry_committed_counter_
= 0;
1515 controller
.GoBack();
1516 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1517 EXPECT_EQ(0U, notifications
.size());
1519 // We should now have a pending navigation to go back.
1520 EXPECT_EQ(controller
.GetEntryCount(), 2);
1521 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1522 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1523 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1524 EXPECT_TRUE(controller
.GetPendingEntry());
1525 EXPECT_FALSE(controller
.CanGoBack());
1526 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1527 EXPECT_TRUE(controller
.CanGoForward());
1528 EXPECT_TRUE(controller
.CanGoToOffset(1));
1529 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go forward 2 steps.
1531 // Timestamp for entry 1 should be on or after that of entry 0.
1532 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1533 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1534 controller
.GetEntryAtIndex(0)->GetTimestamp());
1536 main_test_rfh()->PrepareForCommit();
1537 main_test_rfh()->SendNavigate(0, entry_id
, false, url2
);
1538 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1539 navigation_entry_committed_counter_
= 0;
1541 // The back navigation completed successfully.
1542 EXPECT_EQ(controller
.GetEntryCount(), 2);
1543 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1544 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1545 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1546 EXPECT_FALSE(controller
.GetPendingEntry());
1547 EXPECT_FALSE(controller
.CanGoBack());
1548 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1549 EXPECT_TRUE(controller
.CanGoForward());
1550 EXPECT_TRUE(controller
.CanGoToOffset(1));
1551 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1553 // Timestamp for entry 0 should be on or after that of entry 1
1554 // (since we went back to it).
1555 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1556 controller
.GetEntryAtIndex(1)->GetTimestamp());
1559 // Tests what happens when a back navigation produces a new page.
1560 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1561 NavigationControllerImpl
& controller
= controller_impl();
1562 TestNotificationTracker notifications
;
1563 RegisterForAllNavNotifications(¬ifications
, &controller
);
1565 const GURL
url1("http://foo/1");
1566 const GURL
url2("http://foo/2");
1567 const GURL
url3("http://foo/3");
1570 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1571 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1572 main_test_rfh()->PrepareForCommit();
1573 main_test_rfh()->SendNavigate(0, entry1_id
, true, url1
);
1574 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1575 navigation_entry_committed_counter_
= 0;
1576 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1579 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1580 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1581 main_test_rfh()->PrepareForCommit();
1582 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1583 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1584 navigation_entry_committed_counter_
= 0;
1586 controller
.GoBack();
1587 EXPECT_EQ(0U, notifications
.size());
1589 // We should now have a pending navigation to go back.
1590 EXPECT_EQ(controller
.GetEntryCount(), 2);
1591 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1592 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1593 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1594 EXPECT_TRUE(controller
.GetPendingEntry());
1595 EXPECT_FALSE(controller
.CanGoBack());
1596 EXPECT_TRUE(controller
.CanGoForward());
1598 main_test_rfh()->PrepareForCommitWithServerRedirect(url3
);
1599 main_test_rfh()->SendNavigate(2, entry1_id
, true, url3
);
1600 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1601 navigation_entry_committed_counter_
= 0;
1603 // The back navigation resulted in a completely new navigation.
1604 // TODO(darin): perhaps this behavior will be confusing to users?
1605 EXPECT_EQ(controller
.GetEntryCount(), 3);
1606 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1607 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1608 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1609 EXPECT_FALSE(controller
.GetPendingEntry());
1610 EXPECT_TRUE(controller
.CanGoBack());
1611 EXPECT_FALSE(controller
.CanGoForward());
1614 // Receives a back message when there is a new pending navigation entry.
1615 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1616 NavigationControllerImpl
& controller
= controller_impl();
1617 TestNotificationTracker notifications
;
1618 RegisterForAllNavNotifications(¬ifications
, &controller
);
1620 const GURL
kUrl1("http://foo1");
1621 const GURL
kUrl2("http://foo2");
1622 const GURL
kUrl3("http://foo3");
1624 // First navigate two places so we have some back history.
1625 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
1626 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1627 navigation_entry_committed_counter_
= 0;
1629 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1630 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
1631 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1632 navigation_entry_committed_counter_
= 0;
1634 // Now start a new pending navigation and go back before it commits.
1636 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1637 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1638 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1639 controller
.GoBack();
1641 // The pending navigation should now be the "back" item and the new one
1643 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1644 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1647 // Tests what happens when we navigate forward successfully.
1648 TEST_F(NavigationControllerTest
, Forward
) {
1649 NavigationControllerImpl
& controller
= controller_impl();
1650 TestNotificationTracker notifications
;
1651 RegisterForAllNavNotifications(¬ifications
, &controller
);
1653 const GURL
url1("http://foo1");
1654 const GURL
url2("http://foo2");
1656 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1657 main_test_rfh()->PrepareForCommit();
1658 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1659 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1660 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1661 navigation_entry_committed_counter_
= 0;
1663 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1664 main_test_rfh()->PrepareForCommit();
1665 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1666 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1667 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1668 navigation_entry_committed_counter_
= 0;
1670 controller
.GoBack();
1671 main_test_rfh()->PrepareForCommit();
1672 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1673 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1674 navigation_entry_committed_counter_
= 0;
1676 controller
.GoForward();
1678 // We should now have a pending navigation to go forward.
1679 EXPECT_EQ(controller
.GetEntryCount(), 2);
1680 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1681 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1682 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1683 EXPECT_TRUE(controller
.GetPendingEntry());
1684 EXPECT_TRUE(controller
.CanGoBack());
1685 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1686 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1687 EXPECT_FALSE(controller
.CanGoForward());
1688 EXPECT_FALSE(controller
.CanGoToOffset(1));
1690 // Timestamp for entry 0 should be on or after that of entry 1
1691 // (since we went back to it).
1692 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1693 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1694 controller
.GetEntryAtIndex(1)->GetTimestamp());
1696 main_test_rfh()->PrepareForCommit();
1697 main_test_rfh()->SendNavigate(1, entry2
->GetUniqueID(), false, url2
);
1698 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1699 navigation_entry_committed_counter_
= 0;
1701 // The forward navigation completed successfully.
1702 EXPECT_EQ(controller
.GetEntryCount(), 2);
1703 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1704 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1705 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1706 EXPECT_FALSE(controller
.GetPendingEntry());
1707 EXPECT_TRUE(controller
.CanGoBack());
1708 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1709 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1710 EXPECT_FALSE(controller
.CanGoForward());
1711 EXPECT_FALSE(controller
.CanGoToOffset(1));
1713 // Timestamp for entry 1 should be on or after that of entry 0
1714 // (since we went forward to it).
1715 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1716 controller
.GetEntryAtIndex(0)->GetTimestamp());
1719 // Tests what happens when a forward navigation produces a new page.
1720 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1721 NavigationControllerImpl
& controller
= controller_impl();
1722 TestNotificationTracker notifications
;
1723 RegisterForAllNavNotifications(¬ifications
, &controller
);
1725 const GURL
url1("http://foo1");
1726 const GURL
url2("http://foo2");
1727 const GURL
url3("http://foo3");
1729 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1730 main_test_rfh()->PrepareForCommit();
1731 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1732 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1733 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1734 navigation_entry_committed_counter_
= 0;
1735 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1736 main_test_rfh()->PrepareForCommit();
1737 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1738 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1739 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1740 navigation_entry_committed_counter_
= 0;
1742 controller
.GoBack();
1743 main_test_rfh()->PrepareForCommit();
1744 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1745 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1746 navigation_entry_committed_counter_
= 0;
1748 controller
.GoForward();
1749 EXPECT_EQ(0U, notifications
.size());
1751 // Should now have a pending navigation to go forward.
1752 EXPECT_EQ(controller
.GetEntryCount(), 2);
1753 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1754 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1755 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1756 EXPECT_TRUE(controller
.GetPendingEntry());
1757 EXPECT_TRUE(controller
.CanGoBack());
1758 EXPECT_FALSE(controller
.CanGoForward());
1760 main_test_rfh()->PrepareForCommit();
1761 main_test_rfh()->SendNavigate(2, entry2
->GetUniqueID(), true, url3
);
1762 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1763 navigation_entry_committed_counter_
= 0;
1764 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1766 EXPECT_EQ(controller
.GetEntryCount(), 2);
1767 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1768 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1769 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1770 EXPECT_FALSE(controller
.GetPendingEntry());
1771 EXPECT_TRUE(controller
.CanGoBack());
1772 EXPECT_FALSE(controller
.CanGoForward());
1775 // Two consecutive navigations for the same URL entered in should be considered
1776 // as SAME_PAGE navigation even when we are redirected to some other page.
1777 TEST_F(NavigationControllerTest
, Redirect
) {
1778 NavigationControllerImpl
& controller
= controller_impl();
1779 TestNotificationTracker notifications
;
1780 RegisterForAllNavNotifications(¬ifications
, &controller
);
1782 const GURL
url1("http://foo1");
1783 const GURL
url2("http://foo2"); // Redirection target
1787 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1788 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1790 EXPECT_EQ(0U, notifications
.size());
1792 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1794 params
.nav_entry_id
= entry_id
;
1795 params
.did_create_new_entry
= true;
1797 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1798 params
.redirects
.push_back(GURL("http://foo1"));
1799 params
.redirects
.push_back(GURL("http://foo2"));
1800 params
.should_update_history
= false;
1801 params
.gesture
= NavigationGestureAuto
;
1802 params
.is_post
= false;
1803 params
.page_state
= PageState::CreateFromURL(url2
);
1805 LoadCommittedDetails details
;
1807 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1809 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1810 navigation_entry_committed_counter_
= 0;
1814 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1815 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1817 EXPECT_TRUE(controller
.GetPendingEntry());
1818 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1819 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1821 params
.nav_entry_id
= entry_id
;
1822 params
.did_create_new_entry
= false;
1824 EXPECT_EQ(0U, notifications
.size());
1825 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1827 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1828 navigation_entry_committed_counter_
= 0;
1830 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1831 EXPECT_EQ(controller
.GetEntryCount(), 1);
1832 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1833 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1834 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1835 EXPECT_FALSE(controller
.GetPendingEntry());
1836 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1838 EXPECT_FALSE(controller
.CanGoBack());
1839 EXPECT_FALSE(controller
.CanGoForward());
1842 // Similar to Redirect above, but the first URL is requested by POST,
1843 // the second URL is requested by GET. NavigationEntry::has_post_data_
1844 // must be cleared. http://crbug.com/21245
1845 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1846 NavigationControllerImpl
& controller
= controller_impl();
1847 TestNotificationTracker notifications
;
1848 RegisterForAllNavNotifications(¬ifications
, &controller
);
1850 const GURL
url1("http://foo1");
1851 const GURL
url2("http://foo2"); // Redirection target
1853 // First request as POST.
1855 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1856 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1857 controller
.GetVisibleEntry()->SetHasPostData(true);
1859 EXPECT_EQ(0U, notifications
.size());
1861 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1863 params
.nav_entry_id
= entry_id
;
1864 params
.did_create_new_entry
= true;
1866 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1867 params
.redirects
.push_back(GURL("http://foo1"));
1868 params
.redirects
.push_back(GURL("http://foo2"));
1869 params
.should_update_history
= false;
1870 params
.gesture
= NavigationGestureAuto
;
1871 params
.is_post
= true;
1872 params
.page_state
= PageState::CreateFromURL(url2
);
1874 LoadCommittedDetails details
;
1876 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1878 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1879 navigation_entry_committed_counter_
= 0;
1883 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1884 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1886 EXPECT_TRUE(controller
.GetPendingEntry());
1887 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1888 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1890 params
.nav_entry_id
= entry_id
;
1891 params
.did_create_new_entry
= false;
1892 params
.is_post
= false;
1894 EXPECT_EQ(0U, notifications
.size());
1895 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1897 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1898 navigation_entry_committed_counter_
= 0;
1900 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1901 EXPECT_EQ(controller
.GetEntryCount(), 1);
1902 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1903 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1904 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1905 EXPECT_FALSE(controller
.GetPendingEntry());
1906 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1907 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1909 EXPECT_FALSE(controller
.CanGoBack());
1910 EXPECT_FALSE(controller
.CanGoForward());
1913 // A redirect right off the bat should be a NEW_PAGE.
1914 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1915 NavigationControllerImpl
& controller
= controller_impl();
1916 TestNotificationTracker notifications
;
1917 RegisterForAllNavNotifications(¬ifications
, &controller
);
1919 const GURL
url1("http://foo1");
1920 const GURL
url2("http://foo2"); // Redirection target
1924 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1925 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1927 EXPECT_TRUE(controller
.GetPendingEntry());
1928 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1929 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1931 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1933 params
.nav_entry_id
= entry_id
;
1934 params
.did_create_new_entry
= true;
1936 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1937 params
.redirects
.push_back(GURL("http://foo1"));
1938 params
.redirects
.push_back(GURL("http://foo2"));
1939 params
.should_update_history
= false;
1940 params
.gesture
= NavigationGestureAuto
;
1941 params
.is_post
= false;
1942 params
.page_state
= PageState::CreateFromURL(url2
);
1944 LoadCommittedDetails details
;
1946 EXPECT_EQ(0U, notifications
.size());
1947 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1949 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1950 navigation_entry_committed_counter_
= 0;
1952 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
1953 EXPECT_EQ(controller
.GetEntryCount(), 1);
1954 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1955 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1956 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1957 EXPECT_FALSE(controller
.GetPendingEntry());
1958 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1960 EXPECT_FALSE(controller
.CanGoBack());
1961 EXPECT_FALSE(controller
.CanGoForward());
1964 // If something is pumping the event loop in the browser process and is loading
1965 // pages rapidly one after the other, there can be a race with two closely-
1966 // spaced load requests. Once the first load request is sent, will the renderer
1967 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
1968 // IPC, and have the browser process handle that IPC before the caller makes
1969 // another load request, replacing the pending entry of the first request?
1971 // This test is about what happens in such a race when that pending entry
1972 // replacement happens. If it happens, and the first load had the same URL as
1973 // the page before it, we must make sure that the replacement of the pending
1974 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
1976 // (This is a unit test rather than a browser test because it's not currently
1977 // possible to force this sequence of events with a browser test.)
1978 TEST_F(NavigationControllerTest
,
1979 NavigationTypeClassification_ExistingPageRace
) {
1980 NavigationControllerImpl
& controller
= controller_impl();
1981 const GURL
url1("http://foo1");
1982 const GURL
url2("http://foo2");
1984 // Start with a loaded page.
1985 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1986 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
1988 // Start a load of the same page again.
1990 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1991 int entry_id1
= controller
.GetPendingEntry()->GetUniqueID();
1993 // Immediately start loading a different page...
1995 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1996 int entry_id2
= controller
.GetPendingEntry()->GetUniqueID();
1997 EXPECT_NE(entry_id1
, entry_id2
);
1999 // ... and now the renderer sends a commit for the first navigation.
2000 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2002 params
.nav_entry_id
= entry_id1
;
2003 params
.intended_as_new_entry
= true;
2004 params
.did_create_new_entry
= false;
2006 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
2007 params
.page_state
= PageState::CreateFromURL(url1
);
2009 LoadCommittedDetails details
;
2011 main_test_rfh()->PrepareForCommit();
2012 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2014 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
2017 // Tests navigation via link click within a subframe. A new navigation entry
2018 // should be created.
2019 TEST_F(NavigationControllerTest
, NewSubframe
) {
2020 NavigationControllerImpl
& controller
= controller_impl();
2021 TestNotificationTracker notifications
;
2022 RegisterForAllNavNotifications(¬ifications
, &controller
);
2024 const GURL
url1("http://foo1");
2025 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2026 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2027 navigation_entry_committed_counter_
= 0;
2029 // Prereq: add a subframe with an initial auto-subframe navigation.
2030 main_test_rfh()->OnCreateChildFrame(
2031 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2032 blink::WebSandboxFlags::None
);
2033 RenderFrameHostImpl
* subframe
=
2034 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2035 const GURL
subframe_url("http://foo1/subframe");
2037 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2039 params
.nav_entry_id
= 0;
2040 params
.did_create_new_entry
= false;
2041 params
.url
= subframe_url
;
2042 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2043 params
.should_update_history
= false;
2044 params
.gesture
= NavigationGestureUser
;
2045 params
.is_post
= false;
2046 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2048 // Navigating should do nothing.
2049 LoadCommittedDetails details
;
2050 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2051 EXPECT_EQ(0U, notifications
.size());
2054 // Now do a new navigation in the frame.
2055 const GURL
url2("http://foo2");
2056 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2058 params
.nav_entry_id
= 0;
2059 params
.did_create_new_entry
= true;
2061 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2062 params
.should_update_history
= false;
2063 params
.gesture
= NavigationGestureUser
;
2064 params
.is_post
= false;
2065 params
.page_state
= PageState::CreateFromURL(url2
);
2067 LoadCommittedDetails details
;
2068 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2069 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2070 navigation_entry_committed_counter_
= 0;
2071 EXPECT_EQ(url1
, details
.previous_url
);
2072 EXPECT_FALSE(details
.is_in_page
);
2073 EXPECT_FALSE(details
.is_main_frame
);
2075 // The new entry should be appended.
2076 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2077 EXPECT_EQ(2, controller
.GetEntryCount());
2078 EXPECT_EQ(entry
, details
.entry
);
2080 // New entry should refer to the new page, but the old URL (entries only
2081 // reflect the toplevel URL).
2082 EXPECT_EQ(url1
, entry
->GetURL());
2083 EXPECT_EQ(params
.page_id
, entry
->GetPageID());
2085 // Verify subframe entries if we're in --site-per-process mode.
2086 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2087 switches::kSitePerProcess
)) {
2088 // The entry should have a subframe FrameNavigationEntry.
2089 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2090 EXPECT_EQ(url2
, entry
->root_node()->children
[0]->frame_entry
->url());
2092 // There are no subframe FrameNavigationEntries by default.
2093 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2097 // Auto subframes are ones the page loads automatically like ads. They should
2098 // not create new navigation entries.
2099 // TODO(creis): Test updating entries for history auto subframe navigations.
2100 TEST_F(NavigationControllerTest
, AutoSubframe
) {
2101 NavigationControllerImpl
& controller
= controller_impl();
2102 TestNotificationTracker notifications
;
2103 RegisterForAllNavNotifications(¬ifications
, &controller
);
2105 const GURL
url1("http://foo/1");
2106 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2107 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2108 navigation_entry_committed_counter_
= 0;
2110 // Add a subframe and navigate it.
2111 main_test_rfh()->OnCreateChildFrame(
2112 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2113 blink::WebSandboxFlags::None
);
2114 RenderFrameHostImpl
* subframe
=
2115 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2116 const GURL
url2("http://foo/2");
2118 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2120 params
.nav_entry_id
= 0;
2121 params
.did_create_new_entry
= false;
2123 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2124 params
.should_update_history
= false;
2125 params
.gesture
= NavigationGestureUser
;
2126 params
.is_post
= false;
2127 params
.page_state
= PageState::CreateFromURL(url2
);
2129 // Navigating should do nothing.
2130 LoadCommittedDetails details
;
2131 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2132 EXPECT_EQ(0U, notifications
.size());
2135 // There should still be only one entry.
2136 EXPECT_EQ(1, controller
.GetEntryCount());
2137 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2138 EXPECT_EQ(url1
, entry
->GetURL());
2139 EXPECT_EQ(1, entry
->GetPageID());
2140 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
2141 EXPECT_EQ(url1
, root_entry
->url());
2143 // Verify subframe entries if we're in --site-per-process mode.
2144 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2145 switches::kSitePerProcess
)) {
2146 // The entry should now have a subframe FrameNavigationEntry.
2147 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2148 FrameNavigationEntry
* frame_entry
=
2149 entry
->root_node()->children
[0]->frame_entry
.get();
2150 EXPECT_EQ(url2
, frame_entry
->url());
2152 // There are no subframe FrameNavigationEntries by default.
2153 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2156 // Add a second subframe and navigate.
2157 main_test_rfh()->OnCreateChildFrame(
2158 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2159 blink::WebSandboxFlags::None
);
2160 RenderFrameHostImpl
* subframe2
=
2161 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2162 const GURL
url3("http://foo/3");
2164 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2166 params
.nav_entry_id
= 0;
2167 params
.did_create_new_entry
= false;
2169 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2170 params
.should_update_history
= false;
2171 params
.gesture
= NavigationGestureUser
;
2172 params
.is_post
= false;
2173 params
.page_state
= PageState::CreateFromURL(url3
);
2175 // Navigating should do nothing.
2176 LoadCommittedDetails details
;
2177 EXPECT_FALSE(controller
.RendererDidNavigate(subframe2
, params
, &details
));
2178 EXPECT_EQ(0U, notifications
.size());
2181 // There should still be only one entry, mostly unchanged.
2182 EXPECT_EQ(1, controller
.GetEntryCount());
2183 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2184 EXPECT_EQ(url1
, entry
->GetURL());
2185 EXPECT_EQ(1, entry
->GetPageID());
2186 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2187 EXPECT_EQ(url1
, root_entry
->url());
2189 // Verify subframe entries if we're in --site-per-process mode.
2190 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2191 switches::kSitePerProcess
)) {
2192 // The entry should now have 2 subframe FrameNavigationEntries.
2193 ASSERT_EQ(2U, entry
->root_node()->children
.size());
2194 FrameNavigationEntry
* new_frame_entry
=
2195 entry
->root_node()->children
[1]->frame_entry
.get();
2196 EXPECT_EQ(url3
, new_frame_entry
->url());
2198 // There are no subframe FrameNavigationEntries by default.
2199 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2202 // Add a nested subframe and navigate.
2203 subframe
->OnCreateChildFrame(MSG_ROUTING_NONE
,
2204 blink::WebTreeScopeType::Document
, std::string(),
2205 blink::WebSandboxFlags::None
);
2206 RenderFrameHostImpl
* subframe3
= contents()
2211 ->current_frame_host();
2212 const GURL
url4("http://foo/4");
2214 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2216 params
.nav_entry_id
= 0;
2217 params
.did_create_new_entry
= false;
2219 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2220 params
.should_update_history
= false;
2221 params
.gesture
= NavigationGestureUser
;
2222 params
.is_post
= false;
2223 params
.page_state
= PageState::CreateFromURL(url4
);
2225 // Navigating should do nothing.
2226 LoadCommittedDetails details
;
2227 EXPECT_FALSE(controller
.RendererDidNavigate(subframe3
, params
, &details
));
2228 EXPECT_EQ(0U, notifications
.size());
2231 // There should still be only one entry, mostly unchanged.
2232 EXPECT_EQ(1, controller
.GetEntryCount());
2233 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2234 EXPECT_EQ(url1
, entry
->GetURL());
2235 EXPECT_EQ(1, entry
->GetPageID());
2236 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2237 EXPECT_EQ(url1
, root_entry
->url());
2239 // Verify subframe entries if we're in --site-per-process mode.
2240 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2241 switches::kSitePerProcess
)) {
2242 // The entry should now have a nested FrameNavigationEntry.
2243 EXPECT_EQ(2U, entry
->root_node()->children
.size());
2244 ASSERT_EQ(1U, entry
->root_node()->children
[0]->children
.size());
2245 FrameNavigationEntry
* new_frame_entry
=
2246 entry
->root_node()->children
[0]->children
[0]->frame_entry
.get();
2247 EXPECT_EQ(url4
, new_frame_entry
->url());
2249 // There are no subframe FrameNavigationEntries by default.
2250 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2254 // Tests navigation and then going back to a subframe navigation.
2255 TEST_F(NavigationControllerTest
, BackSubframe
) {
2256 NavigationControllerImpl
& controller
= controller_impl();
2257 TestNotificationTracker notifications
;
2258 RegisterForAllNavNotifications(¬ifications
, &controller
);
2261 const GURL
url1("http://foo1");
2262 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2263 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2264 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2265 navigation_entry_committed_counter_
= 0;
2267 // Prereq: add a subframe with an initial auto-subframe navigation.
2268 main_test_rfh()->OnCreateChildFrame(
2269 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2270 blink::WebSandboxFlags::None
);
2271 RenderFrameHostImpl
* subframe
=
2272 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2273 const GURL
subframe_url("http://foo1/subframe");
2275 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2277 params
.nav_entry_id
= 0;
2278 params
.did_create_new_entry
= false;
2279 params
.url
= subframe_url
;
2280 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2281 params
.should_update_history
= false;
2282 params
.gesture
= NavigationGestureUser
;
2283 params
.is_post
= false;
2284 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2286 // Navigating should do nothing.
2287 LoadCommittedDetails details
;
2288 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2289 EXPECT_EQ(0U, notifications
.size());
2292 // First manual subframe navigation.
2293 const GURL
url2("http://foo2");
2294 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2296 params
.nav_entry_id
= 0;
2297 params
.did_create_new_entry
= true;
2299 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2300 params
.should_update_history
= false;
2301 params
.gesture
= NavigationGestureUser
;
2302 params
.is_post
= false;
2303 params
.page_state
= PageState::CreateFromURL(url2
);
2305 // This should generate a new entry.
2306 LoadCommittedDetails details
;
2307 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2308 NavigationEntryImpl
* entry2
= controller
.GetLastCommittedEntry();
2309 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2310 navigation_entry_committed_counter_
= 0;
2311 EXPECT_EQ(2, controller
.GetEntryCount());
2313 // Verify subframe entries if we're in --site-per-process mode.
2314 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2315 switches::kSitePerProcess
)) {
2316 // The entry should have a subframe FrameNavigationEntry.
2317 ASSERT_EQ(1U, entry2
->root_node()->children
.size());
2318 EXPECT_EQ(url2
, entry2
->root_node()->children
[0]->frame_entry
->url());
2320 // There are no subframe FrameNavigationEntries by default.
2321 EXPECT_EQ(0U, entry2
->root_node()->children
.size());
2324 // Second manual subframe navigation should also make a new entry.
2325 const GURL
url3("http://foo3");
2327 params
.nav_entry_id
= 0;
2328 params
.did_create_new_entry
= true;
2330 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2331 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2332 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2333 navigation_entry_committed_counter_
= 0;
2334 NavigationEntryImpl
* entry3
= controller
.GetLastCommittedEntry();
2335 EXPECT_EQ(3, controller
.GetEntryCount());
2336 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2338 // Verify subframe entries if we're in --site-per-process mode.
2339 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2340 switches::kSitePerProcess
)) {
2341 // The entry should have a subframe FrameNavigationEntry.
2342 ASSERT_EQ(1U, entry3
->root_node()->children
.size());
2343 EXPECT_EQ(url3
, entry3
->root_node()->children
[0]->frame_entry
->url());
2345 // There are no subframe FrameNavigationEntries by default.
2346 EXPECT_EQ(0U, entry3
->root_node()->children
.size());
2350 controller
.GoBack();
2352 params
.nav_entry_id
= entry2
->GetUniqueID();
2353 params
.did_create_new_entry
= false;
2355 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2356 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2357 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2358 navigation_entry_committed_counter_
= 0;
2359 EXPECT_EQ(entry2
, controller
.GetLastCommittedEntry());
2360 EXPECT_EQ(3, controller
.GetEntryCount());
2361 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2362 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2363 EXPECT_FALSE(controller
.GetPendingEntry());
2365 // Go back one more.
2366 controller
.GoBack();
2368 params
.nav_entry_id
= entry1
->GetUniqueID();
2369 params
.did_create_new_entry
= false;
2371 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2372 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2373 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2374 navigation_entry_committed_counter_
= 0;
2375 EXPECT_EQ(entry1
, controller
.GetLastCommittedEntry());
2376 EXPECT_EQ(3, controller
.GetEntryCount());
2377 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2378 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2379 EXPECT_FALSE(controller
.GetPendingEntry());
2382 TEST_F(NavigationControllerTest
, LinkClick
) {
2383 NavigationControllerImpl
& controller
= controller_impl();
2384 TestNotificationTracker notifications
;
2385 RegisterForAllNavNotifications(¬ifications
, &controller
);
2387 const GURL
url1("http://foo1");
2388 const GURL
url2("http://foo2");
2390 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2391 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2392 navigation_entry_committed_counter_
= 0;
2394 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
2395 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2396 navigation_entry_committed_counter_
= 0;
2398 // Should have produced a new session history entry.
2399 EXPECT_EQ(controller
.GetEntryCount(), 2);
2400 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2401 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2402 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2403 EXPECT_FALSE(controller
.GetPendingEntry());
2404 EXPECT_TRUE(controller
.CanGoBack());
2405 EXPECT_FALSE(controller
.CanGoForward());
2408 TEST_F(NavigationControllerTest
, InPage
) {
2409 NavigationControllerImpl
& controller
= controller_impl();
2410 TestNotificationTracker notifications
;
2411 RegisterForAllNavNotifications(¬ifications
, &controller
);
2414 const GURL
url1("http://foo");
2415 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2416 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2417 navigation_entry_committed_counter_
= 0;
2419 // Ensure main page navigation to same url respects the was_within_same_page
2420 // hint provided in the params.
2421 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2422 self_params
.page_id
= 0;
2423 self_params
.nav_entry_id
= 0;
2424 self_params
.did_create_new_entry
= false;
2425 self_params
.url
= url1
;
2426 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2427 self_params
.should_update_history
= false;
2428 self_params
.gesture
= NavigationGestureUser
;
2429 self_params
.is_post
= false;
2430 self_params
.page_state
= PageState::CreateFromURL(url1
);
2431 self_params
.was_within_same_page
= true;
2433 LoadCommittedDetails details
;
2434 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2436 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2437 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2438 navigation_entry_committed_counter_
= 0;
2439 EXPECT_TRUE(details
.is_in_page
);
2440 EXPECT_TRUE(details
.did_replace_entry
);
2441 EXPECT_EQ(1, controller
.GetEntryCount());
2443 // Fragment navigation to a new page_id.
2444 const GURL
url2("http://foo#a");
2445 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2447 params
.nav_entry_id
= 0;
2448 params
.did_create_new_entry
= true;
2450 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2451 params
.should_update_history
= false;
2452 params
.gesture
= NavigationGestureUser
;
2453 params
.is_post
= false;
2454 params
.page_state
= PageState::CreateFromURL(url2
);
2455 params
.was_within_same_page
= true;
2457 // This should generate a new entry.
2458 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2460 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2461 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2462 navigation_entry_committed_counter_
= 0;
2463 EXPECT_TRUE(details
.is_in_page
);
2464 EXPECT_FALSE(details
.did_replace_entry
);
2465 EXPECT_EQ(2, controller
.GetEntryCount());
2468 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2469 controller
.GoBack();
2470 back_params
.url
= url1
;
2471 back_params
.page_id
= 0;
2472 back_params
.nav_entry_id
= entry1
->GetUniqueID();
2473 back_params
.did_create_new_entry
= false;
2474 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2476 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2477 navigation_entry_committed_counter_
= 0;
2478 EXPECT_TRUE(details
.is_in_page
);
2479 EXPECT_EQ(2, controller
.GetEntryCount());
2480 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2481 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2484 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2485 controller
.GoForward();
2486 forward_params
.url
= url2
;
2487 forward_params
.page_id
= 1;
2488 forward_params
.nav_entry_id
= entry2
->GetUniqueID();
2489 forward_params
.did_create_new_entry
= false;
2490 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2492 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2493 navigation_entry_committed_counter_
= 0;
2494 EXPECT_TRUE(details
.is_in_page
);
2495 EXPECT_EQ(2, controller
.GetEntryCount());
2496 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2497 EXPECT_EQ(forward_params
.url
,
2498 controller
.GetVisibleEntry()->GetURL());
2500 // Now go back and forward again. This is to work around a bug where we would
2501 // compare the incoming URL with the last committed entry rather than the
2502 // one identified by an existing page ID. This would result in the second URL
2503 // losing the reference fragment when you navigate away from it and then back.
2504 controller
.GoBack();
2505 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2507 controller
.GoForward();
2508 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2510 EXPECT_EQ(forward_params
.url
,
2511 controller
.GetVisibleEntry()->GetURL());
2513 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2514 const GURL
url3("http://bar");
2516 params
.nav_entry_id
= 0;
2517 params
.did_create_new_entry
= true;
2519 navigation_entry_committed_counter_
= 0;
2520 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2522 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2523 navigation_entry_committed_counter_
= 0;
2524 EXPECT_FALSE(details
.is_in_page
);
2525 EXPECT_EQ(3, controller
.GetEntryCount());
2526 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2529 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2530 NavigationControllerImpl
& controller
= controller_impl();
2531 TestNotificationTracker notifications
;
2532 RegisterForAllNavNotifications(¬ifications
, &controller
);
2535 const GURL
url1("http://foo");
2536 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2537 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2538 navigation_entry_committed_counter_
= 0;
2540 // First navigation.
2541 const GURL
url2("http://foo#a");
2542 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2543 params
.page_id
= 0; // Same page_id
2544 params
.nav_entry_id
= 0;
2545 params
.did_create_new_entry
= false;
2547 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2548 params
.should_update_history
= false;
2549 params
.gesture
= NavigationGestureUser
;
2550 params
.is_post
= false;
2551 params
.page_state
= PageState::CreateFromURL(url2
);
2552 params
.was_within_same_page
= true;
2554 // This should NOT generate a new entry, nor prune the list.
2555 LoadCommittedDetails details
;
2556 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2558 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2559 navigation_entry_committed_counter_
= 0;
2560 EXPECT_TRUE(details
.is_in_page
);
2561 EXPECT_TRUE(details
.did_replace_entry
);
2562 EXPECT_EQ(1, controller
.GetEntryCount());
2565 // Tests for http://crbug.com/40395
2568 // window.location.replace("#a");
2569 // window.location='http://foo3/';
2571 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2572 NavigationControllerImpl
& controller
= controller_impl();
2573 TestNotificationTracker notifications
;
2574 RegisterForAllNavNotifications(¬ifications
, &controller
);
2576 // Load an initial page.
2578 const GURL
url("http://foo/");
2579 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
2580 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2581 navigation_entry_committed_counter_
= 0;
2584 // Navigate to a new page.
2586 const GURL
url("http://foo2/");
2587 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url
);
2588 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2589 navigation_entry_committed_counter_
= 0;
2592 // Navigate within the page.
2594 const GURL
url("http://foo2/#a");
2595 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2596 params
.page_id
= 1; // Same page_id
2597 params
.nav_entry_id
= 0;
2598 params
.did_create_new_entry
= false;
2600 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2601 params
.redirects
.push_back(url
);
2602 params
.should_update_history
= true;
2603 params
.gesture
= NavigationGestureUnknown
;
2604 params
.is_post
= false;
2605 params
.page_state
= PageState::CreateFromURL(url
);
2606 params
.was_within_same_page
= true;
2608 // This should NOT generate a new entry, nor prune the list.
2609 LoadCommittedDetails details
;
2610 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2612 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2613 navigation_entry_committed_counter_
= 0;
2614 EXPECT_TRUE(details
.is_in_page
);
2615 EXPECT_TRUE(details
.did_replace_entry
);
2616 EXPECT_EQ(2, controller
.GetEntryCount());
2619 // Perform a client redirect to a new page.
2621 const GURL
url("http://foo3/");
2622 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2623 params
.page_id
= 2; // New page_id
2624 params
.nav_entry_id
= 0;
2625 params
.did_create_new_entry
= true;
2627 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2628 params
.redirects
.push_back(GURL("http://foo2/#a"));
2629 params
.redirects
.push_back(url
);
2630 params
.should_update_history
= true;
2631 params
.gesture
= NavigationGestureUnknown
;
2632 params
.is_post
= false;
2633 params
.page_state
= PageState::CreateFromURL(url
);
2635 // This SHOULD generate a new entry.
2636 LoadCommittedDetails details
;
2637 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2639 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2640 navigation_entry_committed_counter_
= 0;
2641 EXPECT_FALSE(details
.is_in_page
);
2642 EXPECT_EQ(3, controller
.GetEntryCount());
2645 // Verify that BACK brings us back to http://foo2/.
2647 const GURL
url("http://foo2/");
2648 controller
.GoBack();
2649 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2650 main_test_rfh()->PrepareForCommit();
2651 main_test_rfh()->SendNavigate(1, entry_id
, false, url
);
2652 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2653 navigation_entry_committed_counter_
= 0;
2654 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2658 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2660 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2661 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2662 GURL
url("http://foo");
2664 params
.nav_entry_id
= 0;
2665 params
.did_create_new_entry
= true;
2667 params
.page_state
= PageState::CreateFromURL(url
);
2668 params
.was_within_same_page
= true;
2669 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
2670 main_test_rfh()->PrepareForCommit();
2671 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2672 // We pass if we don't crash.
2675 // NotificationObserver implementation used in verifying we've received the
2676 // NOTIFICATION_NAV_LIST_PRUNED method.
2677 class PrunedListener
: public NotificationObserver
{
2679 explicit PrunedListener(NavigationControllerImpl
* controller
)
2680 : notification_count_(0) {
2681 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2682 Source
<NavigationController
>(controller
));
2685 void Observe(int type
,
2686 const NotificationSource
& source
,
2687 const NotificationDetails
& details
) override
{
2688 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2689 notification_count_
++;
2690 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2694 // Number of times NAV_LIST_PRUNED has been observed.
2695 int notification_count_
;
2697 // Details from the last NAV_LIST_PRUNED.
2698 PrunedDetails details_
;
2701 NotificationRegistrar registrar_
;
2703 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2706 // Tests that we limit the number of navigation entries created correctly.
2707 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2708 NavigationControllerImpl
& controller
= controller_impl();
2709 size_t original_count
= NavigationControllerImpl::max_entry_count();
2710 const int kMaxEntryCount
= 5;
2712 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2715 // Load up to the max count, all entries should be there.
2716 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2717 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2719 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2720 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2721 main_test_rfh()->PrepareForCommit();
2722 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2725 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2727 // Created a PrunedListener to observe prune notifications.
2728 PrunedListener
listener(&controller
);
2730 // Navigate some more.
2731 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2733 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2734 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2735 main_test_rfh()->PrepareForCommit();
2736 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2739 // We should have got a pruned navigation.
2740 EXPECT_EQ(1, listener
.notification_count_
);
2741 EXPECT_TRUE(listener
.details_
.from_front
);
2742 EXPECT_EQ(1, listener
.details_
.count
);
2744 // We expect http://www.a.com/0 to be gone.
2745 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2746 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2747 GURL("http://www.a.com/1"));
2749 // More navigations.
2750 for (int i
= 0; i
< 3; i
++) {
2751 url
= GURL(base::StringPrintf("http://www.a.com/%d", url_index
));
2753 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2754 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2755 main_test_rfh()->PrepareForCommit();
2756 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2759 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2760 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2761 GURL("http://www.a.com/4"));
2763 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2766 // Tests that we can do a restore and navigate to the restored entries and
2767 // everything is updated properly. This can be tricky since there is no
2768 // SiteInstance for the entries created initially.
2769 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2770 // Create a NavigationController with a restored set of tabs.
2771 GURL
url("http://foo");
2772 ScopedVector
<NavigationEntry
> entries
;
2773 scoped_ptr
<NavigationEntry
> entry
=
2774 NavigationControllerImpl::CreateNavigationEntry(
2775 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2777 entry
->SetPageID(0);
2778 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2779 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2780 const base::Time timestamp
= base::Time::Now();
2781 entry
->SetTimestamp(timestamp
);
2782 entries
.push_back(entry
.Pass());
2783 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2784 WebContents::Create(WebContents::CreateParams(browser_context()))));
2785 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2786 our_controller
.Restore(
2788 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2790 ASSERT_EQ(0u, entries
.size());
2792 // Before navigating to the restored entry, it should have a restore_type
2793 // and no SiteInstance.
2794 ASSERT_EQ(1, our_controller
.GetEntryCount());
2795 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2796 our_controller
.GetEntryAtIndex(0)->restore_type());
2797 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2799 // After navigating, we should have one entry, and it should be "pending".
2800 // It should now have a SiteInstance and no restore_type.
2801 our_controller
.GoToIndex(0);
2802 EXPECT_EQ(1, our_controller
.GetEntryCount());
2803 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2804 our_controller
.GetPendingEntry());
2805 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2806 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2807 our_controller
.GetEntryAtIndex(0)->restore_type());
2808 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2810 // Timestamp should remain the same before the navigation finishes.
2811 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2813 // Say we navigated to that entry.
2814 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2816 params
.nav_entry_id
= our_controller
.GetPendingEntry()->GetUniqueID();
2817 params
.did_create_new_entry
= false;
2819 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2820 params
.should_update_history
= false;
2821 params
.gesture
= NavigationGestureUser
;
2822 params
.is_post
= false;
2823 params
.page_state
= PageState::CreateFromURL(url
);
2824 LoadCommittedDetails details
;
2825 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2828 // There should be no longer any pending entry and one committed one. This
2829 // means that we were able to locate the entry, assign its site instance, and
2830 // commit it properly.
2831 EXPECT_EQ(1, our_controller
.GetEntryCount());
2832 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2833 EXPECT_FALSE(our_controller
.GetPendingEntry());
2836 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2837 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2838 our_controller
.GetEntryAtIndex(0)->restore_type());
2840 // Timestamp should have been updated.
2841 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2844 // Tests that we can still navigate to a restored entry after a different
2845 // navigation fails and clears the pending entry. http://crbug.com/90085
2846 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2847 // Create a NavigationController with a restored set of tabs.
2848 GURL
url("http://foo");
2849 ScopedVector
<NavigationEntry
> entries
;
2850 scoped_ptr
<NavigationEntry
> new_entry
=
2851 NavigationControllerImpl::CreateNavigationEntry(
2852 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2854 new_entry
->SetPageID(0);
2855 new_entry
->SetTitle(base::ASCIIToUTF16("Title"));
2856 new_entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2857 entries
.push_back(new_entry
.Pass());
2858 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2859 WebContents::Create(WebContents::CreateParams(browser_context()))));
2860 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2861 our_controller
.Restore(
2862 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2863 ASSERT_EQ(0u, entries
.size());
2865 // Ensure the RenderFrame is initialized before simulating events coming from
2867 main_test_rfh()->InitializeRenderFrameIfNeeded();
2869 // Before navigating to the restored entry, it should have a restore_type
2870 // and no SiteInstance.
2871 NavigationEntry
* entry
= our_controller
.GetEntryAtIndex(0);
2872 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2873 our_controller
.GetEntryAtIndex(0)->restore_type());
2874 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2876 // After navigating, we should have one entry, and it should be "pending".
2877 // It should now have a SiteInstance and no restore_type.
2878 our_controller
.GoToIndex(0);
2879 EXPECT_EQ(1, our_controller
.GetEntryCount());
2880 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2881 our_controller
.GetPendingEntry());
2882 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2883 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2884 our_controller
.GetEntryAtIndex(0)->restore_type());
2885 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2887 // This pending navigation may have caused a different navigation to fail,
2888 // which causes the pending entry to be cleared.
2889 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2890 fail_load_params
.error_code
= net::ERR_ABORTED
;
2891 fail_load_params
.error_description
= base::string16();
2892 fail_load_params
.url
= url
;
2893 fail_load_params
.showing_repost_interstitial
= false;
2894 main_test_rfh()->InitializeRenderFrameIfNeeded();
2895 main_test_rfh()->OnMessageReceived(
2896 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2899 // Now the pending restored entry commits.
2900 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2902 params
.nav_entry_id
= entry
->GetUniqueID();
2903 params
.did_create_new_entry
= false;
2905 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2906 params
.should_update_history
= false;
2907 params
.gesture
= NavigationGestureUser
;
2908 params
.is_post
= false;
2909 params
.page_state
= PageState::CreateFromURL(url
);
2910 LoadCommittedDetails details
;
2911 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2914 // There should be no pending entry and one committed one.
2915 EXPECT_EQ(1, our_controller
.GetEntryCount());
2916 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2917 EXPECT_FALSE(our_controller
.GetPendingEntry());
2920 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2921 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2922 our_controller
.GetEntryAtIndex(0)->restore_type());
2925 // Make sure that the page type and stuff is correct after an interstitial.
2926 TEST_F(NavigationControllerTest
, Interstitial
) {
2927 NavigationControllerImpl
& controller
= controller_impl();
2928 // First navigate somewhere normal.
2929 const GURL
url1("http://foo");
2931 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2932 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2933 main_test_rfh()->PrepareForCommit();
2934 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2936 // Now navigate somewhere with an interstitial.
2937 const GURL
url2("http://bar");
2938 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
,
2940 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2941 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2943 // At this point the interstitial will be displayed and the load will still
2944 // be pending. If the user continues, the load will commit.
2945 main_test_rfh()->PrepareForCommit();
2946 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2948 // The page should be a normal page again.
2949 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2950 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2951 controller
.GetLastCommittedEntry()->GetPageType());
2954 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2955 NavigationControllerImpl
& controller
= controller_impl();
2956 const GURL
url1("http://foo/1");
2957 const GURL
url2("http://foo/2");
2958 const GURL
url3("http://foo/3");
2959 const GURL
url4("http://foo/4");
2960 const GURL
url5("http://foo/5");
2961 const GURL
pending_url("http://foo/pending");
2962 const GURL
default_url("http://foo/default");
2965 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2966 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2967 main_test_rfh()->PrepareForCommit();
2968 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2970 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2971 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2972 main_test_rfh()->PrepareForCommit();
2973 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2975 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2976 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2977 main_test_rfh()->PrepareForCommit();
2978 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2980 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2981 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2982 main_test_rfh()->PrepareForCommit();
2983 main_test_rfh()->SendNavigate(3, entry_id
, true, url4
);
2985 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2986 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2987 main_test_rfh()->PrepareForCommit();
2988 main_test_rfh()->SendNavigate(4, entry_id
, true, url5
);
2990 // Try to remove the last entry. Will fail because it is the current entry.
2991 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2992 EXPECT_EQ(5, controller
.GetEntryCount());
2993 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2995 // Go back, but don't commit yet. Check that we can't delete the current
2996 // and pending entries.
2997 controller
.GoBack();
2998 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2999 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
3000 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
3002 // Now commit and delete the last entry.
3003 main_test_rfh()->PrepareForCommit();
3004 main_test_rfh()->SendNavigate(3, entry_id
, false, url4
);
3005 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
3006 EXPECT_EQ(4, controller
.GetEntryCount());
3007 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
3008 EXPECT_FALSE(controller
.GetPendingEntry());
3010 // Remove an entry which is not the last committed one.
3011 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3012 EXPECT_EQ(3, controller
.GetEntryCount());
3013 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
3014 EXPECT_FALSE(controller
.GetPendingEntry());
3016 // Remove the 2 remaining entries.
3017 controller
.RemoveEntryAtIndex(1);
3018 controller
.RemoveEntryAtIndex(0);
3020 // This should leave us with only the last committed entry.
3021 EXPECT_EQ(1, controller
.GetEntryCount());
3022 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3025 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
3026 NavigationControllerImpl
& controller
= controller_impl();
3027 const GURL
url1("http://foo/1");
3028 const GURL
url2("http://foo/2");
3029 const GURL
url3("http://foo/3");
3030 const GURL
default_url("http://foo/default");
3033 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3034 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3035 main_test_rfh()->PrepareForCommit();
3036 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3038 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3039 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3040 main_test_rfh()->PrepareForCommit();
3041 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
3043 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3044 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3045 main_test_rfh()->PrepareForCommit();
3046 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
3048 // Go back, but don't commit yet. Check that we can't delete the current
3049 // and pending entries.
3050 controller
.GoBack();
3051 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3052 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
3053 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
3055 // Remove the first entry, while there is a pending entry. This is expected
3056 // to discard the pending entry.
3057 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3058 EXPECT_FALSE(controller
.GetPendingEntry());
3059 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
3061 // We should update the last committed entry index.
3062 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
3064 // Now commit and ensure we land on the right entry.
3065 main_test_rfh()->PrepareForCommit();
3066 main_test_rfh()->SendNavigate(1, entry_id
, false, url2
);
3067 EXPECT_EQ(2, controller
.GetEntryCount());
3068 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3069 EXPECT_FALSE(controller
.GetPendingEntry());
3072 // Tests the transient entry, making sure it goes away with all navigations.
3073 TEST_F(NavigationControllerTest
, TransientEntry
) {
3074 NavigationControllerImpl
& controller
= controller_impl();
3075 TestNotificationTracker notifications
;
3076 RegisterForAllNavNotifications(¬ifications
, &controller
);
3078 const GURL
url0("http://foo/0");
3079 const GURL
url1("http://foo/1");
3080 const GURL
url2("http://foo/2");
3081 const GURL
url3("http://foo/3");
3082 const GURL
url3_ref("http://foo/3#bar");
3083 const GURL
url4("http://foo/4");
3084 const GURL
transient_url("http://foo/transient");
3087 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3088 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3089 main_test_rfh()->PrepareForCommit();
3090 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3092 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3093 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3094 main_test_rfh()->PrepareForCommit();
3095 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3097 notifications
.Reset();
3099 // Adding a transient with no pending entry.
3100 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3101 transient_entry
->SetURL(transient_url
);
3102 controller
.SetTransientEntry(transient_entry
.Pass());
3104 // We should not have received any notifications.
3105 EXPECT_EQ(0U, notifications
.size());
3108 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3109 EXPECT_EQ(controller
.GetEntryCount(), 3);
3110 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
3111 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
3112 EXPECT_TRUE(controller
.GetLastCommittedEntry());
3113 EXPECT_FALSE(controller
.GetPendingEntry());
3114 EXPECT_TRUE(controller
.CanGoBack());
3115 EXPECT_FALSE(controller
.CanGoForward());
3116 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3120 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3121 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3122 main_test_rfh()->PrepareForCommit();
3123 main_test_rfh()->SendNavigate(2, entry_id
, true, url2
);
3125 // We should have navigated, transient entry should be gone.
3126 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3127 EXPECT_EQ(controller
.GetEntryCount(), 3);
3129 // Add a transient again, then navigate with no pending entry this time.
3130 transient_entry
.reset(new NavigationEntryImpl
);
3131 transient_entry
->SetURL(transient_url
);
3132 controller
.SetTransientEntry(transient_entry
.Pass());
3133 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3134 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3
, true);
3135 main_test_rfh()->PrepareForCommit();
3136 main_test_rfh()->SendNavigate(3, 0, true, url3
);
3137 // Transient entry should be gone.
3138 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3139 EXPECT_EQ(controller
.GetEntryCount(), 4);
3141 // Initiate a navigation, add a transient then commit navigation.
3143 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3144 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3145 transient_entry
.reset(new NavigationEntryImpl
);
3146 transient_entry
->SetURL(transient_url
);
3147 controller
.SetTransientEntry(transient_entry
.Pass());
3148 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3149 main_test_rfh()->PrepareForCommit();
3150 main_test_rfh()->SendNavigate(4, entry_id
, true, url4
);
3151 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3152 EXPECT_EQ(controller
.GetEntryCount(), 5);
3154 // Add a transient and go back. This should simply remove the transient.
3155 transient_entry
.reset(new NavigationEntryImpl
);
3156 transient_entry
->SetURL(transient_url
);
3157 controller
.SetTransientEntry(transient_entry
.Pass());
3158 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3159 EXPECT_TRUE(controller
.CanGoBack());
3160 EXPECT_FALSE(controller
.CanGoForward());
3161 controller
.GoBack();
3162 // Transient entry should be gone.
3163 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3164 EXPECT_EQ(controller
.GetEntryCount(), 5);
3166 // Suppose the page requested a history navigation backward.
3167 controller
.GoToOffset(-1);
3168 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3169 main_test_rfh()->PrepareForCommit();
3170 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3172 // Add a transient and go to an entry before the current one.
3173 transient_entry
.reset(new NavigationEntryImpl
);
3174 transient_entry
->SetURL(transient_url
);
3175 controller
.SetTransientEntry(transient_entry
.Pass());
3176 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3177 controller
.GoToIndex(1);
3178 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3179 // The navigation should have been initiated, transient entry should be gone.
3180 EXPECT_FALSE(controller
.GetTransientEntry());
3181 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3182 // Visible entry does not update for history navigations until commit.
3183 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3184 main_test_rfh()->PrepareForCommit();
3185 main_test_rfh()->SendNavigate(1, entry_id
, false, url1
);
3186 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3188 // Add a transient and go to an entry after the current one.
3189 transient_entry
.reset(new NavigationEntryImpl
);
3190 transient_entry
->SetURL(transient_url
);
3191 controller
.SetTransientEntry(transient_entry
.Pass());
3192 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3193 controller
.GoToIndex(3);
3194 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3195 // The navigation should have been initiated, transient entry should be gone.
3196 // Because of the transient entry that is removed, going to index 3 makes us
3197 // land on url2 (which is visible after the commit).
3198 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3199 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3200 main_test_rfh()->PrepareForCommit();
3201 main_test_rfh()->SendNavigate(2, entry_id
, false, url2
);
3202 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3204 // Add a transient and go forward.
3205 transient_entry
.reset(new NavigationEntryImpl
);
3206 transient_entry
->SetURL(transient_url
);
3207 controller
.SetTransientEntry(transient_entry
.Pass());
3208 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3209 EXPECT_TRUE(controller
.CanGoForward());
3210 controller
.GoForward();
3211 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3212 // We should have navigated, transient entry should be gone.
3213 EXPECT_FALSE(controller
.GetTransientEntry());
3214 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
3215 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3216 main_test_rfh()->PrepareForCommit();
3217 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3218 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3220 // Add a transient and do an in-page navigation, replacing the current entry.
3221 transient_entry
.reset(new NavigationEntryImpl
);
3222 transient_entry
->SetURL(transient_url
);
3223 controller
.SetTransientEntry(transient_entry
.Pass());
3224 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3226 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref
, false);
3227 main_test_rfh()->PrepareForCommit();
3228 main_test_rfh()->SendNavigate(3, 0, false, url3_ref
);
3229 // Transient entry should be gone.
3230 EXPECT_FALSE(controller
.GetTransientEntry());
3231 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
3233 // Ensure the URLs are correct.
3234 EXPECT_EQ(controller
.GetEntryCount(), 5);
3235 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3236 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
3237 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
3238 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
3239 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
3242 // Test that Reload initiates a new navigation to a transient entry's URL.
3243 TEST_F(NavigationControllerTest
, ReloadTransient
) {
3244 NavigationControllerImpl
& controller
= controller_impl();
3245 const GURL
url0("http://foo/0");
3246 const GURL
url1("http://foo/1");
3247 const GURL
transient_url("http://foo/transient");
3249 // Load |url0|, and start a pending navigation to |url1|.
3251 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3252 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3253 main_test_rfh()->PrepareForCommit();
3254 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3256 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3258 // A transient entry is added, interrupting the navigation.
3259 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3260 transient_entry
->SetURL(transient_url
);
3261 controller
.SetTransientEntry(transient_entry
.Pass());
3262 EXPECT_TRUE(controller
.GetTransientEntry());
3263 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3265 // The page is reloaded, which should remove the pending entry for |url1| and
3266 // the transient entry for |transient_url|, and start a navigation to
3268 controller
.Reload(true);
3269 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3270 EXPECT_FALSE(controller
.GetTransientEntry());
3271 EXPECT_TRUE(controller
.GetPendingEntry());
3272 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3273 ASSERT_EQ(controller
.GetEntryCount(), 1);
3274 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3276 // Load of |transient_url| completes.
3277 main_test_rfh()->PrepareForCommit();
3278 main_test_rfh()->SendNavigate(1, entry_id
, true, transient_url
);
3279 ASSERT_EQ(controller
.GetEntryCount(), 2);
3280 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3281 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
3284 // Ensure that renderer initiated pending entries get replaced, so that we
3285 // don't show a stale virtual URL when a navigation commits.
3286 // See http://crbug.com/266922.
3287 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
3288 NavigationControllerImpl
& controller
= controller_impl();
3289 Navigator
* navigator
=
3290 contents()->GetFrameTree()->root()->navigator();
3292 const GURL
url1("nonexistent:12121");
3293 const GURL
url1_fixed("http://nonexistent:12121/");
3294 const GURL
url2("http://foo");
3296 // We create pending entries for renderer-initiated navigations so that we
3297 // can show them in new tabs when it is safe.
3298 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, false);
3299 main_test_rfh()->PrepareForCommit();
3300 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3302 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3303 // the virtual URL to differ from the URL.
3304 controller
.GetPendingEntry()->SetURL(url1_fixed
);
3305 controller
.GetPendingEntry()->SetVirtualURL(url1
);
3307 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
3308 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
3309 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3311 // If the user clicks another link, we should replace the pending entry.
3312 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3313 main_test_rfh()->PrepareForCommit();
3314 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3315 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3316 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
3318 // Once it commits, the URL and virtual URL should reflect the actual page.
3319 main_test_rfh()->SendNavigate(0, 0, true, url2
);
3320 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3321 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
3323 // We should not replace the pending entry for an error URL.
3324 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3325 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3326 navigator
->DidStartProvisionalLoad(main_test_rfh(),
3327 GURL(kUnreachableWebDataURL
));
3328 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3330 // We should remember if the pending entry will replace the current one.
3331 // http://crbug.com/308444.
3332 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3333 controller
.GetPendingEntry()->set_should_replace_entry(true);
3335 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3336 main_test_rfh()->PrepareForCommit();
3337 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3338 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
3339 main_test_rfh()->SendNavigate(0, 0, false, url2
);
3340 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3343 // Tests that the URLs for renderer-initiated navigations are not displayed to
3344 // the user until the navigation commits, to prevent URL spoof attacks.
3345 // See http://crbug.com/99016.
3346 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
3347 NavigationControllerImpl
& controller
= controller_impl();
3348 TestNotificationTracker notifications
;
3349 RegisterForAllNavNotifications(¬ifications
, &controller
);
3351 const GURL
url0("http://foo/0");
3352 const GURL
url1("http://foo/1");
3354 // For typed navigations (browser-initiated), both pending and visible entries
3355 // should update before commit.
3357 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3358 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3359 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
3360 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3361 main_test_rfh()->PrepareForCommit();
3362 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3364 // For link clicks (renderer-initiated navigations), the pending entry should
3365 // update before commit but the visible should not.
3366 NavigationController::LoadURLParams
load_url_params(url1
);
3367 load_url_params
.is_renderer_initiated
= true;
3368 controller
.LoadURLWithParams(load_url_params
);
3369 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3370 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3371 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3372 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3374 // After commit, both visible should be updated, there should be no pending
3375 // entry, and we should no longer treat the entry as renderer-initiated.
3376 main_test_rfh()->PrepareForCommit();
3377 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3378 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3379 EXPECT_FALSE(controller
.GetPendingEntry());
3380 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3382 notifications
.Reset();
3385 // Tests that the URLs for renderer-initiated navigations in new tabs are
3386 // displayed to the user before commit, as long as the initial about:blank
3387 // page has not been modified. If so, we must revert to showing about:blank.
3388 // See http://crbug.com/9682.
3389 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3390 NavigationControllerImpl
& controller
= controller_impl();
3391 TestNotificationTracker notifications
;
3392 RegisterForAllNavNotifications(¬ifications
, &controller
);
3394 const GURL
url("http://foo");
3396 // For renderer-initiated navigations in new tabs (with no committed entries),
3397 // we show the pending entry's URL as long as the about:blank page is not
3399 NavigationController::LoadURLParams
load_url_params(url
);
3400 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3401 load_url_params
.is_renderer_initiated
= true;
3402 controller
.LoadURLWithParams(load_url_params
);
3403 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3404 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3405 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3406 EXPECT_TRUE(controller
.IsInitialNavigation());
3407 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3409 // There should be no title yet.
3410 EXPECT_TRUE(contents()->GetTitle().empty());
3412 // If something else modifies the contents of the about:blank page, then
3413 // we must revert to showing about:blank to avoid a URL spoof.
3414 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3415 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3416 EXPECT_FALSE(controller
.GetVisibleEntry());
3417 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3419 notifications
.Reset();
3422 // Tests that the URLs for browser-initiated navigations in new tabs are
3423 // displayed to the user even after they fail, as long as the initial
3424 // about:blank page has not been modified. If so, we must revert to showing
3425 // about:blank. See http://crbug.com/355537.
3426 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3427 NavigationControllerImpl
& controller
= controller_impl();
3428 TestNotificationTracker notifications
;
3429 RegisterForAllNavNotifications(¬ifications
, &controller
);
3431 const GURL
url("http://foo");
3433 // For browser-initiated navigations in new tabs (with no committed entries),
3434 // we show the pending entry's URL as long as the about:blank page is not
3435 // modified. This is possible in cases that the user types a URL into a popup
3436 // tab created with a slow URL.
3437 NavigationController::LoadURLParams
load_url_params(url
);
3438 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3439 load_url_params
.is_renderer_initiated
= false;
3440 controller
.LoadURLWithParams(load_url_params
);
3441 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3442 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3443 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3444 EXPECT_TRUE(controller
.IsInitialNavigation());
3445 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3447 // There should be no title yet.
3448 EXPECT_TRUE(contents()->GetTitle().empty());
3450 // Suppose it aborts before committing, if it's a 204 or download or due to a
3451 // stop or a new navigation from the user. The URL should remain visible.
3452 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3453 params
.error_code
= net::ERR_ABORTED
;
3454 params
.error_description
= base::string16();
3456 params
.showing_repost_interstitial
= false;
3457 main_test_rfh()->OnMessageReceived(
3458 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3459 contents()->SetIsLoading(false, true, NULL
);
3460 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3462 // If something else later modifies the contents of the about:blank page, then
3463 // we must revert to showing about:blank to avoid a URL spoof.
3464 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3465 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3466 EXPECT_FALSE(controller
.GetVisibleEntry());
3467 EXPECT_FALSE(controller
.GetPendingEntry());
3469 notifications
.Reset();
3472 // Tests that the URLs for renderer-initiated navigations in new tabs are
3473 // displayed to the user even after they fail, as long as the initial
3474 // about:blank page has not been modified. If so, we must revert to showing
3475 // about:blank. See http://crbug.com/355537.
3476 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3477 NavigationControllerImpl
& controller
= controller_impl();
3478 TestNotificationTracker notifications
;
3479 RegisterForAllNavNotifications(¬ifications
, &controller
);
3481 const GURL
url("http://foo");
3483 // For renderer-initiated navigations in new tabs (with no committed entries),
3484 // we show the pending entry's URL as long as the about:blank page is not
3486 NavigationController::LoadURLParams
load_url_params(url
);
3487 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3488 load_url_params
.is_renderer_initiated
= true;
3489 controller
.LoadURLWithParams(load_url_params
);
3490 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3491 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3492 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3493 EXPECT_TRUE(controller
.IsInitialNavigation());
3494 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3496 // There should be no title yet.
3497 EXPECT_TRUE(contents()->GetTitle().empty());
3499 // Suppose it aborts before committing, if it's a 204 or download or due to a
3500 // stop or a new navigation from the user. The URL should remain visible.
3501 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3502 params
.error_code
= net::ERR_ABORTED
;
3503 params
.error_description
= base::string16();
3505 params
.showing_repost_interstitial
= false;
3506 main_test_rfh()->OnMessageReceived(
3507 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3508 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3510 // If something else later modifies the contents of the about:blank page, then
3511 // we must revert to showing about:blank to avoid a URL spoof.
3512 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3513 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3514 EXPECT_FALSE(controller
.GetVisibleEntry());
3515 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3517 notifications
.Reset();
3520 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3521 NavigationControllerImpl
& controller
= controller_impl();
3522 TestNotificationTracker notifications
;
3523 RegisterForAllNavNotifications(¬ifications
, &controller
);
3525 const GURL
url1("http://foo/eh");
3526 const GURL
url2("http://foo/bee");
3528 // For renderer-initiated navigations in new tabs (with no committed entries),
3529 // we show the pending entry's URL as long as the about:blank page is not
3531 NavigationController::LoadURLParams
load_url_params(url1
);
3532 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3533 load_url_params
.is_renderer_initiated
= true;
3534 controller
.LoadURLWithParams(load_url_params
);
3535 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3536 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3537 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3538 EXPECT_TRUE(controller
.IsInitialNavigation());
3539 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3541 // Simulate a commit and then starting a new pending navigation.
3542 main_test_rfh()->PrepareForCommit();
3543 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3544 NavigationController::LoadURLParams
load_url2_params(url2
);
3545 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3546 load_url2_params
.is_renderer_initiated
= true;
3547 controller
.LoadURLWithParams(load_url2_params
);
3549 // We should not consider this an initial navigation, and thus should
3550 // not show the pending URL.
3551 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3552 EXPECT_FALSE(controller
.IsInitialNavigation());
3553 EXPECT_TRUE(controller
.GetVisibleEntry());
3554 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3556 notifications
.Reset();
3559 // Tests that IsInPageNavigation returns appropriate results. Prevents
3560 // regression for bug 1126349.
3561 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3562 NavigationControllerImpl
& controller
= controller_impl();
3563 const GURL
url("http://www.google.com/home.html");
3565 // If the renderer claims it performed an in-page navigation from
3566 // about:blank, trust the renderer.
3567 // This can happen when an iframe is created and populated via
3568 // document.write(), then tries to perform a fragment navigation.
3569 // TODO(japhet): We should only trust the renderer if the about:blank
3570 // was the first document in the given frame, but we don't have enough
3571 // information to identify that case currently.
3572 const GURL
blank_url(url::kAboutBlankURL
);
3573 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url
);
3574 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3577 // Navigate to URL with no refs.
3578 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3580 // Reloading the page is not an in-page navigation.
3581 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false, main_test_rfh()));
3582 const GURL
other_url("http://www.google.com/add.html");
3583 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3585 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3586 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3589 // Navigate to URL with refs.
3590 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref
);
3592 // Reloading the page is not an in-page navigation.
3593 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3595 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3597 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3599 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3600 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3603 // Going to the same url again will be considered in-page
3604 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3605 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3608 // Going back to the non ref url will be considered in-page if the navigation
3610 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3613 // If the renderer says this is a same-origin in-page navigation, believe it.
3614 // This is the pushState/replaceState case.
3615 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3618 // Test allow_universal_access_from_file_urls flag.
3619 const GURL
different_origin_url("http://www.example.com");
3620 MockRenderProcessHost
* rph
= main_test_rfh()->GetProcess();
3621 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3622 prefs
.allow_universal_access_from_file_urls
= true;
3623 test_rvh()->UpdateWebkitPreferences(prefs
);
3624 prefs
= test_rvh()->GetWebkitPreferences();
3625 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3626 // Allow in page navigation if existing URL is file scheme.
3627 const GURL
file_url("file:///foo/index.html");
3628 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url
);
3629 EXPECT_EQ(0, rph
->bad_msg_count());
3630 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3632 EXPECT_EQ(0, rph
->bad_msg_count());
3633 // Don't honor allow_universal_access_from_file_urls if existing URL is
3635 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3636 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3638 EXPECT_EQ(1, rph
->bad_msg_count());
3640 // Remove allow_universal_access_from_file_urls flag.
3641 prefs
.allow_universal_access_from_file_urls
= false;
3642 test_rvh()->UpdateWebkitPreferences(prefs
);
3643 prefs
= test_rvh()->GetWebkitPreferences();
3644 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3646 // Don't believe the renderer if it claims a cross-origin navigation is
3648 EXPECT_EQ(1, rph
->bad_msg_count());
3649 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3651 EXPECT_EQ(2, rph
->bad_msg_count());
3654 // Some pages can have subframes with the same base URL (minus the reference) as
3655 // the main page. Even though this is hard, it can happen, and we don't want
3656 // these subframe navigations to affect the toplevel document. They should
3657 // instead be ignored. http://crbug.com/5585
3658 TEST_F(NavigationControllerTest
, SameSubframe
) {
3659 NavigationControllerImpl
& controller
= controller_impl();
3660 // Navigate the main frame.
3661 const GURL
url("http://www.google.com/");
3662 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
3664 // We should be at the first navigation entry.
3665 EXPECT_EQ(controller
.GetEntryCount(), 1);
3666 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3668 // Add and navigate a subframe that would normally count as in-page.
3669 main_test_rfh()->OnCreateChildFrame(
3670 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3671 blink::WebSandboxFlags::None
);
3672 RenderFrameHostImpl
* subframe
=
3673 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3674 const GURL
subframe_url("http://www.google.com/#");
3675 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3677 params
.nav_entry_id
= 0;
3678 params
.did_create_new_entry
= false;
3679 params
.url
= subframe_url
;
3680 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3681 params
.should_update_history
= false;
3682 params
.gesture
= NavigationGestureAuto
;
3683 params
.is_post
= false;
3684 params
.page_state
= PageState::CreateFromURL(subframe_url
);
3685 LoadCommittedDetails details
;
3686 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3688 // Nothing should have changed.
3689 EXPECT_EQ(controller
.GetEntryCount(), 1);
3690 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3693 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3695 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3696 NavigationControllerImpl
& controller
= controller_impl();
3697 const GURL
url1("http://foo1");
3698 const GURL
url2("http://foo2");
3699 const base::string16
title(base::ASCIIToUTF16("Title"));
3701 NavigateAndCommit(url1
);
3702 controller
.GetVisibleEntry()->SetTitle(title
);
3703 NavigateAndCommit(url2
);
3705 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3707 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3708 EXPECT_TRUE(clone
->GetController().NeedsReload());
3709 clone
->GetController().GoBack();
3710 // Navigating back should have triggered needs_reload_ to go false.
3711 EXPECT_FALSE(clone
->GetController().NeedsReload());
3713 // Ensure that the pending URL and its title are visible.
3714 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3715 EXPECT_EQ(title
, clone
->GetTitle());
3718 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3719 // See http://crbug.com/234491.
3720 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3721 NavigationControllerImpl
& controller
= controller_impl();
3722 const GURL
url1("http://foo1");
3723 const GURL
url2("http://foo2");
3724 const base::string16
title(base::ASCIIToUTF16("Title"));
3726 NavigateAndCommit(url1
);
3727 controller
.GetVisibleEntry()->SetTitle(title
);
3728 NavigateAndCommit(url2
);
3730 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3731 clone
->GetController().LoadIfNecessary();
3733 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3734 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3736 clone
->GetController().Reload(true);
3737 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3740 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3741 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3742 NavigationControllerImpl
& controller
= controller_impl();
3743 const GURL
url1("http://foo1");
3744 const GURL
url2("http://foo2");
3746 NavigateAndCommit(url1
);
3747 NavigateAndCommit(url2
);
3749 // Add an interstitial entry. Should be deleted with controller.
3750 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3751 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3752 controller
.SetTransientEntry(make_scoped_ptr(interstitial_entry
));
3754 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3756 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3759 // Test requesting and triggering a lazy reload.
3760 TEST_F(NavigationControllerTest
, LazyReload
) {
3761 NavigationControllerImpl
& controller
= controller_impl();
3762 const GURL
url("http://foo");
3763 NavigateAndCommit(url
);
3764 ASSERT_FALSE(controller
.NeedsReload());
3765 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3766 controller
.GetLastCommittedEntry()->GetTransitionType());
3768 // Request a reload to happen when the controller becomes active (e.g. after
3769 // the renderer gets killed in background on Android).
3770 controller
.SetNeedsReload();
3771 ASSERT_TRUE(controller
.NeedsReload());
3772 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3773 controller
.GetLastCommittedEntry()->GetTransitionType());
3775 // Set the controller as active, triggering the requested reload.
3776 controller
.SetActive(true);
3777 ASSERT_FALSE(controller
.NeedsReload());
3778 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3779 controller
.GetPendingEntry()->GetTransitionType());
3782 // Test requesting and triggering a lazy reload without any committed entry.
3783 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3784 NavigationControllerImpl
& controller
= controller_impl();
3785 const GURL
url("http://foo");
3786 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3787 ASSERT_FALSE(controller
.NeedsReload());
3788 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3789 controller
.GetPendingEntry()->GetTransitionType());
3791 // Request a reload to happen when the controller becomes active (e.g. after
3792 // the renderer gets killed in background on Android).
3793 controller
.SetNeedsReload();
3794 ASSERT_TRUE(controller
.NeedsReload());
3795 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3796 controller
.GetPendingEntry()->GetTransitionType());
3798 // Set the controller as active, triggering the requested reload.
3799 controller
.SetActive(true);
3800 ASSERT_FALSE(controller
.NeedsReload());
3801 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3802 controller
.GetPendingEntry()->GetTransitionType());
3805 // Tests a subframe navigation while a toplevel navigation is pending.
3806 // http://crbug.com/43967
3807 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3808 NavigationControllerImpl
& controller
= controller_impl();
3809 // Load the first page.
3810 const GURL
url1("http://foo/");
3811 NavigateAndCommit(url1
);
3813 // Now start a pending load to a totally different page, but don't commit it.
3814 const GURL
url2("http://bar/");
3816 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3818 // Send a subframe update from the first page, as if one had just
3819 // automatically loaded. Auto subframes don't increment the page ID.
3820 main_test_rfh()->OnCreateChildFrame(
3821 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3822 blink::WebSandboxFlags::None
);
3823 RenderFrameHostImpl
* subframe
=
3824 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3825 const GURL
url1_sub("http://foo/subframe");
3826 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3827 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3828 params
.nav_entry_id
= 0;
3829 params
.did_create_new_entry
= false;
3830 params
.url
= url1_sub
;
3831 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3832 params
.should_update_history
= false;
3833 params
.gesture
= NavigationGestureAuto
;
3834 params
.is_post
= false;
3835 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3836 LoadCommittedDetails details
;
3838 // This should return false meaning that nothing was actually updated.
3839 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3841 // The notification should have updated the last committed one, and not
3842 // the pending load.
3843 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3845 // The active entry should be unchanged by the subframe load.
3846 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3849 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3850 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3851 NavigationControllerImpl
& controller
= controller_impl();
3852 const GURL
url1("http://foo1");
3853 const GURL
url2("http://foo2");
3855 NavigateAndCommit(url1
);
3856 NavigateAndCommit(url2
);
3857 controller
.GoBack();
3858 contents()->CommitPendingNavigation();
3860 scoped_ptr
<TestWebContents
> other_contents(
3861 static_cast<TestWebContents
*>(CreateTestWebContents()));
3862 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3863 other_controller
.CopyStateFrom(controller
);
3865 // other_controller should now contain 2 urls.
3866 ASSERT_EQ(2, other_controller
.GetEntryCount());
3867 // We should be looking at the first one.
3868 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3870 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3871 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3872 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3873 // This is a different site than url1, so the IDs start again at 0.
3874 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3876 // The max page ID map should be copied over and updated with the max page ID
3877 // from the current tab.
3878 SiteInstance
* instance1
=
3879 other_controller
.GetEntryAtIndex(0)->site_instance();
3880 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3882 // Ensure the SessionStorageNamespaceMaps are the same size and have
3883 // the same partitons loaded.
3885 // TODO(ajwong): We should load a url from a different partition earlier
3886 // to make sure this map has more than one entry.
3887 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3888 controller
.GetSessionStorageNamespaceMap();
3889 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3890 other_controller
.GetSessionStorageNamespaceMap();
3891 EXPECT_EQ(session_storage_namespace_map
.size(),
3892 other_session_storage_namespace_map
.size());
3893 for (SessionStorageNamespaceMap::const_iterator it
=
3894 session_storage_namespace_map
.begin();
3895 it
!= session_storage_namespace_map
.end();
3897 SessionStorageNamespaceMap::const_iterator other
=
3898 other_session_storage_namespace_map
.find(it
->first
);
3899 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3903 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3904 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3905 NavigationControllerImpl
& controller
= controller_impl();
3906 const GURL
url1("http://foo/1");
3907 const GURL
url2("http://foo/2");
3908 const GURL
url3("http://foo/3");
3910 NavigateAndCommit(url1
);
3911 NavigateAndCommit(url2
);
3913 // First two entries should have the same SiteInstance.
3914 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3915 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3916 EXPECT_EQ(instance1
, instance2
);
3917 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3918 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3919 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3921 scoped_ptr
<TestWebContents
> other_contents(
3922 static_cast<TestWebContents
*>(CreateTestWebContents()));
3923 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3924 other_contents
->NavigateAndCommit(url3
);
3925 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3926 other_controller
.CopyStateFromAndPrune(&controller
, false);
3928 // other_controller should now contain the 3 urls: url1, url2 and url3.
3930 ASSERT_EQ(3, other_controller
.GetEntryCount());
3932 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3934 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3935 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3936 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3937 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3938 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3939 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3941 // A new SiteInstance in a different BrowsingInstance should be used for the
3943 SiteInstance
* instance3
=
3944 other_controller
.GetEntryAtIndex(2)->site_instance();
3945 EXPECT_NE(instance3
, instance1
);
3946 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3948 // The max page ID map should be copied over and updated with the max page ID
3949 // from the current tab.
3950 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3951 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3954 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3956 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3957 NavigationControllerImpl
& controller
= controller_impl();
3958 const GURL
url1("http://foo1");
3959 const GURL
url2("http://foo2");
3960 const GURL
url3("http://foo3");
3962 NavigateAndCommit(url1
);
3963 NavigateAndCommit(url2
);
3964 controller
.GoBack();
3965 contents()->CommitPendingNavigation();
3967 scoped_ptr
<TestWebContents
> other_contents(
3968 static_cast<TestWebContents
*>(CreateTestWebContents()));
3969 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3970 other_contents
->NavigateAndCommit(url3
);
3971 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3972 other_controller
.CopyStateFromAndPrune(&controller
, false);
3974 // other_controller should now contain: url1, url3
3976 ASSERT_EQ(2, other_controller
.GetEntryCount());
3977 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3979 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3980 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3981 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3983 // The max page ID map should be copied over and updated with the max page ID
3984 // from the current tab.
3985 SiteInstance
* instance1
=
3986 other_controller
.GetEntryAtIndex(1)->site_instance();
3987 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3990 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3992 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3993 NavigationControllerImpl
& controller
= controller_impl();
3994 const GURL
url1("http://foo1");
3995 const GURL
url2("http://foo2");
3996 const GURL
url3("http://foo3");
3997 const GURL
url4("http://foo4");
3999 NavigateAndCommit(url1
);
4000 NavigateAndCommit(url2
);
4002 scoped_ptr
<TestWebContents
> other_contents(
4003 static_cast<TestWebContents
*>(CreateTestWebContents()));
4004 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4005 other_contents
->NavigateAndCommit(url3
);
4006 other_contents
->NavigateAndCommit(url4
);
4007 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4008 other_controller
.CopyStateFromAndPrune(&controller
, false);
4010 // other_controller should now contain: url1, url2, url4
4012 ASSERT_EQ(3, other_controller
.GetEntryCount());
4013 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4015 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4016 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4017 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4019 // The max page ID map should be copied over and updated with the max page ID
4020 // from the current tab.
4021 SiteInstance
* instance1
=
4022 other_controller
.GetEntryAtIndex(2)->site_instance();
4023 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4026 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
4027 // not the last entry selected in the target.
4028 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
4029 NavigationControllerImpl
& controller
= controller_impl();
4030 const GURL
url1("http://foo1");
4031 const GURL
url2("http://foo2");
4032 const GURL
url3("http://foo3");
4033 const GURL
url4("http://foo4");
4035 NavigateAndCommit(url1
);
4036 NavigateAndCommit(url2
);
4038 scoped_ptr
<TestWebContents
> other_contents(
4039 static_cast<TestWebContents
*>(CreateTestWebContents()));
4040 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4041 other_contents
->NavigateAndCommit(url3
);
4042 other_contents
->NavigateAndCommit(url4
);
4043 other_controller
.GoBack();
4044 other_contents
->CommitPendingNavigation();
4045 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4046 other_controller
.CopyStateFromAndPrune(&controller
, false);
4048 // other_controller should now contain: url1, url2, url3
4050 ASSERT_EQ(3, other_controller
.GetEntryCount());
4051 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4053 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4054 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4055 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4057 // The max page ID map should be copied over and updated with the max page ID
4058 // from the current tab.
4059 SiteInstance
* instance1
=
4060 other_controller
.GetEntryAtIndex(2)->site_instance();
4061 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4064 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
4065 // a pending entry in the target.
4066 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
4067 NavigationControllerImpl
& controller
= controller_impl();
4068 const GURL
url1("http://foo1");
4069 const GURL
url2("http://foo2");
4070 const GURL
url3("http://foo3");
4071 const GURL
url4("http://foo4");
4073 NavigateAndCommit(url1
);
4074 NavigateAndCommit(url2
);
4075 controller
.GoBack();
4076 contents()->CommitPendingNavigation();
4078 scoped_ptr
<TestWebContents
> other_contents(
4079 static_cast<TestWebContents
*>(CreateTestWebContents()));
4080 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4081 other_contents
->NavigateAndCommit(url3
);
4082 other_controller
.LoadURL(
4083 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4084 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4085 other_controller
.CopyStateFromAndPrune(&controller
, false);
4087 // other_controller should now contain url1, url3, and a pending entry
4090 ASSERT_EQ(2, other_controller
.GetEntryCount());
4091 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4093 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4094 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4096 // And there should be a pending entry for url4.
4097 ASSERT_TRUE(other_controller
.GetPendingEntry());
4098 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
4100 // The max page ID map should be copied over and updated with the max page ID
4101 // from the current tab.
4102 SiteInstance
* instance1
=
4103 other_controller
.GetEntryAtIndex(0)->site_instance();
4104 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4107 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
4108 // client redirect entry (with the same page ID) in the target. This used to
4109 // crash because the last committed entry would be pruned but max_page_id
4110 // remembered the page ID (http://crbug.com/234809).
4111 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
4112 NavigationControllerImpl
& controller
= controller_impl();
4113 const GURL
url1("http://foo1");
4114 const GURL
url2a("http://foo2/a");
4115 const GURL
url2b("http://foo2/b");
4117 NavigateAndCommit(url1
);
4119 scoped_ptr
<TestWebContents
> other_contents(
4120 static_cast<TestWebContents
*>(CreateTestWebContents()));
4121 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4122 other_contents
->NavigateAndCommit(url2a
);
4123 // Simulate a client redirect, which has the same page ID as entry 2a.
4124 other_controller
.LoadURL(
4125 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
4126 NavigationEntry
* entry
= other_controller
.GetPendingEntry();
4127 entry
->SetPageID(other_controller
.GetLastCommittedEntry()->GetPageID());
4129 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4130 other_controller
.CopyStateFromAndPrune(&controller
, false);
4132 // other_controller should now contain url1, url2a, and a pending entry
4135 ASSERT_EQ(2, other_controller
.GetEntryCount());
4136 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4138 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4139 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
4141 // And there should be a pending entry for url4.
4142 ASSERT_TRUE(other_controller
.GetPendingEntry());
4143 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
4145 // Let the pending entry commit.
4146 other_contents
->TestDidNavigate(other_contents
->GetMainFrame(),
4147 entry
->GetPageID(), 0, false, url2b
,
4148 ui::PAGE_TRANSITION_LINK
);
4150 // The max page ID map should be copied over and updated with the max page ID
4151 // from the current tab.
4152 SiteInstance
* instance1
=
4153 other_controller
.GetEntryAtIndex(1)->site_instance();
4154 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4157 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4158 // source, and 1 entry in the target. The back pending entry should be ignored.
4159 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
4160 NavigationControllerImpl
& controller
= controller_impl();
4161 const GURL
url1("http://foo1");
4162 const GURL
url2("http://foo2");
4163 const GURL
url3("http://foo3");
4165 NavigateAndCommit(url1
);
4166 NavigateAndCommit(url2
);
4167 controller
.GoBack();
4169 scoped_ptr
<TestWebContents
> other_contents(
4170 static_cast<TestWebContents
*>(CreateTestWebContents()));
4171 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4172 other_contents
->NavigateAndCommit(url3
);
4173 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4174 other_controller
.CopyStateFromAndPrune(&controller
, false);
4176 // other_controller should now contain: url1, url2, url3
4178 ASSERT_EQ(3, other_controller
.GetEntryCount());
4179 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4181 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4182 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4183 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4184 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4186 // The max page ID map should be copied over and updated with the max page ID
4187 // from the current tab.
4188 SiteInstance
* instance1
=
4189 other_controller
.GetEntryAtIndex(2)->site_instance();
4190 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4193 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4194 // when the max entry count is 3. We should prune one entry.
4195 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
4196 NavigationControllerImpl
& controller
= controller_impl();
4197 size_t original_count
= NavigationControllerImpl::max_entry_count();
4198 const int kMaxEntryCount
= 3;
4200 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4202 const GURL
url1("http://foo/1");
4203 const GURL
url2("http://foo/2");
4204 const GURL
url3("http://foo/3");
4205 const GURL
url4("http://foo/4");
4207 // Create a PrunedListener to observe prune notifications.
4208 PrunedListener
listener(&controller
);
4210 NavigateAndCommit(url1
);
4211 NavigateAndCommit(url2
);
4212 NavigateAndCommit(url3
);
4214 scoped_ptr
<TestWebContents
> other_contents(
4215 static_cast<TestWebContents
*>(CreateTestWebContents()));
4216 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4217 other_contents
->NavigateAndCommit(url4
);
4218 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4219 other_controller
.CopyStateFromAndPrune(&controller
, false);
4221 // We should have received a pruned notification.
4222 EXPECT_EQ(1, listener
.notification_count_
);
4223 EXPECT_TRUE(listener
.details_
.from_front
);
4224 EXPECT_EQ(1, listener
.details_
.count
);
4226 // other_controller should now contain only 3 urls: url2, url3 and url4.
4228 ASSERT_EQ(3, other_controller
.GetEntryCount());
4230 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4232 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
4233 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4234 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4235 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
4236 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
4237 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4239 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4242 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4243 // replace_entry set to true.
4244 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
4245 NavigationControllerImpl
& controller
= controller_impl();
4246 const GURL
url1("http://foo/1");
4247 const GURL
url2("http://foo/2");
4248 const GURL
url3("http://foo/3");
4250 NavigateAndCommit(url1
);
4251 NavigateAndCommit(url2
);
4253 // First two entries should have the same SiteInstance.
4254 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
4255 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
4256 EXPECT_EQ(instance1
, instance2
);
4257 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
4258 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
4259 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
4261 scoped_ptr
<TestWebContents
> other_contents(
4262 static_cast<TestWebContents
*>(CreateTestWebContents()));
4263 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4264 other_contents
->NavigateAndCommit(url3
);
4265 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4266 other_controller
.CopyStateFromAndPrune(&controller
, true);
4268 // other_controller should now contain the 2 urls: url1 and url3.
4270 ASSERT_EQ(2, other_controller
.GetEntryCount());
4272 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
4274 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4275 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4276 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4277 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
4279 // A new SiteInstance in a different BrowsingInstance should be used for the
4281 SiteInstance
* instance3
=
4282 other_controller
.GetEntryAtIndex(1)->site_instance();
4283 EXPECT_NE(instance3
, instance1
);
4284 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
4286 // The max page ID map should be copied over and updated with the max page ID
4287 // from the current tab.
4288 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4289 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
4292 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4293 // entry count is 3 and replace_entry is true. We should not prune entries.
4294 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
4295 NavigationControllerImpl
& controller
= controller_impl();
4296 size_t original_count
= NavigationControllerImpl::max_entry_count();
4297 const int kMaxEntryCount
= 3;
4299 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4301 const GURL
url1("http://foo/1");
4302 const GURL
url2("http://foo/2");
4303 const GURL
url3("http://foo/3");
4304 const GURL
url4("http://foo/4");
4306 // Create a PrunedListener to observe prune notifications.
4307 PrunedListener
listener(&controller
);
4309 NavigateAndCommit(url1
);
4310 NavigateAndCommit(url2
);
4311 NavigateAndCommit(url3
);
4313 scoped_ptr
<TestWebContents
> other_contents(
4314 static_cast<TestWebContents
*>(CreateTestWebContents()));
4315 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4316 other_contents
->NavigateAndCommit(url4
);
4317 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4318 other_controller
.CopyStateFromAndPrune(&controller
, true);
4320 // We should have received no pruned notification.
4321 EXPECT_EQ(0, listener
.notification_count_
);
4323 // other_controller should now contain only 3 urls: url1, url2 and url4.
4325 ASSERT_EQ(3, other_controller
.GetEntryCount());
4327 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4329 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4330 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4331 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4332 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4333 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
4334 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4336 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4339 // Tests that we can navigate to the restored entries
4340 // imported by CopyStateFromAndPrune.
4341 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
4342 const GURL kRestoredUrls
[] = {
4343 GURL("http://site1.com"),
4344 GURL("http://site2.com"),
4346 const GURL
kInitialUrl("http://site3.com");
4348 ScopedVector
<NavigationEntry
> entries
;
4349 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
4350 scoped_ptr
<NavigationEntry
> entry
=
4351 NavigationControllerImpl::CreateNavigationEntry(
4352 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
4353 std::string(), browser_context());
4354 entry
->SetPageID(static_cast<int>(i
));
4355 entries
.push_back(entry
.Pass());
4358 // Create a WebContents with restored entries.
4359 scoped_ptr
<TestWebContents
> source_contents(
4360 static_cast<TestWebContents
*>(CreateTestWebContents()));
4361 NavigationControllerImpl
& source_controller
=
4362 source_contents
->GetController();
4363 source_controller
.Restore(
4365 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
4367 ASSERT_EQ(0u, entries
.size());
4368 source_controller
.LoadIfNecessary();
4369 source_contents
->CommitPendingNavigation();
4371 // Load a page, then copy state from |source_contents|.
4372 NavigateAndCommit(kInitialUrl
);
4373 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4374 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
4375 ASSERT_EQ(3, controller_impl().GetEntryCount());
4377 // Go back to the first entry one at a time and
4378 // verify that it works as expected.
4379 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4380 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
4382 controller_impl().GoBack();
4383 contents()->CommitPendingNavigation();
4384 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4385 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
4387 controller_impl().GoBack();
4388 contents()->CommitPendingNavigation();
4389 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4390 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
4393 // Tests that navigations initiated from the page (with the history object)
4394 // work as expected, creating pending entries.
4395 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
4396 NavigationControllerImpl
& controller
= controller_impl();
4397 const GURL
url1("http://foo/1");
4398 const GURL
url2("http://foo/2");
4399 const GURL
url3("http://foo/3");
4401 NavigateAndCommit(url1
);
4402 NavigateAndCommit(url2
);
4403 NavigateAndCommit(url3
);
4404 controller
.GoBack();
4405 contents()->CommitPendingNavigation();
4406 process()->sink().ClearMessages();
4408 // Simulate the page calling history.back(). It should create a pending entry.
4409 contents()->OnGoToEntryAtOffset(-1);
4410 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4411 // The actual cross-navigation is suspended until the current RVH tells us
4412 // it unloaded, simulate that.
4413 contents()->ProceedWithCrossSiteNavigation();
4414 // Also make sure we told the page to navigate.
4415 GURL nav_url
= GetLastNavigationURL();
4416 EXPECT_EQ(url1
, nav_url
);
4417 contents()->CommitPendingNavigation();
4418 process()->sink().ClearMessages();
4420 // Now test history.forward()
4421 contents()->OnGoToEntryAtOffset(2);
4422 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4423 // The actual cross-navigation is suspended until the current RVH tells us
4424 // it unloaded, simulate that.
4425 contents()->ProceedWithCrossSiteNavigation();
4426 nav_url
= GetLastNavigationURL();
4427 EXPECT_EQ(url3
, nav_url
);
4428 contents()->CommitPendingNavigation();
4429 process()->sink().ClearMessages();
4431 controller
.DiscardNonCommittedEntries();
4433 // Make sure an extravagant history.go() doesn't break.
4434 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4435 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4436 EXPECT_FALSE(HasNavigationRequest());
4439 // Test call to PruneAllButLastCommitted for the only entry.
4440 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4441 NavigationControllerImpl
& controller
= controller_impl();
4442 const GURL
url1("http://foo1");
4443 NavigateAndCommit(url1
);
4445 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4447 controller
.PruneAllButLastCommitted();
4449 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4450 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4453 // Test call to PruneAllButLastCommitted for first entry.
4454 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4455 NavigationControllerImpl
& controller
= controller_impl();
4456 const GURL
url1("http://foo/1");
4457 const GURL
url2("http://foo/2");
4458 const GURL
url3("http://foo/3");
4460 NavigateAndCommit(url1
);
4461 NavigateAndCommit(url2
);
4462 NavigateAndCommit(url3
);
4463 controller
.GoBack();
4464 controller
.GoBack();
4465 contents()->CommitPendingNavigation();
4467 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4469 controller
.PruneAllButLastCommitted();
4471 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4472 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4475 // Test call to PruneAllButLastCommitted for intermediate entry.
4476 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4477 NavigationControllerImpl
& controller
= controller_impl();
4478 const GURL
url1("http://foo/1");
4479 const GURL
url2("http://foo/2");
4480 const GURL
url3("http://foo/3");
4482 NavigateAndCommit(url1
);
4483 NavigateAndCommit(url2
);
4484 NavigateAndCommit(url3
);
4485 controller
.GoBack();
4486 contents()->CommitPendingNavigation();
4488 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4490 controller
.PruneAllButLastCommitted();
4492 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4493 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4496 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4497 // the list of entries.
4498 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4499 NavigationControllerImpl
& controller
= controller_impl();
4500 const GURL
url1("http://foo/1");
4501 const GURL
url2("http://foo/2");
4502 const GURL
url3("http://foo/3");
4504 NavigateAndCommit(url1
);
4505 NavigateAndCommit(url2
);
4507 // Create a pending entry that is not in the entry list.
4509 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4510 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
4511 EXPECT_TRUE(controller
.GetPendingEntry());
4512 EXPECT_EQ(2, controller
.GetEntryCount());
4514 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4515 controller
.PruneAllButLastCommitted();
4517 // We should only have the last committed and pending entries at this point,
4518 // and the pending entry should still not be in the entry list.
4519 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4520 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4521 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4522 EXPECT_TRUE(controller
.GetPendingEntry());
4523 EXPECT_EQ(1, controller
.GetEntryCount());
4525 // Try to commit the pending entry.
4526 main_test_rfh()->PrepareForCommit();
4527 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
4528 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4529 EXPECT_FALSE(controller
.GetPendingEntry());
4530 EXPECT_EQ(2, controller
.GetEntryCount());
4531 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4534 // Test to ensure that when we do a history navigation back to the current
4535 // committed page (e.g., going forward to a slow-loading page, then pressing
4536 // the back button), we just stop the navigation to prevent the throbber from
4537 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4538 // start, but WebKit essentially ignores the navigation and never sends a
4539 // message to stop the throbber.
4540 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4541 NavigationControllerImpl
& controller
= controller_impl();
4542 const GURL
url0("http://foo/0");
4543 const GURL
url1("http://foo/1");
4545 NavigateAndCommit(url0
);
4546 NavigateAndCommit(url1
);
4548 // Go back to the original page, then forward to the slow page, then back
4549 controller
.GoBack();
4550 contents()->CommitPendingNavigation();
4552 controller
.GoForward();
4553 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4555 controller
.GoBack();
4556 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4559 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4560 NavigationControllerImpl
& controller
= controller_impl();
4561 TestNotificationTracker notifications
;
4562 RegisterForAllNavNotifications(¬ifications
, &controller
);
4565 EXPECT_TRUE(controller
.IsInitialNavigation());
4567 // After commit, it stays false.
4568 const GURL
url1("http://foo1");
4569 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
4570 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4571 navigation_entry_committed_counter_
= 0;
4572 EXPECT_FALSE(controller
.IsInitialNavigation());
4574 // After starting a new navigation, it stays false.
4575 const GURL
url2("http://foo2");
4577 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4580 // Check that the favicon is not reused across a client redirect.
4581 // (crbug.com/28515)
4582 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4583 const GURL
kPageWithFavicon("http://withfavicon.html");
4584 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4585 const GURL
kIconURL("http://withfavicon.ico");
4586 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4588 NavigationControllerImpl
& controller
= controller_impl();
4589 TestNotificationTracker notifications
;
4590 RegisterForAllNavNotifications(¬ifications
, &controller
);
4592 main_test_rfh()->NavigateAndCommitRendererInitiated(
4593 0, true, kPageWithFavicon
);
4594 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4595 navigation_entry_committed_counter_
= 0;
4597 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4599 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4601 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4602 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4603 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4604 favicon_status
.url
= kIconURL
;
4605 favicon_status
.valid
= true;
4606 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4608 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon
,
4610 main_test_rfh()->PrepareForCommit();
4611 main_test_rfh()->SendNavigateWithTransition(
4614 false, // no new entry
4615 kPageWithoutFavicon
, ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4616 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4617 navigation_entry_committed_counter_
= 0;
4619 entry
= controller
.GetLastCommittedEntry();
4621 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4623 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4626 // Check that the favicon is not cleared for NavigationEntries which were
4627 // previously navigated to.
4628 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4629 const GURL
kUrl1("http://www.a.com/1");
4630 const GURL
kUrl2("http://www.a.com/2");
4631 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4633 NavigationControllerImpl
& controller
= controller_impl();
4634 TestNotificationTracker notifications
;
4635 RegisterForAllNavNotifications(¬ifications
, &controller
);
4637 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
4638 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4639 navigation_entry_committed_counter_
= 0;
4641 // Simulate Chromium having set the favicon for |kUrl1|.
4642 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4643 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4645 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4646 favicon_status
.image
= favicon_image
;
4647 favicon_status
.url
= kIconURL
;
4648 favicon_status
.valid
= true;
4650 // Navigate to another page and go back to the original page.
4651 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
4652 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4653 navigation_entry_committed_counter_
= 0;
4654 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, false);
4655 main_test_rfh()->PrepareForCommit();
4656 main_test_rfh()->SendNavigateWithTransition(
4657 0, controller
.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1
,
4658 ui::PAGE_TRANSITION_FORWARD_BACK
);
4659 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4660 navigation_entry_committed_counter_
= 0;
4662 // Verify that the favicon for the page at |kUrl1| was not cleared.
4663 entry
= controller
.GetEntryAtIndex(0);
4665 EXPECT_EQ(kUrl1
, entry
->GetURL());
4666 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4669 // The test crashes on android: http://crbug.com/170449
4670 #if defined(OS_ANDROID)
4671 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4673 #define MAYBE_PurgeScreenshot PurgeScreenshot
4675 // Tests that screenshot are purged correctly.
4676 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4677 NavigationControllerImpl
& controller
= controller_impl();
4679 NavigationEntryImpl
* entry
;
4681 // Navigate enough times to make sure that some screenshots are purged.
4682 for (int i
= 0; i
< 12; ++i
) {
4683 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4684 NavigateAndCommit(url
);
4685 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4688 MockScreenshotManager
* screenshot_manager
=
4689 new MockScreenshotManager(&controller
);
4690 controller
.SetScreenshotManager(screenshot_manager
);
4691 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4692 entry
= controller
.GetEntryAtIndex(i
);
4693 screenshot_manager
->TakeScreenshotFor(entry
);
4694 EXPECT_TRUE(entry
->screenshot().get());
4697 NavigateAndCommit(GURL("https://foo/"));
4698 EXPECT_EQ(13, controller
.GetEntryCount());
4699 entry
= controller
.GetEntryAtIndex(11);
4700 screenshot_manager
->TakeScreenshotFor(entry
);
4702 for (int i
= 0; i
< 2; ++i
) {
4703 entry
= controller
.GetEntryAtIndex(i
);
4704 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4708 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4709 entry
= controller
.GetEntryAtIndex(i
);
4710 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4713 // Navigate to index 5 and then try to assign screenshot to all entries.
4714 controller
.GoToIndex(5);
4715 contents()->CommitPendingNavigation();
4716 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4717 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4718 entry
= controller
.GetEntryAtIndex(i
);
4719 screenshot_manager
->TakeScreenshotFor(entry
);
4722 for (int i
= 10; i
<= 12; ++i
) {
4723 entry
= controller
.GetEntryAtIndex(i
);
4724 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4726 screenshot_manager
->TakeScreenshotFor(entry
);
4729 // Navigate to index 7 and assign screenshot to all entries.
4730 controller
.GoToIndex(7);
4731 contents()->CommitPendingNavigation();
4732 EXPECT_EQ(7, controller
.GetCurrentEntryIndex());
4733 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4734 entry
= controller
.GetEntryAtIndex(i
);
4735 screenshot_manager
->TakeScreenshotFor(entry
);
4738 for (int i
= 0; i
< 2; ++i
) {
4739 entry
= controller
.GetEntryAtIndex(i
);
4740 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4744 // Clear all screenshots.
4745 EXPECT_EQ(13, controller
.GetEntryCount());
4746 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4747 controller
.ClearAllScreenshots();
4748 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4749 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4750 entry
= controller
.GetEntryAtIndex(i
);
4751 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4756 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4758 main_test_rfh()->NavigateAndCommitRendererInitiated(
4759 1, true, GURL("http://foo"));
4761 // Set title and favicon.
4762 base::string16
title(base::ASCIIToUTF16("Title"));
4763 FaviconStatus favicon
;
4764 favicon
.valid
= true;
4765 favicon
.url
= GURL("http://foo/favicon.ico");
4766 controller().GetLastCommittedEntry()->SetTitle(title
);
4767 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4769 // history.pushState() is called.
4770 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4771 GURL
kUrl2("http://foo#foo");
4773 params
.nav_entry_id
= 0;
4774 params
.did_create_new_entry
= true;
4776 params
.page_state
= PageState::CreateFromURL(kUrl2
);
4777 params
.was_within_same_page
= true;
4778 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, false);
4779 main_test_rfh()->PrepareForCommit();
4780 main_test_rfh()->SendNavigateWithParams(¶ms
);
4782 // The title should immediately be visible on the new NavigationEntry.
4783 base::string16 new_title
=
4784 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4785 EXPECT_EQ(title
, new_title
);
4786 FaviconStatus new_favicon
=
4787 controller().GetLastCommittedEntry()->GetFavicon();
4788 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4789 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4792 // Test that the navigation controller clears its session history when a
4793 // navigation commits with the clear history list flag set.
4794 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4795 const GURL
url1("http://foo1");
4796 const GURL
url2("http://foo2");
4797 const GURL
url3("http://foo3");
4798 const GURL
url4("http://foo4");
4800 NavigationControllerImpl
& controller
= controller_impl();
4802 // Create a session history with three entries, second entry is active.
4803 NavigateAndCommit(url1
);
4804 NavigateAndCommit(url2
);
4805 NavigateAndCommit(url3
);
4806 controller
.GoBack();
4807 contents()->CommitPendingNavigation();
4808 EXPECT_EQ(3, controller
.GetEntryCount());
4809 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4811 // Create a new pending navigation, and indicate that the session history
4812 // should be cleared.
4813 NavigationController::LoadURLParams
params(url4
);
4814 params
.should_clear_history_list
= true;
4815 controller
.LoadURLWithParams(params
);
4817 // Verify that the pending entry correctly indicates that the session history
4818 // should be cleared.
4819 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4821 EXPECT_TRUE(entry
->should_clear_history_list());
4823 // Assume that the RenderFrame correctly cleared its history and commit the
4825 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4826 switches::kEnableBrowserSideNavigation
)) {
4827 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4829 contents()->GetPendingMainFrame()->
4830 set_simulate_history_list_was_cleared(true);
4831 contents()->CommitPendingNavigation();
4833 // Verify that the NavigationController's session history was correctly
4835 EXPECT_EQ(1, controller
.GetEntryCount());
4836 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4837 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4838 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4839 EXPECT_FALSE(controller
.CanGoBack());
4840 EXPECT_FALSE(controller
.CanGoForward());
4841 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4844 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4845 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4846 EXPECT_FALSE(contents()->GetDelegate());
4847 contents()->SetDelegate(delegate
.get());
4850 GURL
url("http://foo");
4851 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4853 params
.nav_entry_id
= 0;
4854 params
.did_create_new_entry
= true;
4856 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4857 params
.gesture
= NavigationGestureUser
;
4858 params
.page_state
= PageState::CreateFromURL(url
);
4859 params
.was_within_same_page
= false;
4860 params
.is_post
= true;
4862 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
4863 main_test_rfh()->PrepareForCommit();
4864 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4866 // history.replaceState() is called.
4867 GURL
replace_url("http://foo#foo");
4869 params
.nav_entry_id
= 0;
4870 params
.did_create_new_entry
= false;
4871 params
.url
= replace_url
;
4872 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4873 params
.gesture
= NavigationGestureUser
;
4874 params
.page_state
= PageState::CreateFromURL(replace_url
);
4875 params
.was_within_same_page
= true;
4876 params
.is_post
= false;
4877 params
.post_id
= -1;
4878 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url
, false);
4879 main_test_rfh()->PrepareForCommit();
4880 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4882 // Now reload. replaceState overrides the POST, so we should not show a
4883 // repost warning dialog.
4884 controller_impl().Reload(true);
4885 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4888 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4889 GURL
url("http://foo");
4890 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4892 params
.nav_entry_id
= 0;
4893 params
.did_create_new_entry
= true;
4895 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4896 params
.gesture
= NavigationGestureUser
;
4897 params
.page_state
= PageState::CreateFromURL(url
);
4898 params
.was_within_same_page
= false;
4899 params
.is_post
= true;
4901 params
.url_is_unreachable
= true;
4903 // Navigate to new page.
4905 LoadCommittedDetails details
;
4906 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4907 EXPECT_EQ(PAGE_TYPE_ERROR
,
4908 controller_impl().GetLastCommittedEntry()->GetPageType());
4909 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4912 // Navigate to existing page.
4914 params
.did_create_new_entry
= false;
4915 LoadCommittedDetails details
;
4916 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4917 EXPECT_EQ(PAGE_TYPE_ERROR
,
4918 controller_impl().GetLastCommittedEntry()->GetPageType());
4919 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4922 // Navigate to same page.
4923 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4924 // same-page transition.
4925 controller_impl().LoadURL(
4926 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4927 params
.nav_entry_id
= controller_impl().GetPendingEntry()->GetUniqueID();
4928 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4930 LoadCommittedDetails details
;
4931 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4932 EXPECT_EQ(PAGE_TYPE_ERROR
,
4933 controller_impl().GetLastCommittedEntry()->GetPageType());
4934 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4937 // Navigate in page.
4938 params
.url
= GURL("http://foo#foo");
4939 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4940 params
.was_within_same_page
= true;
4942 LoadCommittedDetails details
;
4943 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4944 EXPECT_EQ(PAGE_TYPE_ERROR
,
4945 controller_impl().GetLastCommittedEntry()->GetPageType());
4946 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, details
.type
);
4950 // Tests that if a stale navigation comes back from the renderer, it is properly
4952 TEST_F(NavigationControllerTest
, StaleNavigationsResurrected
) {
4953 NavigationControllerImpl
& controller
= controller_impl();
4954 TestNotificationTracker notifications
;
4955 RegisterForAllNavNotifications(¬ifications
, &controller
);
4958 const GURL
url_a("http://foo.com/a");
4959 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a
);
4960 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4961 navigation_entry_committed_counter_
= 0;
4962 EXPECT_EQ(1, controller
.GetEntryCount());
4963 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4966 const GURL
url_b("http://foo.com/b");
4967 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b
);
4968 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4969 navigation_entry_committed_counter_
= 0;
4970 EXPECT_EQ(2, controller
.GetEntryCount());
4971 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4972 int b_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4973 int b_page_id
= controller
.GetLastCommittedEntry()->GetPageID();
4976 controller
.GoBack();
4977 contents()->CommitPendingNavigation();
4978 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4979 navigation_entry_committed_counter_
= 0;
4980 EXPECT_EQ(2, controller
.GetEntryCount());
4981 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4983 // Start going forward to page B.
4984 controller
.GoForward();
4986 // But the renderer unilaterally navigates to page C, pruning B.
4987 const GURL
url_c("http://foo.com/c");
4988 main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c
);
4989 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4990 navigation_entry_committed_counter_
= 0;
4991 EXPECT_EQ(2, controller
.GetEntryCount());
4992 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4993 int c_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4994 EXPECT_NE(c_entry_id
, b_entry_id
);
4996 // And then the navigation to B gets committed.
4997 main_test_rfh()->SendNavigate(b_page_id
, b_entry_id
, false, url_b
);
4998 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4999 navigation_entry_committed_counter_
= 0;
5001 // Even though we were doing a history navigation, because the entry was
5002 // pruned it will end up as a *new* entry at the end of the entry list. This
5003 // means that occasionally a navigation conflict will end up with one entry
5004 // bubbling to the end of the entry list, but that's the least-bad option.
5005 EXPECT_EQ(3, controller
.GetEntryCount());
5006 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
5007 EXPECT_EQ(url_a
, controller
.GetEntryAtIndex(0)->GetURL());
5008 EXPECT_EQ(url_c
, controller
.GetEntryAtIndex(1)->GetURL());
5009 EXPECT_EQ(url_b
, controller
.GetEntryAtIndex(2)->GetURL());
5012 } // namespace content