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/site_isolation_policy.h"
26 #include "content/common/ssl_status_serialization.h"
27 #include "content/common/view_messages.h"
28 #include "content/public/browser/navigation_details.h"
29 #include "content/public/browser/notification_registrar.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/web_contents_delegate.h"
33 #include "content/public/browser/web_contents_observer.h"
34 #include "content/public/common/content_switches.h"
35 #include "content/public/common/page_state.h"
36 #include "content/public/common/page_type.h"
37 #include "content/public/common/url_constants.h"
38 #include "content/public/test/mock_render_process_host.h"
39 #include "content/public/test/test_notification_tracker.h"
40 #include "content/public/test/test_utils.h"
41 #include "content/test/test_render_frame_host.h"
42 #include "content/test/test_render_view_host.h"
43 #include "content/test/test_web_contents.h"
44 #include "net/base/net_util.h"
45 #include "skia/ext/platform_canvas.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
53 // Creates an image with a 1x1 SkBitmap of the specified |color|.
54 gfx::Image
CreateImage(SkColor color
) {
56 bitmap
.allocN32Pixels(1, 1);
57 bitmap
.eraseColor(color
);
58 return gfx::Image::CreateFrom1xBitmap(bitmap
);
61 // Returns true if images |a| and |b| have the same pixel data.
62 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
63 // Assume that if the 1x bitmaps match, the images match.
64 SkBitmap a_bitmap
= a
.AsBitmap();
65 SkBitmap b_bitmap
= b
.AsBitmap();
67 if (a_bitmap
.width() != b_bitmap
.width() ||
68 a_bitmap
.height() != b_bitmap
.height()) {
71 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
72 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
73 return memcmp(a_bitmap
.getPixels(),
75 a_bitmap
.getSize()) == 0;
78 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
80 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
81 : content::NavigationEntryScreenshotManager(owner
),
82 encoding_screenshot_in_progress_(false) {
85 ~MockScreenshotManager() override
{}
87 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
89 bitmap
.allocPixels(SkImageInfo::Make(
90 1, 1, kAlpha_8_SkColorType
, kPremul_SkAlphaType
));
91 bitmap
.eraseARGB(0, 0, 0, 0);
92 encoding_screenshot_in_progress_
= true;
93 OnScreenshotTaken(entry
->GetUniqueID(), bitmap
, content::READBACK_SUCCESS
);
94 WaitUntilScreenshotIsReady();
97 int GetScreenshotCount() {
98 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
101 void WaitUntilScreenshotIsReady() {
102 if (!encoding_screenshot_in_progress_
)
104 message_loop_runner_
= new content::MessageLoopRunner
;
105 message_loop_runner_
->Run();
109 // Overridden from content::NavigationEntryScreenshotManager:
110 void TakeScreenshotImpl(content::RenderViewHost
* host
,
111 content::NavigationEntryImpl
* entry
) override
{}
113 void OnScreenshotSet(content::NavigationEntryImpl
* entry
) override
{
114 encoding_screenshot_in_progress_
= false;
115 NavigationEntryScreenshotManager::OnScreenshotSet(entry
);
116 if (message_loop_runner_
.get())
117 message_loop_runner_
->Quit();
120 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
121 bool encoding_screenshot_in_progress_
;
123 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager
);
130 // TimeSmoother tests ----------------------------------------------------------
132 // With no duplicates, GetSmoothedTime should be the identity
134 TEST(TimeSmoother
, Basic
) {
135 NavigationControllerImpl::TimeSmoother smoother
;
136 for (int64 i
= 1; i
< 1000; ++i
) {
137 base::Time t
= base::Time::FromInternalValue(i
);
138 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
142 // With a single duplicate and timestamps thereafter increasing by one
143 // microsecond, the smoothed time should always be one behind.
144 TEST(TimeSmoother
, SingleDuplicate
) {
145 NavigationControllerImpl::TimeSmoother smoother
;
146 base::Time t
= base::Time::FromInternalValue(1);
147 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
148 for (int64 i
= 1; i
< 1000; ++i
) {
149 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
150 t
= base::Time::FromInternalValue(i
);
151 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
155 // With k duplicates and timestamps thereafter increasing by one
156 // microsecond, the smoothed time should always be k behind.
157 TEST(TimeSmoother
, ManyDuplicates
) {
158 const int64 kNumDuplicates
= 100;
159 NavigationControllerImpl::TimeSmoother smoother
;
160 base::Time t
= base::Time::FromInternalValue(1);
161 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
162 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
163 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
165 for (int64 i
= 1; i
< 1000; ++i
) {
166 base::Time expected_t
=
167 base::Time::FromInternalValue(i
+ kNumDuplicates
);
168 t
= base::Time::FromInternalValue(i
);
169 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
173 // If the clock jumps far back enough after a run of duplicates, it
174 // should immediately jump to that value.
175 TEST(TimeSmoother
, ClockBackwardsJump
) {
176 const int64 kNumDuplicates
= 100;
177 NavigationControllerImpl::TimeSmoother smoother
;
178 base::Time t
= base::Time::FromInternalValue(1000);
179 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
180 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1000);
181 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
183 t
= base::Time::FromInternalValue(500);
184 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
187 // NavigationControllerTest ----------------------------------------------------
189 class NavigationControllerTest
190 : public RenderViewHostImplTestHarness
,
191 public WebContentsObserver
{
193 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
196 void SetUp() override
{
197 RenderViewHostImplTestHarness::SetUp();
198 WebContents
* web_contents
= RenderViewHostImplTestHarness::web_contents();
199 ASSERT_TRUE(web_contents
); // The WebContents should be created by now.
200 WebContentsObserver::Observe(web_contents
);
203 // WebContentsObserver:
204 void DidStartNavigationToPendingEntry(
206 NavigationController::ReloadType reload_type
) override
{
207 navigated_url_
= url
;
210 void NavigationEntryCommitted(
211 const LoadCommittedDetails
& load_details
) override
{
212 navigation_entry_committed_counter_
++;
215 const GURL
& navigated_url() const {
216 return navigated_url_
;
219 NavigationControllerImpl
& controller_impl() {
220 return static_cast<NavigationControllerImpl
&>(controller());
223 bool HasNavigationRequest() {
224 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
225 switches::kEnableBrowserSideNavigation
)) {
226 return contents()->GetFrameTree()->root()->navigation_request() !=
229 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
)
233 const GURL
GetLastNavigationURL() {
234 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
235 switches::kEnableBrowserSideNavigation
)) {
236 NavigationRequest
* navigation_request
=
237 contents()->GetFrameTree()->root()->navigation_request();
238 CHECK(navigation_request
);
239 return navigation_request
->common_params().url
;
241 const IPC::Message
* message
=
242 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
);
244 base::Tuple
<CommonNavigationParams
, StartNavigationParams
,
245 RequestNavigationParams
> nav_params
;
246 FrameMsg_Navigate::Read(message
, &nav_params
);
247 return base::get
<0>(nav_params
).url
;
252 size_t navigation_entry_committed_counter_
;
255 void RegisterForAllNavNotifications(TestNotificationTracker
* tracker
,
256 NavigationController
* controller
) {
257 tracker
->ListenFor(NOTIFICATION_NAV_LIST_PRUNED
,
258 Source
<NavigationController
>(controller
));
259 tracker
->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED
,
260 Source
<NavigationController
>(controller
));
263 class TestWebContentsDelegate
: public WebContentsDelegate
{
265 explicit TestWebContentsDelegate() :
266 navigation_state_change_count_(0),
267 repost_form_warning_count_(0) {}
269 int navigation_state_change_count() {
270 return navigation_state_change_count_
;
273 int repost_form_warning_count() {
274 return repost_form_warning_count_
;
277 // Keep track of whether the tab has notified us of a navigation state change.
278 void NavigationStateChanged(WebContents
* source
,
279 InvalidateTypes changed_flags
) override
{
280 navigation_state_change_count_
++;
283 void ShowRepostFormWarningDialog(WebContents
* source
) override
{
284 repost_form_warning_count_
++;
288 // The number of times NavigationStateChanged has been called.
289 int navigation_state_change_count_
;
291 // The number of times ShowRepostFormWarningDialog() was called.
292 int repost_form_warning_count_
;
295 // -----------------------------------------------------------------------------
297 TEST_F(NavigationControllerTest
, Defaults
) {
298 NavigationControllerImpl
& controller
= controller_impl();
300 EXPECT_FALSE(controller
.GetPendingEntry());
301 EXPECT_FALSE(controller
.GetVisibleEntry());
302 EXPECT_FALSE(controller
.GetLastCommittedEntry());
303 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
304 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
305 EXPECT_EQ(controller
.GetEntryCount(), 0);
306 EXPECT_FALSE(controller
.CanGoBack());
307 EXPECT_FALSE(controller
.CanGoForward());
310 TEST_F(NavigationControllerTest
, GoToOffset
) {
311 NavigationControllerImpl
& controller
= controller_impl();
312 TestNotificationTracker notifications
;
313 RegisterForAllNavNotifications(¬ifications
, &controller
);
315 const int kNumUrls
= 5;
316 std::vector
<GURL
> urls(kNumUrls
);
317 for (int i
= 0; i
< kNumUrls
; ++i
) {
318 urls
[i
] = GURL(base::StringPrintf("http://www.a.com/%d", i
));
321 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[0], true);
322 main_test_rfh()->PrepareForCommit();
323 main_test_rfh()->SendNavigate(0, 0, true, urls
[0]);
324 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
325 navigation_entry_committed_counter_
= 0;
326 EXPECT_EQ(urls
[0], controller
.GetVisibleEntry()->GetVirtualURL());
327 EXPECT_FALSE(controller
.CanGoBack());
328 EXPECT_FALSE(controller
.CanGoForward());
329 EXPECT_FALSE(controller
.CanGoToOffset(1));
331 for (int i
= 1; i
<= 4; ++i
) {
332 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[i
], true);
333 main_test_rfh()->PrepareForCommit();
334 main_test_rfh()->SendNavigate(i
, 0, true, urls
[i
]);
335 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
336 navigation_entry_committed_counter_
= 0;
337 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
338 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
339 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
340 EXPECT_FALSE(controller
.CanGoToOffset(1));
343 // We have loaded 5 pages, and are currently at the last-loaded page.
347 GO_TO_MIDDLE_PAGE
= -2,
350 GO_TO_BEGINNING
= -2,
355 const int test_offsets
[NUM_TESTS
] = {
363 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
364 int offset
= test_offsets
[test
];
365 controller
.GoToOffset(offset
);
366 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
368 // Check that the GoToOffset will land on the expected page.
369 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
370 main_test_rfh()->PrepareForCommit();
371 main_test_rfh()->SendNavigate(url_index
, entry_id
, false, urls
[url_index
]);
372 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
373 navigation_entry_committed_counter_
= 0;
374 // Check that we can go to any valid offset into the history.
375 for (size_t j
= 0; j
< urls
.size(); ++j
)
376 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
377 // Check that we can't go beyond the beginning or end of the history.
378 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
379 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
383 TEST_F(NavigationControllerTest
, LoadURL
) {
384 NavigationControllerImpl
& controller
= controller_impl();
385 TestNotificationTracker notifications
;
386 RegisterForAllNavNotifications(¬ifications
, &controller
);
388 const GURL
url1("http://foo1");
389 const GURL
url2("http://foo2");
392 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
393 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
394 // Creating a pending notification should not have issued any of the
395 // notifications we're listening for.
396 EXPECT_EQ(0U, notifications
.size());
398 // The load should now be pending.
399 EXPECT_EQ(controller
.GetEntryCount(), 0);
400 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
401 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
402 EXPECT_FALSE(controller
.GetLastCommittedEntry());
403 ASSERT_TRUE(controller
.GetPendingEntry());
404 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
405 EXPECT_FALSE(controller
.CanGoBack());
406 EXPECT_FALSE(controller
.CanGoForward());
407 EXPECT_EQ(contents()->GetMaxPageID(), -1);
409 // Neither the timestamp nor the status code should have been set yet.
410 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
411 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
413 // We should have gotten no notifications from the preceeding checks.
414 EXPECT_EQ(0U, notifications
.size());
416 main_test_rfh()->PrepareForCommit();
417 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
418 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
419 navigation_entry_committed_counter_
= 0;
421 // The load should now be committed.
422 EXPECT_EQ(controller
.GetEntryCount(), 1);
423 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
424 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
425 EXPECT_TRUE(controller
.GetLastCommittedEntry());
426 EXPECT_FALSE(controller
.GetPendingEntry());
427 ASSERT_TRUE(controller
.GetVisibleEntry());
428 EXPECT_FALSE(controller
.CanGoBack());
429 EXPECT_FALSE(controller
.CanGoForward());
430 EXPECT_EQ(contents()->GetMaxPageID(), 0);
431 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
433 // The timestamp should have been set.
434 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
438 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
439 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
441 // The load should now be pending.
442 EXPECT_EQ(controller
.GetEntryCount(), 1);
443 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
444 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
445 EXPECT_TRUE(controller
.GetLastCommittedEntry());
446 ASSERT_TRUE(controller
.GetPendingEntry());
447 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
448 // TODO(darin): maybe this should really be true?
449 EXPECT_FALSE(controller
.CanGoBack());
450 EXPECT_FALSE(controller
.CanGoForward());
451 EXPECT_EQ(contents()->GetMaxPageID(), 0);
453 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
455 // Simulate the beforeunload ack for the cross-site transition, and then the
457 main_test_rfh()->PrepareForCommit();
458 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
459 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
460 navigation_entry_committed_counter_
= 0;
462 // The load should now be committed.
463 EXPECT_EQ(controller
.GetEntryCount(), 2);
464 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
465 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
466 EXPECT_TRUE(controller
.GetLastCommittedEntry());
467 EXPECT_FALSE(controller
.GetPendingEntry());
468 ASSERT_TRUE(controller
.GetVisibleEntry());
469 EXPECT_TRUE(controller
.CanGoBack());
470 EXPECT_FALSE(controller
.CanGoForward());
471 EXPECT_EQ(contents()->GetMaxPageID(), 1);
473 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
478 base::Time
GetFixedTime(base::Time time
) {
484 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
485 NavigationControllerImpl
& controller
= controller_impl();
486 TestNotificationTracker notifications
;
487 RegisterForAllNavNotifications(¬ifications
, &controller
);
489 // Set the clock to always return a timestamp of 1.
490 controller
.SetGetTimestampCallbackForTest(
491 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
493 const GURL
url1("http://foo1");
494 const GURL
url2("http://foo2");
497 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
498 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
500 main_test_rfh()->PrepareForCommit();
501 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
502 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
503 navigation_entry_committed_counter_
= 0;
507 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
508 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
510 // Simulate the beforeunload ack for the cross-site transition, and then the
512 main_test_rfh()->PrepareForCommit();
513 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
514 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
515 navigation_entry_committed_counter_
= 0;
517 // The two loads should now be committed.
518 ASSERT_EQ(controller
.GetEntryCount(), 2);
520 // Timestamps should be distinct despite the clock returning the
523 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
525 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
528 void CheckNavigationEntryMatchLoadParams(
529 NavigationController::LoadURLParams
& load_params
,
530 NavigationEntryImpl
* entry
) {
531 EXPECT_EQ(load_params
.url
, entry
->GetURL());
532 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
533 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
534 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
535 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
537 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
538 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
539 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
540 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
542 if (NavigationController::UA_OVERRIDE_INHERIT
!=
543 load_params
.override_user_agent
) {
544 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
545 load_params
.override_user_agent
);
546 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
548 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
549 entry
->GetBrowserInitiatedPostData());
550 EXPECT_EQ(load_params
.transferred_global_request_id
,
551 entry
->transferred_global_request_id());
554 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
555 NavigationControllerImpl
& controller
= controller_impl();
557 NavigationController::LoadURLParams
load_params(GURL("http://foo"));
558 load_params
.referrer
=
559 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
560 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
561 load_params
.extra_headers
= "content-type: text/plain";
562 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
563 load_params
.is_renderer_initiated
= true;
564 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
565 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
567 controller
.LoadURLWithParams(load_params
);
568 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
570 // The timestamp should not have been set yet.
572 EXPECT_TRUE(entry
->GetTimestamp().is_null());
574 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
577 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
578 NavigationControllerImpl
& controller
= controller_impl();
580 NavigationController::LoadURLParams
load_params(
581 GURL("data:text/html,dataurl"));
582 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
583 load_params
.base_url_for_data_url
= GURL("http://foo");
584 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
585 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
587 controller
.LoadURLWithParams(load_params
);
588 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
590 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
593 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
594 NavigationControllerImpl
& controller
= controller_impl();
596 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
597 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
598 load_params
.load_type
=
599 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
600 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
603 const unsigned char* raw_data
=
604 reinterpret_cast<const unsigned char*>("d\n\0a2");
605 const int length
= 5;
606 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
607 scoped_refptr
<base::RefCountedBytes
> data
=
608 base::RefCountedBytes::TakeVector(&post_data_vector
);
609 load_params
.browser_initiated_post_data
= data
.get();
611 controller
.LoadURLWithParams(load_params
);
612 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
614 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
617 // Tests what happens when the same page is loaded again. Should not create a
618 // new session history entry. This is what happens when you press enter in the
619 // URL bar to reload: a pending entry is created and then it is discarded when
620 // the load commits (because WebCore didn't actually make a new entry).
621 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
622 NavigationControllerImpl
& controller
= controller_impl();
623 TestNotificationTracker notifications
;
624 RegisterForAllNavNotifications(¬ifications
, &controller
);
626 const GURL
url1("http://foo1");
629 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
630 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
631 EXPECT_EQ(0U, notifications
.size());
632 main_test_rfh()->PrepareForCommit();
633 main_test_rfh()->SendNavigateWithTransition(
634 0, entry_id
, true, url1
, ui::PAGE_TRANSITION_TYPED
);
635 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
636 navigation_entry_committed_counter_
= 0;
638 ASSERT_TRUE(controller
.GetVisibleEntry());
639 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
640 EXPECT_FALSE(timestamp
.is_null());
643 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
644 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
645 EXPECT_EQ(0U, notifications
.size());
646 main_test_rfh()->PrepareForCommit();
647 main_test_rfh()->SendNavigateWithTransition(
648 0, entry_id
, false, url1
, ui::PAGE_TRANSITION_TYPED
);
649 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
650 navigation_entry_committed_counter_
= 0;
652 // We should not have produced a new session history entry.
653 EXPECT_EQ(controller
.GetEntryCount(), 1);
654 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
655 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
656 EXPECT_TRUE(controller
.GetLastCommittedEntry());
657 EXPECT_FALSE(controller
.GetPendingEntry());
658 ASSERT_TRUE(controller
.GetVisibleEntry());
659 EXPECT_FALSE(controller
.CanGoBack());
660 EXPECT_FALSE(controller
.CanGoForward());
662 // The timestamp should have been updated.
664 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
665 // EXPECT_GT once we guarantee that timestamps are unique.
666 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
669 // Load the same page twice, once as a GET and once as a POST.
670 // We should update the post state on the NavigationEntry.
671 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
672 NavigationControllerImpl
& controller
= controller_impl();
673 TestNotificationTracker notifications
;
674 RegisterForAllNavNotifications(¬ifications
, &controller
);
676 const GURL
url1("http://foo1");
679 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
680 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
682 params
.nav_entry_id
= controller
.GetPendingEntry()->GetUniqueID();
683 params
.did_create_new_entry
= true;
685 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
686 params
.is_post
= true;
687 params
.post_id
= 123;
688 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
689 main_test_rfh()->PrepareForCommit();
690 main_test_rfh()->SendNavigateWithParams(¶ms
);
692 // The post data should be visible.
693 NavigationEntry
* entry
= controller
.GetVisibleEntry();
695 EXPECT_TRUE(entry
->GetHasPostData());
696 EXPECT_EQ(entry
->GetPostID(), 123);
699 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
700 main_test_rfh()->PrepareForCommit();
701 main_test_rfh()->SendNavigateWithTransition(
702 0, controller
.GetPendingEntry()->GetUniqueID(),
703 false, url1
, ui::PAGE_TRANSITION_TYPED
);
705 // We should not have produced a new session history entry.
706 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
708 // The post data should have been cleared due to the GET.
709 EXPECT_FALSE(entry
->GetHasPostData());
710 EXPECT_EQ(entry
->GetPostID(), 0);
713 // Tests loading a URL but discarding it before the load commits.
714 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
715 NavigationControllerImpl
& controller
= controller_impl();
716 TestNotificationTracker notifications
;
717 RegisterForAllNavNotifications(¬ifications
, &controller
);
719 const GURL
url1("http://foo1");
720 const GURL
url2("http://foo2");
723 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
724 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
725 EXPECT_EQ(0U, notifications
.size());
726 main_test_rfh()->PrepareForCommit();
727 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
728 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
729 navigation_entry_committed_counter_
= 0;
731 ASSERT_TRUE(controller
.GetVisibleEntry());
732 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
733 EXPECT_FALSE(timestamp
.is_null());
736 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
737 controller
.DiscardNonCommittedEntries();
738 EXPECT_EQ(0U, notifications
.size());
740 // Should not have produced a new session history entry.
741 EXPECT_EQ(controller
.GetEntryCount(), 1);
742 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
743 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
744 EXPECT_TRUE(controller
.GetLastCommittedEntry());
745 EXPECT_FALSE(controller
.GetPendingEntry());
746 ASSERT_TRUE(controller
.GetVisibleEntry());
747 EXPECT_FALSE(controller
.CanGoBack());
748 EXPECT_FALSE(controller
.CanGoForward());
750 // Timestamp should not have changed.
751 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
754 // Tests navigations that come in unrequested. This happens when the user
755 // navigates from the web page, and here we test that there is no pending entry.
756 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
757 NavigationControllerImpl
& controller
= controller_impl();
758 TestNotificationTracker notifications
;
759 RegisterForAllNavNotifications(¬ifications
, &controller
);
761 // First make an existing committed entry.
762 const GURL
kExistingURL1("http://eh");
764 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
765 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
766 main_test_rfh()->PrepareForCommit();
767 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
768 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
769 navigation_entry_committed_counter_
= 0;
771 // Do a new navigation without making a pending one.
772 const GURL
kNewURL("http://see");
773 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL
);
775 // There should no longer be any pending entry, and the second navigation we
776 // just made should be committed.
777 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
778 navigation_entry_committed_counter_
= 0;
779 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
780 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
781 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
784 // Tests navigating to a new URL when there is a new pending navigation that is
785 // not the one that just loaded. This will happen if the user types in a URL to
786 // somewhere slow, and then navigates the current page before the typed URL
788 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
789 NavigationControllerImpl
& controller
= controller_impl();
790 TestNotificationTracker notifications
;
791 RegisterForAllNavNotifications(¬ifications
, &controller
);
793 // First make an existing committed entry.
794 const GURL
kExistingURL1("http://eh");
796 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
797 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
798 main_test_rfh()->PrepareForCommit();
799 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
800 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
801 navigation_entry_committed_counter_
= 0;
803 // Make a pending entry to somewhere new.
804 const GURL
kExistingURL2("http://bee");
806 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
807 EXPECT_EQ(0U, notifications
.size());
809 // After the beforeunload but before it commits...
810 main_test_rfh()->PrepareForCommit();
812 // ... Do a new navigation.
813 const GURL
kNewURL("http://see");
814 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
815 main_test_rfh()->PrepareForCommit();
816 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL
);
818 // There should no longer be any pending entry, and the third navigation we
819 // just made should be committed.
820 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
821 navigation_entry_committed_counter_
= 0;
822 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
823 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
824 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
827 // Tests navigating to a new URL when there is a pending back/forward
828 // navigation. This will happen if the user hits back, but before that commits,
829 // they navigate somewhere new.
830 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
831 NavigationControllerImpl
& controller
= controller_impl();
832 TestNotificationTracker notifications
;
833 RegisterForAllNavNotifications(¬ifications
, &controller
);
835 // First make some history.
836 const GURL
kExistingURL1("http://foo/eh");
838 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
839 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
840 main_test_rfh()->PrepareForCommit();
841 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
842 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
843 navigation_entry_committed_counter_
= 0;
845 const GURL
kExistingURL2("http://foo/bee");
847 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
848 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
849 main_test_rfh()->PrepareForCommit();
850 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
851 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
852 navigation_entry_committed_counter_
= 0;
854 // Now make a pending back/forward navigation. The zeroth entry should be
857 EXPECT_EQ(0U, notifications
.size());
858 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
859 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
861 // Before that commits, do a new navigation.
862 const GURL
kNewURL("http://foo/see");
863 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
864 main_test_rfh()->PrepareForCommit();
865 main_test_rfh()->SendNavigate(3, 0, true, kNewURL
);
867 // There should no longer be any pending entry, and the new navigation we
868 // just made should be committed.
869 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
870 navigation_entry_committed_counter_
= 0;
871 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
872 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
873 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
876 // Tests navigating to a new URL when there is a pending back/forward
877 // navigation to a cross-process, privileged URL. This will happen if the user
878 // hits back, but before that commits, they navigate somewhere new.
879 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
880 NavigationControllerImpl
& controller
= controller_impl();
881 TestNotificationTracker notifications
;
882 RegisterForAllNavNotifications(¬ifications
, &controller
);
884 // First make some history, starting with a privileged URL.
885 const GURL
kExistingURL1("http://privileged");
887 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
888 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
889 // Pretend it has bindings so we can tell if we incorrectly copy it.
890 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
891 main_test_rfh()->PrepareForCommit();
892 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
893 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
894 navigation_entry_committed_counter_
= 0;
896 // Navigate cross-process to a second URL.
897 const GURL
kExistingURL2("http://foo/eh");
899 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
900 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
901 main_test_rfh()->PrepareForCommit();
902 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
903 foo_rfh
->SendNavigate(1, entry_id
, true, kExistingURL2
);
904 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
905 navigation_entry_committed_counter_
= 0;
907 // Now make a pending back/forward navigation to a privileged entry.
908 // The zeroth entry should be pending.
910 foo_rfh
->SendBeforeUnloadACK(true);
911 EXPECT_EQ(0U, notifications
.size());
912 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
913 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
914 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
916 // Before that commits, do a new navigation.
917 const GURL
kNewURL("http://foo/bee");
918 foo_rfh
->SendRendererInitiatedNavigationRequest(kNewURL
, true);
919 foo_rfh
->PrepareForCommit();
920 foo_rfh
->SendNavigate(3, 0, true, kNewURL
);
922 // There should no longer be any pending entry, and the new navigation we
923 // just made should be committed.
924 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
925 navigation_entry_committed_counter_
= 0;
926 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
927 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
928 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
929 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
932 // Tests navigating to an existing URL when there is a pending new navigation.
933 // This will happen if the user enters a URL, but before that commits, the
934 // current page fires history.back().
935 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
936 NavigationControllerImpl
& controller
= controller_impl();
937 TestNotificationTracker notifications
;
938 RegisterForAllNavNotifications(¬ifications
, &controller
);
940 // First make some history.
941 const GURL
kExistingURL1("http://foo/eh");
943 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
944 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
945 main_test_rfh()->PrepareForCommit();
946 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
947 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
948 navigation_entry_committed_counter_
= 0;
950 const GURL
kExistingURL2("http://foo/bee");
952 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
953 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
954 main_test_rfh()->PrepareForCommit();
955 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
956 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
957 navigation_entry_committed_counter_
= 0;
959 // A back navigation comes in from the renderer...
960 controller
.GoToOffset(-1);
961 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
963 // ...while the user tries to navigate to a new page...
964 const GURL
kNewURL("http://foo/see");
966 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
967 EXPECT_EQ(0U, notifications
.size());
968 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
969 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
971 // ...and the back navigation commits.
972 main_test_rfh()->PrepareForCommit();
973 main_test_rfh()->SendNavigate(0, entry_id
, false, kExistingURL1
);
975 // There should no longer be any pending entry, and the back navigation should
977 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
978 navigation_entry_committed_counter_
= 0;
979 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
980 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
981 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
984 // Tests an ignored navigation when there is a pending new navigation.
985 // This will happen if the user enters a URL, but before that commits, the
986 // current blank page reloads. See http://crbug.com/77507.
987 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
988 NavigationControllerImpl
& controller
= controller_impl();
989 TestNotificationTracker notifications
;
990 RegisterForAllNavNotifications(¬ifications
, &controller
);
992 // Set a WebContentsDelegate to listen for state changes.
993 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
994 EXPECT_FALSE(contents()->GetDelegate());
995 contents()->SetDelegate(delegate
.get());
997 // Without any navigations, the renderer starts at about:blank.
998 const GURL
kExistingURL(url::kAboutBlankURL
);
1000 // Now make a pending new navigation.
1001 const GURL
kNewURL("http://eh");
1003 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1004 EXPECT_EQ(0U, notifications
.size());
1005 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1006 EXPECT_TRUE(controller
.GetPendingEntry());
1007 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1008 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1010 // Before that commits, a document.write and location.reload can cause the
1011 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1012 // PlzNavigate: this will stop the old navigation and start a new one.
1013 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL
, true);
1014 main_test_rfh()->SendNavigate(-1, 0, false, kExistingURL
);
1016 // This should clear the pending entry and notify of a navigation state
1017 // change, so that we do not keep displaying kNewURL.
1018 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1019 EXPECT_FALSE(controller
.GetPendingEntry());
1020 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1021 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1022 switches::kEnableBrowserSideNavigation
))
1023 EXPECT_EQ(4, delegate
->navigation_state_change_count());
1025 EXPECT_EQ(2, delegate
->navigation_state_change_count());
1027 contents()->SetDelegate(NULL
);
1030 // Tests that the pending entry state is correct after an abort.
1031 // We do not want to clear the pending entry, so that the user doesn't
1032 // lose a typed URL. (See http://crbug.com/9682.)
1033 TEST_F(NavigationControllerTest
, LoadURL_AbortDoesntCancelPending
) {
1034 NavigationControllerImpl
& controller
= controller_impl();
1035 TestNotificationTracker notifications
;
1036 RegisterForAllNavNotifications(¬ifications
, &controller
);
1038 // Set a WebContentsDelegate to listen for state changes.
1039 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1040 EXPECT_FALSE(contents()->GetDelegate());
1041 contents()->SetDelegate(delegate
.get());
1043 // Start with a pending new navigation.
1044 const GURL
kNewURL("http://eh");
1046 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1047 main_test_rfh()->PrepareForCommit();
1048 EXPECT_EQ(0U, notifications
.size());
1049 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1050 EXPECT_TRUE(controller
.GetPendingEntry());
1051 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1052 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1054 // It may abort before committing, if it's a download or due to a stop or
1055 // a new navigation from the user.
1056 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1057 params
.error_code
= net::ERR_ABORTED
;
1058 params
.error_description
= base::string16();
1059 params
.url
= kNewURL
;
1060 params
.showing_repost_interstitial
= false;
1061 main_test_rfh()->OnMessageReceived(
1062 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1065 // This should not clear the pending entry or notify of a navigation state
1066 // change, so that we keep displaying kNewURL (until the user clears it).
1067 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1068 EXPECT_TRUE(controller
.GetPendingEntry());
1069 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1070 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1071 NavigationEntry
* pending_entry
= controller
.GetPendingEntry();
1073 // Ensure that a reload keeps the same pending entry.
1074 controller
.Reload(true);
1075 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1076 EXPECT_TRUE(controller
.GetPendingEntry());
1077 EXPECT_EQ(pending_entry
, controller
.GetPendingEntry());
1078 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1080 contents()->SetDelegate(NULL
);
1083 // Tests that the pending URL is not visible during a renderer-initiated
1084 // redirect and abort. See http://crbug.com/83031.
1085 TEST_F(NavigationControllerTest
, LoadURL_RedirectAbortDoesntShowPendingURL
) {
1086 NavigationControllerImpl
& controller
= controller_impl();
1087 TestNotificationTracker notifications
;
1088 RegisterForAllNavNotifications(¬ifications
, &controller
);
1090 // First make an existing committed entry.
1091 const GURL
kExistingURL("http://foo/eh");
1092 controller
.LoadURL(kExistingURL
, content::Referrer(),
1093 ui::PAGE_TRANSITION_TYPED
, std::string());
1094 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1095 main_test_rfh()->PrepareForCommit();
1096 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL
);
1097 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1098 navigation_entry_committed_counter_
= 0;
1100 // Set a WebContentsDelegate to listen for state changes.
1101 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1102 EXPECT_FALSE(contents()->GetDelegate());
1103 contents()->SetDelegate(delegate
.get());
1105 // Now make a pending new navigation, initiated by the renderer.
1106 const GURL
kNewURL("http://foo/bee");
1107 NavigationController::LoadURLParams
load_url_params(kNewURL
);
1108 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
1109 load_url_params
.is_renderer_initiated
= true;
1110 controller
.LoadURLWithParams(load_url_params
);
1111 EXPECT_EQ(0U, notifications
.size());
1112 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1113 EXPECT_TRUE(controller
.GetPendingEntry());
1114 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1115 EXPECT_EQ(0, delegate
->navigation_state_change_count());
1117 // The visible entry should be the last committed URL, not the pending one.
1118 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1120 // Now the navigation redirects. (There is no corresponding message here.)
1121 const GURL
kRedirectURL("http://foo/see");
1123 // We don't want to change the NavigationEntry's url, in case it cancels.
1124 // Prevents regression of http://crbug.com/77786.
1125 EXPECT_EQ(kNewURL
, controller
.GetPendingEntry()->GetURL());
1127 // It may abort before committing, if it's a download or due to a stop or
1128 // a new navigation from the user.
1129 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1130 params
.error_code
= net::ERR_ABORTED
;
1131 params
.error_description
= base::string16();
1132 params
.url
= kRedirectURL
;
1133 params
.showing_repost_interstitial
= false;
1134 main_test_rfh()->OnMessageReceived(
1135 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1138 // Because the pending entry is renderer initiated and not visible, we
1139 // clear it when it fails.
1140 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1141 EXPECT_FALSE(controller
.GetPendingEntry());
1142 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1143 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1145 // The visible entry should be the last committed URL, not the pending one,
1146 // so that no spoof is possible.
1147 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1149 contents()->SetDelegate(NULL
);
1152 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1153 // at the time they committed. http://crbug.com/173672.
1154 TEST_F(NavigationControllerTest
, LoadURL_WithBindings
) {
1155 NavigationControllerImpl
& controller
= controller_impl();
1156 TestNotificationTracker notifications
;
1157 RegisterForAllNavNotifications(¬ifications
, &controller
);
1158 std::vector
<GURL
> url_chain
;
1160 const GURL
url1("http://foo1");
1161 const GURL
url2("http://foo2");
1163 // Navigate to a first, unprivileged URL.
1165 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1166 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings
,
1167 controller
.GetPendingEntry()->bindings());
1168 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1171 TestRenderFrameHost
* orig_rfh
= contents()->GetMainFrame();
1172 orig_rfh
->PrepareForCommit();
1173 orig_rfh
->SendNavigate(0, entry1_id
, true, url1
);
1174 EXPECT_EQ(controller
.GetEntryCount(), 1);
1175 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1176 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1177 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1179 // Manually increase the number of active frames in the SiteInstance
1180 // that orig_rfh belongs to, to prevent it from being destroyed when
1181 // it gets swapped out, so that we can reuse orig_rfh when the
1182 // controller goes back.
1183 orig_rfh
->GetSiteInstance()->increment_active_frame_count();
1185 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1186 // transition, and set bindings on the pending RenderViewHost to simulate a
1189 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1190 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1191 orig_rfh
->PrepareForCommit();
1192 TestRenderFrameHost
* new_rfh
= contents()->GetPendingMainFrame();
1193 new_rfh
->GetRenderViewHost()->AllowBindings(1);
1194 new_rfh
->SendNavigate(1, entry_id
, true, url2
);
1196 // The second load should be committed, and bindings should be remembered.
1197 EXPECT_EQ(controller
.GetEntryCount(), 2);
1198 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1199 EXPECT_TRUE(controller
.CanGoBack());
1200 EXPECT_EQ(1, controller
.GetLastCommittedEntry()->bindings());
1202 // Going back, the first entry should still appear unprivileged.
1203 controller
.GoBack();
1204 new_rfh
->PrepareForCommit();
1205 contents()->GetPendingMainFrame()->SendNavigate(0, entry1_id
, false, url1
);
1206 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1207 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1210 TEST_F(NavigationControllerTest
, Reload
) {
1211 NavigationControllerImpl
& controller
= controller_impl();
1212 TestNotificationTracker notifications
;
1213 RegisterForAllNavNotifications(¬ifications
, &controller
);
1215 const GURL
url1("http://foo1");
1218 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1219 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1220 EXPECT_EQ(0U, notifications
.size());
1221 main_test_rfh()->PrepareForCommit();
1222 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1223 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1224 navigation_entry_committed_counter_
= 0;
1225 ASSERT_TRUE(controller
.GetVisibleEntry());
1226 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1227 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1229 controller
.Reload(true);
1230 EXPECT_EQ(0U, notifications
.size());
1232 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
1233 EXPECT_FALSE(timestamp
.is_null());
1235 // The reload is pending.
1236 EXPECT_EQ(controller
.GetEntryCount(), 1);
1237 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1238 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1239 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1240 EXPECT_TRUE(controller
.GetPendingEntry());
1241 EXPECT_FALSE(controller
.CanGoBack());
1242 EXPECT_FALSE(controller
.CanGoForward());
1243 // Make sure the title has been cleared (will be redrawn just after reload).
1244 // Avoids a stale cached title when the new page being reloaded has no title.
1245 // See http://crbug.com/96041.
1246 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1248 main_test_rfh()->PrepareForCommit();
1249 main_test_rfh()->SendNavigate(0, entry_id
, false, url1
);
1250 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1251 navigation_entry_committed_counter_
= 0;
1253 // Now the reload is committed.
1254 EXPECT_EQ(controller
.GetEntryCount(), 1);
1255 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1256 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1257 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1258 EXPECT_FALSE(controller
.GetPendingEntry());
1259 EXPECT_FALSE(controller
.CanGoBack());
1260 EXPECT_FALSE(controller
.CanGoForward());
1262 // The timestamp should have been updated.
1263 ASSERT_TRUE(controller
.GetVisibleEntry());
1264 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
1267 // Tests what happens when a reload navigation produces a new page.
1268 TEST_F(NavigationControllerTest
, Reload_GeneratesNewPage
) {
1269 NavigationControllerImpl
& controller
= controller_impl();
1270 TestNotificationTracker notifications
;
1271 RegisterForAllNavNotifications(¬ifications
, &controller
);
1273 const GURL
url1("http://foo1");
1274 const GURL
url2("http://foo2");
1277 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1278 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1279 main_test_rfh()->PrepareForCommit();
1280 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1281 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1282 navigation_entry_committed_counter_
= 0;
1283 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1285 controller
.Reload(true);
1286 EXPECT_EQ(0U, notifications
.size());
1288 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1289 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1290 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1291 navigation_entry_committed_counter_
= 0;
1293 // Now the reload is committed.
1294 EXPECT_EQ(controller
.GetEntryCount(), 2);
1295 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1296 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1297 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1298 EXPECT_FALSE(controller
.GetPendingEntry());
1299 EXPECT_TRUE(controller
.CanGoBack());
1300 EXPECT_FALSE(controller
.CanGoForward());
1303 // This test ensures that when a guest renderer reloads, the reload goes through
1304 // without ending up in the "we have a wrong process for the URL" branch in
1305 // NavigationControllerImpl::ReloadInternal.
1306 TEST_F(NavigationControllerTest
, ReloadWithGuest
) {
1307 NavigationControllerImpl
& controller
= controller_impl();
1309 const GURL
url1("http://foo1");
1311 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1312 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1313 main_test_rfh()->PrepareForCommit();
1314 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1315 ASSERT_TRUE(controller
.GetVisibleEntry());
1317 // Make the entry believe its RenderProcessHost is a guest.
1318 NavigationEntryImpl
* entry1
= controller
.GetVisibleEntry();
1319 reinterpret_cast<MockRenderProcessHost
*>(
1320 entry1
->site_instance()->GetProcess())->set_is_for_guests_only(true);
1323 controller
.Reload(true);
1325 // The reload is pending. Check that the NavigationEntry didn't get replaced
1326 // because of having the wrong process.
1327 EXPECT_EQ(controller
.GetEntryCount(), 1);
1328 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1329 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1331 NavigationEntryImpl
* entry2
= controller
.GetPendingEntry();
1332 EXPECT_EQ(entry1
, entry2
);
1335 #if !defined(OS_ANDROID) // http://crbug.com/157428
1337 void SetOriginalURL(const GURL
& url
,
1338 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1339 params
->original_request_url
= url
;
1343 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1344 NavigationControllerImpl
& controller
= controller_impl();
1345 TestNotificationTracker notifications
;
1346 RegisterForAllNavNotifications(¬ifications
, &controller
);
1348 const GURL
original_url("http://foo1");
1349 const GURL
final_url("http://foo2");
1350 auto set_original_url_callback
= base::Bind(SetOriginalURL
, original_url
);
1352 // Load up the original URL, but get redirected.
1354 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1355 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1356 EXPECT_EQ(0U, notifications
.size());
1357 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1358 main_test_rfh()->SendNavigateWithModificationCallback(
1359 0, entry_id
, true, final_url
, set_original_url_callback
);
1360 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1361 navigation_entry_committed_counter_
= 0;
1362 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1364 // The NavigationEntry should save both the original URL and the final
1367 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1368 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1370 // Reload using the original URL.
1371 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1372 controller
.ReloadOriginalRequestURL(false);
1373 EXPECT_EQ(0U, notifications
.size());
1375 // The reload is pending. The request should point to the original URL.
1376 EXPECT_EQ(original_url
, navigated_url());
1377 EXPECT_EQ(controller
.GetEntryCount(), 1);
1378 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1379 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1380 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1381 EXPECT_TRUE(controller
.GetPendingEntry());
1382 EXPECT_FALSE(controller
.CanGoBack());
1383 EXPECT_FALSE(controller
.CanGoForward());
1385 // Make sure the title has been cleared (will be redrawn just after reload).
1386 // Avoids a stale cached title when the new page being reloaded has no title.
1387 // See http://crbug.com/96041.
1388 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1390 // Send that the navigation has proceeded; say it got redirected again.
1391 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1392 main_test_rfh()->SendNavigate(0, entry_id
, false, final_url
);
1393 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1394 navigation_entry_committed_counter_
= 0;
1396 // Now the reload is committed.
1397 EXPECT_EQ(controller
.GetEntryCount(), 1);
1398 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1399 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1400 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1401 EXPECT_FALSE(controller
.GetPendingEntry());
1402 EXPECT_FALSE(controller
.CanGoBack());
1403 EXPECT_FALSE(controller
.CanGoForward());
1406 #endif // !defined(OS_ANDROID)
1408 // Test that certain non-persisted NavigationEntryImpl values get reset after
1410 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1411 NavigationControllerImpl
& controller
= controller_impl();
1413 // The value of "should replace entry" will be tested, but it's an error to
1414 // specify it when there are no entries. Create a simple entry to be replaced.
1415 const GURL
url0("http://foo/0");
1417 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1418 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1419 main_test_rfh()->PrepareForCommit();
1420 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
1422 // Set up the pending entry.
1423 const GURL
url1("http://foo/1");
1425 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1426 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1428 // Set up some sample values.
1429 const unsigned char* raw_data
=
1430 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1431 const int length
= 11;
1432 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1433 scoped_refptr
<base::RefCountedBytes
> post_data
=
1434 base::RefCountedBytes::TakeVector(&post_data_vector
);
1435 GlobalRequestID
transfer_id(3, 4);
1437 // Set non-persisted values on the pending entry.
1438 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1439 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1440 pending_entry
->set_is_renderer_initiated(true);
1441 pending_entry
->set_transferred_global_request_id(transfer_id
);
1442 pending_entry
->set_should_replace_entry(true);
1443 pending_entry
->set_should_clear_history_list(true);
1444 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1445 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1446 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1447 EXPECT_TRUE(pending_entry
->should_replace_entry());
1448 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1450 // Fake a commit response.
1451 main_test_rfh()->PrepareForCommit();
1452 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
1454 // Certain values that are only used for pending entries get reset after
1456 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1457 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1458 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1459 EXPECT_EQ(GlobalRequestID(-1, -1),
1460 committed_entry
->transferred_global_request_id());
1461 EXPECT_FALSE(committed_entry
->should_replace_entry());
1462 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1466 void SetRedirects(const std::vector
<GURL
>& redirects
,
1467 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1468 params
->redirects
= redirects
;
1472 // Test that Redirects are preserved after a commit.
1473 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1474 NavigationControllerImpl
& controller
= controller_impl();
1475 const GURL
url1("http://foo1");
1476 const GURL
url2("http://foo2");
1478 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1479 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1481 // Set up some redirect values.
1482 std::vector
<GURL
> redirects
;
1483 redirects
.push_back(url2
);
1484 auto set_redirects_callback
= base::Bind(SetRedirects
, redirects
);
1486 // Set redirects on the pending entry.
1487 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1488 pending_entry
->SetRedirectChain(redirects
);
1489 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1490 EXPECT_EQ(url2
, pending_entry
->GetRedirectChain()[0]);
1492 // Normal navigation will preserve redirects in the committed entry.
1493 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1494 main_test_rfh()->SendNavigateWithModificationCallback(0, entry_id
, true, url1
,
1495 set_redirects_callback
);
1496 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1497 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1498 EXPECT_EQ(url2
, committed_entry
->GetRedirectChain()[0]);
1501 // Tests what happens when we navigate back successfully
1502 TEST_F(NavigationControllerTest
, Back
) {
1503 NavigationControllerImpl
& controller
= controller_impl();
1504 TestNotificationTracker notifications
;
1505 RegisterForAllNavNotifications(¬ifications
, &controller
);
1507 const GURL
url1("http://foo1");
1508 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1509 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1510 navigation_entry_committed_counter_
= 0;
1512 const GURL
url2("http://foo2");
1513 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
1514 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1515 navigation_entry_committed_counter_
= 0;
1517 controller
.GoBack();
1518 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1519 EXPECT_EQ(0U, notifications
.size());
1521 // We should now have a pending navigation to go back.
1522 EXPECT_EQ(controller
.GetEntryCount(), 2);
1523 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1524 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1525 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1526 EXPECT_TRUE(controller
.GetPendingEntry());
1527 EXPECT_FALSE(controller
.CanGoBack());
1528 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1529 EXPECT_TRUE(controller
.CanGoForward());
1530 EXPECT_TRUE(controller
.CanGoToOffset(1));
1531 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go forward 2 steps.
1533 // Timestamp for entry 1 should be on or after that of entry 0.
1534 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1535 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1536 controller
.GetEntryAtIndex(0)->GetTimestamp());
1538 main_test_rfh()->PrepareForCommit();
1539 main_test_rfh()->SendNavigate(0, entry_id
, false, url2
);
1540 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1541 navigation_entry_committed_counter_
= 0;
1543 // The back navigation completed successfully.
1544 EXPECT_EQ(controller
.GetEntryCount(), 2);
1545 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1546 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1547 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1548 EXPECT_FALSE(controller
.GetPendingEntry());
1549 EXPECT_FALSE(controller
.CanGoBack());
1550 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1551 EXPECT_TRUE(controller
.CanGoForward());
1552 EXPECT_TRUE(controller
.CanGoToOffset(1));
1553 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1555 // Timestamp for entry 0 should be on or after that of entry 1
1556 // (since we went back to it).
1557 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1558 controller
.GetEntryAtIndex(1)->GetTimestamp());
1561 // Tests what happens when a back navigation produces a new page.
1562 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1563 NavigationControllerImpl
& controller
= controller_impl();
1564 TestNotificationTracker notifications
;
1565 RegisterForAllNavNotifications(¬ifications
, &controller
);
1567 const GURL
url1("http://foo/1");
1568 const GURL
url2("http://foo/2");
1569 const GURL
url3("http://foo/3");
1572 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1573 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1574 main_test_rfh()->PrepareForCommit();
1575 main_test_rfh()->SendNavigate(0, entry1_id
, true, url1
);
1576 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1577 navigation_entry_committed_counter_
= 0;
1578 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1581 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1582 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1583 main_test_rfh()->PrepareForCommit();
1584 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1585 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1586 navigation_entry_committed_counter_
= 0;
1588 controller
.GoBack();
1589 EXPECT_EQ(0U, notifications
.size());
1591 // We should now have a pending navigation to go back.
1592 EXPECT_EQ(controller
.GetEntryCount(), 2);
1593 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1594 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1595 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1596 EXPECT_TRUE(controller
.GetPendingEntry());
1597 EXPECT_FALSE(controller
.CanGoBack());
1598 EXPECT_TRUE(controller
.CanGoForward());
1600 main_test_rfh()->PrepareForCommitWithServerRedirect(url3
);
1601 main_test_rfh()->SendNavigate(2, entry1_id
, true, url3
);
1602 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1603 navigation_entry_committed_counter_
= 0;
1605 // The back navigation resulted in a completely new navigation.
1606 // TODO(darin): perhaps this behavior will be confusing to users?
1607 EXPECT_EQ(controller
.GetEntryCount(), 3);
1608 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1609 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1610 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1611 EXPECT_FALSE(controller
.GetPendingEntry());
1612 EXPECT_TRUE(controller
.CanGoBack());
1613 EXPECT_FALSE(controller
.CanGoForward());
1616 // Receives a back message when there is a new pending navigation entry.
1617 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1618 NavigationControllerImpl
& controller
= controller_impl();
1619 TestNotificationTracker notifications
;
1620 RegisterForAllNavNotifications(¬ifications
, &controller
);
1622 const GURL
kUrl1("http://foo1");
1623 const GURL
kUrl2("http://foo2");
1624 const GURL
kUrl3("http://foo3");
1626 // First navigate two places so we have some back history.
1627 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
1628 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1629 navigation_entry_committed_counter_
= 0;
1631 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1632 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
1633 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1634 navigation_entry_committed_counter_
= 0;
1636 // Now start a new pending navigation and go back before it commits.
1638 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1639 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1640 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1641 controller
.GoBack();
1643 // The pending navigation should now be the "back" item and the new one
1645 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1646 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1649 // Tests what happens when we navigate forward successfully.
1650 TEST_F(NavigationControllerTest
, Forward
) {
1651 NavigationControllerImpl
& controller
= controller_impl();
1652 TestNotificationTracker notifications
;
1653 RegisterForAllNavNotifications(¬ifications
, &controller
);
1655 const GURL
url1("http://foo1");
1656 const GURL
url2("http://foo2");
1658 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1659 main_test_rfh()->PrepareForCommit();
1660 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1661 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1662 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1663 navigation_entry_committed_counter_
= 0;
1665 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1666 main_test_rfh()->PrepareForCommit();
1667 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1668 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1669 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1670 navigation_entry_committed_counter_
= 0;
1672 controller
.GoBack();
1673 main_test_rfh()->PrepareForCommit();
1674 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1675 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1676 navigation_entry_committed_counter_
= 0;
1678 controller
.GoForward();
1680 // We should now have a pending navigation to go forward.
1681 EXPECT_EQ(controller
.GetEntryCount(), 2);
1682 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1683 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1684 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1685 EXPECT_TRUE(controller
.GetPendingEntry());
1686 EXPECT_TRUE(controller
.CanGoBack());
1687 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1688 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1689 EXPECT_FALSE(controller
.CanGoForward());
1690 EXPECT_FALSE(controller
.CanGoToOffset(1));
1692 // Timestamp for entry 0 should be on or after that of entry 1
1693 // (since we went back to it).
1694 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1695 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1696 controller
.GetEntryAtIndex(1)->GetTimestamp());
1698 main_test_rfh()->PrepareForCommit();
1699 main_test_rfh()->SendNavigate(1, entry2
->GetUniqueID(), false, url2
);
1700 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1701 navigation_entry_committed_counter_
= 0;
1703 // The forward navigation completed successfully.
1704 EXPECT_EQ(controller
.GetEntryCount(), 2);
1705 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1706 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1707 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1708 EXPECT_FALSE(controller
.GetPendingEntry());
1709 EXPECT_TRUE(controller
.CanGoBack());
1710 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1711 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1712 EXPECT_FALSE(controller
.CanGoForward());
1713 EXPECT_FALSE(controller
.CanGoToOffset(1));
1715 // Timestamp for entry 1 should be on or after that of entry 0
1716 // (since we went forward to it).
1717 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1718 controller
.GetEntryAtIndex(0)->GetTimestamp());
1721 // Tests what happens when a forward navigation produces a new page.
1722 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1723 NavigationControllerImpl
& controller
= controller_impl();
1724 TestNotificationTracker notifications
;
1725 RegisterForAllNavNotifications(¬ifications
, &controller
);
1727 const GURL
url1("http://foo1");
1728 const GURL
url2("http://foo2");
1729 const GURL
url3("http://foo3");
1731 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1732 main_test_rfh()->PrepareForCommit();
1733 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1734 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1735 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1736 navigation_entry_committed_counter_
= 0;
1737 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1738 main_test_rfh()->PrepareForCommit();
1739 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1740 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1741 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1742 navigation_entry_committed_counter_
= 0;
1744 controller
.GoBack();
1745 main_test_rfh()->PrepareForCommit();
1746 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1747 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1748 navigation_entry_committed_counter_
= 0;
1750 controller
.GoForward();
1751 EXPECT_EQ(0U, notifications
.size());
1753 // Should now have a pending navigation to go forward.
1754 EXPECT_EQ(controller
.GetEntryCount(), 2);
1755 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1756 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1757 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1758 EXPECT_TRUE(controller
.GetPendingEntry());
1759 EXPECT_TRUE(controller
.CanGoBack());
1760 EXPECT_FALSE(controller
.CanGoForward());
1762 main_test_rfh()->PrepareForCommit();
1763 main_test_rfh()->SendNavigate(2, entry2
->GetUniqueID(), true, url3
);
1764 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1765 navigation_entry_committed_counter_
= 0;
1766 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1768 EXPECT_EQ(controller
.GetEntryCount(), 2);
1769 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1770 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1771 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1772 EXPECT_FALSE(controller
.GetPendingEntry());
1773 EXPECT_TRUE(controller
.CanGoBack());
1774 EXPECT_FALSE(controller
.CanGoForward());
1777 // Two consecutive navigations for the same URL entered in should be considered
1778 // as SAME_PAGE navigation even when we are redirected to some other page.
1779 TEST_F(NavigationControllerTest
, Redirect
) {
1780 NavigationControllerImpl
& controller
= controller_impl();
1781 TestNotificationTracker notifications
;
1782 RegisterForAllNavNotifications(¬ifications
, &controller
);
1784 const GURL
url1("http://foo1");
1785 const GURL
url2("http://foo2"); // Redirection target
1789 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1790 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1792 EXPECT_EQ(0U, notifications
.size());
1794 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1796 params
.nav_entry_id
= entry_id
;
1797 params
.did_create_new_entry
= true;
1799 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1800 params
.redirects
.push_back(GURL("http://foo1"));
1801 params
.redirects
.push_back(GURL("http://foo2"));
1802 params
.should_update_history
= false;
1803 params
.gesture
= NavigationGestureAuto
;
1804 params
.is_post
= false;
1805 params
.page_state
= PageState::CreateFromURL(url2
);
1807 LoadCommittedDetails details
;
1809 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1811 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1812 navigation_entry_committed_counter_
= 0;
1816 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1817 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1819 EXPECT_TRUE(controller
.GetPendingEntry());
1820 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1821 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1823 params
.nav_entry_id
= entry_id
;
1824 params
.did_create_new_entry
= false;
1826 EXPECT_EQ(0U, notifications
.size());
1827 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1829 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1830 navigation_entry_committed_counter_
= 0;
1832 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1833 EXPECT_EQ(controller
.GetEntryCount(), 1);
1834 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1835 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1836 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1837 EXPECT_FALSE(controller
.GetPendingEntry());
1838 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1840 EXPECT_FALSE(controller
.CanGoBack());
1841 EXPECT_FALSE(controller
.CanGoForward());
1844 // Similar to Redirect above, but the first URL is requested by POST,
1845 // the second URL is requested by GET. NavigationEntry::has_post_data_
1846 // must be cleared. http://crbug.com/21245
1847 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1848 NavigationControllerImpl
& controller
= controller_impl();
1849 TestNotificationTracker notifications
;
1850 RegisterForAllNavNotifications(¬ifications
, &controller
);
1852 const GURL
url1("http://foo1");
1853 const GURL
url2("http://foo2"); // Redirection target
1855 // First request as POST.
1857 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1858 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1859 controller
.GetVisibleEntry()->SetHasPostData(true);
1861 EXPECT_EQ(0U, notifications
.size());
1863 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1865 params
.nav_entry_id
= entry_id
;
1866 params
.did_create_new_entry
= true;
1868 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1869 params
.redirects
.push_back(GURL("http://foo1"));
1870 params
.redirects
.push_back(GURL("http://foo2"));
1871 params
.should_update_history
= false;
1872 params
.gesture
= NavigationGestureAuto
;
1873 params
.is_post
= true;
1874 params
.page_state
= PageState::CreateFromURL(url2
);
1876 LoadCommittedDetails details
;
1878 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1880 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1881 navigation_entry_committed_counter_
= 0;
1885 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1886 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1888 EXPECT_TRUE(controller
.GetPendingEntry());
1889 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1890 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1892 params
.nav_entry_id
= entry_id
;
1893 params
.did_create_new_entry
= false;
1894 params
.is_post
= false;
1896 EXPECT_EQ(0U, notifications
.size());
1897 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1899 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1900 navigation_entry_committed_counter_
= 0;
1902 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1903 EXPECT_EQ(controller
.GetEntryCount(), 1);
1904 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1905 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1906 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1907 EXPECT_FALSE(controller
.GetPendingEntry());
1908 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1909 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1911 EXPECT_FALSE(controller
.CanGoBack());
1912 EXPECT_FALSE(controller
.CanGoForward());
1915 // A redirect right off the bat should be a NEW_PAGE.
1916 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1917 NavigationControllerImpl
& controller
= controller_impl();
1918 TestNotificationTracker notifications
;
1919 RegisterForAllNavNotifications(¬ifications
, &controller
);
1921 const GURL
url1("http://foo1");
1922 const GURL
url2("http://foo2"); // Redirection target
1926 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1927 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1929 EXPECT_TRUE(controller
.GetPendingEntry());
1930 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1931 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1933 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1935 params
.nav_entry_id
= entry_id
;
1936 params
.did_create_new_entry
= true;
1938 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1939 params
.redirects
.push_back(GURL("http://foo1"));
1940 params
.redirects
.push_back(GURL("http://foo2"));
1941 params
.should_update_history
= false;
1942 params
.gesture
= NavigationGestureAuto
;
1943 params
.is_post
= false;
1944 params
.page_state
= PageState::CreateFromURL(url2
);
1946 LoadCommittedDetails details
;
1948 EXPECT_EQ(0U, notifications
.size());
1949 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1951 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1952 navigation_entry_committed_counter_
= 0;
1954 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
1955 EXPECT_EQ(controller
.GetEntryCount(), 1);
1956 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1957 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1958 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1959 EXPECT_FALSE(controller
.GetPendingEntry());
1960 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1962 EXPECT_FALSE(controller
.CanGoBack());
1963 EXPECT_FALSE(controller
.CanGoForward());
1966 // If something is pumping the event loop in the browser process and is loading
1967 // pages rapidly one after the other, there can be a race with two closely-
1968 // spaced load requests. Once the first load request is sent, will the renderer
1969 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
1970 // IPC, and have the browser process handle that IPC before the caller makes
1971 // another load request, replacing the pending entry of the first request?
1973 // This test is about what happens in such a race when that pending entry
1974 // replacement happens. If it happens, and the first load had the same URL as
1975 // the page before it, we must make sure that the replacement of the pending
1976 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
1978 // (This is a unit test rather than a browser test because it's not currently
1979 // possible to force this sequence of events with a browser test.)
1980 TEST_F(NavigationControllerTest
,
1981 NavigationTypeClassification_ExistingPageRace
) {
1982 NavigationControllerImpl
& controller
= controller_impl();
1983 const GURL
url1("http://foo1");
1984 const GURL
url2("http://foo2");
1986 // Start with a loaded page.
1987 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1988 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
1990 // Start a load of the same page again.
1992 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1993 int entry_id1
= controller
.GetPendingEntry()->GetUniqueID();
1995 // Immediately start loading a different page...
1997 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1998 int entry_id2
= controller
.GetPendingEntry()->GetUniqueID();
1999 EXPECT_NE(entry_id1
, entry_id2
);
2001 // ... and now the renderer sends a commit for the first navigation.
2002 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2004 params
.nav_entry_id
= entry_id1
;
2005 params
.intended_as_new_entry
= true;
2006 params
.did_create_new_entry
= false;
2008 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
2009 params
.page_state
= PageState::CreateFromURL(url1
);
2011 LoadCommittedDetails details
;
2013 main_test_rfh()->PrepareForCommit();
2014 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2016 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
2019 // Tests navigation via link click within a subframe. A new navigation entry
2020 // should be created.
2021 TEST_F(NavigationControllerTest
, NewSubframe
) {
2022 NavigationControllerImpl
& controller
= controller_impl();
2023 TestNotificationTracker notifications
;
2024 RegisterForAllNavNotifications(¬ifications
, &controller
);
2026 const GURL
url1("http://foo1");
2027 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2028 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2029 navigation_entry_committed_counter_
= 0;
2031 // Prereq: add a subframe with an initial auto-subframe navigation.
2032 main_test_rfh()->OnCreateChildFrame(
2033 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2034 blink::WebSandboxFlags::None
);
2035 RenderFrameHostImpl
* subframe
=
2036 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2037 const GURL
subframe_url("http://foo1/subframe");
2039 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2041 params
.nav_entry_id
= 0;
2042 params
.did_create_new_entry
= false;
2043 params
.url
= subframe_url
;
2044 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2045 params
.should_update_history
= false;
2046 params
.gesture
= NavigationGestureUser
;
2047 params
.is_post
= false;
2048 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2050 // Navigating should do nothing.
2051 LoadCommittedDetails details
;
2052 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2053 EXPECT_EQ(0U, notifications
.size());
2056 // Now do a new navigation in the frame.
2057 const GURL
url2("http://foo2");
2058 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2060 params
.nav_entry_id
= 0;
2061 params
.did_create_new_entry
= true;
2063 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2064 params
.should_update_history
= false;
2065 params
.gesture
= NavigationGestureUser
;
2066 params
.is_post
= false;
2067 params
.page_state
= PageState::CreateFromURL(url2
);
2069 LoadCommittedDetails details
;
2070 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2071 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2072 navigation_entry_committed_counter_
= 0;
2073 EXPECT_EQ(url1
, details
.previous_url
);
2074 EXPECT_FALSE(details
.is_in_page
);
2075 EXPECT_FALSE(details
.is_main_frame
);
2077 // The new entry should be appended.
2078 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2079 EXPECT_EQ(2, controller
.GetEntryCount());
2080 EXPECT_EQ(entry
, details
.entry
);
2082 // New entry should refer to the new page, but the old URL (entries only
2083 // reflect the toplevel URL).
2084 EXPECT_EQ(url1
, entry
->GetURL());
2085 EXPECT_EQ(params
.page_id
, entry
->GetPageID());
2087 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2088 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2089 // The entry should have a subframe FrameNavigationEntry.
2090 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2091 EXPECT_EQ(url2
, entry
->root_node()->children
[0]->frame_entry
->url());
2093 // There are no subframe FrameNavigationEntries by default.
2094 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2098 // Auto subframes are ones the page loads automatically like ads. They should
2099 // not create new navigation entries.
2100 // TODO(creis): Test updating entries for history auto subframe navigations.
2101 TEST_F(NavigationControllerTest
, AutoSubframe
) {
2102 NavigationControllerImpl
& controller
= controller_impl();
2103 TestNotificationTracker notifications
;
2104 RegisterForAllNavNotifications(¬ifications
, &controller
);
2106 const GURL
url1("http://foo/1");
2107 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2108 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2109 navigation_entry_committed_counter_
= 0;
2111 // Add a subframe and navigate it.
2112 main_test_rfh()->OnCreateChildFrame(
2113 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2114 blink::WebSandboxFlags::None
);
2115 RenderFrameHostImpl
* subframe
=
2116 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2117 const GURL
url2("http://foo/2");
2119 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2121 params
.nav_entry_id
= 0;
2122 params
.did_create_new_entry
= false;
2124 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2125 params
.should_update_history
= false;
2126 params
.gesture
= NavigationGestureUser
;
2127 params
.is_post
= false;
2128 params
.page_state
= PageState::CreateFromURL(url2
);
2130 // Navigating should do nothing.
2131 LoadCommittedDetails details
;
2132 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2133 EXPECT_EQ(0U, notifications
.size());
2136 // There should still be only one entry.
2137 EXPECT_EQ(1, controller
.GetEntryCount());
2138 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2139 EXPECT_EQ(url1
, entry
->GetURL());
2140 EXPECT_EQ(1, entry
->GetPageID());
2141 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
2142 EXPECT_EQ(url1
, root_entry
->url());
2144 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2145 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
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 they're enabled (e.g. in --site-per-process).
2190 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2191 // The entry should now have 2 subframe FrameNavigationEntries.
2192 ASSERT_EQ(2U, entry
->root_node()->children
.size());
2193 FrameNavigationEntry
* new_frame_entry
=
2194 entry
->root_node()->children
[1]->frame_entry
.get();
2195 EXPECT_EQ(url3
, new_frame_entry
->url());
2197 // There are no subframe FrameNavigationEntries by default.
2198 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2201 // Add a nested subframe and navigate.
2202 subframe
->OnCreateChildFrame(MSG_ROUTING_NONE
,
2203 blink::WebTreeScopeType::Document
, std::string(),
2204 blink::WebSandboxFlags::None
);
2205 RenderFrameHostImpl
* subframe3
= contents()
2210 ->current_frame_host();
2211 const GURL
url4("http://foo/4");
2213 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2215 params
.nav_entry_id
= 0;
2216 params
.did_create_new_entry
= false;
2218 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2219 params
.should_update_history
= false;
2220 params
.gesture
= NavigationGestureUser
;
2221 params
.is_post
= false;
2222 params
.page_state
= PageState::CreateFromURL(url4
);
2224 // Navigating should do nothing.
2225 LoadCommittedDetails details
;
2226 EXPECT_FALSE(controller
.RendererDidNavigate(subframe3
, params
, &details
));
2227 EXPECT_EQ(0U, notifications
.size());
2230 // There should still be only one entry, mostly unchanged.
2231 EXPECT_EQ(1, controller
.GetEntryCount());
2232 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2233 EXPECT_EQ(url1
, entry
->GetURL());
2234 EXPECT_EQ(1, entry
->GetPageID());
2235 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2236 EXPECT_EQ(url1
, root_entry
->url());
2238 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2239 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2240 // The entry should now have a nested FrameNavigationEntry.
2241 EXPECT_EQ(2U, entry
->root_node()->children
.size());
2242 ASSERT_EQ(1U, entry
->root_node()->children
[0]->children
.size());
2243 FrameNavigationEntry
* new_frame_entry
=
2244 entry
->root_node()->children
[0]->children
[0]->frame_entry
.get();
2245 EXPECT_EQ(url4
, new_frame_entry
->url());
2247 // There are no subframe FrameNavigationEntries by default.
2248 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2252 // Tests navigation and then going back to a subframe navigation.
2253 TEST_F(NavigationControllerTest
, BackSubframe
) {
2254 NavigationControllerImpl
& controller
= controller_impl();
2255 TestNotificationTracker notifications
;
2256 RegisterForAllNavNotifications(¬ifications
, &controller
);
2259 const GURL
url1("http://foo1");
2260 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2261 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2262 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2263 navigation_entry_committed_counter_
= 0;
2265 // Prereq: add a subframe with an initial auto-subframe navigation.
2266 main_test_rfh()->OnCreateChildFrame(
2267 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2268 blink::WebSandboxFlags::None
);
2269 RenderFrameHostImpl
* subframe
=
2270 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2271 const GURL
subframe_url("http://foo1/subframe");
2273 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2275 params
.nav_entry_id
= 0;
2276 params
.did_create_new_entry
= false;
2277 params
.url
= subframe_url
;
2278 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2279 params
.should_update_history
= false;
2280 params
.gesture
= NavigationGestureUser
;
2281 params
.is_post
= false;
2282 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2284 // Navigating should do nothing.
2285 LoadCommittedDetails details
;
2286 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2287 EXPECT_EQ(0U, notifications
.size());
2290 // First manual subframe navigation.
2291 const GURL
url2("http://foo2");
2292 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2294 params
.nav_entry_id
= 0;
2295 params
.did_create_new_entry
= true;
2297 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2298 params
.should_update_history
= false;
2299 params
.gesture
= NavigationGestureUser
;
2300 params
.is_post
= false;
2301 params
.page_state
= PageState::CreateFromURL(url2
);
2303 // This should generate a new entry.
2304 LoadCommittedDetails details
;
2305 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2306 NavigationEntryImpl
* entry2
= controller
.GetLastCommittedEntry();
2307 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2308 navigation_entry_committed_counter_
= 0;
2309 EXPECT_EQ(2, controller
.GetEntryCount());
2311 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2312 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2313 // The entry should have a subframe FrameNavigationEntry.
2314 ASSERT_EQ(1U, entry2
->root_node()->children
.size());
2315 EXPECT_EQ(url2
, entry2
->root_node()->children
[0]->frame_entry
->url());
2317 // There are no subframe FrameNavigationEntries by default.
2318 EXPECT_EQ(0U, entry2
->root_node()->children
.size());
2321 // Second manual subframe navigation should also make a new entry.
2322 const GURL
url3("http://foo3");
2324 params
.nav_entry_id
= 0;
2325 params
.did_create_new_entry
= true;
2327 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2328 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2329 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2330 navigation_entry_committed_counter_
= 0;
2331 NavigationEntryImpl
* entry3
= controller
.GetLastCommittedEntry();
2332 EXPECT_EQ(3, controller
.GetEntryCount());
2333 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2335 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2336 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2337 // The entry should have a subframe FrameNavigationEntry.
2338 ASSERT_EQ(1U, entry3
->root_node()->children
.size());
2339 EXPECT_EQ(url3
, entry3
->root_node()->children
[0]->frame_entry
->url());
2341 // There are no subframe FrameNavigationEntries by default.
2342 EXPECT_EQ(0U, entry3
->root_node()->children
.size());
2346 controller
.GoBack();
2348 params
.nav_entry_id
= entry2
->GetUniqueID();
2349 params
.did_create_new_entry
= false;
2351 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2352 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2353 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2354 navigation_entry_committed_counter_
= 0;
2355 EXPECT_EQ(entry2
, controller
.GetLastCommittedEntry());
2356 EXPECT_EQ(3, controller
.GetEntryCount());
2357 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2358 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2359 EXPECT_FALSE(controller
.GetPendingEntry());
2361 // Go back one more.
2362 controller
.GoBack();
2364 params
.nav_entry_id
= entry1
->GetUniqueID();
2365 params
.did_create_new_entry
= false;
2367 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2368 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2369 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2370 navigation_entry_committed_counter_
= 0;
2371 EXPECT_EQ(entry1
, controller
.GetLastCommittedEntry());
2372 EXPECT_EQ(3, controller
.GetEntryCount());
2373 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2374 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2375 EXPECT_FALSE(controller
.GetPendingEntry());
2378 TEST_F(NavigationControllerTest
, LinkClick
) {
2379 NavigationControllerImpl
& controller
= controller_impl();
2380 TestNotificationTracker notifications
;
2381 RegisterForAllNavNotifications(¬ifications
, &controller
);
2383 const GURL
url1("http://foo1");
2384 const GURL
url2("http://foo2");
2386 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2387 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2388 navigation_entry_committed_counter_
= 0;
2390 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
2391 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2392 navigation_entry_committed_counter_
= 0;
2394 // Should have produced a new session history entry.
2395 EXPECT_EQ(controller
.GetEntryCount(), 2);
2396 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2397 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2398 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2399 EXPECT_FALSE(controller
.GetPendingEntry());
2400 EXPECT_TRUE(controller
.CanGoBack());
2401 EXPECT_FALSE(controller
.CanGoForward());
2404 TEST_F(NavigationControllerTest
, InPage
) {
2405 NavigationControllerImpl
& controller
= controller_impl();
2406 TestNotificationTracker notifications
;
2407 RegisterForAllNavNotifications(¬ifications
, &controller
);
2410 const GURL
url1("http://foo");
2411 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2412 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2413 navigation_entry_committed_counter_
= 0;
2415 // Ensure main page navigation to same url respects the was_within_same_page
2416 // hint provided in the params.
2417 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2418 self_params
.page_id
= 0;
2419 self_params
.nav_entry_id
= 0;
2420 self_params
.did_create_new_entry
= false;
2421 self_params
.url
= url1
;
2422 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2423 self_params
.should_update_history
= false;
2424 self_params
.gesture
= NavigationGestureUser
;
2425 self_params
.is_post
= false;
2426 self_params
.page_state
= PageState::CreateFromURL(url1
);
2427 self_params
.was_within_same_page
= true;
2429 LoadCommittedDetails details
;
2430 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2432 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2433 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2434 navigation_entry_committed_counter_
= 0;
2435 EXPECT_TRUE(details
.is_in_page
);
2436 EXPECT_TRUE(details
.did_replace_entry
);
2437 EXPECT_EQ(1, controller
.GetEntryCount());
2439 // Fragment navigation to a new page_id.
2440 const GURL
url2("http://foo#a");
2441 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2443 params
.nav_entry_id
= 0;
2444 params
.did_create_new_entry
= true;
2446 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2447 params
.should_update_history
= false;
2448 params
.gesture
= NavigationGestureUser
;
2449 params
.is_post
= false;
2450 params
.page_state
= PageState::CreateFromURL(url2
);
2451 params
.was_within_same_page
= true;
2453 // This should generate a new entry.
2454 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2456 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2457 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2458 navigation_entry_committed_counter_
= 0;
2459 EXPECT_TRUE(details
.is_in_page
);
2460 EXPECT_FALSE(details
.did_replace_entry
);
2461 EXPECT_EQ(2, controller
.GetEntryCount());
2464 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2465 controller
.GoBack();
2466 back_params
.url
= url1
;
2467 back_params
.page_id
= 0;
2468 back_params
.nav_entry_id
= entry1
->GetUniqueID();
2469 back_params
.did_create_new_entry
= false;
2470 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2472 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2473 navigation_entry_committed_counter_
= 0;
2474 EXPECT_TRUE(details
.is_in_page
);
2475 EXPECT_EQ(2, controller
.GetEntryCount());
2476 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2477 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2480 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2481 controller
.GoForward();
2482 forward_params
.url
= url2
;
2483 forward_params
.page_id
= 1;
2484 forward_params
.nav_entry_id
= entry2
->GetUniqueID();
2485 forward_params
.did_create_new_entry
= false;
2486 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2488 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2489 navigation_entry_committed_counter_
= 0;
2490 EXPECT_TRUE(details
.is_in_page
);
2491 EXPECT_EQ(2, controller
.GetEntryCount());
2492 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2493 EXPECT_EQ(forward_params
.url
,
2494 controller
.GetVisibleEntry()->GetURL());
2496 // Now go back and forward again. This is to work around a bug where we would
2497 // compare the incoming URL with the last committed entry rather than the
2498 // one identified by an existing page ID. This would result in the second URL
2499 // losing the reference fragment when you navigate away from it and then back.
2500 controller
.GoBack();
2501 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2503 controller
.GoForward();
2504 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2506 EXPECT_EQ(forward_params
.url
,
2507 controller
.GetVisibleEntry()->GetURL());
2509 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2510 const GURL
url3("http://bar");
2512 params
.nav_entry_id
= 0;
2513 params
.did_create_new_entry
= true;
2515 navigation_entry_committed_counter_
= 0;
2516 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2518 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2519 navigation_entry_committed_counter_
= 0;
2520 EXPECT_FALSE(details
.is_in_page
);
2521 EXPECT_EQ(3, controller
.GetEntryCount());
2522 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2525 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2526 NavigationControllerImpl
& controller
= controller_impl();
2527 TestNotificationTracker notifications
;
2528 RegisterForAllNavNotifications(¬ifications
, &controller
);
2531 const GURL
url1("http://foo");
2532 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2533 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2534 navigation_entry_committed_counter_
= 0;
2536 // First navigation.
2537 const GURL
url2("http://foo#a");
2538 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2539 params
.page_id
= 0; // Same page_id
2540 params
.nav_entry_id
= 0;
2541 params
.did_create_new_entry
= false;
2543 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2544 params
.should_update_history
= false;
2545 params
.gesture
= NavigationGestureUser
;
2546 params
.is_post
= false;
2547 params
.page_state
= PageState::CreateFromURL(url2
);
2548 params
.was_within_same_page
= true;
2550 // This should NOT generate a new entry, nor prune the list.
2551 LoadCommittedDetails details
;
2552 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2554 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2555 navigation_entry_committed_counter_
= 0;
2556 EXPECT_TRUE(details
.is_in_page
);
2557 EXPECT_TRUE(details
.did_replace_entry
);
2558 EXPECT_EQ(1, controller
.GetEntryCount());
2561 // Tests for http://crbug.com/40395
2564 // window.location.replace("#a");
2565 // window.location='http://foo3/';
2567 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2568 NavigationControllerImpl
& controller
= controller_impl();
2569 TestNotificationTracker notifications
;
2570 RegisterForAllNavNotifications(¬ifications
, &controller
);
2572 // Load an initial page.
2574 const GURL
url("http://foo/");
2575 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
2576 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2577 navigation_entry_committed_counter_
= 0;
2580 // Navigate to a new page.
2582 const GURL
url("http://foo2/");
2583 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url
);
2584 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2585 navigation_entry_committed_counter_
= 0;
2588 // Navigate within the page.
2590 const GURL
url("http://foo2/#a");
2591 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2592 params
.page_id
= 1; // Same page_id
2593 params
.nav_entry_id
= 0;
2594 params
.did_create_new_entry
= false;
2596 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2597 params
.redirects
.push_back(url
);
2598 params
.should_update_history
= true;
2599 params
.gesture
= NavigationGestureUnknown
;
2600 params
.is_post
= false;
2601 params
.page_state
= PageState::CreateFromURL(url
);
2602 params
.was_within_same_page
= true;
2604 // This should NOT generate a new entry, nor prune the list.
2605 LoadCommittedDetails details
;
2606 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2608 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2609 navigation_entry_committed_counter_
= 0;
2610 EXPECT_TRUE(details
.is_in_page
);
2611 EXPECT_TRUE(details
.did_replace_entry
);
2612 EXPECT_EQ(2, controller
.GetEntryCount());
2615 // Perform a client redirect to a new page.
2617 const GURL
url("http://foo3/");
2618 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2619 params
.page_id
= 2; // New page_id
2620 params
.nav_entry_id
= 0;
2621 params
.did_create_new_entry
= true;
2623 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2624 params
.redirects
.push_back(GURL("http://foo2/#a"));
2625 params
.redirects
.push_back(url
);
2626 params
.should_update_history
= true;
2627 params
.gesture
= NavigationGestureUnknown
;
2628 params
.is_post
= false;
2629 params
.page_state
= PageState::CreateFromURL(url
);
2631 // This SHOULD generate a new entry.
2632 LoadCommittedDetails details
;
2633 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2635 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2636 navigation_entry_committed_counter_
= 0;
2637 EXPECT_FALSE(details
.is_in_page
);
2638 EXPECT_EQ(3, controller
.GetEntryCount());
2641 // Verify that BACK brings us back to http://foo2/.
2643 const GURL
url("http://foo2/");
2644 controller
.GoBack();
2645 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2646 main_test_rfh()->PrepareForCommit();
2647 main_test_rfh()->SendNavigate(1, entry_id
, false, url
);
2648 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2649 navigation_entry_committed_counter_
= 0;
2650 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2654 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2656 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2657 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2658 GURL
url("http://foo");
2660 params
.nav_entry_id
= 0;
2661 params
.did_create_new_entry
= true;
2663 params
.page_state
= PageState::CreateFromURL(url
);
2664 params
.was_within_same_page
= true;
2665 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
2666 main_test_rfh()->PrepareForCommit();
2667 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2668 // We pass if we don't crash.
2671 // NotificationObserver implementation used in verifying we've received the
2672 // NOTIFICATION_NAV_LIST_PRUNED method.
2673 class PrunedListener
: public NotificationObserver
{
2675 explicit PrunedListener(NavigationControllerImpl
* controller
)
2676 : notification_count_(0) {
2677 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2678 Source
<NavigationController
>(controller
));
2681 void Observe(int type
,
2682 const NotificationSource
& source
,
2683 const NotificationDetails
& details
) override
{
2684 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2685 notification_count_
++;
2686 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2690 // Number of times NAV_LIST_PRUNED has been observed.
2691 int notification_count_
;
2693 // Details from the last NAV_LIST_PRUNED.
2694 PrunedDetails details_
;
2697 NotificationRegistrar registrar_
;
2699 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2702 // Tests that we limit the number of navigation entries created correctly.
2703 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2704 NavigationControllerImpl
& controller
= controller_impl();
2705 size_t original_count
= NavigationControllerImpl::max_entry_count();
2706 const int kMaxEntryCount
= 5;
2708 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2711 // Load up to the max count, all entries should be there.
2712 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2713 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2715 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2716 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2717 main_test_rfh()->PrepareForCommit();
2718 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2721 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2723 // Created a PrunedListener to observe prune notifications.
2724 PrunedListener
listener(&controller
);
2726 // Navigate some more.
2727 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2729 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2730 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2731 main_test_rfh()->PrepareForCommit();
2732 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2735 // We should have got a pruned navigation.
2736 EXPECT_EQ(1, listener
.notification_count_
);
2737 EXPECT_TRUE(listener
.details_
.from_front
);
2738 EXPECT_EQ(1, listener
.details_
.count
);
2740 // We expect http://www.a.com/0 to be gone.
2741 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2742 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2743 GURL("http://www.a.com/1"));
2745 // More navigations.
2746 for (int i
= 0; i
< 3; i
++) {
2747 url
= GURL(base::StringPrintf("http://www.a.com/%d", url_index
));
2749 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2750 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2751 main_test_rfh()->PrepareForCommit();
2752 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2755 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2756 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2757 GURL("http://www.a.com/4"));
2759 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2762 // Tests that we can do a restore and navigate to the restored entries and
2763 // everything is updated properly. This can be tricky since there is no
2764 // SiteInstance for the entries created initially.
2765 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2766 // Create a NavigationController with a restored set of tabs.
2767 GURL
url("http://foo");
2768 ScopedVector
<NavigationEntry
> entries
;
2769 scoped_ptr
<NavigationEntry
> entry
=
2770 NavigationControllerImpl::CreateNavigationEntry(
2771 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2773 entry
->SetPageID(0);
2774 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2775 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2776 const base::Time timestamp
= base::Time::Now();
2777 entry
->SetTimestamp(timestamp
);
2778 entries
.push_back(entry
.Pass());
2779 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2780 WebContents::Create(WebContents::CreateParams(browser_context()))));
2781 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2782 our_controller
.Restore(
2784 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2786 ASSERT_EQ(0u, entries
.size());
2788 // Before navigating to the restored entry, it should have a restore_type
2789 // and no SiteInstance.
2790 ASSERT_EQ(1, our_controller
.GetEntryCount());
2791 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2792 our_controller
.GetEntryAtIndex(0)->restore_type());
2793 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2795 // After navigating, we should have one entry, and it should be "pending".
2796 // It should now have a SiteInstance and no restore_type.
2797 our_controller
.GoToIndex(0);
2798 EXPECT_EQ(1, our_controller
.GetEntryCount());
2799 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2800 our_controller
.GetPendingEntry());
2801 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2802 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2803 our_controller
.GetEntryAtIndex(0)->restore_type());
2804 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2806 // Timestamp should remain the same before the navigation finishes.
2807 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2809 // Say we navigated to that entry.
2810 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2812 params
.nav_entry_id
= our_controller
.GetPendingEntry()->GetUniqueID();
2813 params
.did_create_new_entry
= false;
2815 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2816 params
.should_update_history
= false;
2817 params
.gesture
= NavigationGestureUser
;
2818 params
.is_post
= false;
2819 params
.page_state
= PageState::CreateFromURL(url
);
2820 LoadCommittedDetails details
;
2821 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2824 // There should be no longer any pending entry and one committed one. This
2825 // means that we were able to locate the entry, assign its site instance, and
2826 // commit it properly.
2827 EXPECT_EQ(1, our_controller
.GetEntryCount());
2828 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2829 EXPECT_FALSE(our_controller
.GetPendingEntry());
2832 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2833 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2834 our_controller
.GetEntryAtIndex(0)->restore_type());
2836 // Timestamp should have been updated.
2837 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2840 // Tests that we can still navigate to a restored entry after a different
2841 // navigation fails and clears the pending entry. http://crbug.com/90085
2842 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2843 // Create a NavigationController with a restored set of tabs.
2844 GURL
url("http://foo");
2845 ScopedVector
<NavigationEntry
> entries
;
2846 scoped_ptr
<NavigationEntry
> new_entry
=
2847 NavigationControllerImpl::CreateNavigationEntry(
2848 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2850 new_entry
->SetPageID(0);
2851 new_entry
->SetTitle(base::ASCIIToUTF16("Title"));
2852 new_entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2853 entries
.push_back(new_entry
.Pass());
2854 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2855 WebContents::Create(WebContents::CreateParams(browser_context()))));
2856 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2857 our_controller
.Restore(
2858 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2859 ASSERT_EQ(0u, entries
.size());
2861 // Ensure the RenderFrame is initialized before simulating events coming from
2863 main_test_rfh()->InitializeRenderFrameIfNeeded();
2865 // Before navigating to the restored entry, it should have a restore_type
2866 // and no SiteInstance.
2867 NavigationEntry
* entry
= our_controller
.GetEntryAtIndex(0);
2868 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2869 our_controller
.GetEntryAtIndex(0)->restore_type());
2870 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2872 // After navigating, we should have one entry, and it should be "pending".
2873 // It should now have a SiteInstance and no restore_type.
2874 our_controller
.GoToIndex(0);
2875 EXPECT_EQ(1, our_controller
.GetEntryCount());
2876 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2877 our_controller
.GetPendingEntry());
2878 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2879 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2880 our_controller
.GetEntryAtIndex(0)->restore_type());
2881 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2883 // This pending navigation may have caused a different navigation to fail,
2884 // which causes the pending entry to be cleared.
2885 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2886 fail_load_params
.error_code
= net::ERR_ABORTED
;
2887 fail_load_params
.error_description
= base::string16();
2888 fail_load_params
.url
= url
;
2889 fail_load_params
.showing_repost_interstitial
= false;
2890 main_test_rfh()->InitializeRenderFrameIfNeeded();
2891 main_test_rfh()->OnMessageReceived(
2892 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2895 // Now the pending restored entry commits.
2896 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2898 params
.nav_entry_id
= entry
->GetUniqueID();
2899 params
.did_create_new_entry
= false;
2901 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2902 params
.should_update_history
= false;
2903 params
.gesture
= NavigationGestureUser
;
2904 params
.is_post
= false;
2905 params
.page_state
= PageState::CreateFromURL(url
);
2906 LoadCommittedDetails details
;
2907 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2910 // There should be no pending entry and one committed one.
2911 EXPECT_EQ(1, our_controller
.GetEntryCount());
2912 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2913 EXPECT_FALSE(our_controller
.GetPendingEntry());
2916 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2917 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2918 our_controller
.GetEntryAtIndex(0)->restore_type());
2921 // Make sure that the page type and stuff is correct after an interstitial.
2922 TEST_F(NavigationControllerTest
, Interstitial
) {
2923 NavigationControllerImpl
& controller
= controller_impl();
2924 // First navigate somewhere normal.
2925 const GURL
url1("http://foo");
2927 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2928 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2929 main_test_rfh()->PrepareForCommit();
2930 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2932 // Now navigate somewhere with an interstitial.
2933 const GURL
url2("http://bar");
2934 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
,
2936 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2937 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2939 // At this point the interstitial will be displayed and the load will still
2940 // be pending. If the user continues, the load will commit.
2941 main_test_rfh()->PrepareForCommit();
2942 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2944 // The page should be a normal page again.
2945 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2946 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2947 controller
.GetLastCommittedEntry()->GetPageType());
2950 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2951 NavigationControllerImpl
& controller
= controller_impl();
2952 const GURL
url1("http://foo/1");
2953 const GURL
url2("http://foo/2");
2954 const GURL
url3("http://foo/3");
2955 const GURL
url4("http://foo/4");
2956 const GURL
url5("http://foo/5");
2957 const GURL
pending_url("http://foo/pending");
2958 const GURL
default_url("http://foo/default");
2961 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2962 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2963 main_test_rfh()->PrepareForCommit();
2964 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2966 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2967 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2968 main_test_rfh()->PrepareForCommit();
2969 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2971 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2972 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2973 main_test_rfh()->PrepareForCommit();
2974 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2976 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2977 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2978 main_test_rfh()->PrepareForCommit();
2979 main_test_rfh()->SendNavigate(3, entry_id
, true, url4
);
2981 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2982 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2983 main_test_rfh()->PrepareForCommit();
2984 main_test_rfh()->SendNavigate(4, entry_id
, true, url5
);
2986 // Try to remove the last entry. Will fail because it is the current entry.
2987 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2988 EXPECT_EQ(5, controller
.GetEntryCount());
2989 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2991 // Go back, but don't commit yet. Check that we can't delete the current
2992 // and pending entries.
2993 controller
.GoBack();
2994 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2995 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2996 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
2998 // Now commit and delete the last entry.
2999 main_test_rfh()->PrepareForCommit();
3000 main_test_rfh()->SendNavigate(3, entry_id
, false, url4
);
3001 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
3002 EXPECT_EQ(4, controller
.GetEntryCount());
3003 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
3004 EXPECT_FALSE(controller
.GetPendingEntry());
3006 // Remove an entry which is not the last committed one.
3007 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3008 EXPECT_EQ(3, controller
.GetEntryCount());
3009 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
3010 EXPECT_FALSE(controller
.GetPendingEntry());
3012 // Remove the 2 remaining entries.
3013 controller
.RemoveEntryAtIndex(1);
3014 controller
.RemoveEntryAtIndex(0);
3016 // This should leave us with only the last committed entry.
3017 EXPECT_EQ(1, controller
.GetEntryCount());
3018 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3021 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
3022 NavigationControllerImpl
& controller
= controller_impl();
3023 const GURL
url1("http://foo/1");
3024 const GURL
url2("http://foo/2");
3025 const GURL
url3("http://foo/3");
3026 const GURL
default_url("http://foo/default");
3029 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3030 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3031 main_test_rfh()->PrepareForCommit();
3032 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3034 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3035 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3036 main_test_rfh()->PrepareForCommit();
3037 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
3039 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3040 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3041 main_test_rfh()->PrepareForCommit();
3042 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
3044 // Go back, but don't commit yet. Check that we can't delete the current
3045 // and pending entries.
3046 controller
.GoBack();
3047 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3048 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
3049 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
3051 // Remove the first entry, while there is a pending entry. This is expected
3052 // to discard the pending entry.
3053 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3054 EXPECT_FALSE(controller
.GetPendingEntry());
3055 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
3057 // We should update the last committed entry index.
3058 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
3060 // Now commit and ensure we land on the right entry.
3061 main_test_rfh()->PrepareForCommit();
3062 main_test_rfh()->SendNavigate(1, entry_id
, false, url2
);
3063 EXPECT_EQ(2, controller
.GetEntryCount());
3064 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3065 EXPECT_FALSE(controller
.GetPendingEntry());
3068 // Tests the transient entry, making sure it goes away with all navigations.
3069 TEST_F(NavigationControllerTest
, TransientEntry
) {
3070 NavigationControllerImpl
& controller
= controller_impl();
3071 TestNotificationTracker notifications
;
3072 RegisterForAllNavNotifications(¬ifications
, &controller
);
3074 const GURL
url0("http://foo/0");
3075 const GURL
url1("http://foo/1");
3076 const GURL
url2("http://foo/2");
3077 const GURL
url3("http://foo/3");
3078 const GURL
url3_ref("http://foo/3#bar");
3079 const GURL
url4("http://foo/4");
3080 const GURL
transient_url("http://foo/transient");
3083 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3084 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3085 main_test_rfh()->PrepareForCommit();
3086 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3088 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3089 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3090 main_test_rfh()->PrepareForCommit();
3091 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3093 notifications
.Reset();
3095 // Adding a transient with no pending entry.
3096 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3097 transient_entry
->SetURL(transient_url
);
3098 controller
.SetTransientEntry(transient_entry
.Pass());
3100 // We should not have received any notifications.
3101 EXPECT_EQ(0U, notifications
.size());
3104 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3105 EXPECT_EQ(controller
.GetEntryCount(), 3);
3106 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
3107 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
3108 EXPECT_TRUE(controller
.GetLastCommittedEntry());
3109 EXPECT_FALSE(controller
.GetPendingEntry());
3110 EXPECT_TRUE(controller
.CanGoBack());
3111 EXPECT_FALSE(controller
.CanGoForward());
3112 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3116 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3117 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3118 main_test_rfh()->PrepareForCommit();
3119 main_test_rfh()->SendNavigate(2, entry_id
, true, url2
);
3121 // We should have navigated, transient entry should be gone.
3122 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3123 EXPECT_EQ(controller
.GetEntryCount(), 3);
3125 // Add a transient again, then navigate with no pending entry this time.
3126 transient_entry
.reset(new NavigationEntryImpl
);
3127 transient_entry
->SetURL(transient_url
);
3128 controller
.SetTransientEntry(transient_entry
.Pass());
3129 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3130 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3
, true);
3131 main_test_rfh()->PrepareForCommit();
3132 main_test_rfh()->SendNavigate(3, 0, true, url3
);
3133 // Transient entry should be gone.
3134 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3135 EXPECT_EQ(controller
.GetEntryCount(), 4);
3137 // Initiate a navigation, add a transient then commit navigation.
3139 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3140 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3141 transient_entry
.reset(new NavigationEntryImpl
);
3142 transient_entry
->SetURL(transient_url
);
3143 controller
.SetTransientEntry(transient_entry
.Pass());
3144 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3145 main_test_rfh()->PrepareForCommit();
3146 main_test_rfh()->SendNavigate(4, entry_id
, true, url4
);
3147 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3148 EXPECT_EQ(controller
.GetEntryCount(), 5);
3150 // Add a transient and go back. This should simply remove the transient.
3151 transient_entry
.reset(new NavigationEntryImpl
);
3152 transient_entry
->SetURL(transient_url
);
3153 controller
.SetTransientEntry(transient_entry
.Pass());
3154 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3155 EXPECT_TRUE(controller
.CanGoBack());
3156 EXPECT_FALSE(controller
.CanGoForward());
3157 controller
.GoBack();
3158 // Transient entry should be gone.
3159 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3160 EXPECT_EQ(controller
.GetEntryCount(), 5);
3162 // Suppose the page requested a history navigation backward.
3163 controller
.GoToOffset(-1);
3164 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3165 main_test_rfh()->PrepareForCommit();
3166 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3168 // Add a transient and go to an entry before the current one.
3169 transient_entry
.reset(new NavigationEntryImpl
);
3170 transient_entry
->SetURL(transient_url
);
3171 controller
.SetTransientEntry(transient_entry
.Pass());
3172 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3173 controller
.GoToIndex(1);
3174 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3175 // The navigation should have been initiated, transient entry should be gone.
3176 EXPECT_FALSE(controller
.GetTransientEntry());
3177 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3178 // Visible entry does not update for history navigations until commit.
3179 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3180 main_test_rfh()->PrepareForCommit();
3181 main_test_rfh()->SendNavigate(1, entry_id
, false, url1
);
3182 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3184 // Add a transient and go to an entry after the current one.
3185 transient_entry
.reset(new NavigationEntryImpl
);
3186 transient_entry
->SetURL(transient_url
);
3187 controller
.SetTransientEntry(transient_entry
.Pass());
3188 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3189 controller
.GoToIndex(3);
3190 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3191 // The navigation should have been initiated, transient entry should be gone.
3192 // Because of the transient entry that is removed, going to index 3 makes us
3193 // land on url2 (which is visible after the commit).
3194 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3195 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3196 main_test_rfh()->PrepareForCommit();
3197 main_test_rfh()->SendNavigate(2, entry_id
, false, url2
);
3198 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3200 // Add a transient and go forward.
3201 transient_entry
.reset(new NavigationEntryImpl
);
3202 transient_entry
->SetURL(transient_url
);
3203 controller
.SetTransientEntry(transient_entry
.Pass());
3204 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3205 EXPECT_TRUE(controller
.CanGoForward());
3206 controller
.GoForward();
3207 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3208 // We should have navigated, transient entry should be gone.
3209 EXPECT_FALSE(controller
.GetTransientEntry());
3210 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
3211 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3212 main_test_rfh()->PrepareForCommit();
3213 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3214 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3216 // Add a transient and do an in-page navigation, replacing the current entry.
3217 transient_entry
.reset(new NavigationEntryImpl
);
3218 transient_entry
->SetURL(transient_url
);
3219 controller
.SetTransientEntry(transient_entry
.Pass());
3220 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3222 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref
, false);
3223 main_test_rfh()->PrepareForCommit();
3224 main_test_rfh()->SendNavigate(3, 0, false, url3_ref
);
3225 // Transient entry should be gone.
3226 EXPECT_FALSE(controller
.GetTransientEntry());
3227 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
3229 // Ensure the URLs are correct.
3230 EXPECT_EQ(controller
.GetEntryCount(), 5);
3231 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3232 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
3233 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
3234 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
3235 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
3238 // Test that Reload initiates a new navigation to a transient entry's URL.
3239 TEST_F(NavigationControllerTest
, ReloadTransient
) {
3240 NavigationControllerImpl
& controller
= controller_impl();
3241 const GURL
url0("http://foo/0");
3242 const GURL
url1("http://foo/1");
3243 const GURL
transient_url("http://foo/transient");
3245 // Load |url0|, and start a pending navigation to |url1|.
3247 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3248 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3249 main_test_rfh()->PrepareForCommit();
3250 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3252 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3254 // A transient entry is added, interrupting the navigation.
3255 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3256 transient_entry
->SetURL(transient_url
);
3257 controller
.SetTransientEntry(transient_entry
.Pass());
3258 EXPECT_TRUE(controller
.GetTransientEntry());
3259 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3261 // The page is reloaded, which should remove the pending entry for |url1| and
3262 // the transient entry for |transient_url|, and start a navigation to
3264 controller
.Reload(true);
3265 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3266 EXPECT_FALSE(controller
.GetTransientEntry());
3267 EXPECT_TRUE(controller
.GetPendingEntry());
3268 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3269 ASSERT_EQ(controller
.GetEntryCount(), 1);
3270 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3272 // Load of |transient_url| completes.
3273 main_test_rfh()->PrepareForCommit();
3274 main_test_rfh()->SendNavigate(1, entry_id
, true, transient_url
);
3275 ASSERT_EQ(controller
.GetEntryCount(), 2);
3276 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3277 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
3280 // Ensure that renderer initiated pending entries get replaced, so that we
3281 // don't show a stale virtual URL when a navigation commits.
3282 // See http://crbug.com/266922.
3283 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
3284 NavigationControllerImpl
& controller
= controller_impl();
3285 Navigator
* navigator
=
3286 contents()->GetFrameTree()->root()->navigator();
3288 const GURL
url1("nonexistent:12121");
3289 const GURL
url1_fixed("http://nonexistent:12121/");
3290 const GURL
url2("http://foo");
3292 // We create pending entries for renderer-initiated navigations so that we
3293 // can show them in new tabs when it is safe.
3294 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, false);
3295 main_test_rfh()->PrepareForCommit();
3296 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3298 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3299 // the virtual URL to differ from the URL.
3300 controller
.GetPendingEntry()->SetURL(url1_fixed
);
3301 controller
.GetPendingEntry()->SetVirtualURL(url1
);
3303 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
3304 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
3305 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3307 // If the user clicks another link, we should replace the pending entry.
3308 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3309 main_test_rfh()->PrepareForCommit();
3310 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3311 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3312 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
3314 // Once it commits, the URL and virtual URL should reflect the actual page.
3315 main_test_rfh()->SendNavigate(0, 0, true, url2
);
3316 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3317 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
3319 // We should not replace the pending entry for an error URL.
3320 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3321 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3322 navigator
->DidStartProvisionalLoad(main_test_rfh(),
3323 GURL(kUnreachableWebDataURL
));
3324 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3326 // We should remember if the pending entry will replace the current one.
3327 // http://crbug.com/308444.
3328 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3329 controller
.GetPendingEntry()->set_should_replace_entry(true);
3331 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3332 main_test_rfh()->PrepareForCommit();
3333 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3334 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
3335 main_test_rfh()->SendNavigate(0, 0, false, url2
);
3336 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3339 // Tests that the URLs for renderer-initiated navigations are not displayed to
3340 // the user until the navigation commits, to prevent URL spoof attacks.
3341 // See http://crbug.com/99016.
3342 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
3343 NavigationControllerImpl
& controller
= controller_impl();
3344 TestNotificationTracker notifications
;
3345 RegisterForAllNavNotifications(¬ifications
, &controller
);
3347 const GURL
url0("http://foo/0");
3348 const GURL
url1("http://foo/1");
3350 // For typed navigations (browser-initiated), both pending and visible entries
3351 // should update before commit.
3353 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3354 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3355 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
3356 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3357 main_test_rfh()->PrepareForCommit();
3358 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3360 // For link clicks (renderer-initiated navigations), the pending entry should
3361 // update before commit but the visible should not.
3362 NavigationController::LoadURLParams
load_url_params(url1
);
3363 load_url_params
.is_renderer_initiated
= true;
3364 controller
.LoadURLWithParams(load_url_params
);
3365 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3366 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3367 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3368 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3370 // After commit, both visible should be updated, there should be no pending
3371 // entry, and we should no longer treat the entry as renderer-initiated.
3372 main_test_rfh()->PrepareForCommit();
3373 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3374 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3375 EXPECT_FALSE(controller
.GetPendingEntry());
3376 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3378 notifications
.Reset();
3381 // Tests that the URLs for renderer-initiated navigations in new tabs are
3382 // displayed to the user before commit, as long as the initial about:blank
3383 // page has not been modified. If so, we must revert to showing about:blank.
3384 // See http://crbug.com/9682.
3385 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3386 NavigationControllerImpl
& controller
= controller_impl();
3387 TestNotificationTracker notifications
;
3388 RegisterForAllNavNotifications(¬ifications
, &controller
);
3390 const GURL
url("http://foo");
3392 // For renderer-initiated navigations in new tabs (with no committed entries),
3393 // we show the pending entry's URL as long as the about:blank page is not
3395 NavigationController::LoadURLParams
load_url_params(url
);
3396 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3397 load_url_params
.is_renderer_initiated
= true;
3398 controller
.LoadURLWithParams(load_url_params
);
3399 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3400 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3401 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3402 EXPECT_TRUE(controller
.IsInitialNavigation());
3403 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3405 // There should be no title yet.
3406 EXPECT_TRUE(contents()->GetTitle().empty());
3408 // If something else modifies the contents of the about:blank page, then
3409 // we must revert to showing about:blank to avoid a URL spoof.
3410 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3411 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3412 EXPECT_FALSE(controller
.GetVisibleEntry());
3413 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3415 notifications
.Reset();
3418 // Tests that the URLs for browser-initiated navigations in new tabs are
3419 // displayed to the user even after they fail, as long as the initial
3420 // about:blank page has not been modified. If so, we must revert to showing
3421 // about:blank. See http://crbug.com/355537.
3422 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3423 NavigationControllerImpl
& controller
= controller_impl();
3424 TestNotificationTracker notifications
;
3425 RegisterForAllNavNotifications(¬ifications
, &controller
);
3427 const GURL
url("http://foo");
3429 // For browser-initiated navigations in new tabs (with no committed entries),
3430 // we show the pending entry's URL as long as the about:blank page is not
3431 // modified. This is possible in cases that the user types a URL into a popup
3432 // tab created with a slow URL.
3433 NavigationController::LoadURLParams
load_url_params(url
);
3434 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3435 load_url_params
.is_renderer_initiated
= false;
3436 controller
.LoadURLWithParams(load_url_params
);
3437 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3438 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3439 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3440 EXPECT_TRUE(controller
.IsInitialNavigation());
3441 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3443 // There should be no title yet.
3444 EXPECT_TRUE(contents()->GetTitle().empty());
3446 // Suppose it aborts before committing, if it's a 204 or download or due to a
3447 // stop or a new navigation from the user. The URL should remain visible.
3448 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3449 params
.error_code
= net::ERR_ABORTED
;
3450 params
.error_description
= base::string16();
3452 params
.showing_repost_interstitial
= false;
3453 main_test_rfh()->OnMessageReceived(
3454 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3455 contents()->SetIsLoading(false, true, NULL
);
3456 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3458 // If something else later modifies the contents of the about:blank page, then
3459 // we must revert to showing about:blank to avoid a URL spoof.
3460 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3461 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3462 EXPECT_FALSE(controller
.GetVisibleEntry());
3463 EXPECT_FALSE(controller
.GetPendingEntry());
3465 notifications
.Reset();
3468 // Tests that the URLs for renderer-initiated navigations in new tabs are
3469 // displayed to the user even after they fail, as long as the initial
3470 // about:blank page has not been modified. If so, we must revert to showing
3471 // about:blank. See http://crbug.com/355537.
3472 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3473 NavigationControllerImpl
& controller
= controller_impl();
3474 TestNotificationTracker notifications
;
3475 RegisterForAllNavNotifications(¬ifications
, &controller
);
3477 const GURL
url("http://foo");
3479 // For renderer-initiated navigations in new tabs (with no committed entries),
3480 // we show the pending entry's URL as long as the about:blank page is not
3482 NavigationController::LoadURLParams
load_url_params(url
);
3483 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3484 load_url_params
.is_renderer_initiated
= true;
3485 controller
.LoadURLWithParams(load_url_params
);
3486 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3487 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3488 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3489 EXPECT_TRUE(controller
.IsInitialNavigation());
3490 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3492 // There should be no title yet.
3493 EXPECT_TRUE(contents()->GetTitle().empty());
3495 // Suppose it aborts before committing, if it's a 204 or download or due to a
3496 // stop or a new navigation from the user. The URL should remain visible.
3497 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3498 params
.error_code
= net::ERR_ABORTED
;
3499 params
.error_description
= base::string16();
3501 params
.showing_repost_interstitial
= false;
3502 main_test_rfh()->OnMessageReceived(
3503 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3504 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3506 // If something else later modifies the contents of the about:blank page, then
3507 // we must revert to showing about:blank to avoid a URL spoof.
3508 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3509 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3510 EXPECT_FALSE(controller
.GetVisibleEntry());
3511 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3513 notifications
.Reset();
3516 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3517 NavigationControllerImpl
& controller
= controller_impl();
3518 TestNotificationTracker notifications
;
3519 RegisterForAllNavNotifications(¬ifications
, &controller
);
3521 const GURL
url1("http://foo/eh");
3522 const GURL
url2("http://foo/bee");
3524 // For renderer-initiated navigations in new tabs (with no committed entries),
3525 // we show the pending entry's URL as long as the about:blank page is not
3527 NavigationController::LoadURLParams
load_url_params(url1
);
3528 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3529 load_url_params
.is_renderer_initiated
= true;
3530 controller
.LoadURLWithParams(load_url_params
);
3531 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3532 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3533 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3534 EXPECT_TRUE(controller
.IsInitialNavigation());
3535 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3537 // Simulate a commit and then starting a new pending navigation.
3538 main_test_rfh()->PrepareForCommit();
3539 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3540 NavigationController::LoadURLParams
load_url2_params(url2
);
3541 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3542 load_url2_params
.is_renderer_initiated
= true;
3543 controller
.LoadURLWithParams(load_url2_params
);
3545 // We should not consider this an initial navigation, and thus should
3546 // not show the pending URL.
3547 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3548 EXPECT_FALSE(controller
.IsInitialNavigation());
3549 EXPECT_TRUE(controller
.GetVisibleEntry());
3550 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3552 notifications
.Reset();
3555 // Tests that IsInPageNavigation returns appropriate results. Prevents
3556 // regression for bug 1126349.
3557 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3558 NavigationControllerImpl
& controller
= controller_impl();
3559 const GURL
url("http://www.google.com/home.html");
3561 // If the renderer claims it performed an in-page navigation from
3562 // about:blank, trust the renderer.
3563 // This can happen when an iframe is created and populated via
3564 // document.write(), then tries to perform a fragment navigation.
3565 // TODO(japhet): We should only trust the renderer if the about:blank
3566 // was the first document in the given frame, but we don't have enough
3567 // information to identify that case currently.
3568 const GURL
blank_url(url::kAboutBlankURL
);
3569 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url
);
3570 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3573 // Navigate to URL with no refs.
3574 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3576 // Reloading the page is not an in-page navigation.
3577 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false, main_test_rfh()));
3578 const GURL
other_url("http://www.google.com/add.html");
3579 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3581 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3582 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3585 // Navigate to URL with refs.
3586 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref
);
3588 // Reloading the page is not an in-page navigation.
3589 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3591 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3593 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3595 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3596 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3599 // Going to the same url again will be considered in-page
3600 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3601 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3604 // Going back to the non ref url will be considered in-page if the navigation
3606 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3609 // If the renderer says this is a same-origin in-page navigation, believe it.
3610 // This is the pushState/replaceState case.
3611 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3614 // Test allow_universal_access_from_file_urls flag.
3615 const GURL
different_origin_url("http://www.example.com");
3616 MockRenderProcessHost
* rph
= main_test_rfh()->GetProcess();
3617 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3618 prefs
.allow_universal_access_from_file_urls
= true;
3619 test_rvh()->UpdateWebkitPreferences(prefs
);
3620 prefs
= test_rvh()->GetWebkitPreferences();
3621 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3622 // Allow in page navigation if existing URL is file scheme.
3623 const GURL
file_url("file:///foo/index.html");
3624 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url
);
3625 EXPECT_EQ(0, rph
->bad_msg_count());
3626 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3628 EXPECT_EQ(0, rph
->bad_msg_count());
3629 // Don't honor allow_universal_access_from_file_urls if existing URL is
3631 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3632 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3634 EXPECT_EQ(1, rph
->bad_msg_count());
3636 // Remove allow_universal_access_from_file_urls flag.
3637 prefs
.allow_universal_access_from_file_urls
= false;
3638 test_rvh()->UpdateWebkitPreferences(prefs
);
3639 prefs
= test_rvh()->GetWebkitPreferences();
3640 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3642 // Don't believe the renderer if it claims a cross-origin navigation is
3644 EXPECT_EQ(1, rph
->bad_msg_count());
3645 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3647 EXPECT_EQ(2, rph
->bad_msg_count());
3650 // Some pages can have subframes with the same base URL (minus the reference) as
3651 // the main page. Even though this is hard, it can happen, and we don't want
3652 // these subframe navigations to affect the toplevel document. They should
3653 // instead be ignored. http://crbug.com/5585
3654 TEST_F(NavigationControllerTest
, SameSubframe
) {
3655 NavigationControllerImpl
& controller
= controller_impl();
3656 // Navigate the main frame.
3657 const GURL
url("http://www.google.com/");
3658 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
3660 // We should be at the first navigation entry.
3661 EXPECT_EQ(controller
.GetEntryCount(), 1);
3662 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3664 // Add and navigate a subframe that would normally count as in-page.
3665 main_test_rfh()->OnCreateChildFrame(
3666 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3667 blink::WebSandboxFlags::None
);
3668 RenderFrameHostImpl
* subframe
=
3669 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3670 const GURL
subframe_url("http://www.google.com/#");
3671 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3673 params
.nav_entry_id
= 0;
3674 params
.did_create_new_entry
= false;
3675 params
.url
= subframe_url
;
3676 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3677 params
.should_update_history
= false;
3678 params
.gesture
= NavigationGestureAuto
;
3679 params
.is_post
= false;
3680 params
.page_state
= PageState::CreateFromURL(subframe_url
);
3681 LoadCommittedDetails details
;
3682 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3684 // Nothing should have changed.
3685 EXPECT_EQ(controller
.GetEntryCount(), 1);
3686 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3689 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3691 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3692 NavigationControllerImpl
& controller
= controller_impl();
3693 const GURL
url1("http://foo1");
3694 const GURL
url2("http://foo2");
3695 const base::string16
title(base::ASCIIToUTF16("Title"));
3697 NavigateAndCommit(url1
);
3698 controller
.GetVisibleEntry()->SetTitle(title
);
3699 NavigateAndCommit(url2
);
3701 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3703 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3704 EXPECT_TRUE(clone
->GetController().NeedsReload());
3705 clone
->GetController().GoBack();
3706 // Navigating back should have triggered needs_reload_ to go false.
3707 EXPECT_FALSE(clone
->GetController().NeedsReload());
3709 // Ensure that the pending URL and its title are visible.
3710 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3711 EXPECT_EQ(title
, clone
->GetTitle());
3714 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3715 // See http://crbug.com/234491.
3716 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3717 NavigationControllerImpl
& controller
= controller_impl();
3718 const GURL
url1("http://foo1");
3719 const GURL
url2("http://foo2");
3720 const base::string16
title(base::ASCIIToUTF16("Title"));
3722 NavigateAndCommit(url1
);
3723 controller
.GetVisibleEntry()->SetTitle(title
);
3724 NavigateAndCommit(url2
);
3726 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3727 clone
->GetController().LoadIfNecessary();
3729 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3730 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3732 clone
->GetController().Reload(true);
3733 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3736 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3737 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3738 NavigationControllerImpl
& controller
= controller_impl();
3739 const GURL
url1("http://foo1");
3740 const GURL
url2("http://foo2");
3742 NavigateAndCommit(url1
);
3743 NavigateAndCommit(url2
);
3745 // Add an interstitial entry. Should be deleted with controller.
3746 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3747 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3748 controller
.SetTransientEntry(make_scoped_ptr(interstitial_entry
));
3750 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3752 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3755 // Test requesting and triggering a lazy reload.
3756 TEST_F(NavigationControllerTest
, LazyReload
) {
3757 NavigationControllerImpl
& controller
= controller_impl();
3758 const GURL
url("http://foo");
3759 NavigateAndCommit(url
);
3760 ASSERT_FALSE(controller
.NeedsReload());
3761 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3762 controller
.GetLastCommittedEntry()->GetTransitionType());
3764 // Request a reload to happen when the controller becomes active (e.g. after
3765 // the renderer gets killed in background on Android).
3766 controller
.SetNeedsReload();
3767 ASSERT_TRUE(controller
.NeedsReload());
3768 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3769 controller
.GetLastCommittedEntry()->GetTransitionType());
3771 // Set the controller as active, triggering the requested reload.
3772 controller
.SetActive(true);
3773 ASSERT_FALSE(controller
.NeedsReload());
3774 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3775 controller
.GetPendingEntry()->GetTransitionType());
3778 // Test requesting and triggering a lazy reload without any committed entry.
3779 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3780 NavigationControllerImpl
& controller
= controller_impl();
3781 const GURL
url("http://foo");
3782 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3783 ASSERT_FALSE(controller
.NeedsReload());
3784 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3785 controller
.GetPendingEntry()->GetTransitionType());
3787 // Request a reload to happen when the controller becomes active (e.g. after
3788 // the renderer gets killed in background on Android).
3789 controller
.SetNeedsReload();
3790 ASSERT_TRUE(controller
.NeedsReload());
3791 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3792 controller
.GetPendingEntry()->GetTransitionType());
3794 // Set the controller as active, triggering the requested reload.
3795 controller
.SetActive(true);
3796 ASSERT_FALSE(controller
.NeedsReload());
3797 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3798 controller
.GetPendingEntry()->GetTransitionType());
3801 // Tests a subframe navigation while a toplevel navigation is pending.
3802 // http://crbug.com/43967
3803 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3804 NavigationControllerImpl
& controller
= controller_impl();
3805 // Load the first page.
3806 const GURL
url1("http://foo/");
3807 NavigateAndCommit(url1
);
3809 // Now start a pending load to a totally different page, but don't commit it.
3810 const GURL
url2("http://bar/");
3812 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3814 // Send a subframe update from the first page, as if one had just
3815 // automatically loaded. Auto subframes don't increment the page ID.
3816 main_test_rfh()->OnCreateChildFrame(
3817 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3818 blink::WebSandboxFlags::None
);
3819 RenderFrameHostImpl
* subframe
=
3820 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3821 const GURL
url1_sub("http://foo/subframe");
3822 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3823 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3824 params
.nav_entry_id
= 0;
3825 params
.did_create_new_entry
= false;
3826 params
.url
= url1_sub
;
3827 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3828 params
.should_update_history
= false;
3829 params
.gesture
= NavigationGestureAuto
;
3830 params
.is_post
= false;
3831 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3832 LoadCommittedDetails details
;
3834 // This should return false meaning that nothing was actually updated.
3835 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3837 // The notification should have updated the last committed one, and not
3838 // the pending load.
3839 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3841 // The active entry should be unchanged by the subframe load.
3842 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3845 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3846 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3847 NavigationControllerImpl
& controller
= controller_impl();
3848 const GURL
url1("http://foo1");
3849 const GURL
url2("http://foo2");
3851 NavigateAndCommit(url1
);
3852 NavigateAndCommit(url2
);
3853 controller
.GoBack();
3854 contents()->CommitPendingNavigation();
3856 scoped_ptr
<TestWebContents
> other_contents(
3857 static_cast<TestWebContents
*>(CreateTestWebContents()));
3858 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3859 other_controller
.CopyStateFrom(controller
);
3861 // other_controller should now contain 2 urls.
3862 ASSERT_EQ(2, other_controller
.GetEntryCount());
3863 // We should be looking at the first one.
3864 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3866 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3867 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3868 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3869 // This is a different site than url1, so the IDs start again at 0.
3870 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3872 // The max page ID map should be copied over and updated with the max page ID
3873 // from the current tab.
3874 SiteInstance
* instance1
=
3875 other_controller
.GetEntryAtIndex(0)->site_instance();
3876 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3878 // Ensure the SessionStorageNamespaceMaps are the same size and have
3879 // the same partitons loaded.
3881 // TODO(ajwong): We should load a url from a different partition earlier
3882 // to make sure this map has more than one entry.
3883 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3884 controller
.GetSessionStorageNamespaceMap();
3885 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3886 other_controller
.GetSessionStorageNamespaceMap();
3887 EXPECT_EQ(session_storage_namespace_map
.size(),
3888 other_session_storage_namespace_map
.size());
3889 for (SessionStorageNamespaceMap::const_iterator it
=
3890 session_storage_namespace_map
.begin();
3891 it
!= session_storage_namespace_map
.end();
3893 SessionStorageNamespaceMap::const_iterator other
=
3894 other_session_storage_namespace_map
.find(it
->first
);
3895 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3899 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3900 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3901 NavigationControllerImpl
& controller
= controller_impl();
3902 const GURL
url1("http://foo/1");
3903 const GURL
url2("http://foo/2");
3904 const GURL
url3("http://foo/3");
3906 NavigateAndCommit(url1
);
3907 NavigateAndCommit(url2
);
3909 // First two entries should have the same SiteInstance.
3910 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3911 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3912 EXPECT_EQ(instance1
, instance2
);
3913 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3914 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3915 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3917 scoped_ptr
<TestWebContents
> other_contents(
3918 static_cast<TestWebContents
*>(CreateTestWebContents()));
3919 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3920 other_contents
->NavigateAndCommit(url3
);
3921 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3922 other_controller
.CopyStateFromAndPrune(&controller
, false);
3924 // other_controller should now contain the 3 urls: url1, url2 and url3.
3926 ASSERT_EQ(3, other_controller
.GetEntryCount());
3928 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3930 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3931 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3932 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3933 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3934 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3935 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3937 // A new SiteInstance in a different BrowsingInstance should be used for the
3939 SiteInstance
* instance3
=
3940 other_controller
.GetEntryAtIndex(2)->site_instance();
3941 EXPECT_NE(instance3
, instance1
);
3942 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3944 // The max page ID map should be copied over and updated with the max page ID
3945 // from the current tab.
3946 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3947 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3950 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3952 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3953 NavigationControllerImpl
& controller
= controller_impl();
3954 const GURL
url1("http://foo1");
3955 const GURL
url2("http://foo2");
3956 const GURL
url3("http://foo3");
3958 NavigateAndCommit(url1
);
3959 NavigateAndCommit(url2
);
3960 controller
.GoBack();
3961 contents()->CommitPendingNavigation();
3963 scoped_ptr
<TestWebContents
> other_contents(
3964 static_cast<TestWebContents
*>(CreateTestWebContents()));
3965 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3966 other_contents
->NavigateAndCommit(url3
);
3967 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3968 other_controller
.CopyStateFromAndPrune(&controller
, false);
3970 // other_controller should now contain: url1, url3
3972 ASSERT_EQ(2, other_controller
.GetEntryCount());
3973 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3975 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3976 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3977 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3979 // The max page ID map should be copied over and updated with the max page ID
3980 // from the current tab.
3981 SiteInstance
* instance1
=
3982 other_controller
.GetEntryAtIndex(1)->site_instance();
3983 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3986 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3988 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3989 NavigationControllerImpl
& controller
= controller_impl();
3990 const GURL
url1("http://foo1");
3991 const GURL
url2("http://foo2");
3992 const GURL
url3("http://foo3");
3993 const GURL
url4("http://foo4");
3995 NavigateAndCommit(url1
);
3996 NavigateAndCommit(url2
);
3998 scoped_ptr
<TestWebContents
> other_contents(
3999 static_cast<TestWebContents
*>(CreateTestWebContents()));
4000 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4001 other_contents
->NavigateAndCommit(url3
);
4002 other_contents
->NavigateAndCommit(url4
);
4003 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4004 other_controller
.CopyStateFromAndPrune(&controller
, false);
4006 // other_controller should now contain: url1, url2, url4
4008 ASSERT_EQ(3, other_controller
.GetEntryCount());
4009 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4011 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4012 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4013 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4015 // The max page ID map should be copied over and updated with the max page ID
4016 // from the current tab.
4017 SiteInstance
* instance1
=
4018 other_controller
.GetEntryAtIndex(2)->site_instance();
4019 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4022 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
4023 // not the last entry selected in the target.
4024 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
4025 NavigationControllerImpl
& controller
= controller_impl();
4026 const GURL
url1("http://foo1");
4027 const GURL
url2("http://foo2");
4028 const GURL
url3("http://foo3");
4029 const GURL
url4("http://foo4");
4031 NavigateAndCommit(url1
);
4032 NavigateAndCommit(url2
);
4034 scoped_ptr
<TestWebContents
> other_contents(
4035 static_cast<TestWebContents
*>(CreateTestWebContents()));
4036 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4037 other_contents
->NavigateAndCommit(url3
);
4038 other_contents
->NavigateAndCommit(url4
);
4039 other_controller
.GoBack();
4040 other_contents
->CommitPendingNavigation();
4041 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4042 other_controller
.CopyStateFromAndPrune(&controller
, false);
4044 // other_controller should now contain: url1, url2, url3
4046 ASSERT_EQ(3, other_controller
.GetEntryCount());
4047 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4049 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4050 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4051 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4053 // The max page ID map should be copied over and updated with the max page ID
4054 // from the current tab.
4055 SiteInstance
* instance1
=
4056 other_controller
.GetEntryAtIndex(2)->site_instance();
4057 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4060 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
4061 // a pending entry in the target.
4062 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
4063 NavigationControllerImpl
& controller
= controller_impl();
4064 const GURL
url1("http://foo1");
4065 const GURL
url2("http://foo2");
4066 const GURL
url3("http://foo3");
4067 const GURL
url4("http://foo4");
4069 NavigateAndCommit(url1
);
4070 NavigateAndCommit(url2
);
4071 controller
.GoBack();
4072 contents()->CommitPendingNavigation();
4074 scoped_ptr
<TestWebContents
> other_contents(
4075 static_cast<TestWebContents
*>(CreateTestWebContents()));
4076 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4077 other_contents
->NavigateAndCommit(url3
);
4078 other_controller
.LoadURL(
4079 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4080 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4081 other_controller
.CopyStateFromAndPrune(&controller
, false);
4083 // other_controller should now contain url1, url3, and a pending entry
4086 ASSERT_EQ(2, other_controller
.GetEntryCount());
4087 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4089 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4090 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4092 // And there should be a pending entry for url4.
4093 ASSERT_TRUE(other_controller
.GetPendingEntry());
4094 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
4096 // The max page ID map should be copied over and updated with the max page ID
4097 // from the current tab.
4098 SiteInstance
* instance1
=
4099 other_controller
.GetEntryAtIndex(0)->site_instance();
4100 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4103 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
4104 // client redirect entry (with the same page ID) in the target. This used to
4105 // crash because the last committed entry would be pruned but max_page_id
4106 // remembered the page ID (http://crbug.com/234809).
4107 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
4108 NavigationControllerImpl
& controller
= controller_impl();
4109 const GURL
url1("http://foo1");
4110 const GURL
url2a("http://foo2/a");
4111 const GURL
url2b("http://foo2/b");
4113 NavigateAndCommit(url1
);
4115 scoped_ptr
<TestWebContents
> other_contents(
4116 static_cast<TestWebContents
*>(CreateTestWebContents()));
4117 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4118 other_contents
->NavigateAndCommit(url2a
);
4119 // Simulate a client redirect, which has the same page ID as entry 2a.
4120 other_controller
.LoadURL(
4121 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
4122 NavigationEntry
* entry
= other_controller
.GetPendingEntry();
4123 entry
->SetPageID(other_controller
.GetLastCommittedEntry()->GetPageID());
4125 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4126 other_controller
.CopyStateFromAndPrune(&controller
, false);
4128 // other_controller should now contain url1, url2a, and a pending entry
4131 ASSERT_EQ(2, other_controller
.GetEntryCount());
4132 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4134 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4135 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
4137 // And there should be a pending entry for url4.
4138 ASSERT_TRUE(other_controller
.GetPendingEntry());
4139 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
4141 // Let the pending entry commit.
4142 other_contents
->TestDidNavigate(other_contents
->GetMainFrame(),
4143 entry
->GetPageID(), 0, false, url2b
,
4144 ui::PAGE_TRANSITION_LINK
);
4146 // The max page ID map should be copied over and updated with the max page ID
4147 // from the current tab.
4148 SiteInstance
* instance1
=
4149 other_controller
.GetEntryAtIndex(1)->site_instance();
4150 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4153 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4154 // source, and 1 entry in the target. The back pending entry should be ignored.
4155 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
4156 NavigationControllerImpl
& controller
= controller_impl();
4157 const GURL
url1("http://foo1");
4158 const GURL
url2("http://foo2");
4159 const GURL
url3("http://foo3");
4161 NavigateAndCommit(url1
);
4162 NavigateAndCommit(url2
);
4163 controller
.GoBack();
4165 scoped_ptr
<TestWebContents
> other_contents(
4166 static_cast<TestWebContents
*>(CreateTestWebContents()));
4167 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4168 other_contents
->NavigateAndCommit(url3
);
4169 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4170 other_controller
.CopyStateFromAndPrune(&controller
, false);
4172 // other_controller should now contain: url1, url2, url3
4174 ASSERT_EQ(3, other_controller
.GetEntryCount());
4175 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4177 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4178 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4179 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4180 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4182 // The max page ID map should be copied over and updated with the max page ID
4183 // from the current tab.
4184 SiteInstance
* instance1
=
4185 other_controller
.GetEntryAtIndex(2)->site_instance();
4186 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4189 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4190 // when the max entry count is 3. We should prune one entry.
4191 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
4192 NavigationControllerImpl
& controller
= controller_impl();
4193 size_t original_count
= NavigationControllerImpl::max_entry_count();
4194 const int kMaxEntryCount
= 3;
4196 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4198 const GURL
url1("http://foo/1");
4199 const GURL
url2("http://foo/2");
4200 const GURL
url3("http://foo/3");
4201 const GURL
url4("http://foo/4");
4203 // Create a PrunedListener to observe prune notifications.
4204 PrunedListener
listener(&controller
);
4206 NavigateAndCommit(url1
);
4207 NavigateAndCommit(url2
);
4208 NavigateAndCommit(url3
);
4210 scoped_ptr
<TestWebContents
> other_contents(
4211 static_cast<TestWebContents
*>(CreateTestWebContents()));
4212 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4213 other_contents
->NavigateAndCommit(url4
);
4214 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4215 other_controller
.CopyStateFromAndPrune(&controller
, false);
4217 // We should have received a pruned notification.
4218 EXPECT_EQ(1, listener
.notification_count_
);
4219 EXPECT_TRUE(listener
.details_
.from_front
);
4220 EXPECT_EQ(1, listener
.details_
.count
);
4222 // other_controller should now contain only 3 urls: url2, url3 and url4.
4224 ASSERT_EQ(3, other_controller
.GetEntryCount());
4226 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4228 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
4229 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4230 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4231 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
4232 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
4233 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4235 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4238 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4239 // replace_entry set to true.
4240 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
4241 NavigationControllerImpl
& controller
= controller_impl();
4242 const GURL
url1("http://foo/1");
4243 const GURL
url2("http://foo/2");
4244 const GURL
url3("http://foo/3");
4246 NavigateAndCommit(url1
);
4247 NavigateAndCommit(url2
);
4249 // First two entries should have the same SiteInstance.
4250 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
4251 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
4252 EXPECT_EQ(instance1
, instance2
);
4253 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
4254 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
4255 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
4257 scoped_ptr
<TestWebContents
> other_contents(
4258 static_cast<TestWebContents
*>(CreateTestWebContents()));
4259 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4260 other_contents
->NavigateAndCommit(url3
);
4261 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4262 other_controller
.CopyStateFromAndPrune(&controller
, true);
4264 // other_controller should now contain the 2 urls: url1 and url3.
4266 ASSERT_EQ(2, other_controller
.GetEntryCount());
4268 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
4270 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4271 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4272 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4273 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
4275 // A new SiteInstance in a different BrowsingInstance should be used for the
4277 SiteInstance
* instance3
=
4278 other_controller
.GetEntryAtIndex(1)->site_instance();
4279 EXPECT_NE(instance3
, instance1
);
4280 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
4282 // The max page ID map should be copied over and updated with the max page ID
4283 // from the current tab.
4284 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4285 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
4288 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4289 // entry count is 3 and replace_entry is true. We should not prune entries.
4290 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
4291 NavigationControllerImpl
& controller
= controller_impl();
4292 size_t original_count
= NavigationControllerImpl::max_entry_count();
4293 const int kMaxEntryCount
= 3;
4295 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4297 const GURL
url1("http://foo/1");
4298 const GURL
url2("http://foo/2");
4299 const GURL
url3("http://foo/3");
4300 const GURL
url4("http://foo/4");
4302 // Create a PrunedListener to observe prune notifications.
4303 PrunedListener
listener(&controller
);
4305 NavigateAndCommit(url1
);
4306 NavigateAndCommit(url2
);
4307 NavigateAndCommit(url3
);
4309 scoped_ptr
<TestWebContents
> other_contents(
4310 static_cast<TestWebContents
*>(CreateTestWebContents()));
4311 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4312 other_contents
->NavigateAndCommit(url4
);
4313 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4314 other_controller
.CopyStateFromAndPrune(&controller
, true);
4316 // We should have received no pruned notification.
4317 EXPECT_EQ(0, listener
.notification_count_
);
4319 // other_controller should now contain only 3 urls: url1, url2 and url4.
4321 ASSERT_EQ(3, other_controller
.GetEntryCount());
4323 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4325 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4326 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4327 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4328 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4329 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
4330 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4332 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4335 // Tests that we can navigate to the restored entries
4336 // imported by CopyStateFromAndPrune.
4337 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
4338 const GURL kRestoredUrls
[] = {
4339 GURL("http://site1.com"),
4340 GURL("http://site2.com"),
4342 const GURL
kInitialUrl("http://site3.com");
4344 ScopedVector
<NavigationEntry
> entries
;
4345 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
4346 scoped_ptr
<NavigationEntry
> entry
=
4347 NavigationControllerImpl::CreateNavigationEntry(
4348 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
4349 std::string(), browser_context());
4350 entry
->SetPageID(static_cast<int>(i
));
4351 entries
.push_back(entry
.Pass());
4354 // Create a WebContents with restored entries.
4355 scoped_ptr
<TestWebContents
> source_contents(
4356 static_cast<TestWebContents
*>(CreateTestWebContents()));
4357 NavigationControllerImpl
& source_controller
=
4358 source_contents
->GetController();
4359 source_controller
.Restore(
4361 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
4363 ASSERT_EQ(0u, entries
.size());
4364 source_controller
.LoadIfNecessary();
4365 source_contents
->CommitPendingNavigation();
4367 // Load a page, then copy state from |source_contents|.
4368 NavigateAndCommit(kInitialUrl
);
4369 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4370 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
4371 ASSERT_EQ(3, controller_impl().GetEntryCount());
4373 // Go back to the first entry one at a time and
4374 // verify that it works as expected.
4375 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4376 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
4378 controller_impl().GoBack();
4379 contents()->CommitPendingNavigation();
4380 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4381 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
4383 controller_impl().GoBack();
4384 contents()->CommitPendingNavigation();
4385 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4386 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
4389 // Tests that navigations initiated from the page (with the history object)
4390 // work as expected, creating pending entries.
4391 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
4392 NavigationControllerImpl
& controller
= controller_impl();
4393 const GURL
url1("http://foo/1");
4394 const GURL
url2("http://foo/2");
4395 const GURL
url3("http://foo/3");
4397 NavigateAndCommit(url1
);
4398 NavigateAndCommit(url2
);
4399 NavigateAndCommit(url3
);
4400 controller
.GoBack();
4401 contents()->CommitPendingNavigation();
4402 process()->sink().ClearMessages();
4404 // Simulate the page calling history.back(). It should create a pending entry.
4405 contents()->OnGoToEntryAtOffset(-1);
4406 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4407 // The actual cross-navigation is suspended until the current RVH tells us
4408 // it unloaded, simulate that.
4409 contents()->ProceedWithCrossSiteNavigation();
4410 // Also make sure we told the page to navigate.
4411 GURL nav_url
= GetLastNavigationURL();
4412 EXPECT_EQ(url1
, nav_url
);
4413 contents()->CommitPendingNavigation();
4414 process()->sink().ClearMessages();
4416 // Now test history.forward()
4417 contents()->OnGoToEntryAtOffset(2);
4418 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4419 // The actual cross-navigation is suspended until the current RVH tells us
4420 // it unloaded, simulate that.
4421 contents()->ProceedWithCrossSiteNavigation();
4422 nav_url
= GetLastNavigationURL();
4423 EXPECT_EQ(url3
, nav_url
);
4424 contents()->CommitPendingNavigation();
4425 process()->sink().ClearMessages();
4427 controller
.DiscardNonCommittedEntries();
4429 // Make sure an extravagant history.go() doesn't break.
4430 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4431 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4432 EXPECT_FALSE(HasNavigationRequest());
4435 // Test call to PruneAllButLastCommitted for the only entry.
4436 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4437 NavigationControllerImpl
& controller
= controller_impl();
4438 const GURL
url1("http://foo1");
4439 NavigateAndCommit(url1
);
4441 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4443 controller
.PruneAllButLastCommitted();
4445 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4446 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4449 // Test call to PruneAllButLastCommitted for first entry.
4450 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4451 NavigationControllerImpl
& controller
= controller_impl();
4452 const GURL
url1("http://foo/1");
4453 const GURL
url2("http://foo/2");
4454 const GURL
url3("http://foo/3");
4456 NavigateAndCommit(url1
);
4457 NavigateAndCommit(url2
);
4458 NavigateAndCommit(url3
);
4459 controller
.GoBack();
4460 controller
.GoBack();
4461 contents()->CommitPendingNavigation();
4463 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4465 controller
.PruneAllButLastCommitted();
4467 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4468 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4471 // Test call to PruneAllButLastCommitted for intermediate entry.
4472 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4473 NavigationControllerImpl
& controller
= controller_impl();
4474 const GURL
url1("http://foo/1");
4475 const GURL
url2("http://foo/2");
4476 const GURL
url3("http://foo/3");
4478 NavigateAndCommit(url1
);
4479 NavigateAndCommit(url2
);
4480 NavigateAndCommit(url3
);
4481 controller
.GoBack();
4482 contents()->CommitPendingNavigation();
4484 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4486 controller
.PruneAllButLastCommitted();
4488 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4489 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4492 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4493 // the list of entries.
4494 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4495 NavigationControllerImpl
& controller
= controller_impl();
4496 const GURL
url1("http://foo/1");
4497 const GURL
url2("http://foo/2");
4498 const GURL
url3("http://foo/3");
4500 NavigateAndCommit(url1
);
4501 NavigateAndCommit(url2
);
4503 // Create a pending entry that is not in the entry list.
4505 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4506 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
4507 EXPECT_TRUE(controller
.GetPendingEntry());
4508 EXPECT_EQ(2, controller
.GetEntryCount());
4510 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4511 controller
.PruneAllButLastCommitted();
4513 // We should only have the last committed and pending entries at this point,
4514 // and the pending entry should still not be in the entry list.
4515 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4516 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4517 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4518 EXPECT_TRUE(controller
.GetPendingEntry());
4519 EXPECT_EQ(1, controller
.GetEntryCount());
4521 // Try to commit the pending entry.
4522 main_test_rfh()->PrepareForCommit();
4523 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
4524 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4525 EXPECT_FALSE(controller
.GetPendingEntry());
4526 EXPECT_EQ(2, controller
.GetEntryCount());
4527 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4530 // Test to ensure that when we do a history navigation back to the current
4531 // committed page (e.g., going forward to a slow-loading page, then pressing
4532 // the back button), we just stop the navigation to prevent the throbber from
4533 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4534 // start, but WebKit essentially ignores the navigation and never sends a
4535 // message to stop the throbber.
4536 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4537 NavigationControllerImpl
& controller
= controller_impl();
4538 const GURL
url0("http://foo/0");
4539 const GURL
url1("http://foo/1");
4541 NavigateAndCommit(url0
);
4542 NavigateAndCommit(url1
);
4544 // Go back to the original page, then forward to the slow page, then back
4545 controller
.GoBack();
4546 contents()->CommitPendingNavigation();
4548 controller
.GoForward();
4549 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4551 controller
.GoBack();
4552 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4555 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4556 NavigationControllerImpl
& controller
= controller_impl();
4557 TestNotificationTracker notifications
;
4558 RegisterForAllNavNotifications(¬ifications
, &controller
);
4561 EXPECT_TRUE(controller
.IsInitialNavigation());
4563 // After commit, it stays false.
4564 const GURL
url1("http://foo1");
4565 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
4566 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4567 navigation_entry_committed_counter_
= 0;
4568 EXPECT_FALSE(controller
.IsInitialNavigation());
4570 // After starting a new navigation, it stays false.
4571 const GURL
url2("http://foo2");
4573 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4576 // Check that the favicon is not reused across a client redirect.
4577 // (crbug.com/28515)
4578 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4579 const GURL
kPageWithFavicon("http://withfavicon.html");
4580 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4581 const GURL
kIconURL("http://withfavicon.ico");
4582 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4584 NavigationControllerImpl
& controller
= controller_impl();
4585 TestNotificationTracker notifications
;
4586 RegisterForAllNavNotifications(¬ifications
, &controller
);
4588 main_test_rfh()->NavigateAndCommitRendererInitiated(
4589 0, true, kPageWithFavicon
);
4590 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4591 navigation_entry_committed_counter_
= 0;
4593 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4595 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4597 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4598 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4599 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4600 favicon_status
.url
= kIconURL
;
4601 favicon_status
.valid
= true;
4602 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4604 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon
,
4606 main_test_rfh()->PrepareForCommit();
4607 main_test_rfh()->SendNavigateWithTransition(
4610 false, // no new entry
4611 kPageWithoutFavicon
, ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4612 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4613 navigation_entry_committed_counter_
= 0;
4615 entry
= controller
.GetLastCommittedEntry();
4617 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4619 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4622 // Check that the favicon is not cleared for NavigationEntries which were
4623 // previously navigated to.
4624 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4625 const GURL
kUrl1("http://www.a.com/1");
4626 const GURL
kUrl2("http://www.a.com/2");
4627 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4629 NavigationControllerImpl
& controller
= controller_impl();
4630 TestNotificationTracker notifications
;
4631 RegisterForAllNavNotifications(¬ifications
, &controller
);
4633 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
4634 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4635 navigation_entry_committed_counter_
= 0;
4637 // Simulate Chromium having set the favicon for |kUrl1|.
4638 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4639 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4641 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4642 favicon_status
.image
= favicon_image
;
4643 favicon_status
.url
= kIconURL
;
4644 favicon_status
.valid
= true;
4646 // Navigate to another page and go back to the original page.
4647 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
4648 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4649 navigation_entry_committed_counter_
= 0;
4650 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, false);
4651 main_test_rfh()->PrepareForCommit();
4652 main_test_rfh()->SendNavigateWithTransition(
4653 0, controller
.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1
,
4654 ui::PAGE_TRANSITION_FORWARD_BACK
);
4655 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4656 navigation_entry_committed_counter_
= 0;
4658 // Verify that the favicon for the page at |kUrl1| was not cleared.
4659 entry
= controller
.GetEntryAtIndex(0);
4661 EXPECT_EQ(kUrl1
, entry
->GetURL());
4662 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4665 // The test crashes on android: http://crbug.com/170449
4666 #if defined(OS_ANDROID)
4667 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4669 #define MAYBE_PurgeScreenshot PurgeScreenshot
4671 // Tests that screenshot are purged correctly.
4672 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4673 NavigationControllerImpl
& controller
= controller_impl();
4675 NavigationEntryImpl
* entry
;
4677 // Navigate enough times to make sure that some screenshots are purged.
4678 for (int i
= 0; i
< 12; ++i
) {
4679 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4680 NavigateAndCommit(url
);
4681 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4684 MockScreenshotManager
* screenshot_manager
=
4685 new MockScreenshotManager(&controller
);
4686 controller
.SetScreenshotManager(make_scoped_ptr(screenshot_manager
));
4687 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4688 entry
= controller
.GetEntryAtIndex(i
);
4689 screenshot_manager
->TakeScreenshotFor(entry
);
4690 EXPECT_TRUE(entry
->screenshot().get());
4693 NavigateAndCommit(GURL("https://foo/"));
4694 EXPECT_EQ(13, controller
.GetEntryCount());
4695 entry
= controller
.GetEntryAtIndex(11);
4696 screenshot_manager
->TakeScreenshotFor(entry
);
4698 for (int i
= 0; i
< 2; ++i
) {
4699 entry
= controller
.GetEntryAtIndex(i
);
4700 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4704 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4705 entry
= controller
.GetEntryAtIndex(i
);
4706 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4709 // Navigate to index 5 and then try to assign screenshot to all entries.
4710 controller
.GoToIndex(5);
4711 contents()->CommitPendingNavigation();
4712 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4713 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4714 entry
= controller
.GetEntryAtIndex(i
);
4715 screenshot_manager
->TakeScreenshotFor(entry
);
4718 for (int i
= 10; i
<= 12; ++i
) {
4719 entry
= controller
.GetEntryAtIndex(i
);
4720 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4722 screenshot_manager
->TakeScreenshotFor(entry
);
4725 // Navigate to index 7 and assign screenshot to all entries.
4726 controller
.GoToIndex(7);
4727 contents()->CommitPendingNavigation();
4728 EXPECT_EQ(7, controller
.GetCurrentEntryIndex());
4729 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4730 entry
= controller
.GetEntryAtIndex(i
);
4731 screenshot_manager
->TakeScreenshotFor(entry
);
4734 for (int i
= 0; i
< 2; ++i
) {
4735 entry
= controller
.GetEntryAtIndex(i
);
4736 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4740 // Clear all screenshots.
4741 EXPECT_EQ(13, controller
.GetEntryCount());
4742 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4743 controller
.ClearAllScreenshots();
4744 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4745 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4746 entry
= controller
.GetEntryAtIndex(i
);
4747 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4752 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4754 main_test_rfh()->NavigateAndCommitRendererInitiated(
4755 1, true, GURL("http://foo"));
4757 // Set title and favicon.
4758 base::string16
title(base::ASCIIToUTF16("Title"));
4759 FaviconStatus favicon
;
4760 favicon
.valid
= true;
4761 favicon
.url
= GURL("http://foo/favicon.ico");
4762 controller().GetLastCommittedEntry()->SetTitle(title
);
4763 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4765 // history.pushState() is called.
4766 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4767 GURL
kUrl2("http://foo#foo");
4769 params
.nav_entry_id
= 0;
4770 params
.did_create_new_entry
= true;
4772 params
.page_state
= PageState::CreateFromURL(kUrl2
);
4773 params
.was_within_same_page
= true;
4774 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, false);
4775 main_test_rfh()->PrepareForCommit();
4776 main_test_rfh()->SendNavigateWithParams(¶ms
);
4778 // The title should immediately be visible on the new NavigationEntry.
4779 base::string16 new_title
=
4780 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4781 EXPECT_EQ(title
, new_title
);
4782 FaviconStatus new_favicon
=
4783 controller().GetLastCommittedEntry()->GetFavicon();
4784 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4785 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4788 // Test that the navigation controller clears its session history when a
4789 // navigation commits with the clear history list flag set.
4790 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4791 const GURL
url1("http://foo1");
4792 const GURL
url2("http://foo2");
4793 const GURL
url3("http://foo3");
4794 const GURL
url4("http://foo4");
4796 NavigationControllerImpl
& controller
= controller_impl();
4798 // Create a session history with three entries, second entry is active.
4799 NavigateAndCommit(url1
);
4800 NavigateAndCommit(url2
);
4801 NavigateAndCommit(url3
);
4802 controller
.GoBack();
4803 contents()->CommitPendingNavigation();
4804 EXPECT_EQ(3, controller
.GetEntryCount());
4805 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4807 // Create a new pending navigation, and indicate that the session history
4808 // should be cleared.
4809 NavigationController::LoadURLParams
params(url4
);
4810 params
.should_clear_history_list
= true;
4811 controller
.LoadURLWithParams(params
);
4813 // Verify that the pending entry correctly indicates that the session history
4814 // should be cleared.
4815 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4817 EXPECT_TRUE(entry
->should_clear_history_list());
4819 // Assume that the RenderFrame correctly cleared its history and commit the
4821 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4822 switches::kEnableBrowserSideNavigation
)) {
4823 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4825 contents()->GetPendingMainFrame()->
4826 set_simulate_history_list_was_cleared(true);
4827 contents()->CommitPendingNavigation();
4829 // Verify that the NavigationController's session history was correctly
4831 EXPECT_EQ(1, controller
.GetEntryCount());
4832 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4833 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4834 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4835 EXPECT_FALSE(controller
.CanGoBack());
4836 EXPECT_FALSE(controller
.CanGoForward());
4837 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4840 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4841 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4842 EXPECT_FALSE(contents()->GetDelegate());
4843 contents()->SetDelegate(delegate
.get());
4846 GURL
url("http://foo");
4847 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4849 params
.nav_entry_id
= 0;
4850 params
.did_create_new_entry
= true;
4852 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4853 params
.gesture
= NavigationGestureUser
;
4854 params
.page_state
= PageState::CreateFromURL(url
);
4855 params
.was_within_same_page
= false;
4856 params
.is_post
= true;
4858 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
4859 main_test_rfh()->PrepareForCommit();
4860 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4862 // history.replaceState() is called.
4863 GURL
replace_url("http://foo#foo");
4865 params
.nav_entry_id
= 0;
4866 params
.did_create_new_entry
= false;
4867 params
.url
= replace_url
;
4868 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4869 params
.gesture
= NavigationGestureUser
;
4870 params
.page_state
= PageState::CreateFromURL(replace_url
);
4871 params
.was_within_same_page
= true;
4872 params
.is_post
= false;
4873 params
.post_id
= -1;
4874 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url
, false);
4875 main_test_rfh()->PrepareForCommit();
4876 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4878 // Now reload. replaceState overrides the POST, so we should not show a
4879 // repost warning dialog.
4880 controller_impl().Reload(true);
4881 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4884 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4885 GURL
url("http://foo");
4886 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4888 params
.nav_entry_id
= 0;
4889 params
.did_create_new_entry
= true;
4891 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4892 params
.gesture
= NavigationGestureUser
;
4893 params
.page_state
= PageState::CreateFromURL(url
);
4894 params
.was_within_same_page
= false;
4895 params
.is_post
= true;
4897 params
.url_is_unreachable
= true;
4899 // Navigate to new page.
4901 LoadCommittedDetails details
;
4902 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4903 EXPECT_EQ(PAGE_TYPE_ERROR
,
4904 controller_impl().GetLastCommittedEntry()->GetPageType());
4905 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4908 // Navigate to existing page.
4910 params
.did_create_new_entry
= false;
4911 LoadCommittedDetails details
;
4912 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4913 EXPECT_EQ(PAGE_TYPE_ERROR
,
4914 controller_impl().GetLastCommittedEntry()->GetPageType());
4915 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4918 // Navigate to same page.
4919 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4920 // same-page transition.
4921 controller_impl().LoadURL(
4922 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4923 params
.nav_entry_id
= controller_impl().GetPendingEntry()->GetUniqueID();
4924 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4926 LoadCommittedDetails details
;
4927 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4928 EXPECT_EQ(PAGE_TYPE_ERROR
,
4929 controller_impl().GetLastCommittedEntry()->GetPageType());
4930 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4933 // Navigate in page.
4934 params
.url
= GURL("http://foo#foo");
4935 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4936 params
.was_within_same_page
= true;
4938 LoadCommittedDetails details
;
4939 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4940 EXPECT_EQ(PAGE_TYPE_ERROR
,
4941 controller_impl().GetLastCommittedEntry()->GetPageType());
4942 EXPECT_TRUE(details
.is_in_page
);
4946 // Tests that if a stale navigation comes back from the renderer, it is properly
4948 TEST_F(NavigationControllerTest
, StaleNavigationsResurrected
) {
4949 NavigationControllerImpl
& controller
= controller_impl();
4950 TestNotificationTracker notifications
;
4951 RegisterForAllNavNotifications(¬ifications
, &controller
);
4954 const GURL
url_a("http://foo.com/a");
4955 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a
);
4956 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4957 navigation_entry_committed_counter_
= 0;
4958 EXPECT_EQ(1, controller
.GetEntryCount());
4959 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4962 const GURL
url_b("http://foo.com/b");
4963 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b
);
4964 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4965 navigation_entry_committed_counter_
= 0;
4966 EXPECT_EQ(2, controller
.GetEntryCount());
4967 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4968 int b_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4969 int b_page_id
= controller
.GetLastCommittedEntry()->GetPageID();
4972 controller
.GoBack();
4973 contents()->CommitPendingNavigation();
4974 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4975 navigation_entry_committed_counter_
= 0;
4976 EXPECT_EQ(2, controller
.GetEntryCount());
4977 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4979 // Start going forward to page B.
4980 controller
.GoForward();
4982 // But the renderer unilaterally navigates to page C, pruning B.
4983 const GURL
url_c("http://foo.com/c");
4984 main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c
);
4985 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4986 navigation_entry_committed_counter_
= 0;
4987 EXPECT_EQ(2, controller
.GetEntryCount());
4988 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4989 int c_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4990 EXPECT_NE(c_entry_id
, b_entry_id
);
4992 // And then the navigation to B gets committed.
4993 main_test_rfh()->SendNavigate(b_page_id
, b_entry_id
, false, url_b
);
4994 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4995 navigation_entry_committed_counter_
= 0;
4997 // Even though we were doing a history navigation, because the entry was
4998 // pruned it will end up as a *new* entry at the end of the entry list. This
4999 // means that occasionally a navigation conflict will end up with one entry
5000 // bubbling to the end of the entry list, but that's the least-bad option.
5001 EXPECT_EQ(3, controller
.GetEntryCount());
5002 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
5003 EXPECT_EQ(url_a
, controller
.GetEntryAtIndex(0)->GetURL());
5004 EXPECT_EQ(url_c
, controller
.GetEntryAtIndex(1)->GetURL());
5005 EXPECT_EQ(url_b
, controller
.GetEntryAtIndex(2)->GetURL());
5008 // Test that if a renderer provides bogus security info (that fails to
5009 // deserialize properly) when reporting a navigation, the renderer gets
5011 TEST_F(NavigationControllerTest
, RendererNavigateBogusSecurityInfo
) {
5012 GURL
url("http://foo.test");
5013 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
5015 params
.nav_entry_id
= 0;
5016 params
.did_create_new_entry
= true;
5018 params
.transition
= ui::PAGE_TRANSITION_LINK
;
5019 params
.should_update_history
= true;
5020 params
.gesture
= NavigationGestureUser
;
5021 params
.is_post
= false;
5022 params
.page_state
= PageState::CreateFromURL(url
);
5023 params
.was_within_same_page
= false;
5024 params
.security_info
= "bogus security info!";
5026 LoadCommittedDetails details
;
5027 EXPECT_EQ(0, main_test_rfh()->GetProcess()->bad_msg_count());
5029 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
));
5031 SSLStatus default_ssl_status
;
5032 EXPECT_EQ(default_ssl_status
.security_style
,
5033 details
.ssl_status
.security_style
);
5034 EXPECT_EQ(default_ssl_status
.cert_id
, details
.ssl_status
.cert_id
);
5035 EXPECT_EQ(default_ssl_status
.cert_status
, details
.ssl_status
.cert_status
);
5036 EXPECT_EQ(default_ssl_status
.security_bits
, details
.ssl_status
.security_bits
);
5037 EXPECT_EQ(default_ssl_status
.connection_status
,
5038 details
.ssl_status
.connection_status
);
5039 EXPECT_EQ(default_ssl_status
.content_status
,
5040 details
.ssl_status
.content_status
);
5041 EXPECT_EQ(0u, details
.ssl_status
.signed_certificate_timestamp_ids
.size());
5043 EXPECT_EQ(1, main_test_rfh()->GetProcess()->bad_msg_count());
5046 } // namespace content